Java tutorial
/* * IntentsLab - Android app for playing with Intents and Binder IPC * Copyright (C) 2014 Micha Bednarski * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.github.michalbednarski.intentslab.editor; import android.app.AlertDialog; import android.content.ComponentName; import android.content.Intent; import android.content.IntentFilter; import android.content.res.Resources; import android.net.Uri; import android.os.Bundle; import android.support.v4.app.ActivityCompat; import android.text.Editable; import android.text.TextWatcher; import android.text.method.LinkMovementMethod; import android.text.style.ClickableSpan; import android.view.ContextMenu; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.AdapterView.OnItemSelectedListener; import android.widget.ArrayAdapter; import android.widget.AutoCompleteTextView; import android.widget.Button; import android.widget.CheckBox; import android.widget.Spinner; import android.widget.TextView; import com.github.michalbednarski.intentslab.FormattedTextBuilder; import com.github.michalbednarski.intentslab.NameAutocompleteAdapter; import com.github.michalbednarski.intentslab.PackageNameAutocompleteAdapter; import com.github.michalbednarski.intentslab.R; import com.github.michalbednarski.intentslab.Utils; import com.github.michalbednarski.intentslab.providerlab.AdvancedQueryActivity; import com.github.michalbednarski.intentslab.providerlab.UriAutocompleteAdapter; import com.github.michalbednarski.intentslab.providerlab.proxy.ProxyProvider; import com.github.michalbednarski.intentslab.providerlab.proxy.ProxyProviderForGrantUriPermission; import com.github.michalbednarski.intentslab.xposedhooks.api.IntentTracker; import com.github.michalbednarski.intentslab.xposedhooks.api.ReadBundleEntryInfo; import com.github.michalbednarski.intentslab.xposedhooks.api.TrackerUpdateListener; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.Iterator; import java.util.Set; public class IntentGeneralFragment extends IntentEditorPanel implements OnItemSelectedListener, TrackerUpdateListener { /** * List of actions for which we don't imply that if there is accepted data type but no scheme then schemes * can be file: or content:. * * System makes such assumption for every IntentFilter, but in case of these we don't think application will expect * such behavior. */ private static final HashSet<String> NO_IMPLICIT_URI_ACTIONS = new HashSet<String>(2); static { NO_IMPLICIT_URI_ACTIONS.add(Intent.ACTION_PICK); NO_IMPLICIT_URI_ACTIONS.add(Intent.ACTION_GET_CONTENT); }; private static final String STATE_SHOW_ADVANCED = "intenteditor.general.showadvanced"; private boolean mShowAdvanced; private AutoCompleteTextView mDataText; private View mDataTextWrapper; private View mDataTextHeader; private TextView mComponentText; private Spinner mComponentTypeSpinner; private Spinner mMethodSpinner; private TextView mResponseCodeTextView; private Intent mEditedIntent; private UriAutocompleteAdapter mUriAutocompleteAdapter; private AutoCompleteTextView mPackageNameText; private View mResultCodeWrapper; private View mComponentTypeAndMethodSpinners; private View mComponentHeader; private View mComponentFieldWithButtons; private View mPackageNameHeader; private TextView mIntentTrackerSummary; public IntentGeneralFragment() { } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (savedInstanceState != null) { mShowAdvanced = savedInstanceState.getBoolean(STATE_SHOW_ADVANCED); } setHasOptionsMenu(true); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { super.onCreateView(inflater, container, savedInstanceState); View v = inflater.inflate(R.layout.intent_editor_general, container, false); // Prepare form mActionText = (AutoCompleteTextView) v.findViewById(R.id.action); mActionsSpinner = (Spinner) v.findViewById(R.id.action_spinner); mDataText = (AutoCompleteTextView) v.findViewById(R.id.data); mDataTextWrapper = v.findViewById(R.id.data_wrapper); mDataTextHeader = v.findViewById(R.id.data_header); mDataTypeHeader = v.findViewById(R.id.data_type_header); mDataTypeText = (TextView) v.findViewById(R.id.data_type); mDataTypeSpinner = (Spinner) v.findViewById(R.id.data_type_spinner); mDataTypeSpinnerWrapper = v.findViewById(R.id.data_type_spinner_wrapper); mDataTypeSlash = v.findViewById(R.id.data_type_slash); mDataSubtypeText = (TextView) v.findViewById(R.id.data_type_after_slash); mComponentText = (TextView) v.findViewById(R.id.component); mComponentTypeSpinner = (Spinner) v.findViewById(R.id.componenttype); mComponentTypeSpinner.setAdapter(new ArrayAdapter<String>(getActivity(), android.R.layout.simple_spinner_item, getResources().getStringArray(R.array.componenttypes))); mMethodSpinner = (Spinner) v.findViewById(R.id.method); mCategoriesContainer = (ViewGroup) v.findViewById(R.id.categories); mAddCategoryButton = (Button) v.findViewById(R.id.category_add); mCategoriesHeader = v.findViewById(R.id.categories_header); mResponseCodeTextView = (TextView) v.findViewById(R.id.response_code); mPackageNameText = (AutoCompleteTextView) v.findViewById(R.id.package_name); mResultCodeWrapper = v.findViewById(R.id.result_intent_wrapper); mComponentTypeAndMethodSpinners = v.findViewById(R.id.component_and_method_spinners); mComponentHeader = v.findViewById(R.id.component_header); mComponentFieldWithButtons = v.findViewById(R.id.component_field_with_buttons); mPackageNameHeader = v.findViewById(R.id.package_name_header); mIntentTrackerSummary = (TextView) v.findViewById(R.id.intent_tracker_summary); mIntentTrackerSummary.setMovementMethod(LinkMovementMethod.getInstance()); // Apparently using android:scrollHorizontally="true" does not work. // http://stackoverflow.com/questions/9011944/android-ice-cream-sandwich-edittext-disabling-spell-check-and-word-wrap mComponentText.setHorizontallyScrolling(true); // Bind button actions (android:onClick="" applies to hosting activity) mAddCategoryButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { addCategoryTextField(""); } }); v.findViewById(R.id.component_pick).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { pickComponent(); } }); v.findViewById(R.id.component_pick).setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { findComponent(); return true; } }); v.findViewById(R.id.component_clear).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { mComponentText.setText(""); } }); v.findViewById(R.id.data_query_button).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { getIntentEditor().updateIntent(); startActivity( new Intent(getActivity(), AdvancedQueryActivity.class).setData(mEditedIntent.getData())); } }); v.findViewById(R.id.data_query_button) .setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() { @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { getIntentEditor().updateIntent(); final Uri uri = mEditedIntent.getData(); if (uri != null) { final String scheme = uri.getScheme(); final String authority = uri.getAuthority(); if ("content".equals(scheme) && authority != null) { menu.add("Wrap").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { mDataText.setText("content://" + ((mEditedIntent.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION)) != 0 ? ProxyProviderForGrantUriPermission.AUTHORITY : ProxyProvider.AUTHORITY) + "/" + authority + uri.getPath()); return true; } }); } } } }); // Set up autocomplete mUriAutocompleteAdapter = new UriAutocompleteAdapter(getActivity()); mDataText.setAdapter(mUriAutocompleteAdapter); mPackageNameText.setAdapter(new PackageNameAutocompleteAdapter(getActivity())); // Get edited intent for form filling mEditedIntent = getEditedIntent(); // Component field, affects options menu if (mEditedIntent.getComponent() != null) { mComponentText.setText(mEditedIntent.getComponent().flattenToShortString()); } mComponentText.addTextChangedListener(new TextWatcher() { @Override public void onTextChanged(CharSequence s, int start, int before, int count) { updateIntentComponent(); } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void afterTextChanged(Editable s) { } }); // Fill the form setupActionSpinnerOrField(); updateNonActionIntentFilter(true); mDataText.setText(mEditedIntent.getDataString()); mPackageNameText.setText(mEditedIntent.getPackage()); showOrHideFieldsForResultIntent(); showOrHideAdvancedFields(); if (getComponentType() == IntentEditorConstants.RESULT) { mResponseCodeTextView.setText(String.valueOf(getIntentEditor().getMethodId())); mResponseCodeTextView.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { try { getIntentEditor().setMethodId(Integer.parseInt(s.toString())); mResponseCodeTextView.setError(null); } catch (NumberFormatException e) { mResponseCodeTextView.setError(getIntentEditor().getText(R.string.value_parse_error)); } } @Override public void afterTextChanged(Editable s) { } }); } else { initComponentAndMethodSpinners(); } setupActionAutocomplete(); // Prepare intent tracker { IntentTracker tracker = getIntentEditor().getIntentTracker(); if (tracker != null) { tracker.setUpdateListener(this, true); } else { onNoTracker(); } } return v; } private void initComponentAndMethodSpinners() { int componentType = getComponentType(); initMethodSpinner(componentType); mComponentTypeSpinner.setSelection(componentType); mMethodSpinner.setSelection(getIntentEditor().getMethodId()); mComponentTypeSpinner.setOnItemSelectedListener(this); mMethodSpinner.setOnItemSelectedListener(this); } @Override public void onDestroyView() { IntentTracker tracker = getIntentEditor().getIntentTracker(); if (tracker != null) { tracker.clearUpdateListener(); } super.onDestroyView(); mActionText = null; mDataText = null; mComponentText = null; mComponentTypeSpinner = null; mMethodSpinner = null; mCategoriesContainer = null; mCategoryTextInputs = null; mCategoryCheckBoxes = null; mAddCategoryButton = null; } // Component type and method @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { if (parent == mComponentTypeSpinner) { if (getComponentType() != position) { initMethodSpinner(position); getIntentEditor().setComponentType(position); } } else if (parent == mMethodSpinner) { if (getIntentEditor().getMethodId() != position) { getIntentEditor().setMethodId(position); } } else if (parent == mActionsSpinner) { updateNonActionIntentFilter(false); } } @Override public void onNothingSelected(AdapterView<?> parent) { // Spinner won't have nothing selected } static String[] getMethodNamesArray(Resources res, int componentType) { return res.getStringArray(componentType == IntentEditorConstants.ACTIVITY ? R.array.activitymethods : componentType == IntentEditorConstants.BROADCAST ? R.array.broadcastmethods : componentType == IntentEditorConstants.SERVICE ? R.array.servicemethods : 0); } private void initMethodSpinner(int componentType) { mMethodSpinner.setAdapter(new ArrayAdapter<String>(getActivity(), android.R.layout.simple_spinner_item, getMethodNamesArray(getResources(), componentType))); } // ACTIONS private AutoCompleteTextView mActionText; private Spinner mActionsSpinner; private String[] mAvailbleActions; private void setupActionSpinnerOrField() { IntentFilter[] intentFilters = getIntentEditor().getAttachedIntentFilters(); if (intentFilters != null) { // Build action set Set<String> actionSet = new HashSet<String>(); for (IntentFilter filter : intentFilters) { for (int i = 0, j = filter.countActions(); i < j; i++) { actionSet.add(filter.getAction(i)); } } // Check if we have any action and if not (invalid intent-filter) // switch to textfield if (actionSet.isEmpty()) { setupActionField(); return; } // Convert action set to array mAvailbleActions = actionSet.toArray(new String[actionSet.size()]); Arrays.sort(mAvailbleActions); // Select current action String action = mEditedIntent.getAction(); if (action == null) { action = Intent.ACTION_VIEW; } int position = Arrays.binarySearch(mAvailbleActions, action); mActionsSpinner.setAdapter(new ArrayAdapter<String>(getActivity(), android.R.layout.simple_spinner_item, mAvailbleActions)); if (position < 0) { position = 0; } mActionsSpinner.setSelection(position); // Show spinner and hide textfield mActionText.setVisibility(View.GONE); mActionsSpinner.setVisibility(View.VISIBLE); // Set listener for action change to refresh intent filter mActionsSpinner.setOnItemSelectedListener(this); } else { setupActionField(); } } private void setupActionField() { mAvailbleActions = null; mActionsSpinner.setOnItemSelectedListener(null); mActionText.setText(mEditedIntent.getAction()); mActionText.setVisibility(View.VISIBLE); mActionsSpinner.setVisibility(View.GONE); } // CATEGORIES private ViewGroup mCategoriesContainer; private ArrayList<TextView> mCategoryTextInputs; private CheckBox[] mCategoryCheckBoxes; private Button mAddCategoryButton; private View mCategoriesHeader; public void addCategoryTextField(String category) { final ViewGroup row = (ViewGroup) getActivity().getLayoutInflater().inflate(R.layout.category_row, mCategoriesContainer, false); final TextView textField = ((TextView) row.findViewById(R.id.categoryText)); textField.setText(category); row.findViewById(R.id.row_remove).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { mCategoriesContainer.removeView(row); mCategoryTextInputs.remove(textField); } }); mCategoriesContainer.addView(row); mCategoryTextInputs.add(textField); } private void setupCategoryCheckBoxes(Set<String> availableCategories) { mCategoriesContainer.removeAllViews(); mCategoryCheckBoxes = new CheckBox[availableCategories.size()]; int i = 0; for (String category : availableCategories) { CheckBox cb = new CheckBox(getActivity()); cb.setText(category); cb.setTag(category); cb.setChecked(mEditedIntent.hasCategory(category)); mCategoriesContainer.addView(cb); mCategoryCheckBoxes[i++] = cb; } mAddCategoryButton.setVisibility(View.GONE); mCategoriesHeader.setVisibility(availableCategories.size() == 0 ? View.GONE : View.VISIBLE); mCategoryTextInputs = null; } private void setFreeFormCategoryEditor() { if (mCategoryTextInputs != null) { return; // We are already in free form mode } mCategoriesContainer.removeAllViews(); mCategoryTextInputs = new ArrayList<TextView>(); Set<String> categories = mEditedIntent.getCategories(); if (categories != null) { for (String category : categories) { addCategoryTextField(category); } } mAddCategoryButton.setVisibility(View.VISIBLE); mCategoriesHeader.setVisibility(View.VISIBLE); mCategoryCheckBoxes = null; } // COMPONENT private void pickComponent() { updateEditedIntent(mEditedIntent); ComponentPickerDialogFragment componentPickerDialog = new ComponentPickerDialogFragment(); componentPickerDialog.setTargetFragment(this, 0); componentPickerDialog.show(getActivity().getSupportFragmentManager(), "component-picker"); } private void findComponent() { getIntentEditor().updateIntent(); new FindComponentDialog().show(getActivity().getSupportFragmentManager(), "component-finder"); } void setComponentText(String text) { mComponentText.setText(text); } private void updateIntentComponent() { ComponentName newComponentName = ComponentName.unflattenFromString(mComponentText.getText().toString()); ComponentName oldComponentName = mEditedIntent.getComponent(); mEditedIntent.setComponent(newComponentName); if ((newComponentName != null) != (oldComponentName != null)) { getIntentEditor().safelyInvalidateOptionsMenu(); } } // Contact with activity @Override public void updateEditedIntent(Intent editedIntent) { // Intent action if (mAvailbleActions != null) { editedIntent.setAction((String) mActionsSpinner.getSelectedItem()); } else { String action = mActionText.getText().toString(); if ("".equals(action)) { action = null; } editedIntent.setAction(action); } // Categories { // Clear categories (why there's no api for this) Set<String> origCategories = editedIntent.getCategories(); if (origCategories != null) { for (String category : origCategories.toArray(new String[origCategories.size()])) { editedIntent.removeCategory(category); } } } // Fill categories if (mCategoryCheckBoxes != null) { // Fill categories from checkboxes for (CheckBox cb : mCategoryCheckBoxes) { String category = (String) cb.getTag(); if (cb.isChecked()) { editedIntent.addCategory(category); } } } else { // Fill categories from textfields for (TextView categoryTextView : mCategoryTextInputs) { editedIntent.addCategory(categoryTextView.getText().toString()); } } // Intent data (Uri) and type (MIME) String data = mDataText.getText().toString(); editedIntent.setDataAndType(data.equals("") ? null : Uri.parse(data), getDataType()); // Package name { String packageName = mPackageNameText.getText().toString(); if ("".equals(packageName)) { editedIntent.setPackage(null); } else { editedIntent.setPackage(packageName); } } // Set component for explicit intent updateIntentComponent(); } // DATA TYPE private View mDataTypeHeader; private TextView mDataTypeText; private Spinner mDataTypeSpinner; private View mDataTypeSpinnerWrapper; private View mDataTypeSlash; private TextView mDataSubtypeText; private boolean mUseDataType; private boolean mUseDataTypeSpinner; private boolean mDataTypeMayBeNull; /** * Switch data type selector to filtered (Spinner) mode and initialize it's value */ private void setupFilteredDataTypeFields(boolean mayBeUntyped, boolean mayAutoDetect, Set<String> dataTypes) { mDataTypeMayBeNull = mayBeUntyped; if (dataTypes.size() == 0) { // IntentFilter doesn't accept data type mDataTypeHeader.setVisibility(View.GONE); mDataTypeText.setVisibility(View.GONE); mDataTypeSpinnerWrapper.setVisibility(View.GONE); mUseDataType = false; } else { // We have set of accepted data types, show them in spinner // Build array of items String[] spinnerItems = dataTypes.toArray(new String[dataTypes.size()]); // Sort them Arrays.sort(spinnerItems); // Find current int currentPosition = -1; int slashPos = -1; if (mEditedIntent.getType() != null) { currentPosition = Arrays.binarySearch(spinnerItems, mEditedIntent.getType()); if (currentPosition < 0) { // Try also partial type matching slashPos = mEditedIntent.getType().indexOf('/'); if (slashPos != -1) { currentPosition = Arrays.binarySearch(spinnerItems, mEditedIntent.getType().substring(0, slashPos)); } } } // If we also accept untyped variant add that option at first position mDataTypeMayBeNull = mayBeUntyped || mayAutoDetect; if (mDataTypeMayBeNull) { String[] shiftedSpinnerItems = new String[spinnerItems.length + 1]; System.arraycopy(spinnerItems, 0, shiftedSpinnerItems, 1, spinnerItems.length); currentPosition++; shiftedSpinnerItems[0] = mayBeUntyped ? getActivity().getString(R.string.data_type_unspecified) : getActivity().getString(R.string.data_type_autodetect); spinnerItems = shiftedSpinnerItems; } // Put data in Spinner mDataTypeSpinner.setAdapter( new ArrayAdapter<String>(getActivity(), android.R.layout.simple_spinner_item, spinnerItems)); if (currentPosition < 0) { currentPosition = 0; } mDataTypeSpinner.setSelection(currentPosition); // Show the Spinner mDataTypeHeader.setVisibility(View.VISIBLE); mDataTypeText.setVisibility(View.GONE); mDataTypeSpinnerWrapper.setVisibility(View.VISIBLE); // Prepare subtype field boolean isPartialType = useSpinnerAndTextEditForDataType(); if (isPartialType && slashPos != -1) { mDataSubtypeText.setText(mEditedIntent.getType().substring(slashPos + 1)); } mDataTypeSlash.setVisibility(isPartialType ? View.VISIBLE : View.GONE); mDataSubtypeText.setVisibility(isPartialType ? View.VISIBLE : View.GONE); // Bind event mDataTypeSpinner.setOnItemSelectedListener(mTypeSpinnerListener); // Set flags mUseDataType = true; mUseDataTypeSpinner = true; } } /** * Switch data type to unfiltered (TextView) mode and initialize it's value */ private void setupUnfilteredDataTypeFields() { // Unbind event mDataTypeSpinner.setOnItemSelectedListener(null); // Show field and hide spinner mDataTypeHeader.setVisibility(View.VISIBLE); mDataTypeText.setVisibility(View.VISIBLE); mDataTypeSpinnerWrapper.setVisibility(View.GONE); // Show text mDataTypeText.setText(mEditedIntent.getType()); // Set flags mUseDataType = true; mUseDataTypeSpinner = false; } private OnItemSelectedListener mTypeSpinnerListener = new OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { // Show or hide data subtype field boolean isPartialType = useSpinnerAndTextEditForDataType(); mDataTypeSlash.setVisibility(isPartialType ? View.VISIBLE : View.GONE); mDataSubtypeText.setVisibility(isPartialType ? View.VISIBLE : View.GONE); } @Override public void onNothingSelected(AdapterView<?> parent) { // Spinner won't have nothing selected } }; /** * Returns true if we should have subtype text field next to data type spinner * If mUseDataTypeSpinner is false the result is not defined and method should not be called */ private boolean useSpinnerAndTextEditForDataType() { if (mDataTypeMayBeNull && mDataTypeSpinner.getSelectedItemPosition() == 0) { return false; } else { return !((String) mDataTypeSpinner.getSelectedItem()).contains("/"); } } /** * Get usable data type as String or null if user choose to not provide any. */ private String getDataType() { if (!mUseDataType) { // Intent filter disallow data type return null; } if (mUseDataTypeSpinner) { // We are using spinner for data type // Check if this is 'Unspecified' option if (mDataTypeMayBeNull && mDataTypeSpinner.getSelectedItemPosition() == 0) { return null; } // Get value from spinner String type = (String) mDataTypeSpinner.getSelectedItem(); // And add value from subtype field if needed if (useSpinnerAndTextEditForDataType()) { type += "/" + mDataSubtypeText.getText().toString(); } return type; } else { // Just use value from normal text field String dataType = mDataTypeText.getText().toString(); return dataType.equals("") ? null : dataType; } } // COMMON private void showOrHideFieldsForResultIntent() { boolean isResultIntent = getComponentType() == IntentEditorConstants.RESULT; mResultCodeWrapper.setVisibility(isResultIntent ? View.VISIBLE : View.GONE); mComponentTypeAndMethodSpinners.setVisibility(isResultIntent ? View.GONE : View.VISIBLE); } @Override void onIntentFiltersChanged(IntentFilter[] newIntentFilters) { updateEditedIntent(mEditedIntent); setupActionSpinnerOrField(); updateNonActionIntentFilter(false); } void updateComponent() { if (mEditedIntent.getComponent() != null) { mComponentText.setText(mEditedIntent.getComponent().flattenToShortString()); } else { mComponentText.setText(""); } } @Override public void onComponentTypeChanged(int newComponentType) { if (mComponentTypeSpinner.getSelectedItemId() != newComponentType) { mComponentTypeSpinner.setSelection(newComponentType); } setupActionAutocomplete(); } private void setupActionAutocomplete() { final int componentType = getComponentType(); mActionText.setAdapter(componentType == IntentEditorConstants.ACTIVITY ? new NameAutocompleteAdapter(getActivity(), R.raw.activity_actions) : componentType == IntentEditorConstants.BROADCAST ? new NameAutocompleteAdapter(getActivity(), R.raw.broadcast_actions) : null); } /** * Update IntentFilter of categories and data * @param isInit */ private void updateNonActionIntentFilter(boolean isInit) { // If we don't have any IntentFilter use non-filtering editors if (mAvailbleActions == null) { setFreeFormCategoryEditor(); mDataTextWrapper.setVisibility(View.VISIBLE); mDataTextHeader.setVisibility(View.VISIBLE); mUriAutocompleteAdapter.setIntentFilters(null); setupUnfilteredDataTypeFields(); return; } // Update edited intent if (!isInit) { updateEditedIntent(mEditedIntent); } // Get all IntentFilters final IntentFilter[] allIntentFilters = getIntentEditor().getAttachedIntentFilters(); // Get selected action String action = (String) mActionsSpinner.getSelectedItem(); HashSet<String> availableCategories = new HashSet<String>(); HashSet<String> availableMimeTypes = new HashSet<String>(); boolean acceptsAnyDataType = false; boolean acceptsUntypedData = false; boolean mayAutoDetectType = false; boolean acceptsUris = false; ArrayList<IntentFilter> selectedIntentFilters = new ArrayList<IntentFilter>(); // Iterate over intent filters that has selected action for (IntentFilter filter : allIntentFilters) { if (filter.hasAction(action)) { // List categories if (filter.countCategories() != 0) { for (Iterator<String> iterator = filter.categoriesIterator(); iterator.hasNext();) { availableCategories.add(iterator.next()); } } // List available types or set flag that we don't need them if (filter.countDataTypes() == 0) { acceptsUntypedData = true; } else { for (Iterator<String> iterator = filter.typesIterator(); iterator.hasNext();) { final String type = iterator.next(); if ("*".equals(type)) { acceptsAnyDataType = true; } else { availableMimeTypes.add(type); } } } // Scan schemes to see if system can auto detect type if (!mayAutoDetectType) { if (filter.countDataSchemes() != 0) { for (Iterator<String> iterator = filter.schemesIterator(); iterator.hasNext();) { String scheme = iterator.next(); if ("content".equals(scheme) || "file".equals(scheme)) { mayAutoDetectType = true; break; } } } else if ( // No schemes declared filter.countDataTypes() != 0 && ( // There's at least one !NO_IMPLICIT_URI_ACTIONS.contains(action) || // Action is not on list filter.countDataAuthorities() != 0 // There is host specified )) { // Intent will match empty, content: or file: scheme acceptsUris = true; mayAutoDetectType = true; } } // Check if we have data if (filter.countDataSchemes() != 0) { acceptsUris = true; } // Save used IntentFilter to list because UriAutocompleteAdapter scans them on his own selectedIntentFilters.add(filter); } } // Setup categories setupCategoryCheckBoxes(availableCategories); // Setup data type if (acceptsAnyDataType) { setupUnfilteredDataTypeFields(); } else { setupFilteredDataTypeFields(acceptsUntypedData, mayAutoDetectType, availableMimeTypes); } // Setup data uri mDataTextWrapper.setVisibility(acceptsUris ? View.VISIBLE : View.GONE); mDataTextHeader.setVisibility(acceptsUris ? View.VISIBLE : View.GONE); if (!acceptsUris) { mDataText.setText(""); } mUriAutocompleteAdapter .setIntentFilters(selectedIntentFilters.toArray(new IntentFilter[selectedIntentFilters.size()])); } // Advanced toggle private void showOrHideAdvancedFields() { boolean isResultIntent = getComponentType() == IntentEditorConstants.RESULT; // Component, advanced only in result intent boolean showComponent = !isResultIntent || mShowAdvanced || mEditedIntent.getComponent() != null; mComponentHeader.setVisibility(showComponent ? View.VISIBLE : View.GONE); mComponentFieldWithButtons.setVisibility(showComponent ? View.VISIBLE : View.GONE); // Package name boolean showPackage = mShowAdvanced || mEditedIntent.getPackage() != null; mPackageNameText.setVisibility(showPackage ? View.VISIBLE : View.GONE); mPackageNameHeader.setVisibility(showPackage ? View.VISIBLE : View.GONE); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { inflater.inflate(R.menu.intent_editor_general, menu); } @Override public void onPrepareOptionsMenu(Menu menu) { MenuItem advanced = menu.findItem(R.id.advanced); advanced.setChecked(mShowAdvanced); Utils.updateLegacyCheckedIcon(advanced); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.advanced: updateEditedIntent(mEditedIntent); mShowAdvanced = !mShowAdvanced; showOrHideAdvancedFields(); ActivityCompat.invalidateOptionsMenu(getActivity()); return true; default: return false; } } // Intent tracker @Override void onIntentTrackerChanged(IntentTracker newTracker, IntentTracker oldTracker) { if (oldTracker != null) { oldTracker.clearUpdateListener(); oldTracker.getExtrasTracker().clearUpdateListener(); } if (newTracker != null) { newTracker.setUpdateListener(this, true); newTracker.getExtrasTracker().setUpdateListener(this, true); } else { onNoTracker(); } } @Override public void onTrackerUpdate() { IntentTracker tracker = getIntentEditor().getIntentTracker(); FormattedTextBuilder ftb = new FormattedTextBuilder(); ftb.appendRaw("[Tracking intent]\naction " + (tracker.actionRead() ? "" : "NOT ") + "read"); ftb.appendText("Read extras:"); for (final ReadBundleEntryInfo entryInfo : tracker.getExtrasTracker().getReadEntries()) { ftb.appendClickable(entryInfo.name, new ClickableSpan() { @Override public void onClick(View widget) { String message = entryInfo.methodName; if (entryInfo.stackTrace != null) { message += "\n\n"; for (StackTraceElement element : entryInfo.stackTrace) { message += element.getClassName() + " " + element.getMethodName() + " " + element.getLineNumber() + "\n"; } } new AlertDialog.Builder(getActivity()).setTitle(entryInfo.name).setMessage(message).show(); } }); } mIntentTrackerSummary.setText(ftb.getText()); mIntentTrackerSummary.setVisibility(View.VISIBLE); } private void onNoTracker() { mIntentTrackerSummary.setVisibility(View.GONE); } }