uk.co.ashtonbrsc.intentexplode.Explode.java Source code

Java tutorial

Introduction

Here is the source code for uk.co.ashtonbrsc.intentexplode.Explode.java

Source

//   Copyright 2012-2014 Intrications (intrications.com)
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

package uk.co.ashtonbrsc.intentexplode;

import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.Typeface;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.ShareActionProvider;
import android.text.ClipboardManager;
import android.text.Editable;
import android.text.Html;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextWatcher;
import android.text.style.LeadingMarginSpan;
import android.text.style.ParagraphStyle;
import android.util.DisplayMetrics;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.TextView;
import android.widget.Toast;

import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import uk.co.ashtonbrsc.android.intentintercept.R;

//TODO add icon -which icon - app icons???
//TODO add bitmaps/images (from intent extras?)
//TODO add getCallingActivity() - will only give details for startActivityForResult();

/**
 * Should really be called IntentDetailsActivity but this may cause problems with launcher
 * shortcuts and the enabled/disabled state of interception.
 */
public class Explode extends AppCompatActivity {

    private abstract class IntentUpdateTextWatcher implements TextWatcher {
        private final TextView textView;

        IntentUpdateTextWatcher(TextView textView) {
            this.textView = textView;
        }

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            if (textWatchersActive) {
                try {
                    String modifiedContent = textView.getText().toString();
                    onUpdateIntent(modifiedContent);
                    showTextViewIntentData(textView);
                    showResetIntentButton(true);
                    refreshUI();
                } catch (Exception e) {
                    Toast.makeText(Explode.this, e.getMessage(), Toast.LENGTH_SHORT).show();
                    e.printStackTrace();
                }
            }
        }

        abstract protected void onUpdateIntent(String modifiedContent);

        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        }

        @Override
        public void afterTextChanged(Editable s) {
        }
    }

    private static final String INTENT_EDITED = "intent_edited";
    private static final int STANDARD_INDENT_SIZE_IN_DIP = 10;
    private static final String NEWLINE = "\n<br>";

    private ShareActionProvider shareActionProvider; // api-14 or compat
    private EditText action;
    private EditText data;
    private EditText type;
    private EditText uri;
    private TextView categoriesHeader;
    private LinearLayout categoriesLayout;
    private LinearLayout flagsLayout;
    private LinearLayout extrasLayout;
    private LinearLayout activitiesLayout;
    private TextView activitiesHeader;
    private Button resendIntentButton;
    private Button resetIntentButton;
    private float density;

    /** String representation of intent as uri */
    private String originalIntent;

    /** Bugfix #14: extras that are lost in the intent <-> string conversion */
    private Bundle additionalExtras;

    private Intent editableIntent;

    // support for onActivityResult
    private Integer lastResultCode = null;
    private String lastResultIntent = null;

    /** false: text-change-events are not active. */
    private boolean textWatchersActive;

    private static final Map<Integer, String> FLAGS_MAP = new HashMap<Integer, String>() {
        {
            put(Integer.valueOf(Intent.FLAG_GRANT_READ_URI_PERMISSION), "FLAG_GRANT_READ_URI_PERMISSION");
            put(Integer.valueOf(Intent.FLAG_GRANT_WRITE_URI_PERMISSION), "FLAG_GRANT_WRITE_URI_PERMISSION");
            put(Integer.valueOf(Intent.FLAG_FROM_BACKGROUND), "FLAG_FROM_BACKGROUND");
            put(Integer.valueOf(Intent.FLAG_DEBUG_LOG_RESOLUTION), "FLAG_DEBUG_LOG_RESOLUTION");
            put(Integer.valueOf(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES), "FLAG_EXCLUDE_STOPPED_PACKAGES");
            put(Integer.valueOf(Intent.FLAG_INCLUDE_STOPPED_PACKAGES), "FLAG_INCLUDE_STOPPED_PACKAGES");
            put(Integer.valueOf(Intent.FLAG_ACTIVITY_NO_HISTORY), "FLAG_ACTIVITY_NO_HISTORY");
            put(Integer.valueOf(Intent.FLAG_ACTIVITY_SINGLE_TOP), "FLAG_ACTIVITY_SINGLE_TOP");
            put(Integer.valueOf(Intent.FLAG_ACTIVITY_NEW_TASK), "FLAG_ACTIVITY_NEW_TASK");
            put(Integer.valueOf(Intent.FLAG_ACTIVITY_MULTIPLE_TASK), "FLAG_ACTIVITY_MULTIPLE_TASK");
            put(Integer.valueOf(Intent.FLAG_ACTIVITY_CLEAR_TOP), "FLAG_ACTIVITY_CLEAR_TOP");
            put(Integer.valueOf(Intent.FLAG_ACTIVITY_FORWARD_RESULT), "FLAG_ACTIVITY_FORWARD_RESULT");
            put(Integer.valueOf(Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP), "FLAG_ACTIVITY_PREVIOUS_IS_TOP");
            put(Integer.valueOf(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS), "FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS");
            put(Integer.valueOf(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT), "FLAG_ACTIVITY_BROUGHT_TO_FRONT");
            put(Integer.valueOf(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED), "FLAG_ACTIVITY_RESET_TASK_IF_NEEDED");
            put(Integer.valueOf(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY), "FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY");
            put(Integer.valueOf(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET), "FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET");
            put(Integer.valueOf(Intent.FLAG_ACTIVITY_NO_USER_ACTION), "FLAG_ACTIVITY_NO_USER_ACTION");
            put(Integer.valueOf(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT), "FLAG_ACTIVITY_REORDER_TO_FRONT");
            put(Integer.valueOf(Intent.FLAG_ACTIVITY_NO_ANIMATION), "FLAG_ACTIVITY_NO_ANIMATION");
            put(Integer.valueOf(Intent.FLAG_ACTIVITY_CLEAR_TASK), "FLAG_ACTIVITY_CLEAR_TASK");
            put(Integer.valueOf(Intent.FLAG_ACTIVITY_TASK_ON_HOME), "FLAG_ACTIVITY_TASK_ON_HOME");
            put(Integer.valueOf(Intent.FLAG_RECEIVER_REGISTERED_ONLY), "FLAG_RECEIVER_REGISTERED_ONLY");
            put(Integer.valueOf(Intent.FLAG_RECEIVER_REPLACE_PENDING), "FLAG_RECEIVER_REPLACE_PENDING");
            put(Integer.valueOf(Intent.FLAG_RECEIVER_FOREGROUND), "FLAG_RECEIVER_FOREGROUND");
            put(Integer.valueOf(0x08000000), "FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT");
            put(Integer.valueOf(0x04000000), "FLAG_RECEIVER_BOOT_UPGRADE");
        }
    };

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

        setContentView(R.layout.explode);

        rememberIntent(getIntent());

        final boolean isVisible = savedInstanceState != null && savedInstanceState.getBoolean(INTENT_EDITED);
        showInitialIntent(isVisible);
    }

    private void rememberIntent(Intent original) {
        this.originalIntent = getUri(original);

        Intent copy = cloneIntent(this.originalIntent);

        final Bundle originalExtras = original.getExtras();

        if (originalExtras != null) {
            // bugfix #14: collect extras that are lost in the intent <-> string conversion
            Bundle additionalExtrasBundle = new Bundle(originalExtras);
            for (String key : originalExtras.keySet()) {
                if (copy.hasExtra(key)) {
                    additionalExtrasBundle.remove(key);
                }
            }

            if (!additionalExtrasBundle.isEmpty()) {
                additionalExtras = additionalExtrasBundle;
            }
        }

    }

    /**
     * creates a clone of originalIntent and displays it for editing
     * @param isVisible
     */
    private void showInitialIntent(boolean isVisible) {
        editableIntent = cloneIntent(this.originalIntent);

        editableIntent.setComponent(null);

        setupVariables();

        setupTextWatchers();

        showAllIntentData(null);

        showResetIntentButton(isVisible);
    }

    /** textViewToIgnore is not updated so current selected char in that textview will not change */
    private void showAllIntentData(TextView textViewToIgnore) {
        showTextViewIntentData(textViewToIgnore);

        categoriesLayout.removeAllViews();
        Set<String> categories = editableIntent.getCategories();
        StringBuilder stringBuilder = new StringBuilder();
        if (categories != null) {
            categoriesHeader.setVisibility(View.VISIBLE);
            stringBuilder.append(getResources().getString(R.string.categories));
            for (String category : categories) {
                stringBuilder.append(category).append(NEWLINE);
                TextView categoryTextView = new TextView(this);
                categoryTextView.setText(category);
                categoryTextView.setTextAppearance(this, R.style.TextFlags);
                categoriesLayout.addView(categoryTextView);
            }
        } else {
            categoriesHeader.setVisibility(View.GONE);
            // addTextToLayout("NONE", Typeface.NORMAL, categoriesLayout);
        }

        flagsLayout.removeAllViews();
        ArrayList<String> flagsStrings = getFlags();
        if (!flagsStrings.isEmpty()) {
            for (String thisFlagString : flagsStrings) {
                addTextToLayout(thisFlagString, Typeface.NORMAL, flagsLayout);
            }
        } else {
            addTextToLayout(getResources().getString(R.string.none), Typeface.NORMAL, flagsLayout);
        }

        extrasLayout.removeAllViews();
        try {
            Bundle intentBundle = editableIntent.getExtras();
            if (intentBundle != null) {
                Set<String> keySet = intentBundle.keySet();
                stringBuilder.append(getResources().getString(R.string.bundle_title_bold));
                int count = 0;

                for (String key : keySet) {
                    count++;
                    Object thisObject = intentBundle.get(key);
                    addTextToLayout(getResources().getString(R.string.extra) + count, Typeface.BOLD, extrasLayout);
                    String thisClass = thisObject.getClass().getName();
                    if (thisClass != null) {
                        addTextToLayout(getResources().getString(R.string.class_text) + thisClass, Typeface.ITALIC,
                                STANDARD_INDENT_SIZE_IN_DIP, extrasLayout);
                    }
                    addTextToLayout(getResources().getString(R.string.key) + key, Typeface.ITALIC,
                            STANDARD_INDENT_SIZE_IN_DIP, extrasLayout);
                    if (thisObject instanceof String || thisObject instanceof Long || thisObject instanceof Integer
                            || thisObject instanceof Boolean || thisObject instanceof Uri) {
                        addTextToLayout(getResources().getString(R.string.value) + thisObject.toString(),
                                Typeface.ITALIC, STANDARD_INDENT_SIZE_IN_DIP, extrasLayout);
                    } else if (thisObject instanceof ArrayList) {
                        addTextToLayout(getResources().getString(R.string.values), Typeface.ITALIC, extrasLayout);
                        ArrayList thisArrayList = (ArrayList) thisObject;
                        for (Object thisArrayListObject : thisArrayList) {
                            addTextToLayout(thisArrayListObject.toString(), Typeface.ITALIC,
                                    STANDARD_INDENT_SIZE_IN_DIP, extrasLayout);
                        }
                    }
                }
            } else {
                addTextToLayout(getResources().getString(R.string.none), Typeface.NORMAL, extrasLayout);
            }
        } catch (Exception e) {
            // TODO Should make this red to highlight error
            addTextToLayout(getResources().getString(R.string.error_extracting_extras), Typeface.NORMAL,
                    extrasLayout);
            e.printStackTrace();
        }

        // resolveInfo = pm.queryIntentServices(intent, 0);
        // stringBuilder.append("<br><b><u>" + resolveInfo.size()
        // + " services match this intent:</u></b><br>");
        // for (int i = 0; i < resolveInfo.size(); i++) {
        // ResolveInfo info = resolveInfo.get(i);
        // ActivityInfo activityinfo = info.activityInfo;
        // stringBuilder.append(activityinfo.packageName + "<br>");
        // }

        // intentDetailsHtml = stringBuilder.toString();
        // (((TextView) findViewById(R.id.text))).setText(intentDetailsHtml);
        refreshUI();
    }

    /** textViewToIgnore is not updated so current selected char in that textview will not change */
    private void showTextViewIntentData(TextView textViewToIgnore) {
        textWatchersActive = false;
        if (textViewToIgnore != action)
            action.setText(editableIntent.getAction());
        if ((textViewToIgnore != data) && (editableIntent.getDataString() != null)) {
            data.setText(editableIntent.getDataString());
        }
        if (textViewToIgnore != type)
            type.setText(editableIntent.getType());
        if (textViewToIgnore != uri)
            uri.setText(getUri(editableIntent));
        textWatchersActive = true;
    }

    private ArrayList<String> getFlags() {
        ArrayList<String> flagsStrings = new ArrayList<>();
        int flags = editableIntent.getFlags();
        Set<Entry<Integer, String>> set = FLAGS_MAP.entrySet();
        Iterator<Entry<Integer, String>> i = set.iterator();
        while (i.hasNext()) {
            Entry<Integer, String> thisFlag = (Entry<Integer, String>) i.next();
            if ((flags & thisFlag.getKey()) != 0) {
                flagsStrings.add(thisFlag.getValue());
            }
        }
        return flagsStrings;
    }

    private void checkAndShowMatchingActivites() {

        activitiesLayout.removeAllViews();
        PackageManager pm = getPackageManager();
        List<ResolveInfo> resolveInfo = pm.queryIntentActivities(editableIntent, 0);

        // Remove Intent Intercept from matching activities
        int numberOfMatchingActivities = resolveInfo.size() - 1;

        if (numberOfMatchingActivities < 1) {
            resendIntentButton.setEnabled(false);
            activitiesHeader.setText(getResources().getString(R.string.no_activities_match_intent));
        } else {
            resendIntentButton.setEnabled(true);
            activitiesHeader.setText(getResources().getQuantityString(R.plurals.activities_match_intent,
                    numberOfMatchingActivities, numberOfMatchingActivities));
            for (int i = 0; i <= numberOfMatchingActivities; i++) {
                ResolveInfo info = resolveInfo.get(i);
                ActivityInfo activityinfo = info.activityInfo;
                if (!activityinfo.packageName.equals(getPackageName())) {
                    addTextToLayout(
                            activityinfo.loadLabel(pm) + getResources().getString(R.string.open_bracket)
                                    + activityinfo.packageName + getResources().getString(R.string.dash)
                                    + activityinfo.name + getResources().getString(R.string.close_bracket),
                            Typeface.NORMAL, activitiesLayout);
                }
            }
        }
    }

    private void addTextToLayout(String text, int typeface, int paddingLeft, LinearLayout layout) {
        TextView textView = new TextView(this);
        ParagraphStyle style_para = new LeadingMarginSpan.Standard(0,
                (int) (STANDARD_INDENT_SIZE_IN_DIP * density));
        SpannableString styledText = new SpannableString(text);
        styledText.setSpan(style_para, 0, styledText.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
        textView.setText(styledText);
        textView.setTextAppearance(this, R.style.TextFlags);
        textView.setTypeface(null, typeface);
        LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
        params.setMargins((int) (paddingLeft * density), 0, 0, 0);
        layout.addView(textView, params);
    }

    private void addTextToLayout(String text, int typeface, LinearLayout layout) {
        addTextToLayout(text, typeface, 0, layout);
    }

    private void setupVariables() {
        action = (EditText) findViewById(R.id.action);
        data = (EditText) findViewById(R.id.data);
        type = (EditText) findViewById(R.id.type);
        uri = (EditText) findViewById(R.id.uri);
        categoriesHeader = (TextView) findViewById(R.id.categories_header);
        categoriesLayout = (LinearLayout) findViewById(R.id.categories_layout);
        flagsLayout = (LinearLayout) findViewById(R.id.flags_layout);
        extrasLayout = (LinearLayout) findViewById(R.id.extras_layout);
        activitiesHeader = (TextView) findViewById(R.id.activities_header);
        activitiesLayout = (LinearLayout) findViewById(R.id.activities_layout);
        resendIntentButton = (Button) findViewById(R.id.resend_intent_button);
        resetIntentButton = (Button) findViewById(R.id.reset_intent_button);

        DisplayMetrics metrics = new DisplayMetrics();
        WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
        wm.getDefaultDisplay().getMetrics(metrics);
        density = metrics.density;
    }

    private void setupTextWatchers() {
        action.addTextChangedListener(new IntentUpdateTextWatcher(action) {
            @Override
            protected void onUpdateIntent(String modifiedContent) {
                editableIntent.setAction(modifiedContent);
            }
        });
        data.addTextChangedListener(new IntentUpdateTextWatcher(data) {
            @Override
            protected void onUpdateIntent(String modifiedContent) {
                // setData clears type so we save it
                String savedType = editableIntent.getType();
                editableIntent.setDataAndType(Uri.parse(modifiedContent), savedType);
            }
        });
        type.addTextChangedListener(new IntentUpdateTextWatcher(type) {
            @Override
            protected void onUpdateIntent(String modifiedContent) {
                // setData clears type so we save it
                String dataString = editableIntent.getDataString();
                editableIntent.setDataAndType(Uri.parse(dataString), modifiedContent);
            }
        });
        uri.addTextChangedListener(new IntentUpdateTextWatcher(uri) {
            @Override
            protected void onUpdateIntent(String modifiedContent) {
                Intent newIntent = cloneIntent(modifiedContent);

                // no error yet so continue
                editableIntent = newIntent;
                // this time must update all content since extras/flags may have been changed
                showAllIntentData(uri);
            }
        });
    }

    private void showResetIntentButton(boolean visible) {
        resendIntentButton.setText(R.string.send_edited_intent_button);
        resetIntentButton.setVisibility((visible) ? View.VISIBLE : View.GONE);
    }

    public void onSendIntent(View v) {
        try {
            startActivityForResult(Intent.createChooser(editableIntent, resendIntentButton.getText()), 1);
        } catch (Exception e) {
            Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
            e.printStackTrace();
        }
    }

    public void onResetIntent(View v) {
        // this would break onActivityResult
        // startActivity(this.originalIntent); // reload this with original data
        // finish();
        textWatchersActive = false;
        showInitialIntent(false);
        textWatchersActive = true;

        refreshUI();
    }

    private void copyIntentDetails() {
        ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
        clipboard.setText(getIntentDetailsString());
        Toast.makeText(this, R.string.intent_details_copied_to_clipboard, Toast.LENGTH_SHORT).show();
    }

    private void refreshUI() {
        // if (!intent.getAction().equals(getIntent().getAction())
        // || (intent.getDataString() != null && !intent.getDataString()
        // .equals(getIntent().getDataString()))
        // || !intent.getType().equals(getIntent().getType())) {
        //
        // }
        checkAndShowMatchingActivites();
        if (shareActionProvider != null) {
            Intent share = createShareIntent();
            shareActionProvider.setShareIntent(share);
        }
    }

    private Intent createShareIntent() {
        Intent share = new Intent(Intent.ACTION_SEND);
        share.setType(getResources().getString(R.string.text_plain));
        share.putExtra(Intent.EXTRA_TEXT, getIntentDetailsString());
        return share;
    }

    private Spanned getIntentDetailsString() { // TODO make sure this has all
        // the details
        StringBuilder stringBuilder = new StringBuilder();

        // k3b so intent can be reloaded using
        // Intent.parseUri("Intent:....", Intent.URI_INTENT_SCHEME)
        stringBuilder.append(getUri(editableIntent)).append(NEWLINE);

        // support for onActivityResult
        if (this.lastResultCode != null) {
            stringBuilder.append(getResources().getString(R.string.last_result))
                    .append(this.lastResultCode.toString());

            if (this.lastResultIntent != null) {
                stringBuilder.append(getResources().getString(R.string.data)).append(lastResultIntent);
            }
            stringBuilder.append(NEWLINE);
        }

        stringBuilder.append(NEWLINE).append(getResources().getString(R.string.action_bold))
                .append(editableIntent.getAction()).append(NEWLINE);
        stringBuilder.append(getResources().getString(R.string.data_bold)).append(editableIntent.getData())
                .append(NEWLINE);
        stringBuilder.append(getResources().getString(R.string.type_bold)).append(editableIntent.getType())
                .append(NEWLINE);

        Set<String> categories = editableIntent.getCategories();
        if (categories != null) {
            stringBuilder.append(getResources().getString(R.string.categories_title_bold));
            for (String category : categories) {
                stringBuilder.append(category).append(NEWLINE);
            }
        }

        stringBuilder.append(getResources().getString(R.string.flags_title_bold));
        ArrayList<String> flagsStrings = getFlags();
        if (!flagsStrings.isEmpty()) {
            for (String thisFlagString : flagsStrings) {
                stringBuilder.append(thisFlagString).append(NEWLINE);
            }
        } else {
            stringBuilder.append(getResources().getString(R.string.none)).append(NEWLINE);
        }

        try {
            Bundle intentBundle = editableIntent.getExtras();
            if (intentBundle != null) {
                Set<String> keySet = intentBundle.keySet();
                stringBuilder.append(getResources().getString(R.string.extras_title_bold));
                int count = 0;

                for (String key : keySet) {
                    count++;
                    Object thisObject = intentBundle.get(key);
                    stringBuilder.append(getResources().getQuantityString(R.plurals.extra_count, count, count));
                    String thisClass = thisObject.getClass().getName();
                    if (thisClass != null) {
                        stringBuilder.append(getResources().getString(R.string.class_text)).append(thisClass)
                                .append(NEWLINE);
                    }
                    stringBuilder.append(getResources().getString(R.string.key)).append(key).append(NEWLINE);

                    if (thisObject instanceof String || thisObject instanceof Long || thisObject instanceof Integer
                            || thisObject instanceof Boolean || thisObject instanceof Uri) {
                        stringBuilder.append(getResources().getString(R.string.value)).append(thisObject.toString())
                                .append(NEWLINE);
                    } else if (thisObject instanceof ArrayList) {
                        stringBuilder.append(getResources().getString(R.string.values_break));
                        ArrayList thisArrayList = (ArrayList) thisObject;
                        for (Object thisArrayListObject : thisArrayList) {
                            stringBuilder.append(thisArrayListObject.toString()).append(NEWLINE);
                        }
                    }
                }
            }
        } catch (Exception e) {
            stringBuilder.append(getResources().getString(R.string.bundle_title_bold_uppercase));
            stringBuilder.append(getResources().getString(R.string.error_extracting_extras_red));
            e.printStackTrace();
        }

        PackageManager pm = getPackageManager();
        List<ResolveInfo> resolveInfo = pm.queryIntentActivities(editableIntent, 0);

        // Remove Intent Intercept from matching activities
        int numberOfMatchingActivities = resolveInfo.size() - 1;

        if (numberOfMatchingActivities < 1) {
            stringBuilder.append(getResources().getString(R.string.no_activities_match_intent_title_bold));
        } else {
            stringBuilder.append(getResources().getQuantityString(R.plurals.activites_match_intent_title_bold,
                    numberOfMatchingActivities, numberOfMatchingActivities));
            for (int i = 0; i <= numberOfMatchingActivities; i++) {
                ResolveInfo info = resolveInfo.get(i);
                ActivityInfo activityinfo = info.activityInfo;
                if (!activityinfo.packageName.equals(getPackageName())) {
                    stringBuilder.append(activityinfo.loadLabel(pm))
                            .append(getResources().getString(R.string.open_bracket))
                            .append(activityinfo.packageName).append(getResources().getString(R.string.dash))
                            .append(activityinfo.name)
                            .append(getResources().getString(R.string.close_bracket_break));
                }
            }
        }

        return Html.fromHtml(stringBuilder.toString());
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu, menu);
        MenuItem actionItem = menu.findItem(R.id.share);

        shareActionProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(actionItem);

        if (shareActionProvider == null) {
            shareActionProvider = new ShareActionProvider(this);
            MenuItemCompat.setActionProvider(actionItem, shareActionProvider);
        }

        shareActionProvider.setShareHistoryFileName(ShareActionProvider.DEFAULT_SHARE_HISTORY_FILE_NAME);
        refreshUI();
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.copy:
            copyIntentDetails();
            break;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    protected void onPause() {
        super.onPause();
        textWatchersActive = false;
    }

    @Override
    protected void onResume() {
        super.onResume();
        overridePendingTransition(0, 0); // inhibit new activity animation when
        // resetting intent details
        textWatchersActive = true;
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putBoolean(INTENT_EDITED, resetIntentButton.getVisibility() == View.VISIBLE);
    }

    // support for onActivityResult
    // OriginatorActivity -> IntentIntercept -> resendIntentActivity
    // Forward result of sub-activity {resendIntentActivity}
    // to caller of this activity {OriginatorActivity}.
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        this.lastResultCode = Integer.valueOf(resultCode);
        this.lastResultIntent = getUri(data);
        super.onActivityResult(requestCode, resultCode, data);
        setResult(resultCode, data);
        refreshUI();
    }

    private static String getUri(Intent src) {
        return (src != null) ? src.toUri(Intent.URI_INTENT_SCHEME) : null;
    }

    private Intent cloneIntent(String intentUri) {
        if (intentUri != null) {
            try {
                Intent clone = Intent.parseUri(intentUri, Intent.URI_INTENT_SCHEME);

                // bugfix #14: restore extras that are lost in the intent <-> string conversion
                if (additionalExtras != null) {
                    clone.putExtras(additionalExtras);
                }

                return clone;
            } catch (URISyntaxException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}