com.wit.android.support.content.intent.ContentIntent.java Source code

Java tutorial

Introduction

Here is the source code for com.wit.android.support.content.intent.ContentIntent.java

Source

/*
 * =================================================================================================
 *                    Copyright (C) 2014 Martin Albedinsky [Wolf-ITechnologies]
 * =================================================================================================
 *         Licensed under the Apache License, Version 2.0 or later (further "License" only).
 * -------------------------------------------------------------------------------------------------
 * You may use this file only in compliance with the License. More details and copy of this License 
 * you may obtain at
 * 
 *       http://www.apache.org/licenses/LICENSE-2.0
 * 
 * You can redistribute, modify or publish any part of the code written within this file but as it 
 * is described in the License, the software distributed under the License is distributed on an 
 * "AS IS" BASIS, WITHOUT WARRANTIES or CONDITIONS OF ANY KIND.
 * 
 * See the License for the specific language governing permissions and limitations under the License.
 * =================================================================================================
 */
package com.wit.android.support.content.intent;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StringDef;
import android.support.v4.app.Fragment;
import android.text.TextUtils;
import android.util.Log;

import com.wit.android.support.content.MimeType;

import java.io.File;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

/**
 * <h3>Class Overview</h3>
 * Intent builder specialized to create CONTENT specific intents. This can be intent to obtain or
 * preview specific type of content, like <b>image, audio, video, ...</b>. To decide which type of
 * intent (obtain/preview) should be started, the current set of {@link ContentIntent.ContentProviderItem}
 * is checked, if it isn't empty the <b>OBTAIN</b> intent will be started, so <b>chooser</b> dialog
 * will be showed with a list item specific for each of current providers. If there are no providers
 * assigned to this intent builder and there was passed valid {@link android.net.Uri} to {@link #input(android.net.Uri)},
 * the <b>PREVIEW</b> intent will be started.
 *
 * @author Martin Albedinsky
 */
public abstract class ContentIntent extends BaseIntent<ContentIntent> {

    /**
     * Interface ===================================================================================
     */

    /**
     * <h3>Annotation Overview</h3>
     * Defines an annotation for determining set of allowed mime types for {@link #dataType(String)}
     * method.
     */
    @Retention(RetentionPolicy.SOURCE)
    @StringDef({ MimeType.TEXT, MimeType.TEXT_PLAIN, MimeType.TEXT_HTML, MimeType.IMAGE, MimeType.IMAGE_JPEG,
            MimeType.IMAGE_PNG, MimeType.IMAGE_BITMAP, MimeType.AUDIO, MimeType.AUDIO_MP3, MimeType.AUDIO_MP4,
            MimeType.AUDIO_MPEG, MimeType.VIDEO, MimeType.VIDEO_MP4, MimeType.VIDEO_MPEG, MimeType.VIDEO_JPEG,
            MimeType.VIDEO_3GP, MimeType.VIDEO_3GP2 })
    public @interface DataType {
    }

    /**
     * Constants ===================================================================================
     */

    /**
     * Log TAG.
     */
    private static final String TAG = "ContentIntent";

    /**
     * Flag indicating whether the output trough log-cat is enabled or not.
     */
    // private static final boolean LOG_ENABLED = ContentConfig.LIBRARY_LOG_ENABLED;

    /**
     * Flag indicating whether the debug output trough log-cat is enabled or not.
     */
    // private static final boolean DEBUG_ENABLED = true;

    /**
     * Name format for files created by this type of intent.
     * <p>
     * Constant Value: <b>yyyyMMdd_HHmmss</b>
     */
    public static final String CONTENT_FILE_TIME_STAMP_FORMAT = "yyyyMMdd_HHmmss";

    /**
     * Static members ==============================================================================
     */

    /**
     * Members =====================================================================================
     */

    /**
     * Uri to content handled by this intent.
     */
    Uri mUri;

    /**
     * Data type of content handled by this intent.
     */
    String mDataType = MimeType.TEXT;

    /**
     * Set of simple content providers, where each of providers should provide an instance of intent
     * to start when that specific provider's item in the chooser dialog was clicked.
     */
    private List<ContentProviderItem> mProviders;

    /**
     * Constructors ================================================================================
     */

    /**
     * Creates a new instance of GetContentIntent for the given <var>activity</var> context.
     * See {@link com.wit.android.support.content.intent.BaseIntent#BaseIntent(android.app.Activity)}
     * for additional info.
     */
    public ContentIntent(@NonNull Activity activity) {
        super(activity);
    }

    /**
     * Creates a new instance of GetContentIntent for the given <var>fragment</var> context.
     * See {@link com.wit.android.support.content.intent.BaseIntent#BaseIntent(android.support.v4.app.Fragment)}
     * for additional info.
     */
    public ContentIntent(@NonNull Fragment fragment) {
        super(fragment);
    }

    /**
     * Methods =====================================================================================
     */

    /**
     * Public --------------------------------------------------------------------------------------
     */

    /**
     */
    @Override
    public boolean start() {
        final Context context = mContextWrapper.getContext();
        if (context == null) {
            return false;
        }

        if (hasProviders()) {
            final int n = mProviders.size();
            // Build and show dialog to allow pick one from content providers.
            final CharSequence[] providerNames = new CharSequence[n];
            for (int i = 0; i < n; i++) {
                providerNames[i] = mProviders.get(i).name;
            }

            final AlertDialog.Builder builder = new AlertDialog.Builder(context);
            builder.setTitle(mDialogTitle);
            builder.setItems(providerNames, new DialogInterface.OnClickListener() {

                /**
                 */
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    final ContentProviderItem provider = mProviders.get(which);
                    mContextWrapper.startIntentForResult(provider.intent, provider.requestCode);
                }
            });
            builder.show();
            return true;
        } else if (mUri != null) {
            if (mContextWrapper.getContext() == null) {
                throw new NullPointerException("Context is already invalid.");
            }
            final Intent intent = buildIntent();
            return intent != null && onStart(intent);
        }
        Log.e(TAG,
                "Intent does not started. No content providers to build chooser dialog to obtain content or no Uri to preview content.");
        return false;
    }

    /**
     * Returns {@code null} if there was assigned at least one {@link com.wit.android.support.content.intent.ContentIntent.ContentProviderItem},
     * otherwise returns valid instance of Intent to preview specific content.
     */
    @Nullable
    @Override
    public Intent buildIntent() {
        if (!hasProviders()) {
            if (mUri == null) {
                Log.e(TAG, "Can not to create PREVIEW CONTENT intent. Missing content Uri.");
                return null;
            }
            if (TextUtils.isEmpty(mDataType)) {
                Log.e(TAG, "Can not to create PREVIEW CONTENT intent. Missing content MIME type.");
                return null;
            }
            final Intent intent = new Intent(Intent.ACTION_VIEW);
            intent.setDataAndType(mUri, mDataType);
            return intent;
        }
        return null;
    }

    /**
     * Called to populate this intent builder with default provider ({@link ContentProviderItem}) items.
     * <p>
     * Type and count of default providers may differ depending on the specific ContentIntent implementation.
     *
     * @return This intent builder instance.
     */
    public abstract ContentIntent withDefaultProviders();

    /**
     * Assigns the given <var>provider</var> to this content intent builder, so an item for such a
     * provider will be created within the <b>chooser dialog</b> list with its name ({@link ContentIntent.ContentProviderItem#getName()})
     * displayed by this intent builder.
     * <p>
     * All providers can be accessed by {@link #getProviders()}.
     *
     * @param provider New provider to assign.
     * @return This intent builder instance.
     * @see #clearProviders()
     */
    public ContentIntent withProvider(@NonNull ContentProviderItem provider) {
        this.initProvidersList();
        if (!mProviders.contains(provider)) {
            mProviders.add(provider);
        }
        return this;
    }

    /**
     * Same as {@link #withProvider(ContentIntent.ContentProviderItem)} for the given set of <var>providers</var>.
     * <p>
     * <b>Note</b>, that this will not check for already assigned "same" providers, just appends the
     * given array as collection to current providers.
     *
     * @param providers The set of providers to assign.
     * @return This intent builder to allow methods chaining.
     */
    public ContentIntent withProviders(@NonNull ContentProviderItem... providers) {
        if (providers.length > 0) {
            this.initProvidersList();
            mProviders.addAll(Arrays.asList(providers));
        }
        return this;
    }

    /**
     * Returns set of content providers for this intent builder. New providers can be added by
     * {@link #withProvider(ContentIntent.ContentProviderItem)}.
     *
     * @return Current set of providers.
     * @see #hasProviders()
     * @see #clearProviders()
     */
    @Nullable
    public List<ContentProviderItem> getProviders() {
        return mProviders;
    }

    /**
     * Clears the current set of providers. New providers can be assigned by
     * {@link #withProvider(ContentIntent.ContentProviderItem)} or
     * {@link #withProviders(ContentIntent.ContentProviderItem...)}.
     */
    public void clearProviders() {
        if (mProviders != null) {
            mProviders.clear();
        }
    }

    /**
     * Getters + Setters ---------------------------------------------------------------------------
     */

    /**
     * Same as {@link #input(android.net.Uri)} with <var>uri</var> created from the given <var>file</var>.
     *
     * @param file A file used to crate Uri.
     */
    public ContentIntent input(@Nullable File file) {
        if (file != null) {
            input(Uri.fromFile(file));
        } else {
            input((Uri) null);
        }
        return this;
    }

    /**
     * Sets an uri of the content which should be previewed by the activity which can handle content
     * of the current data type.
     * <p>
     * <b>Note</b>, that the current <b>data type</b> will be set to {@code null}, so {@link #dataType(String)}
     * should be called immediately after new uri was set. The specific implementations of this
     * ContentIntent builder can here set default data type.
     *
     * @param uri The desired uri, which should be delivered to handling activity.
     * @return This intent builder to allow methods chaining.
     */
    public ContentIntent input(@Nullable Uri uri) {
        this.mUri = uri;
        this.mDataType = null;
        return this;
    }

    /**
     * Same as {@link #output(android.net.Uri)} with <var>uri</var> created from the given <var>file</var>.
     *
     * @param file A file used to crate Uri.
     */
    public ContentIntent output(@Nullable File file) {
        if (file != null) {
            output(Uri.fromFile(file));
        } else {
            output((Uri) null);
        }
        return this;
    }

    /**
     * Sets the current uri. Specific implementations of this ContentIntent builder can use this to
     * set here passed <var>uri</var> to the specific {@link ContentProviderItem} item which holds an
     * Intent with some of <b>ACTION_..._CAPTURE</b> based actions.
     *
     * @param uri The desired uri.
     * @return This intent builder to allow methods chaining.
     */
    public ContentIntent output(@Nullable Uri uri) {
        this.mUri = uri;
        this.mDataType = null;
        return this;
    }

    /**
     * Returns the uri passed to {@link #input(android.net.Uri)} or {@link #output(android.net.Uri)}.
     *
     * @return Current uri or {@code null} if there was no uri set yet.
     */
    @Nullable
    public Uri getUri() {
        return mUri;
    }

    /**
     * Sets the data (MIME) type for the current uri.
     *
     * @param type The desired MIME type for the current uri.
     * @return This intent builder to allow methods chaining.
     */
    public ContentIntent dataType(@NonNull @DataType String type) {
        this.mDataType = type;
        return this;
    }

    /**
     * Returns the current data (MIME) type.
     *
     * @return Current MIME type for the current uri or {@link MimeType#TEXT} by default.
     */
    @NonNull
    @DataType
    public String getDataType() {
        return mDataType;
    }

    /**
     * Protected -----------------------------------------------------------------------------------
     */

    /**
     * Creates time stamp in the {@link #CONTENT_FILE_TIME_STAMP_FORMAT} format for the current {@link java.util.Date}.
     *
     * @return Current time stamp string representation.
     */
    @NonNull
    protected static String createContentFileTimeStamp() {
        return new SimpleDateFormat(CONTENT_FILE_TIME_STAMP_FORMAT).format(new Date());
    }

    /**
     * Same as {@link #createContentFile(String, String, java.io.File)} with <b>.jpg</b> as <var>imageType</var>
     * and {@link android.os.Environment#getExternalStoragePublicDirectory(String)} with {@link android.os.Environment#DIRECTORY_PICTURES}
     * as <var>directory</var>.
     *
     * @param fileName                 Desired name for the requested file.
     * @param environmentDirectoryType One of {@link Environment#DIRECTORY_PICTURES}, {@link Environment#DIRECTORY_MOVIES}, ....
     */
    @Nullable
    protected static File createContentFile(@NonNull String fileName, @Nullable String fileType,
            @NonNull String environmentDirectoryType) {
        return createContentFile(fileName, fileType,
                Environment.getExternalStoragePublicDirectory(environmentDirectoryType));
    }

    /**
     * Creates a new file with the given parameters within the specified <var>directory</var>.
     *
     * @param fileName  Desired name for the requested file.
     * @param fileType  Desired suffix for the requested file.
     * @param directory Directory within which should be the requested file created.
     * @return New instance of temporary file or {@code null} if some IO error occurs..
     * @see #createContentFile(String, String, String)
     */
    @Nullable
    protected static File createContentFile(@NonNull String fileName, @Nullable String fileType,
            @Nullable File directory) {
        try {
            return File.createTempFile(fileName, fileType, directory);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     */
    @Override
    protected boolean onStart(@NonNull Intent intent) {
        startActivity(Intent.createChooser(intent, mDialogTitle));
        return true;
    }

    /**
     * Private -------------------------------------------------------------------------------------
     */

    /**
     * Checks whether there are currently some provider items.
     *
     * @return {@code True} if this builder has some provider items, {@code false} otherwise.
     */
    private boolean hasProviders() {
        return mProviders != null && !mProviders.isEmpty();
    }

    /**
     * Initializes the current list of provider items if is not initialized yet.
     */
    private void initProvidersList() {
        if (mProviders == null) {
            this.mProviders = new ArrayList<>();
        }
    }

    /**
     * Inner classes ===============================================================================
     */

    /**
     * <h3>Class Overview</h3>
     * Required implementation for simple "content provider", which can provide an instance of
     * {@link android.content.Intent} to obtain specific type of content. Each provider of {@link ContentIntent}
     * will have its item within <b>chooser dialog</b> list.
     *
     * @author Martin Albedinsky
     */
    public static class ContentProviderItem {

        /**
         * Members =================================================================================
         */

        /**
         * Name of this "content provider" to be displayed within chooser dialog list.
         */
        CharSequence name;

        /**
         * Intent to be started when an item addressed to this provider was clicked.
         */
        Intent intent;

        /**
         * Request code used for {@link com.wit.android.support.content.SimpleContextWrapper#startIntentForResult(android.content.Intent, int) SimpleContextWrapper#startIntentForResult(android.content.Intent, int)}.
         */
        int requestCode;

        /**
         * Methods =================================================================================
         */

        /**
         * Sets the name of this "content provider" to be displayed within <b>chooser dialog</b> list.
         *
         * @param name The desired name
         * @return This provider instance.
         */
        public ContentProviderItem name(@NonNull CharSequence name) {
            this.name = name;
            return this;
        }

        /**
         * Returns name of this provider.
         *
         * @return This provider's name.
         * @see #name(java.lang.CharSequence)
         */
        @NonNull
        public CharSequence getName() {
            return name;
        }

        /**
         * Sets the intent to be started when an item addressed to this provider within <b>chooser dialog</b>
         * was clicked.
         * <p>
         * <b>Note</b>, that the given <var>intent</var> will be started by
         * {@link com.wit.android.support.content.SimpleContextWrapper#startIntentForResult(android.content.Intent, int) SimpleContextWrapper#startIntentForResult(android.content.Intent, int)}
         * with the request code passed to {@link #requestCode(int)}.
         *
         * @param intent Valid instance of intent.
         * @return This provider instance.
         */
        public ContentProviderItem intent(@NonNull Intent intent) {
            this.intent = intent;
            return this;
        }

        /**
         * Returns intent attached to this provider.
         *
         * @return This provider's intent.
         * @see #intent(android.content.Intent)
         */
        @NonNull
        public Intent getIntent() {
            return intent;
        }

        /**
         * Sets the request code used for
         * {@link com.wit.android.support.content.SimpleContextWrapper#startIntentForResult(android.content.Intent, int) SimpleContextWrapper#startIntentForResult(android.content.Intent, int)}
         * when starting this provider's intent.
         *
         * @param code The desired request code. This code should be used to identify result from
         *             started intent in {@link Activity#onActivityResult(int, int, android.content.Intent)}
         *             or {@link android.support.v4.app.Fragment#onActivityResult(int, int, android.content.Intent)},
         *             depends on for which context was {@link ImageIntent} builder created.
         * @return This provider instance.
         */
        public ContentProviderItem requestCode(int code) {
            this.requestCode = code;
            return this;
        }

        /**
         * Returns request code of this provider.
         *
         * @return This provider's request code.
         * @see #requestCode(int)
         */
        public int getRequestCode() {
            return requestCode;
        }
    }
}