Android Handle/Override/Interrupt Intent from the same Activity that fired it - android

I have an android application that has to parse fairly large amounts of HTML and place it inside one or more TextViews. So any one of these given text views might contain one more html link. These links are created dynamically and embedded in the rest of the text. It seems the only way I can allow these links to work is by using the following code.
TextView textView = new TextView(mContext);
textView.setAutoLinkMask(0);
textView.setLinksClickable(true);
Linkify.addLinks(textView, Patterns.WEB_URL, "ref://");
textView.setMovementMethod(LinkMovementMethod.getInstance());
textView.append(getSpannable(htmlString));
This works just fine for any http link, I simply added filter to a WebActivity in my a filter in my manifest and call it a day.
My problem however is that I have links that I want to send with additional data not contained in the link itself. Additionally the the extra data I want to send would change based on the target of the link. I cannot do this because the intents are fired based on the HTML data the user clicks.
Is there any way to modify / intercept/ override an Intent fired by my Activity in order to add additional data to it after it has been fired?
Edit With Answer Based on #Tanis.7x Suggestion
In my original question I failed to specify that I was using a Spanned derived from Html.fromHtml(htmlString);
I was able to figured out a solution based on #Tanis.7x answer and this question's accepted answer Android TextView with Clickable Links: how to capture clicks?
TextView textView = new TextView(mContext);
textView.setAutoLinkMask(0);
textView.setLinksClickable(true);
Linkify.addLinks(textView, Patterns.WEB_URL, "ref://");
textView.setMovementMethod(LinkMovementMethod.getInstance());
textView.append(getHtmlStringForTextView(Html.fromHtml(htmlString)));
private SpannableStringBuilder getHtmlStringForTextView(String html)
{
CharSequence sequence = Html.fromHtml(html);
SpannableStringBuilder strBuilder = new SpannableStringBuilder(sequence);
URLSpan[] urls = strBuilder.getSpans(0, sequence.length(), URLSpan.class);
for(URLSpan span : urls) {
makeLinkClickable(strBuilder, span);
}
return strBuilder;
}
private void makeLinkClickable(SpannableStringBuilder spannableStringBuilder, final URLSpan span)
{
int start = spannableStringBuilder.getSpanStart(span);
int end = spannableStringBuilder.getSpanEnd(span);
int flags = spannableStringBuilder.getSpanFlags(span);
ClickableSpan clickable = new ClickableSpan() {
public void onClick(View view) {
//handle click here
}
};
spannableStringBuilder.setSpan(clickable, start, end, flags);
spannableStringBuilder.removeSpan(span);
}

You cannot intercept intents after they are fired.
If you look at the source of URLSpan (which is responsible for handling links in TextViews), you will see that in it's onClick() method it creates an appropriate Intent, then fires it off with a call to context.startActivity(intent);. At that point in time the Intent is out of your hands and responsibility for handling the Intent lies within the Android framework.
You can, however, get the behavior you desire. First you need to create your own URLSpan subclass. Override onClick() and handle the click however you want.
public class CustomUrlSpan extends URLSpan {
public CustomUrlSpan(String url) {
super(url);
}
/* You may want to properly support the ParcelableSpan interface as well */
#Override
public void onClick(View widget) {
Uri uri = Uri.parse(getURL());
Context context = widget.getContext();
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
// Put your custom intent handling in here
try {
context.startActivity(intent);
} catch (ActivityNotFoundException e) {
Log.w("URLSpan", "Actvity was not found for intent, " + intent.toString());
}
}
}
Then, instead of using Linkify to make your links clickable, you will need to create the spans yourself. Take a look at the Linkify source if you need a starting point- you'll probably want to do something very similar to addLinks(), but with your CustomUrlSpan instead of the standard URLSpan.

Related

Android JAVA navigate to fragment passing data with Navigation Component

I'm trying to navigate from a RecyclerView adapter to a detail fragment, passing an id value.
I can navigate from an item selected in the RecyclerView to open the GameDetailFragment using the following:
this.cardGame.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
NavDirections action = ScoreboardFragmentDirections.actionScoreboardFragmentToGameDetailFragment();
Navigation.findNavController(v).navigate(action);
}
});
However, I can't seem to find how to pass data when clicking on the CardView item to the GameDetailFragment to query the details.
I can find many examples in Kotlin, but I'm just learning Java and Android and would rather not start with Kotlin.
Using the example from the documentation:
#Override
public void onClick(View view) {
EditText amountTv = (EditText) getView().findViewById(R.id.editTextAmount);
int amount = Integer.parseInt(amountTv.getText().toString());
ConfirmationAction action = SpecifyAmountFragmentDirections.confirmationAction();
action.setAmount(amount);
Navigation.findNavController(view).navigate(action);
}
It seems it's inaccurate as the documentation indicates that the word "action" is added to the END of the class name.
I decided to look at the actual generated class file, without modifying it, of course. The word "action" is added to the BEGINNING of the class file name or combination of the originating and receiving destinations.
The result:
this.cardGame.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String strTemp = "todo";
ScoreboardFragmentDirections.ActionScoreboardFragmentToGameDetailFragment action = ScoreboardFragmentDirections.actionScoreboardFragmentToGameDetailFragment();
action.setMessage(strTemp);
Navigation.findNavController(v).navigate(action);
}
});
P.S. Have you ever even tried to follow along with the documentation? There is very little, if any, flow to their descriptions.
One paragraph they're talking about cats & dogs, then the next paragraph they talk about apples and oranges. As a noobie/beginner, it's incredibly frustrating because somehow you have to figure out how they all interconnect/interrelate to one another.
Then, when we ask, we get snooty responses referring right back to the same documentation. Like really...
But, what do I know, I'm only a beginner.

Button Text and Booleans (Simple Android App)

I'm build the Fun Facts app on the Android Development Track. I decided to take a exploratory detour and try to create a very basic introductory message to the user. I changed the factTextView text to "You can click the button below to see a new fact!" and changed the showFactButton text to "Try it out!"
From there, I changed the final line onClick object (is that an object?) to the following:
public void onClick(View view) {
String fact = mFactBook.getFact();
// Update the label with our dynamic fact
factLabel.setText(fact);
// Set button text to new fact prompt
showFactButton.setText("Show another fun fact.");
This seems to work fine. However, I feel like "updating" the button text to the same new string on every press isn't always the best practice, even if it is easy and readable. I tried to add a boolean that will check the text of the button, and update it only if it has not already been updated. This is what I've come up with so far:
View.OnClickListener listener = new View.OnClickListener() {
#Override
public String launchText = getResources().getString(R.string.start_text);
public String nextText = getResources().getString(R.string.next_text);
public String buttonText = (String) showFactButton.getText();
public boolean updateLaunchText() {
if (buttonText.equals(launchText)) {
buttonText.replaceAll(launchText, nextText);
return true;
}
else {
return true;
}
}
public void onClick(View view) {
String fact = mFactBook.getFact();
// Update the label with our dynamic fact
factLabel.setText(fact);
}
};
With the following added to strings.xml:
<string name="start_text">Try it out!</string>
<string name="next_text">Show another Fun Fact!</string>
No errors, but the button text stays on "Try it out!" I'm sure that all the extra objects are totally unnecessary compared to the first, working method for the scope of this app, but I'd still like to figure it out since I don't really have any idea what I'm doing with the boolean.
Questions: 1) What am I missing in the longer boolean approach? 2) What's the actual most efficient approach to accomplish this task?
Did you connect the listener to the button object?Without that connection no logic is applied to a button click.It goes like this:
buttonName.setOnClickListener(...)
You'd have to initialize the button object first though :)
Where r u call to method updateLaunchText() ?
you should change the objects to global object (not to create the into the listener):
private String launchText = getResources().getString(R.string.start_text);
private String nextText = getResources().getString(R.string.next_text);
private String buttonText = (String) showFactButton.getText();
and take the method updateLaunchText() out of the listener too.
and then into the onClick(View view) call to updateLaunchText() like this:
public void onClick(View view) {
updateLaunchText();
String fact = mFactBook.getFact();
// Update the label with our dynamic fact
factLabel.setText(fact);
}

Basics ---> Checking strings using if else to set value of an int, possible wrong use of onResume

So I'm still working on my first little app here, new to Android and Java, so I'm stuck on a basic little problem here. Answers to my first questions were really helpful, so after researching and not coming up with anything, I thought I'd ask for some more help!
The idea is that on another screen the user makes a choice A, B, C, or D, and that choices is passed as a string through the intent. OnResume checks if the choice is not null and sets an integer that corresponds to that string. Later when the user pushes another button, some if else logic checks that int and performs and action based on which was chosen. The problem is that the App crashed at onResume.
I learned that I have to use equals(string) to compare string reference, but maybe the problem is that I am trying to compare a string in reference to a literal string? Any help would be greatly appreciated.
Thanks!
protected void onResume() {
super.onResume();
// Get the message from the intent
Intent intent = getIntent();
String choice = intent
.getStringExtra(ExtensionSetupSlector.TORQUE_SETUP);
// Create the text view
TextView displayChoice = (TextView) findViewById(R.id.displayChoice);
if (!choice.equals("")){
displayChoice.setText(choice);
if (choice.equals("A")) {
myChoice = 1;
}
if (choice.equals("B")) {
myChoice = 2;
}
if (choice.equals("C")) {
myChoice = 3;
}
if (choice.equals("D")) {
myChoice = 4;
}
}
}
myChoice is declare right after ...extends Activity{ Also I'm not quite sure If this should really be in onResume, but it was working before I started try to set myChoice in the onResume (when I was just displaying the choice). Thanks again!
Change if (!choice.equals("")) to check for null instead. Otherwise your app attempts to access an empty reference and crashes.

Converting a SpannableString to a ClickableSpan

I am trying to make clickable links in text strings that launch an activity when clicked. I have used Linkify() to detect links in my text. This function is capable of adding links to text, but only web URLs. I need to turn my output into a ClickableSpan so that I can implement this technique.
How do I get Linkify()'s identified links to become ClickableSpans which direct to an activity?
Below is the code I have used in Linkify:
// Linkify parameters
final static Pattern pattern = Pattern.compile("\\[[^]]*]"); // defines the fact that links are bound by [square brackets]
final String scheme = "http://"; // TODO: Currently this is just a blank link
Linkify.addLinks(linkText, pattern, scheme);
For what you want to achieve, it's probably simpler to just override the startActivity() method in your Activity and intercept the ACTION_VIEW intents with the URLs in your text. Something like this:
public class MyActivity extends Activity {
#Override
public void startActivity(Intent intent) {
final String action = intent.getAction();
if (action.equals(Intent.ACTION_VIEW)) {
// launch our other activity instead
Intent ourIntent = new Intent(this, MyOtherActivity.class);
ourIntent.setData(intent.getData());
super.startActivity(ourIntent);
// we're done!
return;
}
// else, normal handling by the framework
super.startActivity(intent);
}
// the rest of your activity code
}
For reference, here's the source code for URLSpan which will trigger the startActivity() method above.
How do I get Linkify()'s identified links to become ClickableSpans which direct to an activity?
After your call to addLinks(), call getText() to retrieve the Spanned object from the TextView. This object will have a series of URLSpan objects, one per matched link -- you can get an array of those through a call to getSpans(). You will need to note where each those spans start and end (via getSpanStart() and getSpanEnd()), remove the URLSpan, and replace it with your own spans to do what you want.

Link keyword in TextView to file/directory

Is there any way that I can link a keyword in a TextView to a file or directory on the user's SD card? My app produces stack trace files when it crashes and I want the user to able to click a link in my About dialog to view either the latest one or the folder containing all of them. (Something like "If this app crashes, please send [link]the latest stack.trace file[/link] to us at myapp#example.com.")
I know it is possible to use the following code to make a Web link but I tried to modify it to use "file:///sdcard/path/to/file/stack.trace" which causes my app to Force Close when the link is clicked.
<string name="strSample">This is Web link to <a href="http://www.google.com">Google</a>!</string>
final TextView tvSample = (TextView)findViewById(R.id.tvSample);
tvSample.setMovementMethod(LinkMovementMethod.getInstance());
String strSample = getResources().getString(R.string.strSample);
tvSample.setText(Html.fromHtml(strSample));
You can extend your method by replacing Html.fromHtml(strSample) part which returns Spannable. Create your own Spannable and add ClickableSpan to it. In the onClick method you can place whatever logic you need. Here's quick examle that you can extend
public Spannable getProfileLink(final Context context) {
final String name = firstName + " " + lastName;
Spannable spans = SpannableStringBuilder.valueOf(name);
ClickableSpan clickSpan = new ClickableSpan() {
#Override
public void onClick(View widget) {
// TODO - call profile here
Toast.makeText(context, "Will call profile", Toast.LENGTH_LONG);
}
};
spans.setSpan(clickSpan, 0, name.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
return spans;
}

Categories

Resources