Skip to content

Commit

Permalink
Feature/link renderer (#1)
Browse files Browse the repository at this point in the history
* Rename .java to .kt

* Add parsing links inside of block

* Add colored url spannable
  • Loading branch information
nailshaykhraziev authored Jul 22, 2021
1 parent 2f8f9ce commit ee4f4ff
Show file tree
Hide file tree
Showing 10 changed files with 406 additions and 323 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package com.contentful.rich.android

import androidx.annotation.ColorInt
import androidx.annotation.ColorRes
import androidx.annotation.FontRes

data class AndroidConfig(
@ColorRes val textColor: Int = android.R.color.black,
@ColorInt val linkColor: Int? = null,
@FontRes val font: Int? = null,
val textSizeMultiplier: Float = 0.75f,
val marginTop: Int = 0
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package com.contentful.rich.android.renderer.views

import android.text.SpannableStringBuilder
import android.text.Spanned
import android.text.method.LinkMovementMethod
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import com.contentful.java.cda.rich.CDARichBlock
import com.contentful.java.cda.rich.CDARichHyperLink
import com.contentful.java.cda.rich.CDARichNode
import com.contentful.rich.android.AndroidContext
import com.contentful.rich.android.AndroidProcessor
import com.contentful.rich.android.AndroidRenderer
import com.contentful.rich.android.R
import com.contentful.rich.android.renderer.views.span.UrlSpan

open class BlockRenderer(
processor: AndroidProcessor<View>
) : AndroidRenderer<AndroidContext, View>(processor) {

override fun canRender(context: AndroidContext?, node: CDARichNode): Boolean = node is CDARichBlock

override fun render(context: AndroidContext, node: CDARichNode): View? {
val block = node as CDARichBlock
val result = inflateRichLayout(context, node)
val content = result.findViewById<ViewGroup>(R.id.rich_content)
var lastTextView: TextView? = null
block.content.forEach { childNode ->
val childView = processor.process(context, childNode!!)
if (childView != null) {
when {
childView is TextView -> {
lastTextView?.let {
it.text = SpannableStringBuilder(it.text).append(" ").append(childView.text)
} ?: run {
lastTextView = childView
content.addView(childView)
}
}
childNode is CDARichHyperLink -> {
val childLayout = childView.findViewById<ViewGroup>(R.id.rich_content)
if (childLayout.childCount > 0) {
val childTextView = childLayout.getChildAt(0) as TextView
val span = UrlSpan(childNode.data as String)
val text = lastTextView?.let {
SpannableStringBuilder(it.text).append(" ").append(childTextView.text)
} ?: SpannableStringBuilder(childTextView.text)

context.config?.linkColor?.let {
span.textColor = it
}

text.setSpan(
span,
lastTextView?.text?.length?.plus(1) ?: 0,
text.length,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)

lastTextView?.let {
it.movementMethod = LinkMovementMethod.getInstance()
it.text = text
} ?: run {
childTextView.text = text
lastTextView = childTextView
content.addView(childView)
}
}
}
context.path?.size ?: 0 > 1 -> {
val indented = context.inflater.inflate(R.layout.rich_indention_layout, null, false)
(indented.findViewById<View>(R.id.rich_content) as ViewGroup).addView(childView)
content.addView(indented)
}
else -> content.addView(childView)
}
}
}
return result
}

protected open fun inflateRichLayout(
context: AndroidContext,
node: CDARichNode
): View = context.inflater.inflate(R.layout.rich_block_layout, null, false)
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,50 +20,54 @@
import static com.contentful.rich.android.renderer.chars.EmbeddedLinkRenderer.defaultBitmapProvider;

public class EmbeddedLinkRenderer extends BlockRenderer {
private final BitmapProvider provider;

public EmbeddedLinkRenderer(@Nonnull AndroidProcessor<View> processor) {
this(processor, defaultBitmapProvider);
}
private final BitmapProvider provider;

public EmbeddedLinkRenderer(@Nonnull AndroidProcessor<View> processor, BitmapProvider provider) {
super(processor);
this.provider = provider;
}
public EmbeddedLinkRenderer(@Nonnull AndroidProcessor<View> processor) {
this(processor, defaultBitmapProvider);
}

public EmbeddedLinkRenderer(@Nonnull AndroidProcessor<View> processor, BitmapProvider provider) {
super(processor);
this.provider = provider;
}

@Override public boolean canRender(@Nullable AndroidContext context, @Nonnull CDARichNode node) {
return node instanceof CDARichEmbeddedInline;
}
@Override
public boolean canRender(@Nullable AndroidContext context, @Nonnull CDARichNode node) {
return node instanceof CDARichEmbeddedInline;
}

@Override protected View inflateRichLayout(@Nonnull AndroidContext context, @Nonnull CDARichNode node) {
final View embedded = context.getInflater().inflate(R.layout.rich_embedded_layout, null, false);
final ViewGroup content = embedded.findViewById(R.id.rich_content);
@Nonnull
@Override
protected View inflateRichLayout(@Nonnull AndroidContext context, @Nonnull CDARichNode node) {
final View embedded = context.getInflater().inflate(R.layout.rich_embedded_layout, null, false);
final ViewGroup content = embedded.findViewById(R.id.rich_content);

final CDARichEmbeddedInline link = (CDARichEmbeddedInline) node;
final Object data = link.getData();
final CDARichEmbeddedInline link = (CDARichEmbeddedInline) node;
final Object data = link.getData();

final View toBeEmbedded;
if (data instanceof CDAEntry) {
final CDAEntry entry = (CDAEntry) data;
final View toBeEmbedded;
if (data instanceof CDAEntry) {
final CDAEntry entry = (CDAEntry) data;

final TextView textView = new TextView(context.getAndroidContext());
textView.setText(entry.getField("title"));
final TextView textView = new TextView(context.getAndroidContext());
textView.setText(entry.getField("title"));

toBeEmbedded = textView;
} else if (data instanceof CDAAsset) {
final CDAAsset asset = (CDAAsset) data;
final ImageView image = new ImageView(context.getAndroidContext());
image.setImageBitmap(provider.provide(context.getAndroidContext(), asset));
toBeEmbedded = textView;
} else if (data instanceof CDAAsset) {
final CDAAsset asset = (CDAAsset) data;
final ImageView image = new ImageView(context.getAndroidContext());
image.setImageBitmap(provider.provide(context.getAndroidContext(), asset));

toBeEmbedded = image;
} else {
final TextView textView = new TextView(context.getAndroidContext());
textView.setText("⚠️");
toBeEmbedded = image;
} else {
final TextView textView = new TextView(context.getAndroidContext());
textView.setText("⚠️");

toBeEmbedded = textView;
}
toBeEmbedded = textView;
}

content.addView(toBeEmbedded, 0);
return embedded;
}
content.addView(toBeEmbedded, 0);
return embedded;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,30 +18,34 @@
import javax.annotation.Nullable;

public class HyperLinkRenderer extends BlockRenderer {
public HyperLinkRenderer(@Nonnull AndroidProcessor<View> processor) {
super(processor);
}

@Override public boolean canRender(@Nullable AndroidContext context, @Nonnull CDARichNode node) {
return node instanceof CDARichHyperLink && ((CDARichHyperLink) node).getData() instanceof String;
}

@Override protected View inflateRichLayout(@Nonnull AndroidContext context, @Nonnull CDARichNode node) {
final View inflate = context.getInflater().inflate(R.layout.rich_hyperlink_layout, null, false);
inflate.setOnClickListener(v -> HyperLinkRenderer.this.onClick(context, node));
return inflate;
}

public void onClick(AndroidContext context, CDARichNode node) {
final Context androidContext = context.getAndroidContext();
final Uri uri = Uri.parse((String) ((CDARichHyperLink) node).getData());

final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
intent.putExtra(Browser.EXTRA_APPLICATION_ID, androidContext.getPackageName());
try {
androidContext.startActivity(intent);
} catch (ActivityNotFoundException e) {
Log.w("URLSpan", "Activity was not found for intent, " + intent.toString());

public HyperLinkRenderer(@Nonnull AndroidProcessor<View> processor) {
super(processor);
}

@Override
public boolean canRender(@Nullable AndroidContext context, @Nonnull CDARichNode node) {
return node instanceof CDARichHyperLink && ((CDARichHyperLink) node).getData() instanceof String;
}

@Nonnull
@Override
protected View inflateRichLayout(@Nonnull AndroidContext context, @Nonnull CDARichNode node) {
final View inflate = context.getInflater().inflate(R.layout.rich_block_layout, null, false);
inflate.setOnClickListener(v -> HyperLinkRenderer.this.onClick(context, node));
return inflate;
}

public void onClick(AndroidContext context, CDARichNode node) {
final Context androidContext = context.getAndroidContext();
final Uri uri = Uri.parse((String) ((CDARichHyperLink) node).getData());

final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
intent.putExtra(Browser.EXTRA_APPLICATION_ID, androidContext.getPackageName());
try {
androidContext.startActivity(intent);
} catch (ActivityNotFoundException e) {
Log.w("URLSpan", "Activity was not found for intent, " + intent.toString());
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.contentful.rich.android.renderer.views.span

import android.text.TextPaint
import android.text.style.URLSpan
import androidx.annotation.ColorInt

class UrlSpan(url: String) : URLSpan(url) {

@ColorInt
var textColor: Int? = null

override fun updateDrawState(ds: TextPaint) {
textColor?.also {
ds.linkColor = it
}
super.updateDrawState(ds)
}
}
Loading

0 comments on commit ee4f4ff

Please sign in to comment.