com.albedinsky.android.support.intent.EmailIntent.java Source code

Java tutorial

Introduction

Here is the source code for com.albedinsky.android.support.intent.EmailIntent.java

Source

/*
 * =================================================================================================
 *                             Copyright (C) 2014 Martin Albedinsky
 * =================================================================================================
 *         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.albedinsky.android.support.intent;

import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.util.Patterns;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;

/**
 * A {@link BaseIntent} builder implementation providing API for building of intents targeting
 * a <b>e-mail</b> related applications.
 * <p>
 * Primary e-mail addresses can be specified via {@link #to(String)} or {@link #to(String...)}.
 * <b>Carbon copy</b> addresses can be specified via {@link #cc(String)} or {@link #cc(String...)}.
 * If  you want to include also <b>blind carbon copy</b> addresses, these can be specified via
 * {@link #bcc(String)} or {@link #bcc(String...)}.
 *
 * @author Martin Albedinsky
 */
public class EmailIntent extends BaseIntent<EmailIntent> {

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

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

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

    /**
     * Uri scheme for <b>e-mail</b> targeting intents.
     * <p>
     * Constant value: <b>mailto</b>
     */
    public static final String URI_SCHEME = "mailto";

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

    /**
     * Matcher for valid e-mail address.
     */
    protected static final Matcher EMAIL_MATCHER = Patterns.EMAIL_ADDRESS.matcher("");

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

    /**
     * Subject of an email to send.
     */
    private CharSequence mSubject;

    /**
     * Message of an email to send.
     */
    private CharSequence mMessage;

    /**
     * Array with e-mail addresses to send e-mail to.
     */
    private List<String> mAddresses;

    /**
     * Array with e-mail addresses for carbon copy to send e-mail to.
     */
    private List<String> mCcAddresses;

    /**
     * Array with e-mail addresses for blind carbon copy to send e-mail to.
     */
    private List<String> mBccAddresses;

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

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

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

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

    /**
     * Same as {@link #to(String)} for resource id.
     *
     * @param resId Resource id of the desired e-mail address.
     */
    public EmailIntent to(@StringRes int resId) {
        return to(obtainString(resId));
    }

    /**
     * Appends the given <var>address</var> to the current <b>primary</b> e-mail addresses to which
     * will be e-mail message send.
     * <p>
     * <b>Note</b>, that only valid e-mail address will be added.
     *
     * @param address The desired e-mail address to add.
     * @return This intent builder to allow methods chaining.
     * @see #to(String...)
     * @see #addresses()
     */
    public EmailIntent to(@NonNull String address) {
        ensureAddresses(1);
        appendEmailAddress(mAddresses, address);
        return this;
    }

    /**
     * Same as {@link #to(List)} for array of addresses.
     *
     * @param addresses The desired array of e-mail addresses to add.
     */
    public EmailIntent to(@NonNull String... addresses) {
        return to(Arrays.asList(addresses));
    }

    /**
     * Appends the given set of <var>addresses</var> to the current <b>primary</b> e-mail addresses
     * to which will be e-mail message send.
     * <p>
     * <b>Note</b>, that only valid e-mail addresses will be added.
     *
     * @param addresses The desired set of e-mail addresses to add.
     * @return This intent builder to allow methods chaining.
     * @see #addresses()
     */
    public EmailIntent to(@NonNull List<String> addresses) {
        ensureAddresses(addresses.size());
        appendEmailAddresses(mAddresses, addresses);
        return this;
    }

    /**
     * Ensures that the list with addresses is properly initialized.
     *
     * @param initialSize Initial capacity for the list.
     */
    private void ensureAddresses(int initialSize) {
        if (mAddresses == null)
            mAddresses = new ArrayList<>(initialSize);
    }

    /**
     * Returns the list with valid <b>primary</b> e-mail addresses.
     *
     * @return List with e-mail addresses specified via one of <b>to(...)</b> methods or
     * {@link Collections#EMPTY_LIST} if there were no addresses specified yet.
     * @see #to(int)
     * @see #to(String)
     * @see #to(String...)
     */
    @NonNull
    @SuppressWarnings("unchecked")
    public List<String> addresses() {
        return mAddresses != null ? new ArrayList<>(mAddresses) : Collections.EMPTY_LIST;
    }

    /**
     * Clears the list with addresses specified via one of {@code to(...)} methods.
     *
     * @return This intent builder to allow methods chaining.
     * @see #to(int)
     * @see #to(String)
     * @see #to(String...)
     * @see #to(List)
     * @see #addresses()
     */
    public EmailIntent clearAddresses() {
        if (mAddresses != null) {
            mAddresses.clear();
            this.mAddresses = null;
        }
        return this;
    }

    /**
     * Same as {@link #cc(String)} for resource id.
     *
     * @param resId Resource id of the desired e-mail address.
     * @see #cc(String...)
     * @see #ccAddresses()
     */
    public EmailIntent cc(@StringRes int resId) {
        return cc(obtainString(resId));
    }

    /**
     * Appends the given <var>address</var> to the current <b>carbon copy</b> e-mail addresses.
     * See {@link Intent#EXTRA_CC} for more info.
     * <p>
     * <b>Note</b>, that only valid e-mail address will be added.
     *
     * @param address The desired e-mail address to add.
     * @return This intent builder to allow methods chaining.
     * @see #cc(int)
     * @see #cc(String...)
     * @see #ccAddresses()
     */
    public EmailIntent cc(@NonNull String address) {
        ensureCcAddresses(1);
        appendEmailAddress(mCcAddresses, address);
        return this;
    }

    /**
     * Same as {@link #cc(List)} for array of addresses.
     *
     * @param addresses The desired array of e-mail addresses to add.
     */
    public EmailIntent cc(@NonNull String... addresses) {
        return cc(Arrays.asList(addresses));
    }

    /**
     * Appends the given set of <var>addresses</var> to the current <b>carbon copy</b> e-mail addresses.
     * See {@link Intent#EXTRA_CC} for more info.
     * <p>
     * <b>Note</b>, that only valid e-mail addresses will be added.
     *
     * @param addresses The desired set of e-mail addresses to add.
     * @return This intent builder to allow methods chaining.
     * @see #cc(int)
     * @see #cc(String)
     * @see #ccAddresses()
     */
    public EmailIntent cc(@NonNull List<String> addresses) {
        ensureCcAddresses(addresses.size());
        appendEmailAddresses(mCcAddresses, addresses);
        return this;
    }

    /**
     * Ensures that the list with <b>carbon-copy</b> addresses is properly initialized.
     *
     * @param initialSize Initial capacity for the list.
     */
    private void ensureCcAddresses(int initialSize) {
        if (mCcAddresses == null)
            mCcAddresses = new ArrayList<>(initialSize);
    }

    /**
     * Returns the list with valid <b>carbon copy</b> e-mail addresses.
     *
     * @return List with e-mail addresses specified via one of <b>cc(...)</b> methods or
     * {@link Collections#EMPTY_LIST} if there were no addresses specified yet.
     * @see #cc(int)
     * @see #cc(String)
     * @see #cc(String...)
     */
    @NonNull
    @SuppressWarnings("unchecked")
    public List<String> ccAddresses() {
        return mCcAddresses != null ? new ArrayList<>(mCcAddresses) : Collections.EMPTY_LIST;
    }

    /**
     * Clears the list with <b>carbon-copy</b> addresses specified via one of {@code cc(...)} methods.
     *
     * @return This intent builder to allow methods chaining.
     * @see #cc(int)
     * @see #cc(String)
     * @see #cc(String...)
     * @see #cc(List)
     * @see #ccAddresses()
     */
    public EmailIntent clearCcAddresses() {
        if (mCcAddresses != null) {
            mCcAddresses.clear();
            this.mCcAddresses = null;
        }
        return this;
    }

    /**
     * Same as {@link #bcc(String)} for resource id.
     *
     * @param resId Resource id of the desired e-mail address.
     * @see #bcc(String...)
     * @see #bccAddresses()
     */
    public EmailIntent bcc(@StringRes int resId) {
        return bcc(obtainString(resId));
    }

    /**
     * Appends the given <var>address</var> to the current <b>blind carbon copy</b> e-mail addresses.
     * See {@link Intent#EXTRA_BCC} for more info.
     * <p>
     * <b>Note</b>, that only valid e-mail address will be added.
     *
     * @param address The desired e-mail address to add.
     * @return This intent builder to allow methods chaining.
     * @see #bcc(int)
     * @see #bcc(String...)
     * @see #bccAddresses()
     */
    public EmailIntent bcc(@NonNull String address) {
        ensureBccAddresses(1);
        appendEmailAddress(mBccAddresses, address);
        return this;
    }

    /**
     * Same as {@link #bcc(List)} for array of addresses.
     *
     * @param addresses The desired array of e-mail addresses to add.
     */
    public EmailIntent bcc(@NonNull String... addresses) {
        return bcc(Arrays.asList(addresses));
    }

    /**
     * Appends the given set of <var>addresses</var> to the current <b>blind carbon copy</b> e-mail
     * addresses. See {@link Intent#EXTRA_CC} for more info.
     * <p>
     * <b>Note</b>, that only valid e-mail addresses will be added.
     *
     * @param addresses The desired set of e-mail addresses to add.
     * @return This intent builder to allow methods chaining.
     * @see #bccAddresses()
     */
    public EmailIntent bcc(@NonNull List<String> addresses) {
        ensureBccAddresses(addresses.size());
        appendEmailAddresses(mBccAddresses, addresses);
        return this;
    }

    /**
     * Ensures that the list with <b>blind-carbon-copy</b> addresses is properly initialized.
     *
     * @param initialSize Initial capacity for the list.
     */
    private void ensureBccAddresses(int initialSize) {
        if (mBccAddresses == null)
            mBccAddresses = new ArrayList<>(initialSize);
    }

    /**
     * Returns the list with valid <b>blind carbon copy</b> e-mail addresses.
     *
     * @return List with e-mail addresses specified via one of <b>bcc(...)</b> methods or
     * {@link Collections#EMPTY_LIST} if there were no addresses specified yet.
     * @see #bcc(int)
     * @see #bcc(String)
     * @see #bcc(String...)
     */
    @NonNull
    @SuppressWarnings("unchecked")
    public List<String> bccAddresses() {
        return mBccAddresses != null ? new ArrayList<>(mBccAddresses) : Collections.EMPTY_LIST;
    }

    /**
     * Clears the list with <b>blind-carbon-copy</b> addresses specified via one of {@code bcc(...)} methods.
     *
     * @return This intent builder to allow methods chaining.
     * @see #bcc(int)
     * @see #bcc(String)
     * @see #bcc(String...)
     * @see #bcc(List)
     * @see #bccAddresses()
     */
    public EmailIntent clearBccAddresses() {
        if (mBccAddresses != null) {
            mBccAddresses.clear();
            this.mBccAddresses = null;
        }
        return this;
    }

    /**
     * Same as {@link #subject(CharSequence)} for resource id.
     *
     * @param resId Resource id of the desired subject text.
     */
    public EmailIntent subject(@StringRes int resId) {
        return subject(obtainText(resId));
    }

    /**
     * Set a subject for e-mail to send. See {@link Intent#EXTRA_SUBJECT} for more info.
     *
     * @param subject The desired subject text.
     * @return This intent builder to allow methods chaining.
     * @see #subject()
     */
    public EmailIntent subject(@NonNull CharSequence subject) {
        this.mSubject = subject;
        return this;
    }

    /**
     * Returns the subject for e-mail to send.
     *
     * @return Subject of e-mail or empty text if not specified yet.
     * @see #subject(CharSequence)
     */
    @NonNull
    public CharSequence subject() {
        return mSubject != null ? mSubject : "";
    }

    /**
     * Same as {@link #message(CharSequence)} for resource id.
     *
     * @param resId Resource id of the desired e-mail message.
     */
    public EmailIntent message(@StringRes int resId) {
        return message(obtainText(resId));
    }

    /**
     * Sets a message for e-mail to send.
     *
     * @param message The desired message text.
     * @return This intent builder to allow methods chaining.
     * @see #message()
     */
    public EmailIntent message(@NonNull CharSequence message) {
        this.mMessage = message;
        return this;
    }

    /**
     * Returns the message for e-mail to send.
     *
     * @return Message of e-mail or empty text if not specified yet.
     * @see #message(CharSequence)
     */
    @NonNull
    public CharSequence message() {
        return mMessage != null ? mMessage : "";
    }

    /**
     */
    @Override
    protected void ensureCanBuildOrThrow() {
        super.ensureCanBuildOrThrow();
        if (mAddresses == null) {
            throw cannotBuildIntentException("No e-mail address/-es specified.");
        }
    }

    /**
     */
    @NonNull
    @Override
    protected Intent onBuild() {
        final Intent intent = new Intent(Intent.ACTION_SENDTO, createUri(mAddresses));
        intent.putExtra(Intent.EXTRA_SUBJECT, mSubject);
        intent.putExtra(Intent.EXTRA_TEXT, mMessage);
        if (mCcAddresses != null) {
            intent.putExtra(Intent.EXTRA_CC, addressesToArray(mCcAddresses));
        }
        if (mBccAddresses != null) {
            intent.putExtra(Intent.EXTRA_BCC, addressesToArray(mBccAddresses));
        }
        return intent;
    }

    /**
     * Creates an URI specific for the {@link Intent#ACTION_SENDTO} intent action
     * from the given set of e-mails.
     *
     * @param addresses List with e-mail addresses.
     * @return Created URI containing the specified addresses or {@code null} if the given list of
     * addresses is empty.
     */
    @Nullable
    protected static Uri createUri(@NonNull List<String> addresses) {
        final int n = addresses.size();
        if (n == 0)
            return null;
        final StringBuilder data = new StringBuilder(n * 15);
        for (int i = 0; i < n; i++) {
            data.append(addresses.get(i));
            if (i < (n - 1)) {
                data.append(",");
            }
        }
        return Uri.fromParts(URI_SCHEME, data.toString(), null);
    }

    /**
     * Transforms the specified list of addresses into array.
     *
     * @param addresses The list of addresses to be transformed.
     * @return Array of same size as the specified addresses and also with the same content.
     */
    private static String[] addressesToArray(List<String> addresses) {
        final String[] addressesArray = new String[addresses.size()];
        addresses.toArray(addressesArray);
        return addressesArray;
    }

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

    /**
     * Appends the given <var>addresses</var> into the given <var>list</var>.
     *
     * @param list      The list into which append the addresses.
     * @param addresses Addresses to append.
     *                  otherwise, same instance with appended addresses.
     */
    private static void appendEmailAddresses(List<String> list, List<String> addresses) {
        if (addresses.size() > 0) {
            for (String address : addresses) {
                appendEmailAddress(list, address);
            }
        }
    }

    /**
     * Appends the given <var>address</var> into the given <var>list</var>.
     *
     * @param list    The list into which append the address.
     * @param address Address to append.
     *                otherwise, same instance with appended address.
     */
    static void appendEmailAddress(List<String> list, String address) {
        if (!EMAIL_MATCHER.reset(address).matches()) {
            Log.e(TAG, "Invalid e-mail address('" + address + "') specified.");
            return;
        }
        list.add(address);
    }

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