**Markwon view
- variables names are modified to better match the [google codestyle](https://source.android.com/source/code-style#follow-field-naming-conventions)
This commit is contained in:
		
							parent
							
								
									18938a1aa2
								
							
						
					
					
						commit
						92ccf583c5
					
				@ -34,6 +34,6 @@ dependencies {
 | 
			
		||||
 | 
			
		||||
    compile OK_HTTP
 | 
			
		||||
 | 
			
		||||
    compile 'com.google.dagger:dagger:2.10'
 | 
			
		||||
    annotationProcessor 'com.google.dagger:dagger-compiler:2.10'
 | 
			
		||||
    compile 'com.google.dagger:dagger:2.12'
 | 
			
		||||
    annotationProcessor 'com.google.dagger:dagger-compiler:2.12'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										10
									
								
								build.gradle
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								build.gradle
									
									
									
									
									
								
							@ -4,7 +4,7 @@ buildscript {
 | 
			
		||||
        google()
 | 
			
		||||
    }
 | 
			
		||||
    dependencies {
 | 
			
		||||
        classpath 'com.android.tools.build:gradle:2.3.3'
 | 
			
		||||
        classpath 'com.android.tools.build:gradle:3.0.0'
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -28,12 +28,12 @@ task wrapper(type: Wrapper) {
 | 
			
		||||
ext {
 | 
			
		||||
 | 
			
		||||
    // Config
 | 
			
		||||
    BUILD_TOOLS = '26.0.2'
 | 
			
		||||
    TARGET_SDK = 26
 | 
			
		||||
    BUILD_TOOLS = '27.0.0'
 | 
			
		||||
    TARGET_SDK = 27
 | 
			
		||||
    MIN_SDK = 16
 | 
			
		||||
 | 
			
		||||
    // Dependencies
 | 
			
		||||
    final def supportVersion = '26.1.0'
 | 
			
		||||
    final def supportVersion = '27.0.0'
 | 
			
		||||
    SUPPORT_ANNOTATIONS = "com.android.support:support-annotations:$supportVersion"
 | 
			
		||||
    SUPPORT_APP_COMPAT = "com.android.support:appcompat-v7:$supportVersion"
 | 
			
		||||
 | 
			
		||||
@ -44,5 +44,5 @@ ext {
 | 
			
		||||
 | 
			
		||||
    ANDROID_SVG = 'com.caverock:androidsvg:1.2.1'
 | 
			
		||||
    ANDROID_GIF = 'pl.droidsonroids.gif:android-gif-drawable:1.2.7'
 | 
			
		||||
    OK_HTTP = 'com.squareup.okhttp3:okhttp:3.8.0'
 | 
			
		||||
    OK_HTTP = 'com.squareup.okhttp3:okhttp:3.9.0'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -28,6 +28,8 @@ import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.concurrent.ExecutorService;
 | 
			
		||||
import java.util.concurrent.Future;
 | 
			
		||||
import java.util.regex.Matcher;
 | 
			
		||||
import java.util.regex.Pattern;
 | 
			
		||||
 | 
			
		||||
import okhttp3.Call;
 | 
			
		||||
import okhttp3.OkHttpClient;
 | 
			
		||||
@ -53,39 +55,38 @@ public class AsyncDrawableLoader implements AsyncDrawable.Loader {
 | 
			
		||||
 | 
			
		||||
    private static final String FILE_ANDROID_ASSETS = "android_asset";
 | 
			
		||||
 | 
			
		||||
    private final OkHttpClient client;
 | 
			
		||||
    private final Resources resources;
 | 
			
		||||
    private final ExecutorService executorService;
 | 
			
		||||
    private final Handler mainThread;
 | 
			
		||||
    private final Drawable errorDrawable;
 | 
			
		||||
    private final OkHttpClient mClient;
 | 
			
		||||
    private final Resources mResources;
 | 
			
		||||
    private final ExecutorService mExecutorService;
 | 
			
		||||
    private final Handler mMainThread;
 | 
			
		||||
    private final Drawable mErrorDrawable;
 | 
			
		||||
 | 
			
		||||
    private final Map<String, Future<?>> requests;
 | 
			
		||||
    private final Map<String, Future<?>> mRequests;
 | 
			
		||||
 | 
			
		||||
    AsyncDrawableLoader(Builder builder) {
 | 
			
		||||
        this.client = builder.client;
 | 
			
		||||
        this.resources = builder.resources;
 | 
			
		||||
        this.executorService = builder.executorService;
 | 
			
		||||
        this.mainThread = new Handler(Looper.getMainLooper());
 | 
			
		||||
        this.errorDrawable = builder.errorDrawable;
 | 
			
		||||
        this.requests = new HashMap<>(3);
 | 
			
		||||
        mClient = builder.client;
 | 
			
		||||
        mResources = builder.resources;
 | 
			
		||||
        mExecutorService = builder.executorService;
 | 
			
		||||
        mMainThread = new Handler(Looper.getMainLooper());
 | 
			
		||||
        mErrorDrawable = builder.errorDrawable;
 | 
			
		||||
        mRequests = new HashMap<>(3);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void load(@NonNull String destination, @NonNull AsyncDrawable drawable) {
 | 
			
		||||
        // if drawable is not a link -> show loading placeholder...
 | 
			
		||||
        requests.put(destination, execute(destination, drawable));
 | 
			
		||||
        mRequests.put(destination, execute(destination, drawable));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void cancel(@NonNull String destination) {
 | 
			
		||||
 | 
			
		||||
        final Future<?> request = requests.remove(destination);
 | 
			
		||||
        final Future<?> request = mRequests.remove(destination);
 | 
			
		||||
        if (request != null) {
 | 
			
		||||
            request.cancel(true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        final List<Call> calls = client.dispatcher().queuedCalls();
 | 
			
		||||
        final List<Call> calls = mClient.dispatcher().queuedCalls();
 | 
			
		||||
        if (calls != null) {
 | 
			
		||||
            for (Call call : calls) {
 | 
			
		||||
                if (!call.isCanceled()) {
 | 
			
		||||
@ -97,14 +98,27 @@ public class AsyncDrawableLoader implements AsyncDrawable.Loader {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Future<?> execute(@NonNull final String destination, @NonNull AsyncDrawable drawable) {
 | 
			
		||||
        final WeakReference<AsyncDrawable> reference = new WeakReference<AsyncDrawable>(drawable);
 | 
			
		||||
    @NonNull
 | 
			
		||||
    private Future<?> execute(@NonNull final String destinationCandidate, @NonNull AsyncDrawable drawable) {
 | 
			
		||||
        final WeakReference<AsyncDrawable> reference = new WeakReference<>(drawable);
 | 
			
		||||
        // todo, if not a link -> show placeholder
 | 
			
		||||
        return executorService.submit(new Runnable() {
 | 
			
		||||
        return mExecutorService.submit(new Runnable() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void run() {
 | 
			
		||||
 | 
			
		||||
                final Item item;
 | 
			
		||||
                int height = -1;
 | 
			
		||||
                int width = -1;
 | 
			
		||||
                Matcher matcher = Pattern.compile("(.*)/(\\d+)\\$(\\d+)").matcher(destinationCandidate);
 | 
			
		||||
 | 
			
		||||
                final String destination;
 | 
			
		||||
                if (matcher.matches()){
 | 
			
		||||
                    width = Integer.parseInt(matcher.group(2));
 | 
			
		||||
                    height = Integer.parseInt(matcher.group(3));
 | 
			
		||||
                    destination = matcher.group(1);
 | 
			
		||||
                } else {
 | 
			
		||||
                    destination = destinationCandidate;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                final Uri uri = Uri.parse(destination);
 | 
			
		||||
                if ("file".equals(uri.getScheme())) {
 | 
			
		||||
@ -115,8 +129,7 @@ public class AsyncDrawableLoader implements AsyncDrawable.Loader {
 | 
			
		||||
 | 
			
		||||
                Drawable result = null;
 | 
			
		||||
 | 
			
		||||
                if (item != null
 | 
			
		||||
                        && item.inputStream != null) {
 | 
			
		||||
                if (item != null && item.inputStream != null) {
 | 
			
		||||
                    try {
 | 
			
		||||
                        if (CONTENT_TYPE_SVG.equals(item.type)) {
 | 
			
		||||
                            result = handleSvg(item.inputStream);
 | 
			
		||||
@ -136,12 +149,17 @@ public class AsyncDrawableLoader implements AsyncDrawable.Loader {
 | 
			
		||||
 | 
			
		||||
                // if result is null, we assume it's an error
 | 
			
		||||
                if (result == null) {
 | 
			
		||||
                    result = errorDrawable;
 | 
			
		||||
                    result = mErrorDrawable;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (result != null) {
 | 
			
		||||
                    final Drawable out = result;
 | 
			
		||||
                    mainThread.post(new Runnable() {
 | 
			
		||||
                    final Drawable out;
 | 
			
		||||
                    if (height != -1 && width != -1) {
 | 
			
		||||
                        out = AsyncDrawableLoader.this.resize(result, height, width);
 | 
			
		||||
                    } else {
 | 
			
		||||
                        out = result;
 | 
			
		||||
                    }
 | 
			
		||||
                    mMainThread.post(new Runnable() {
 | 
			
		||||
                        @Override
 | 
			
		||||
                        public void run() {
 | 
			
		||||
                            final AsyncDrawable asyncDrawable = reference.get();
 | 
			
		||||
@ -152,16 +170,39 @@ public class AsyncDrawableLoader implements AsyncDrawable.Loader {
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                requests.remove(destination);
 | 
			
		||||
                mRequests.remove(destinationCandidate);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Drawable resize(Drawable image, int height, float width) {
 | 
			
		||||
        final float density = mResources.getDisplayMetrics().density;
 | 
			
		||||
 | 
			
		||||
        if (width == 0 || height == 0) {
 | 
			
		||||
            final float imageDensity = (float) image.getIntrinsicWidth() / image.getIntrinsicHeight();
 | 
			
		||||
            if (width == 0) {
 | 
			
		||||
                width = intCeil(height * imageDensity);
 | 
			
		||||
            }
 | 
			
		||||
            if (height == 0) {
 | 
			
		||||
                height = intCeil(width / imageDensity);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        final int widthPx = intCeil(width * density);
 | 
			
		||||
        final int heightPx = intCeil(height * density);
 | 
			
		||||
 | 
			
		||||
        image.setBounds(0, 0, widthPx, heightPx);
 | 
			
		||||
        return image;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private int intCeil(double d){
 | 
			
		||||
        return (int) Math.ceil(d);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Item fromFile(Uri uri) {
 | 
			
		||||
 | 
			
		||||
        final List<String> segments = uri.getPathSegments();
 | 
			
		||||
        if (segments == null
 | 
			
		||||
                || segments.size() == 0) {
 | 
			
		||||
        if (segments == null || segments.size() == 0) {
 | 
			
		||||
            // pointing to file & having no path segments is no use
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
@ -192,7 +233,7 @@ public class AsyncDrawableLoader implements AsyncDrawable.Loader {
 | 
			
		||||
            // load assets
 | 
			
		||||
            InputStream inner = null;
 | 
			
		||||
            try {
 | 
			
		||||
                inner = resources.getAssets().open(path.toString());
 | 
			
		||||
                inner = mResources.getAssets().open(path.toString());
 | 
			
		||||
            } catch (IOException e) {
 | 
			
		||||
                e.printStackTrace();
 | 
			
		||||
            }
 | 
			
		||||
@ -227,7 +268,7 @@ public class AsyncDrawableLoader implements AsyncDrawable.Loader {
 | 
			
		||||
 | 
			
		||||
        Response response = null;
 | 
			
		||||
        try {
 | 
			
		||||
            response = client.newCall(request).execute();
 | 
			
		||||
            response = mClient.newCall(request).execute();
 | 
			
		||||
        } catch (IOException e) {
 | 
			
		||||
            e.printStackTrace();
 | 
			
		||||
        }
 | 
			
		||||
@ -239,8 +280,7 @@ public class AsyncDrawableLoader implements AsyncDrawable.Loader {
 | 
			
		||||
                if (inputStream != null) {
 | 
			
		||||
                    final String type;
 | 
			
		||||
                    final String contentType = response.header(HEADER_CONTENT_TYPE);
 | 
			
		||||
                    if (!TextUtils.isEmpty(contentType)
 | 
			
		||||
                            && contentType.startsWith(CONTENT_TYPE_SVG)) {
 | 
			
		||||
                    if (!TextUtils.isEmpty(contentType) && contentType.startsWith(CONTENT_TYPE_SVG)) {
 | 
			
		||||
                        type = CONTENT_TYPE_SVG;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        type = contentType;
 | 
			
		||||
@ -270,7 +310,7 @@ public class AsyncDrawableLoader implements AsyncDrawable.Loader {
 | 
			
		||||
 | 
			
		||||
            final float w = svg.getDocumentWidth();
 | 
			
		||||
            final float h = svg.getDocumentHeight();
 | 
			
		||||
            final float density = resources.getDisplayMetrics().density;
 | 
			
		||||
            final float density = mResources.getDisplayMetrics().density;
 | 
			
		||||
 | 
			
		||||
            final int width = (int) (w * density + .5F);
 | 
			
		||||
            final int height = (int) (h * density + .5F);
 | 
			
		||||
@ -280,7 +320,7 @@ public class AsyncDrawableLoader implements AsyncDrawable.Loader {
 | 
			
		||||
            canvas.scale(density, density);
 | 
			
		||||
            svg.renderToCanvas(canvas);
 | 
			
		||||
 | 
			
		||||
            out = new BitmapDrawable(resources, bitmap);
 | 
			
		||||
            out = new BitmapDrawable(mResources, bitmap);
 | 
			
		||||
            DrawableUtils.intrinsicBounds(out);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -310,7 +350,7 @@ public class AsyncDrawableLoader implements AsyncDrawable.Loader {
 | 
			
		||||
 | 
			
		||||
        final Bitmap bitmap = BitmapFactory.decodeStream(stream);
 | 
			
		||||
        if (bitmap != null) {
 | 
			
		||||
            out = new BitmapDrawable(resources, bitmap);
 | 
			
		||||
            out = new BitmapDrawable(mResources, bitmap);
 | 
			
		||||
            DrawableUtils.intrinsicBounds(out);
 | 
			
		||||
        } else {
 | 
			
		||||
            out = null;
 | 
			
		||||
 | 
			
		||||
@ -9,17 +9,17 @@ import ru.noties.markwon.view.IMarkwonView;
 | 
			
		||||
 | 
			
		||||
public class DebugConfigurationProvider implements IMarkwonView.ConfigurationProvider {
 | 
			
		||||
 | 
			
		||||
    private SpannableConfiguration cached;
 | 
			
		||||
    private SpannableConfiguration mCached;
 | 
			
		||||
 | 
			
		||||
    @NonNull
 | 
			
		||||
    @Override
 | 
			
		||||
    public SpannableConfiguration provide(@NonNull Context context) {
 | 
			
		||||
        if (cached == null) {
 | 
			
		||||
            cached = SpannableConfiguration.builder(context)
 | 
			
		||||
        if (mCached == null) {
 | 
			
		||||
            mCached = SpannableConfiguration.builder(context)
 | 
			
		||||
                    .theme(debugTheme(context))
 | 
			
		||||
                    .build();
 | 
			
		||||
        }
 | 
			
		||||
        return cached;
 | 
			
		||||
        return mCached;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static SpannableTheme debugTheme(@NonNull Context context) {
 | 
			
		||||
 | 
			
		||||
@ -12,7 +12,7 @@ import ru.noties.markwon.SpannableConfiguration;
 | 
			
		||||
@SuppressLint("AppCompatCustomView")
 | 
			
		||||
public class MarkwonView extends TextView implements IMarkwonView {
 | 
			
		||||
 | 
			
		||||
    private MarkwonViewHelper helper;
 | 
			
		||||
    private MarkwonViewHelper mHelper;
 | 
			
		||||
 | 
			
		||||
    public MarkwonView(Context context) {
 | 
			
		||||
        super(context);
 | 
			
		||||
@ -25,26 +25,26 @@ public class MarkwonView extends TextView implements IMarkwonView {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void init(Context context, AttributeSet attributeSet) {
 | 
			
		||||
        helper = MarkwonViewHelper.create(this);
 | 
			
		||||
        helper.init(context, attributeSet);
 | 
			
		||||
        mHelper = MarkwonViewHelper.create(this);
 | 
			
		||||
        mHelper.init(context, attributeSet);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void setConfigurationProvider(@NonNull ConfigurationProvider provider) {
 | 
			
		||||
        helper.setConfigurationProvider(provider);
 | 
			
		||||
        mHelper.setConfigurationProvider(provider);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setMarkdown(@Nullable String markdown) {
 | 
			
		||||
        helper.setMarkdown(markdown);
 | 
			
		||||
        mHelper.setMarkdown(markdown);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setMarkdown(@Nullable SpannableConfiguration configuration, @Nullable String markdown) {
 | 
			
		||||
        helper.setMarkdown(configuration, markdown);
 | 
			
		||||
        mHelper.setMarkdown(configuration, markdown);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nullable
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getMarkdown() {
 | 
			
		||||
        return helper.getMarkdown();
 | 
			
		||||
        return mHelper.getMarkdown();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -10,7 +10,7 @@ import ru.noties.markwon.SpannableConfiguration;
 | 
			
		||||
 | 
			
		||||
public class MarkwonViewCompat extends AppCompatTextView implements IMarkwonView {
 | 
			
		||||
 | 
			
		||||
    private MarkwonViewHelper helper;
 | 
			
		||||
    private MarkwonViewHelper mHelper;
 | 
			
		||||
 | 
			
		||||
    public MarkwonViewCompat(Context context) {
 | 
			
		||||
        super(context);
 | 
			
		||||
@ -23,28 +23,28 @@ public class MarkwonViewCompat extends AppCompatTextView implements IMarkwonView
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void init(Context context, AttributeSet attributeSet) {
 | 
			
		||||
        helper = MarkwonViewHelper.create(this);
 | 
			
		||||
        helper.init(context, attributeSet);
 | 
			
		||||
        mHelper = MarkwonViewHelper.create(this);
 | 
			
		||||
        mHelper.init(context, attributeSet);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void setConfigurationProvider(@NonNull ConfigurationProvider provider) {
 | 
			
		||||
        helper.setConfigurationProvider(provider);
 | 
			
		||||
        mHelper.setConfigurationProvider(provider);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void setMarkdown(@Nullable String markdown) {
 | 
			
		||||
        helper.setMarkdown(markdown);
 | 
			
		||||
        mHelper.setMarkdown(markdown);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void setMarkdown(@Nullable SpannableConfiguration configuration, @Nullable String markdown) {
 | 
			
		||||
        helper.setMarkdown(configuration, markdown);
 | 
			
		||||
        mHelper.setMarkdown(configuration, markdown);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nullable
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getMarkdown() {
 | 
			
		||||
        return helper.getMarkdown();
 | 
			
		||||
        return mHelper.getMarkdown();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -17,15 +17,15 @@ public class MarkwonViewHelper implements IMarkwonView {
 | 
			
		||||
        return new MarkwonViewHelper(view);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private final TextView textView;
 | 
			
		||||
    private final TextView mTextView;
 | 
			
		||||
 | 
			
		||||
    private ConfigurationProvider provider;
 | 
			
		||||
    private ConfigurationProvider mProvider;
 | 
			
		||||
 | 
			
		||||
    private SpannableConfiguration configuration;
 | 
			
		||||
    private String markdown;
 | 
			
		||||
    private SpannableConfiguration mConfiguration;
 | 
			
		||||
    private String mMarkdown;
 | 
			
		||||
 | 
			
		||||
    private MarkwonViewHelper(@NonNull TextView textView) {
 | 
			
		||||
        this.textView = textView;
 | 
			
		||||
        mTextView = textView;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void init(Context context, AttributeSet attributeSet) {
 | 
			
		||||
@ -57,11 +57,11 @@ public class MarkwonViewHelper implements IMarkwonView {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void setConfigurationProvider(@NonNull ConfigurationProvider provider) {
 | 
			
		||||
        this.provider = provider;
 | 
			
		||||
        this.configuration = provider.provide(textView.getContext());
 | 
			
		||||
        if (!TextUtils.isEmpty(markdown)) {
 | 
			
		||||
        mProvider = provider;
 | 
			
		||||
        mConfiguration = provider.provide(mTextView.getContext());
 | 
			
		||||
        if (!TextUtils.isEmpty(mMarkdown)) {
 | 
			
		||||
            // invalidate rendered markdown
 | 
			
		||||
            setMarkdown(markdown);
 | 
			
		||||
            setMarkdown(mMarkdown);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -72,24 +72,24 @@ public class MarkwonViewHelper implements IMarkwonView {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void setMarkdown(@Nullable SpannableConfiguration configuration, @Nullable String markdown) {
 | 
			
		||||
        this.markdown = markdown;
 | 
			
		||||
        mMarkdown = markdown;
 | 
			
		||||
        if (configuration == null) {
 | 
			
		||||
            if (this.configuration == null) {
 | 
			
		||||
                if (provider != null) {
 | 
			
		||||
                    this.configuration = provider.provide(textView.getContext());
 | 
			
		||||
            if (mConfiguration == null) {
 | 
			
		||||
                if (mProvider != null) {
 | 
			
		||||
                    mConfiguration = mProvider.provide(mTextView.getContext());
 | 
			
		||||
                } else {
 | 
			
		||||
                    this.configuration = SpannableConfiguration.create(textView.getContext());
 | 
			
		||||
                    mConfiguration = SpannableConfiguration.create(mTextView.getContext());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            configuration = this.configuration;
 | 
			
		||||
            configuration = mConfiguration;
 | 
			
		||||
        }
 | 
			
		||||
        Markwon.setMarkdown(textView, configuration, markdown);
 | 
			
		||||
        Markwon.setMarkdown(mTextView, configuration, markdown);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nullable
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getMarkdown() {
 | 
			
		||||
        return markdown;
 | 
			
		||||
        return mMarkdown;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nullable
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,13 @@
 | 
			
		||||
package ru.noties.markwon;
 | 
			
		||||
 | 
			
		||||
import android.support.annotation.NonNull;
 | 
			
		||||
import android.view.View;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author pa.gulko zTrap (25.10.2017)
 | 
			
		||||
 * @since 1.0.1
 | 
			
		||||
 */
 | 
			
		||||
public interface ImageClickResolver {
 | 
			
		||||
 | 
			
		||||
    void resolve(View view, @NonNull String link);
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,30 @@
 | 
			
		||||
package ru.noties.markwon;
 | 
			
		||||
 | 
			
		||||
import android.content.ActivityNotFoundException;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import android.net.Uri;
 | 
			
		||||
import android.provider.Browser;
 | 
			
		||||
import android.support.annotation.NonNull;
 | 
			
		||||
import android.util.Log;
 | 
			
		||||
import android.view.View;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author pa.gulko zTrap (25.10.2017)
 | 
			
		||||
 * @since 1.0.1
 | 
			
		||||
 */
 | 
			
		||||
public class ImageClickResolverDef implements ImageClickResolver {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void resolve(View view, @NonNull String link) {
 | 
			
		||||
        final Uri uri = Uri.parse(LinkUtils.cropImageSizes(link));
 | 
			
		||||
        final Context context = view.getContext();
 | 
			
		||||
        final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
 | 
			
		||||
        intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
 | 
			
		||||
        try {
 | 
			
		||||
            context.startActivity(intent);
 | 
			
		||||
        } catch (ActivityNotFoundException e) {
 | 
			
		||||
            Log.w("LinkResolverDef", "Actvity was not found for intent, " + intent.toString());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -12,6 +12,7 @@ import android.view.View;
 | 
			
		||||
import ru.noties.markwon.spans.LinkSpan;
 | 
			
		||||
 | 
			
		||||
public class LinkResolverDef implements LinkSpan.Resolver {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void resolve(View view, @NonNull String link) {
 | 
			
		||||
        final Uri uri = Uri.parse(link);
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										26
									
								
								library/src/main/java/ru/noties/markwon/LinkUtils.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								library/src/main/java/ru/noties/markwon/LinkUtils.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,26 @@
 | 
			
		||||
package ru.noties.markwon;
 | 
			
		||||
 | 
			
		||||
import android.support.annotation.NonNull;
 | 
			
		||||
 | 
			
		||||
import java.util.regex.Matcher;
 | 
			
		||||
import java.util.regex.Pattern;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author pa.gulko zTrap (26.10.2017)
 | 
			
		||||
 * @since 1.0.1
 | 
			
		||||
 */
 | 
			
		||||
public class LinkUtils {
 | 
			
		||||
 | 
			
		||||
    private LinkUtils() {
 | 
			
		||||
        //no instance
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static String cropImageSizes(@NonNull String link){
 | 
			
		||||
        Matcher matcher = Pattern.compile("(.*)/(\\d+)\\$(\\d+)").matcher(link);
 | 
			
		||||
        if (matcher.matches()){
 | 
			
		||||
            return matcher.group(1);
 | 
			
		||||
        } else {
 | 
			
		||||
            return link;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -20,44 +20,50 @@ public class SpannableConfiguration {
 | 
			
		||||
        return new Builder(context);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private final SpannableTheme theme;
 | 
			
		||||
    private final AsyncDrawable.Loader asyncDrawableLoader;
 | 
			
		||||
    private final SyntaxHighlight syntaxHighlight;
 | 
			
		||||
    private final LinkSpan.Resolver linkResolver;
 | 
			
		||||
    private final UrlProcessor urlProcessor;
 | 
			
		||||
    private final SpannableHtmlParser htmlParser;
 | 
			
		||||
    private final SpannableTheme mTheme;
 | 
			
		||||
    private final AsyncDrawable.Loader mAsyncDrawableLoader;
 | 
			
		||||
    private final SyntaxHighlight mSyntaxHighlight;
 | 
			
		||||
    private final LinkSpan.Resolver mLinkResolver;
 | 
			
		||||
    private final UrlProcessor mUrlProcessor;
 | 
			
		||||
    private final SpannableHtmlParser mHtmlParser;
 | 
			
		||||
    private final ImageClickResolver mImageClickResolver;
 | 
			
		||||
 | 
			
		||||
    private SpannableConfiguration(Builder builder) {
 | 
			
		||||
        this.theme = builder.theme;
 | 
			
		||||
        this.asyncDrawableLoader = builder.asyncDrawableLoader;
 | 
			
		||||
        this.syntaxHighlight = builder.syntaxHighlight;
 | 
			
		||||
        this.linkResolver = builder.linkResolver;
 | 
			
		||||
        this.urlProcessor = builder.urlProcessor;
 | 
			
		||||
        this.htmlParser = builder.htmlParser;
 | 
			
		||||
        mTheme = builder.theme;
 | 
			
		||||
        mAsyncDrawableLoader = builder.asyncDrawableLoader;
 | 
			
		||||
        mSyntaxHighlight = builder.syntaxHighlight;
 | 
			
		||||
        mLinkResolver = builder.linkResolver;
 | 
			
		||||
        mUrlProcessor = builder.urlProcessor;
 | 
			
		||||
        mHtmlParser = builder.htmlParser;
 | 
			
		||||
        mImageClickResolver = builder.imageClickResolver;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public SpannableTheme theme() {
 | 
			
		||||
        return theme;
 | 
			
		||||
        return mTheme;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public AsyncDrawable.Loader asyncDrawableLoader() {
 | 
			
		||||
        return asyncDrawableLoader;
 | 
			
		||||
        return mAsyncDrawableLoader;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public SyntaxHighlight syntaxHighlight() {
 | 
			
		||||
        return syntaxHighlight;
 | 
			
		||||
        return mSyntaxHighlight;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public LinkSpan.Resolver linkResolver() {
 | 
			
		||||
        return linkResolver;
 | 
			
		||||
        return mLinkResolver;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public UrlProcessor urlProcessor() {
 | 
			
		||||
        return urlProcessor;
 | 
			
		||||
        return mUrlProcessor;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public SpannableHtmlParser htmlParser() {
 | 
			
		||||
        return htmlParser;
 | 
			
		||||
        return mHtmlParser;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ImageClickResolver imageClickResolver() {
 | 
			
		||||
        return mImageClickResolver;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static class Builder {
 | 
			
		||||
@ -69,6 +75,7 @@ public class SpannableConfiguration {
 | 
			
		||||
        private LinkSpan.Resolver linkResolver;
 | 
			
		||||
        private UrlProcessor urlProcessor;
 | 
			
		||||
        private SpannableHtmlParser htmlParser;
 | 
			
		||||
        private ImageClickResolver imageClickResolver;
 | 
			
		||||
 | 
			
		||||
        Builder(Context context) {
 | 
			
		||||
            this.context = context;
 | 
			
		||||
@ -104,6 +111,11 @@ public class SpannableConfiguration {
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Builder setImageClickResolver(ImageClickResolver imageClickResolver) {
 | 
			
		||||
            this.imageClickResolver = imageClickResolver;
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public SpannableConfiguration build() {
 | 
			
		||||
            if (theme == null) {
 | 
			
		||||
                theme = SpannableTheme.create(context);
 | 
			
		||||
@ -120,8 +132,12 @@ public class SpannableConfiguration {
 | 
			
		||||
            if (urlProcessor == null) {
 | 
			
		||||
                urlProcessor = new UrlProcessorNoOp();
 | 
			
		||||
            }
 | 
			
		||||
            if (imageClickResolver == null) {
 | 
			
		||||
                imageClickResolver = new ImageClickResolverDef();
 | 
			
		||||
            }
 | 
			
		||||
            if (htmlParser == null) {
 | 
			
		||||
                htmlParser = SpannableHtmlParser.create(theme, asyncDrawableLoader, urlProcessor, linkResolver);
 | 
			
		||||
                htmlParser = SpannableHtmlParser.create(theme, asyncDrawableLoader, urlProcessor,
 | 
			
		||||
                        linkResolver, imageClickResolver);
 | 
			
		||||
            }
 | 
			
		||||
            return new SpannableConfiguration(this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -13,8 +13,7 @@ abstract class TableRowsScheduler {
 | 
			
		||||
 | 
			
		||||
    static void schedule(@NonNull final TextView view) {
 | 
			
		||||
        final Object[] spans = extract(view);
 | 
			
		||||
        if (spans != null
 | 
			
		||||
                && spans.length > 0) {
 | 
			
		||||
        if (spans != null && spans.length > 0) {
 | 
			
		||||
 | 
			
		||||
            if (view.getTag(R.id.markwon_tables_scheduler) == null) {
 | 
			
		||||
                final View.OnAttachStateChangeListener listener = new View.OnAttachStateChangeListener() {
 | 
			
		||||
 | 
			
		||||
@ -7,17 +7,17 @@ import android.text.TextUtils;
 | 
			
		||||
 | 
			
		||||
public class UrlProcessorAndroidAssets implements UrlProcessor {
 | 
			
		||||
 | 
			
		||||
    private final UrlProcessorRelativeToAbsolute assetsProcessor
 | 
			
		||||
    private final UrlProcessorRelativeToAbsolute mAssetsProcessor
 | 
			
		||||
            = new UrlProcessorRelativeToAbsolute("file:///android_asset/");
 | 
			
		||||
 | 
			
		||||
    private final UrlProcessor processor;
 | 
			
		||||
    private final UrlProcessor mProcessor;
 | 
			
		||||
 | 
			
		||||
    public UrlProcessorAndroidAssets() {
 | 
			
		||||
        this(null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public UrlProcessorAndroidAssets(@Nullable UrlProcessor parent) {
 | 
			
		||||
        this.processor = parent;
 | 
			
		||||
        mProcessor = parent;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @NonNull
 | 
			
		||||
@ -26,10 +26,10 @@ public class UrlProcessorAndroidAssets implements UrlProcessor {
 | 
			
		||||
        final String out;
 | 
			
		||||
        final Uri uri = Uri.parse(destination);
 | 
			
		||||
        if (TextUtils.isEmpty(uri.getScheme())) {
 | 
			
		||||
            out = assetsProcessor.process(destination);
 | 
			
		||||
            out = mAssetsProcessor.process(destination);
 | 
			
		||||
        } else {
 | 
			
		||||
            if (processor != null) {
 | 
			
		||||
                out = processor.process(destination);
 | 
			
		||||
            if (mProcessor != null) {
 | 
			
		||||
                out = mProcessor.process(destination);
 | 
			
		||||
            } else {
 | 
			
		||||
                out = destination;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@ -9,10 +9,10 @@ import java.net.URL;
 | 
			
		||||
@SuppressWarnings("WeakerAccess")
 | 
			
		||||
public class UrlProcessorRelativeToAbsolute implements UrlProcessor {
 | 
			
		||||
 | 
			
		||||
    private final URL base;
 | 
			
		||||
    private final URL mBase;
 | 
			
		||||
 | 
			
		||||
    public UrlProcessorRelativeToAbsolute(@NonNull String base) {
 | 
			
		||||
        this.base = obtain(base);
 | 
			
		||||
        mBase = obtain(base);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @NonNull
 | 
			
		||||
@ -21,9 +21,9 @@ public class UrlProcessorRelativeToAbsolute implements UrlProcessor {
 | 
			
		||||
 | 
			
		||||
        String out = destination;
 | 
			
		||||
 | 
			
		||||
        if (base != null) {
 | 
			
		||||
        if (mBase != null) {
 | 
			
		||||
            try {
 | 
			
		||||
                final URL u = new URL(base, destination);
 | 
			
		||||
                final URL u = new URL(mBase, destination);
 | 
			
		||||
                out = u.toString();
 | 
			
		||||
            } catch (MalformedURLException e) {
 | 
			
		||||
                e.printStackTrace();
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,9 @@ import android.support.annotation.NonNull;
 | 
			
		||||
import android.text.SpannableStringBuilder;
 | 
			
		||||
import android.text.Spanned;
 | 
			
		||||
import android.text.TextUtils;
 | 
			
		||||
import android.text.style.ClickableSpan;
 | 
			
		||||
import android.text.style.StrikethroughSpan;
 | 
			
		||||
import android.view.View;
 | 
			
		||||
 | 
			
		||||
import org.commonmark.ext.gfm.strikethrough.Strikethrough;
 | 
			
		||||
import org.commonmark.ext.gfm.tables.TableBody;
 | 
			
		||||
@ -60,41 +62,41 @@ import ru.noties.markwon.tasklist.TaskListItem;
 | 
			
		||||
@SuppressWarnings("WeakerAccess")
 | 
			
		||||
public class SpannableMarkdownVisitor extends AbstractVisitor {
 | 
			
		||||
 | 
			
		||||
    private final SpannableConfiguration configuration;
 | 
			
		||||
    private final SpannableStringBuilder builder;
 | 
			
		||||
    private final Deque<HtmlInlineItem> htmlInlineItems;
 | 
			
		||||
    private final SpannableConfiguration mConfiguration;
 | 
			
		||||
    private final SpannableStringBuilder mBuilder;
 | 
			
		||||
    private final Deque<HtmlInlineItem> mHtmlInlineItems;
 | 
			
		||||
 | 
			
		||||
    private int blockQuoteIndent;
 | 
			
		||||
    private int listLevel;
 | 
			
		||||
    private int mBlockQuoteIndent;
 | 
			
		||||
    private int mListLevel;
 | 
			
		||||
 | 
			
		||||
    private List<TableRowSpan.Cell> pendingTableRow;
 | 
			
		||||
    private boolean tableRowIsHeader;
 | 
			
		||||
    private int tableRows;
 | 
			
		||||
    private List<TableRowSpan.Cell> mPendingTableRow;
 | 
			
		||||
    private boolean mTableRowIsHeader;
 | 
			
		||||
    private int mTableRows;
 | 
			
		||||
 | 
			
		||||
    public SpannableMarkdownVisitor(
 | 
			
		||||
            @NonNull SpannableConfiguration configuration,
 | 
			
		||||
            @NonNull SpannableStringBuilder builder
 | 
			
		||||
    ) {
 | 
			
		||||
        this.configuration = configuration;
 | 
			
		||||
        this.builder = builder;
 | 
			
		||||
        this.htmlInlineItems = new ArrayDeque<>(2);
 | 
			
		||||
        mConfiguration = configuration;
 | 
			
		||||
        mBuilder = builder;
 | 
			
		||||
        mHtmlInlineItems = new ArrayDeque<>(2);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void visit(Text text) {
 | 
			
		||||
        builder.append(text.getLiteral());
 | 
			
		||||
        mBuilder.append(text.getLiteral());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void visit(StrongEmphasis strongEmphasis) {
 | 
			
		||||
        final int length = builder.length();
 | 
			
		||||
        final int length = mBuilder.length();
 | 
			
		||||
        visitChildren(strongEmphasis);
 | 
			
		||||
        setSpan(length, new StrongEmphasisSpan());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void visit(Emphasis emphasis) {
 | 
			
		||||
        final int length = builder.length();
 | 
			
		||||
        final int length = mBuilder.length();
 | 
			
		||||
        visitChildren(emphasis);
 | 
			
		||||
        setSpan(length, new EmphasisSpan());
 | 
			
		||||
    }
 | 
			
		||||
@ -103,42 +105,42 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
 | 
			
		||||
    public void visit(BlockQuote blockQuote) {
 | 
			
		||||
 | 
			
		||||
        newLine();
 | 
			
		||||
        if (blockQuoteIndent != 0) {
 | 
			
		||||
            builder.append('\n');
 | 
			
		||||
        if (mBlockQuoteIndent != 0) {
 | 
			
		||||
            mBuilder.append('\n');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        final int length = builder.length();
 | 
			
		||||
        final int length = mBuilder.length();
 | 
			
		||||
 | 
			
		||||
        blockQuoteIndent += 1;
 | 
			
		||||
        mBlockQuoteIndent += 1;
 | 
			
		||||
 | 
			
		||||
        visitChildren(blockQuote);
 | 
			
		||||
 | 
			
		||||
        setSpan(length, new BlockQuoteSpan(
 | 
			
		||||
                configuration.theme(),
 | 
			
		||||
                blockQuoteIndent
 | 
			
		||||
                mConfiguration.theme(),
 | 
			
		||||
                mBlockQuoteIndent
 | 
			
		||||
        ));
 | 
			
		||||
 | 
			
		||||
        blockQuoteIndent -= 1;
 | 
			
		||||
        mBlockQuoteIndent -= 1;
 | 
			
		||||
 | 
			
		||||
        newLine();
 | 
			
		||||
        if (blockQuoteIndent == 0) {
 | 
			
		||||
            builder.append('\n');
 | 
			
		||||
        if (mBlockQuoteIndent == 0) {
 | 
			
		||||
            mBuilder.append('\n');
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void visit(Code code) {
 | 
			
		||||
 | 
			
		||||
        final int length = builder.length();
 | 
			
		||||
        final int length = mBuilder.length();
 | 
			
		||||
 | 
			
		||||
        // NB, in order to provide a _padding_ feeling code is wrapped inside two unbreakable spaces
 | 
			
		||||
        // unfortunately we cannot use this for multiline code as we cannot control where a new line break will be inserted
 | 
			
		||||
        builder.append('\u00a0');
 | 
			
		||||
        builder.append(code.getLiteral());
 | 
			
		||||
        builder.append('\u00a0');
 | 
			
		||||
        mBuilder.append('\u00a0');
 | 
			
		||||
        mBuilder.append(code.getLiteral());
 | 
			
		||||
        mBuilder.append('\u00a0');
 | 
			
		||||
 | 
			
		||||
        setSpan(length, new CodeSpan(
 | 
			
		||||
                configuration.theme(),
 | 
			
		||||
                mConfiguration.theme(),
 | 
			
		||||
                false
 | 
			
		||||
        ));
 | 
			
		||||
    }
 | 
			
		||||
@ -148,23 +150,23 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
 | 
			
		||||
 | 
			
		||||
        newLine();
 | 
			
		||||
 | 
			
		||||
        final int length = builder.length();
 | 
			
		||||
        final int length = mBuilder.length();
 | 
			
		||||
 | 
			
		||||
        // empty lines on top & bottom
 | 
			
		||||
        builder.append('\u00a0').append('\n');
 | 
			
		||||
        builder.append(
 | 
			
		||||
                configuration.syntaxHighlight()
 | 
			
		||||
        mBuilder.append('\u00a0').append('\n');
 | 
			
		||||
        mBuilder.append(
 | 
			
		||||
                mConfiguration.syntaxHighlight()
 | 
			
		||||
                        .highlight(fencedCodeBlock.getInfo(), fencedCodeBlock.getLiteral())
 | 
			
		||||
        );
 | 
			
		||||
        builder.append('\u00a0').append('\n');
 | 
			
		||||
        mBuilder.append('\u00a0').append('\n');
 | 
			
		||||
 | 
			
		||||
        setSpan(length, new CodeSpan(
 | 
			
		||||
                configuration.theme(),
 | 
			
		||||
                mConfiguration.theme(),
 | 
			
		||||
                true
 | 
			
		||||
        ));
 | 
			
		||||
 | 
			
		||||
        newLine();
 | 
			
		||||
        builder.append('\n');
 | 
			
		||||
        mBuilder.append('\n');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@ -181,18 +183,18 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
 | 
			
		||||
        newLine();
 | 
			
		||||
        visitChildren(node);
 | 
			
		||||
        newLine();
 | 
			
		||||
        if (listLevel == 0 && blockQuoteIndent == 0) {
 | 
			
		||||
            builder.append('\n');
 | 
			
		||||
        if (mListLevel == 0 && mBlockQuoteIndent == 0) {
 | 
			
		||||
            mBuilder.append('\n');
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void visit(ListItem listItem) {
 | 
			
		||||
 | 
			
		||||
        final int length = builder.length();
 | 
			
		||||
        final int length = mBuilder.length();
 | 
			
		||||
 | 
			
		||||
        blockQuoteIndent += 1;
 | 
			
		||||
        listLevel += 1;
 | 
			
		||||
        mBlockQuoteIndent += 1;
 | 
			
		||||
        mListLevel += 1;
 | 
			
		||||
 | 
			
		||||
        final Node parent = listItem.getParent();
 | 
			
		||||
        if (parent instanceof OrderedList) {
 | 
			
		||||
@ -202,9 +204,9 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
 | 
			
		||||
            visitChildren(listItem);
 | 
			
		||||
 | 
			
		||||
            setSpan(length, new OrderedListItemSpan(
 | 
			
		||||
                    configuration.theme(),
 | 
			
		||||
                    mConfiguration.theme(),
 | 
			
		||||
                    String.valueOf(start) + "." + '\u00a0',
 | 
			
		||||
                    blockQuoteIndent
 | 
			
		||||
                    mBlockQuoteIndent
 | 
			
		||||
            ));
 | 
			
		||||
 | 
			
		||||
            // after we have visited the children increment start number
 | 
			
		||||
@ -216,14 +218,14 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
 | 
			
		||||
            visitChildren(listItem);
 | 
			
		||||
 | 
			
		||||
            setSpan(length, new BulletListItemSpan(
 | 
			
		||||
                    configuration.theme(),
 | 
			
		||||
                    blockQuoteIndent,
 | 
			
		||||
                    listLevel - 1
 | 
			
		||||
                    mConfiguration.theme(),
 | 
			
		||||
                    mBlockQuoteIndent,
 | 
			
		||||
                    mListLevel - 1
 | 
			
		||||
            ));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        blockQuoteIndent -= 1;
 | 
			
		||||
        listLevel -= 1;
 | 
			
		||||
        mBlockQuoteIndent -= 1;
 | 
			
		||||
        mListLevel -= 1;
 | 
			
		||||
 | 
			
		||||
        newLine();
 | 
			
		||||
    }
 | 
			
		||||
@ -233,12 +235,12 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
 | 
			
		||||
 | 
			
		||||
        newLine();
 | 
			
		||||
 | 
			
		||||
        final int length = builder.length();
 | 
			
		||||
        builder.append(' '); // without space it won't render
 | 
			
		||||
        setSpan(length, new ThematicBreakSpan(configuration.theme()));
 | 
			
		||||
        final int length = mBuilder.length();
 | 
			
		||||
        mBuilder.append(' '); // without space it won't render
 | 
			
		||||
        setSpan(length, new ThematicBreakSpan(mConfiguration.theme()));
 | 
			
		||||
 | 
			
		||||
        newLine();
 | 
			
		||||
        builder.append('\n');
 | 
			
		||||
        mBuilder.append('\n');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@ -246,24 +248,24 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
 | 
			
		||||
 | 
			
		||||
        newLine();
 | 
			
		||||
 | 
			
		||||
        final int length = builder.length();
 | 
			
		||||
        final int length = mBuilder.length();
 | 
			
		||||
        visitChildren(heading);
 | 
			
		||||
        setSpan(length, new HeadingSpan(
 | 
			
		||||
                configuration.theme(),
 | 
			
		||||
                mConfiguration.theme(),
 | 
			
		||||
                heading.getLevel(),
 | 
			
		||||
                builder.length() - length)
 | 
			
		||||
                mBuilder.length() - length)
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        newLine();
 | 
			
		||||
 | 
			
		||||
        // after heading we add another line anyway (no additional checks)
 | 
			
		||||
        builder.append('\n');
 | 
			
		||||
        mBuilder.append('\n');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void visit(SoftLineBreak softLineBreak) {
 | 
			
		||||
        // at first here was a new line, but here should be a space char
 | 
			
		||||
        builder.append(' ');
 | 
			
		||||
        mBuilder.append(' ');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@ -277,11 +279,11 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
 | 
			
		||||
    @Override
 | 
			
		||||
    public void visit(CustomBlock customBlock) {
 | 
			
		||||
        if (customBlock instanceof TaskListBlock) {
 | 
			
		||||
            blockQuoteIndent += 1;
 | 
			
		||||
            mBlockQuoteIndent += 1;
 | 
			
		||||
            visitChildren(customBlock);
 | 
			
		||||
            blockQuoteIndent -= 1;
 | 
			
		||||
            mBlockQuoteIndent -= 1;
 | 
			
		||||
            newLine();
 | 
			
		||||
            builder.append('\n');
 | 
			
		||||
            mBuilder.append('\n');
 | 
			
		||||
        } else {
 | 
			
		||||
            super.visit(customBlock);
 | 
			
		||||
        }
 | 
			
		||||
@ -292,7 +294,7 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
 | 
			
		||||
 | 
			
		||||
        if (customNode instanceof Strikethrough) {
 | 
			
		||||
 | 
			
		||||
            final int length = builder.length();
 | 
			
		||||
            final int length = mBuilder.length();
 | 
			
		||||
            visitChildren(customNode);
 | 
			
		||||
            setSpan(length, new StrikethroughSpan());
 | 
			
		||||
 | 
			
		||||
@ -302,22 +304,22 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
 | 
			
		||||
 | 
			
		||||
            final TaskListItem listItem = (TaskListItem) customNode;
 | 
			
		||||
 | 
			
		||||
            final int length = builder.length();
 | 
			
		||||
            final int length = mBuilder.length();
 | 
			
		||||
 | 
			
		||||
            blockQuoteIndent += listItem.indent();
 | 
			
		||||
            mBlockQuoteIndent += listItem.indent();
 | 
			
		||||
 | 
			
		||||
            visitChildren(customNode);
 | 
			
		||||
 | 
			
		||||
            setSpan(length, new TaskListSpan(
 | 
			
		||||
                    configuration.theme(),
 | 
			
		||||
                    blockQuoteIndent,
 | 
			
		||||
                    mConfiguration.theme(),
 | 
			
		||||
                    mBlockQuoteIndent,
 | 
			
		||||
                    length,
 | 
			
		||||
                    listItem.done()
 | 
			
		||||
            ));
 | 
			
		||||
 | 
			
		||||
            newLine();
 | 
			
		||||
 | 
			
		||||
            blockQuoteIndent -= listItem.indent();
 | 
			
		||||
            mBlockQuoteIndent -= listItem.indent();
 | 
			
		||||
 | 
			
		||||
        } else if (!handleTableNodes(customNode)) {
 | 
			
		||||
            super.visit(customNode);
 | 
			
		||||
@ -330,50 +332,50 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
 | 
			
		||||
 | 
			
		||||
        if (node instanceof TableBody) {
 | 
			
		||||
            visitChildren(node);
 | 
			
		||||
            tableRows = 0;
 | 
			
		||||
            mTableRows = 0;
 | 
			
		||||
            handled = true;
 | 
			
		||||
            newLine();
 | 
			
		||||
            builder.append('\n');
 | 
			
		||||
            mBuilder.append('\n');
 | 
			
		||||
        } else if (node instanceof TableRow) {
 | 
			
		||||
 | 
			
		||||
            final int length = builder.length();
 | 
			
		||||
            final int length = mBuilder.length();
 | 
			
		||||
            visitChildren(node);
 | 
			
		||||
 | 
			
		||||
            if (pendingTableRow != null) {
 | 
			
		||||
                builder.append(' ');
 | 
			
		||||
            if (mPendingTableRow != null) {
 | 
			
		||||
                mBuilder.append(' ');
 | 
			
		||||
 | 
			
		||||
                final TableRowSpan span = new TableRowSpan(
 | 
			
		||||
                        configuration.theme(),
 | 
			
		||||
                        pendingTableRow,
 | 
			
		||||
                        tableRowIsHeader,
 | 
			
		||||
                        tableRows % 2 == 1
 | 
			
		||||
                        mConfiguration.theme(),
 | 
			
		||||
                        mPendingTableRow,
 | 
			
		||||
                        mTableRowIsHeader,
 | 
			
		||||
                        mTableRows % 2 == 1
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                tableRows = tableRowIsHeader
 | 
			
		||||
                mTableRows = mTableRowIsHeader
 | 
			
		||||
                        ? 0
 | 
			
		||||
                        : tableRows + 1;
 | 
			
		||||
                        : mTableRows + 1;
 | 
			
		||||
 | 
			
		||||
                setSpan(length, span);
 | 
			
		||||
                newLine();
 | 
			
		||||
                pendingTableRow = null;
 | 
			
		||||
                mPendingTableRow = null;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            handled = true;
 | 
			
		||||
        } else if (node instanceof TableCell) {
 | 
			
		||||
 | 
			
		||||
            final TableCell cell = (TableCell) node;
 | 
			
		||||
            final int length = builder.length();
 | 
			
		||||
            final int length = mBuilder.length();
 | 
			
		||||
            visitChildren(cell);
 | 
			
		||||
            if (pendingTableRow == null) {
 | 
			
		||||
                pendingTableRow = new ArrayList<>(2);
 | 
			
		||||
            if (mPendingTableRow == null) {
 | 
			
		||||
                mPendingTableRow = new ArrayList<>(2);
 | 
			
		||||
            }
 | 
			
		||||
            pendingTableRow.add(new TableRowSpan.Cell(
 | 
			
		||||
            mPendingTableRow.add(new TableRowSpan.Cell(
 | 
			
		||||
                    tableCellAlignment(cell.getAlignment()),
 | 
			
		||||
                    builder.subSequence(length, builder.length())
 | 
			
		||||
                    mBuilder.subSequence(length, mBuilder.length())
 | 
			
		||||
            ));
 | 
			
		||||
            builder.replace(length, builder.length(), "");
 | 
			
		||||
            mBuilder.replace(length, mBuilder.length(), "");
 | 
			
		||||
 | 
			
		||||
            tableRowIsHeader = cell.isHeader();
 | 
			
		||||
            mTableRowIsHeader = cell.isHeader();
 | 
			
		||||
 | 
			
		||||
            handled = true;
 | 
			
		||||
        } else {
 | 
			
		||||
@ -396,8 +398,8 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
 | 
			
		||||
        if (!inTightList) {
 | 
			
		||||
            newLine();
 | 
			
		||||
 | 
			
		||||
            if (blockQuoteIndent == 0) {
 | 
			
		||||
                builder.append('\n');
 | 
			
		||||
            if (mBlockQuoteIndent == 0) {
 | 
			
		||||
                mBuilder.append('\n');
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@ -405,49 +407,53 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
 | 
			
		||||
    @Override
 | 
			
		||||
    public void visit(Image image) {
 | 
			
		||||
 | 
			
		||||
        final int length = builder.length();
 | 
			
		||||
        final int length = mBuilder.length();
 | 
			
		||||
 | 
			
		||||
        visitChildren(image);
 | 
			
		||||
 | 
			
		||||
        // we must check if anything _was_ added, as we need at least one char to render
 | 
			
		||||
        if (length == builder.length()) {
 | 
			
		||||
            builder.append('\uFFFC');
 | 
			
		||||
        if (length == mBuilder.length()) {
 | 
			
		||||
            mBuilder.append('\uFFFC');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        final Node parent = image.getParent();
 | 
			
		||||
        final boolean link = parent != null && parent instanceof Link;
 | 
			
		||||
        final String destination = configuration.urlProcessor().process(image.getDestination());
 | 
			
		||||
        final String destination = mConfiguration.urlProcessor().process(image.getDestination());
 | 
			
		||||
 | 
			
		||||
        setSpan(
 | 
			
		||||
                length,
 | 
			
		||||
                new AsyncDrawableSpan(
 | 
			
		||||
                        configuration.theme(),
 | 
			
		||||
                        mConfiguration.theme(),
 | 
			
		||||
                        new AsyncDrawable(
 | 
			
		||||
                                destination,
 | 
			
		||||
                                configuration.asyncDrawableLoader()
 | 
			
		||||
                                mConfiguration.asyncDrawableLoader()
 | 
			
		||||
                        ),
 | 
			
		||||
                        AsyncDrawableSpan.ALIGN_BOTTOM,
 | 
			
		||||
                        link
 | 
			
		||||
                )
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // todo, maybe, if image is not inside a link, we should make it clickable, so
 | 
			
		||||
        // user can open it in external viewer?
 | 
			
		||||
        setSpan(length, new ClickableSpan() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void onClick(View view) {
 | 
			
		||||
                mConfiguration.imageClickResolver().resolve(view, destination);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void visit(HtmlBlock htmlBlock) {
 | 
			
		||||
        // http://spec.commonmark.org/0.18/#html-blocks
 | 
			
		||||
        final Spanned spanned = configuration.htmlParser().getSpanned(null, htmlBlock.getLiteral());
 | 
			
		||||
        final Spanned spanned = mConfiguration.htmlParser().getSpanned(null, htmlBlock.getLiteral());
 | 
			
		||||
        if (!TextUtils.isEmpty(spanned)) {
 | 
			
		||||
            builder.append(spanned);
 | 
			
		||||
            mBuilder.append(spanned);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void visit(HtmlInline htmlInline) {
 | 
			
		||||
 | 
			
		||||
        final SpannableHtmlParser htmlParser = configuration.htmlParser();
 | 
			
		||||
        final SpannableHtmlParser htmlParser = mConfiguration.htmlParser();
 | 
			
		||||
        final SpannableHtmlParser.Tag tag = htmlParser.parseTag(htmlInline.getLiteral());
 | 
			
		||||
 | 
			
		||||
        if (tag != null) {
 | 
			
		||||
@ -455,13 +461,13 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
 | 
			
		||||
            final boolean voidTag = tag.voidTag();
 | 
			
		||||
            if (!voidTag && tag.opening()) {
 | 
			
		||||
                // push in stack
 | 
			
		||||
                htmlInlineItems.push(new HtmlInlineItem(tag, builder.length()));
 | 
			
		||||
                mHtmlInlineItems.push(new HtmlInlineItem(tag, mBuilder.length()));
 | 
			
		||||
                visitChildren(htmlInline);
 | 
			
		||||
            } else {
 | 
			
		||||
 | 
			
		||||
                if (!voidTag) {
 | 
			
		||||
                    if (htmlInlineItems.size() > 0) {
 | 
			
		||||
                        final HtmlInlineItem item = htmlInlineItems.pop();
 | 
			
		||||
                    if (mHtmlInlineItems.size() > 0) {
 | 
			
		||||
                        final HtmlInlineItem item = mHtmlInlineItems.pop();
 | 
			
		||||
                        final Object span = htmlParser.getSpanForTag(item.tag);
 | 
			
		||||
                        if (span != null) {
 | 
			
		||||
                            setSpan(item.start, span);
 | 
			
		||||
@ -471,34 +477,34 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
 | 
			
		||||
 | 
			
		||||
                    final Spanned html = htmlParser.getSpanned(tag, htmlInline.getLiteral());
 | 
			
		||||
                    if (!TextUtils.isEmpty(html)) {
 | 
			
		||||
                        builder.append(html);
 | 
			
		||||
                        mBuilder.append(html);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            // todo, should we append just literal?
 | 
			
		||||
//            builder.append(htmlInline.getLiteral());
 | 
			
		||||
//            mBuilder.append(htmlInline.getLiteral());
 | 
			
		||||
            visitChildren(htmlInline);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void visit(Link link) {
 | 
			
		||||
        final int length = builder.length();
 | 
			
		||||
        final int length = mBuilder.length();
 | 
			
		||||
        visitChildren(link);
 | 
			
		||||
        final String destination = configuration.urlProcessor().process(link.getDestination());
 | 
			
		||||
        setSpan(length, new LinkSpan(configuration.theme(), destination, configuration.linkResolver()));
 | 
			
		||||
        final String destination = mConfiguration.urlProcessor().process(link.getDestination());
 | 
			
		||||
        setSpan(length, new LinkSpan(mConfiguration.theme(), destination, mConfiguration.linkResolver()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void setSpan(int start, @NonNull Object span) {
 | 
			
		||||
        builder.setSpan(span, start, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
 | 
			
		||||
        mBuilder.setSpan(span, start, mBuilder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void newLine() {
 | 
			
		||||
        if (builder.length() > 0
 | 
			
		||||
                && '\n' != builder.charAt(builder.length() - 1)) {
 | 
			
		||||
            builder.append('\n');
 | 
			
		||||
        if (mBuilder.length() > 0
 | 
			
		||||
                && '\n' != mBuilder.charAt(mBuilder.length() - 1)) {
 | 
			
		||||
            mBuilder.append('\n');
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -4,9 +4,12 @@ import android.support.annotation.NonNull;
 | 
			
		||||
import android.text.SpannableString;
 | 
			
		||||
import android.text.Spanned;
 | 
			
		||||
import android.text.TextUtils;
 | 
			
		||||
import android.text.style.ClickableSpan;
 | 
			
		||||
import android.view.View;
 | 
			
		||||
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
import ru.noties.markwon.ImageClickResolver;
 | 
			
		||||
import ru.noties.markwon.UrlProcessor;
 | 
			
		||||
import ru.noties.markwon.spans.AsyncDrawable;
 | 
			
		||||
import ru.noties.markwon.spans.AsyncDrawableSpan;
 | 
			
		||||
@ -14,17 +17,20 @@ import ru.noties.markwon.spans.SpannableTheme;
 | 
			
		||||
 | 
			
		||||
class ImageProviderImpl implements SpannableHtmlParser.ImageProvider {
 | 
			
		||||
 | 
			
		||||
    private final SpannableTheme theme;
 | 
			
		||||
    private final AsyncDrawable.Loader loader;
 | 
			
		||||
    private final UrlProcessor urlProcessor;
 | 
			
		||||
    private final SpannableTheme mTheme;
 | 
			
		||||
    private final AsyncDrawable.Loader mLoader;
 | 
			
		||||
    private final UrlProcessor mUrlProcessor;
 | 
			
		||||
    private final ImageClickResolver mImageClickResolver;
 | 
			
		||||
 | 
			
		||||
    ImageProviderImpl(
 | 
			
		||||
            @NonNull SpannableTheme theme,
 | 
			
		||||
            @NonNull AsyncDrawable.Loader loader,
 | 
			
		||||
            @NonNull UrlProcessor urlProcessor) {
 | 
			
		||||
        this.theme = theme;
 | 
			
		||||
        this.loader = loader;
 | 
			
		||||
        this.urlProcessor = urlProcessor;
 | 
			
		||||
            @NonNull UrlProcessor urlProcessor,
 | 
			
		||||
            @NonNull ImageClickResolver imageClickResolver) {
 | 
			
		||||
        mTheme = theme;
 | 
			
		||||
        mLoader = loader;
 | 
			
		||||
        mUrlProcessor = urlProcessor;
 | 
			
		||||
        mImageClickResolver = imageClickResolver;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@ -38,7 +44,7 @@ class ImageProviderImpl implements SpannableHtmlParser.ImageProvider {
 | 
			
		||||
 | 
			
		||||
        if (!TextUtils.isEmpty(src)) {
 | 
			
		||||
 | 
			
		||||
            final String destination = urlProcessor.process(src);
 | 
			
		||||
            final String destination = mUrlProcessor.process(src);
 | 
			
		||||
 | 
			
		||||
            final String replacement;
 | 
			
		||||
            if (!TextUtils.isEmpty(alt)) {
 | 
			
		||||
@ -47,12 +53,20 @@ class ImageProviderImpl implements SpannableHtmlParser.ImageProvider {
 | 
			
		||||
                replacement = "\uFFFC";
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            final AsyncDrawable drawable = new AsyncDrawable(destination, loader);
 | 
			
		||||
            final AsyncDrawableSpan span = new AsyncDrawableSpan(theme, drawable);
 | 
			
		||||
            final AsyncDrawable drawable = new AsyncDrawable(destination, mLoader);
 | 
			
		||||
            final AsyncDrawableSpan span = new AsyncDrawableSpan(mTheme, drawable);
 | 
			
		||||
 | 
			
		||||
            final SpannableString string = new SpannableString(replacement);
 | 
			
		||||
            string.setSpan(span, 0, string.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
 | 
			
		||||
 | 
			
		||||
            final ClickableSpan clickableSpan = new ClickableSpan() {
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onClick(View view) {
 | 
			
		||||
                    mImageClickResolver.resolve(view, destination);
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
            string.setSpan(clickableSpan, 0, string.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
 | 
			
		||||
 | 
			
		||||
            spanned = string;
 | 
			
		||||
        } else {
 | 
			
		||||
            spanned = null;
 | 
			
		||||
 | 
			
		||||
@ -11,17 +11,17 @@ import ru.noties.markwon.spans.SpannableTheme;
 | 
			
		||||
 | 
			
		||||
class LinkProvider implements SpannableHtmlParser.SpanProvider {
 | 
			
		||||
 | 
			
		||||
    private final SpannableTheme theme;
 | 
			
		||||
    private final UrlProcessor urlProcessor;
 | 
			
		||||
    private final LinkSpan.Resolver resolver;
 | 
			
		||||
    private final SpannableTheme mTheme;
 | 
			
		||||
    private final UrlProcessor mUrlProcessor;
 | 
			
		||||
    private final LinkSpan.Resolver mResolver;
 | 
			
		||||
 | 
			
		||||
    LinkProvider(
 | 
			
		||||
            @NonNull SpannableTheme theme,
 | 
			
		||||
            @NonNull UrlProcessor urlProcessor,
 | 
			
		||||
            @NonNull LinkSpan.Resolver resolver) {
 | 
			
		||||
        this.theme = theme;
 | 
			
		||||
        this.urlProcessor = urlProcessor;
 | 
			
		||||
        this.resolver = resolver;
 | 
			
		||||
        mTheme = theme;
 | 
			
		||||
        mUrlProcessor = urlProcessor;
 | 
			
		||||
        mResolver = resolver;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@ -33,8 +33,8 @@ class LinkProvider implements SpannableHtmlParser.SpanProvider {
 | 
			
		||||
        final String href = attributes.get("href");
 | 
			
		||||
        if (!TextUtils.isEmpty(href)) {
 | 
			
		||||
 | 
			
		||||
            final String destination = urlProcessor.process(href);
 | 
			
		||||
            span = new LinkSpan(theme, destination, resolver);
 | 
			
		||||
            final String destination = mUrlProcessor.process(href);
 | 
			
		||||
            span = new LinkSpan(mTheme, destination, mResolver);
 | 
			
		||||
 | 
			
		||||
        } else {
 | 
			
		||||
            span = null;
 | 
			
		||||
 | 
			
		||||
@ -10,6 +10,8 @@ import android.text.Spanned;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
import ru.noties.markwon.ImageClickResolver;
 | 
			
		||||
import ru.noties.markwon.ImageClickResolverDef;
 | 
			
		||||
import ru.noties.markwon.LinkResolverDef;
 | 
			
		||||
import ru.noties.markwon.UrlProcessor;
 | 
			
		||||
import ru.noties.markwon.UrlProcessorNoOp;
 | 
			
		||||
@ -25,7 +27,8 @@ public class SpannableHtmlParser {
 | 
			
		||||
            @NonNull SpannableTheme theme,
 | 
			
		||||
            @NonNull AsyncDrawable.Loader loader
 | 
			
		||||
    ) {
 | 
			
		||||
        return builderWithDefaults(theme, loader, null, null)
 | 
			
		||||
        return builderWithDefaults(theme, loader, null, null,
 | 
			
		||||
                null)
 | 
			
		||||
                .build();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -33,9 +36,10 @@ public class SpannableHtmlParser {
 | 
			
		||||
            @NonNull SpannableTheme theme,
 | 
			
		||||
            @NonNull AsyncDrawable.Loader loader,
 | 
			
		||||
            @NonNull UrlProcessor urlProcessor,
 | 
			
		||||
            @NonNull LinkSpan.Resolver resolver
 | 
			
		||||
            @NonNull LinkSpan.Resolver linkResolver,
 | 
			
		||||
            @NonNull ImageClickResolver imageClickResolver
 | 
			
		||||
    ) {
 | 
			
		||||
        return builderWithDefaults(theme, loader, urlProcessor, resolver)
 | 
			
		||||
        return builderWithDefaults(theme, loader, urlProcessor, linkResolver, imageClickResolver)
 | 
			
		||||
                .build();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -44,22 +48,28 @@ public class SpannableHtmlParser {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static Builder builderWithDefaults(@NonNull SpannableTheme theme) {
 | 
			
		||||
        return builderWithDefaults(theme, null, null, null);
 | 
			
		||||
        return builderWithDefaults(theme, null, null, null,
 | 
			
		||||
                null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static Builder builderWithDefaults(
 | 
			
		||||
            @NonNull SpannableTheme theme,
 | 
			
		||||
            @Nullable AsyncDrawable.Loader asyncDrawableLoader,
 | 
			
		||||
            @Nullable UrlProcessor urlProcessor,
 | 
			
		||||
            @Nullable LinkSpan.Resolver resolver
 | 
			
		||||
            @Nullable LinkSpan.Resolver linkResolver,
 | 
			
		||||
            @Nullable ImageClickResolver imageClickResolver
 | 
			
		||||
    ) {
 | 
			
		||||
 | 
			
		||||
        if (urlProcessor == null) {
 | 
			
		||||
            urlProcessor = new UrlProcessorNoOp();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (resolver == null) {
 | 
			
		||||
            resolver = new LinkResolverDef();
 | 
			
		||||
        if (linkResolver == null) {
 | 
			
		||||
            linkResolver = new LinkResolverDef();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (imageClickResolver == null) {
 | 
			
		||||
            imageClickResolver = new ImageClickResolverDef();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        final BoldProvider boldProvider = new BoldProvider();
 | 
			
		||||
@ -68,7 +78,8 @@ public class SpannableHtmlParser {
 | 
			
		||||
 | 
			
		||||
        final ImageProvider imageProvider;
 | 
			
		||||
        if (asyncDrawableLoader != null) {
 | 
			
		||||
            imageProvider = new ImageProviderImpl(theme, asyncDrawableLoader, urlProcessor);
 | 
			
		||||
            imageProvider = new ImageProviderImpl(theme, asyncDrawableLoader, urlProcessor,
 | 
			
		||||
                    imageClickResolver);
 | 
			
		||||
        } else {
 | 
			
		||||
            imageProvider = null;
 | 
			
		||||
        }
 | 
			
		||||
@ -86,7 +97,7 @@ public class SpannableHtmlParser {
 | 
			
		||||
                .simpleTag("del", strikeProvider)
 | 
			
		||||
                .simpleTag("s", strikeProvider)
 | 
			
		||||
                .simpleTag("strike", strikeProvider)
 | 
			
		||||
                .simpleTag("a", new LinkProvider(theme, urlProcessor, resolver))
 | 
			
		||||
                .simpleTag("a", new LinkProvider(theme, urlProcessor, linkResolver))
 | 
			
		||||
                .imageProvider(imageProvider);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -7,14 +7,14 @@ import ru.noties.markwon.spans.SubScriptSpan;
 | 
			
		||||
 | 
			
		||||
class SubScriptProvider implements SpannableHtmlParser.SpanProvider {
 | 
			
		||||
 | 
			
		||||
    private final SpannableTheme theme;
 | 
			
		||||
    private final SpannableTheme mTheme;
 | 
			
		||||
 | 
			
		||||
    public SubScriptProvider(SpannableTheme theme) {
 | 
			
		||||
        this.theme = theme;
 | 
			
		||||
        mTheme = theme;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Object provide(@NonNull SpannableHtmlParser.Tag tag) {
 | 
			
		||||
        return new SubScriptSpan(theme);
 | 
			
		||||
        return new SubScriptSpan(mTheme);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -7,14 +7,14 @@ import ru.noties.markwon.spans.SuperScriptSpan;
 | 
			
		||||
 | 
			
		||||
class SuperScriptProvider implements SpannableHtmlParser.SpanProvider {
 | 
			
		||||
 | 
			
		||||
    private final SpannableTheme theme;
 | 
			
		||||
    private final SpannableTheme mTheme;
 | 
			
		||||
 | 
			
		||||
    SuperScriptProvider(SpannableTheme theme) {
 | 
			
		||||
        this.theme = theme;
 | 
			
		||||
        mTheme = theme;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Object provide(@NonNull SpannableHtmlParser.Tag tag) {
 | 
			
		||||
        return new SuperScriptSpan(theme);
 | 
			
		||||
        return new SuperScriptSpan(mTheme);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -17,27 +17,27 @@ public class AsyncDrawable extends Drawable {
 | 
			
		||||
        void cancel(@NonNull String destination);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private final String destination;
 | 
			
		||||
    private final Loader loader;
 | 
			
		||||
    private final String mDestination;
 | 
			
		||||
    private final Loader mLoader;
 | 
			
		||||
 | 
			
		||||
    private Drawable result;
 | 
			
		||||
    private Callback callback;
 | 
			
		||||
    private Drawable mResult;
 | 
			
		||||
    private Callback mCallback;
 | 
			
		||||
 | 
			
		||||
    public AsyncDrawable(@NonNull String destination, @NonNull Loader loader) {
 | 
			
		||||
        this.destination = destination;
 | 
			
		||||
        this.loader = loader;
 | 
			
		||||
        mDestination = destination;
 | 
			
		||||
        mLoader = loader;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getDestination() {
 | 
			
		||||
        return destination;
 | 
			
		||||
        return mDestination;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Drawable getResult() {
 | 
			
		||||
        return result;
 | 
			
		||||
        return mResult;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public boolean hasResult() {
 | 
			
		||||
        return result != null;
 | 
			
		||||
        return mResult != null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public boolean isAttached() {
 | 
			
		||||
@ -47,39 +47,39 @@ public class AsyncDrawable extends Drawable {
 | 
			
		||||
    // yeah
 | 
			
		||||
    public void setCallback2(@Nullable Callback callback) {
 | 
			
		||||
 | 
			
		||||
        this.callback = callback;
 | 
			
		||||
        mCallback = callback;
 | 
			
		||||
        super.setCallback(callback);
 | 
			
		||||
 | 
			
		||||
        // if not null -> means we are attached
 | 
			
		||||
        if (callback != null) {
 | 
			
		||||
            loader.load(destination, this);
 | 
			
		||||
            mLoader.load(mDestination, this);
 | 
			
		||||
        } else {
 | 
			
		||||
            if (result != null) {
 | 
			
		||||
            if (mResult != null) {
 | 
			
		||||
 | 
			
		||||
                result.setCallback(null);
 | 
			
		||||
                mResult.setCallback(null);
 | 
			
		||||
 | 
			
		||||
                // let's additionally stop if it Animatable
 | 
			
		||||
                if (result instanceof Animatable) {
 | 
			
		||||
                    ((Animatable) result).stop();
 | 
			
		||||
                if (mResult instanceof Animatable) {
 | 
			
		||||
                    ((Animatable) mResult).stop();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            loader.cancel(destination);
 | 
			
		||||
            mLoader.cancel(mDestination);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setResult(@NonNull Drawable result) {
 | 
			
		||||
 | 
			
		||||
        // if we have previous one, detach it
 | 
			
		||||
        if (this.result != null) {
 | 
			
		||||
            this.result.setCallback(null);
 | 
			
		||||
        if (mResult != null) {
 | 
			
		||||
            mResult.setCallback(null);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.result = result;
 | 
			
		||||
        this.result.setCallback(callback);
 | 
			
		||||
        mResult = result;
 | 
			
		||||
        mResult.setCallback(mCallback);
 | 
			
		||||
 | 
			
		||||
        // should we copy the data here? like bounds etc?
 | 
			
		||||
        // if we are async and we load some image from some source
 | 
			
		||||
        // thr bounds might change... so we are better off copy `result` bounds to this instance
 | 
			
		||||
        // thr bounds might change... so we are better off copy `mResult` bounds to this instance
 | 
			
		||||
        setBounds(result.getBounds());
 | 
			
		||||
        invalidateSelf();
 | 
			
		||||
    }
 | 
			
		||||
@ -87,7 +87,7 @@ public class AsyncDrawable extends Drawable {
 | 
			
		||||
    @Override
 | 
			
		||||
    public void draw(@NonNull Canvas canvas) {
 | 
			
		||||
        if (hasResult()) {
 | 
			
		||||
            result.draw(canvas);
 | 
			
		||||
            mResult.draw(canvas);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -105,7 +105,7 @@ public class AsyncDrawable extends Drawable {
 | 
			
		||||
    public int getOpacity() {
 | 
			
		||||
        final int opacity;
 | 
			
		||||
        if (hasResult()) {
 | 
			
		||||
            opacity = result.getOpacity();
 | 
			
		||||
            opacity = mResult.getOpacity();
 | 
			
		||||
        } else {
 | 
			
		||||
            opacity = PixelFormat.TRANSPARENT;
 | 
			
		||||
        }
 | 
			
		||||
@ -116,7 +116,7 @@ public class AsyncDrawable extends Drawable {
 | 
			
		||||
    public int getIntrinsicWidth() {
 | 
			
		||||
        final int out;
 | 
			
		||||
        if (hasResult()) {
 | 
			
		||||
            out = result.getIntrinsicWidth();
 | 
			
		||||
            out = mResult.getIntrinsicWidth();
 | 
			
		||||
        } else {
 | 
			
		||||
            out = 0;
 | 
			
		||||
        }
 | 
			
		||||
@ -127,7 +127,7 @@ public class AsyncDrawable extends Drawable {
 | 
			
		||||
    public int getIntrinsicHeight() {
 | 
			
		||||
        final int out;
 | 
			
		||||
        if (hasResult()) {
 | 
			
		||||
            out = result.getIntrinsicHeight();
 | 
			
		||||
            out = mResult.getIntrinsicHeight();
 | 
			
		||||
        } else {
 | 
			
		||||
            out = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -22,15 +22,15 @@ public class AsyncDrawableSpan extends ReplacementSpan {
 | 
			
		||||
 | 
			
		||||
    public static final int ALIGN_BOTTOM = 0;
 | 
			
		||||
    public static final int ALIGN_BASELINE = 1;
 | 
			
		||||
    public static final int ALIGN_CENTER = 2; // will only center if drawable height is less than text line height
 | 
			
		||||
    public static final int ALIGN_CENTER = 2; // will only center if drawable height is less than mText line height
 | 
			
		||||
 | 
			
		||||
    private final SpannableTheme theme;
 | 
			
		||||
    private final AsyncDrawable drawable;
 | 
			
		||||
    private final int alignment;
 | 
			
		||||
    private final boolean replacementTextIsLink;
 | 
			
		||||
    private final SpannableTheme mTheme;
 | 
			
		||||
    private final AsyncDrawable mDrawable;
 | 
			
		||||
    private final int mAlignment;
 | 
			
		||||
    private final boolean mReplacementTextIsLink;
 | 
			
		||||
 | 
			
		||||
    private int lastKnownDrawX;
 | 
			
		||||
    private int lastKnownDrawY;
 | 
			
		||||
    private int mLastKnownDrawX;
 | 
			
		||||
    private int mLastKnownDrawY;
 | 
			
		||||
 | 
			
		||||
    public AsyncDrawableSpan(@NonNull SpannableTheme theme, @NonNull AsyncDrawable drawable) {
 | 
			
		||||
        this(theme, drawable, ALIGN_BOTTOM);
 | 
			
		||||
@ -48,10 +48,10 @@ public class AsyncDrawableSpan extends ReplacementSpan {
 | 
			
		||||
            @NonNull AsyncDrawable drawable,
 | 
			
		||||
            @Alignment int alignment,
 | 
			
		||||
            boolean replacementTextIsLink) {
 | 
			
		||||
        this.theme = theme;
 | 
			
		||||
        this.drawable = drawable;
 | 
			
		||||
        this.alignment = alignment;
 | 
			
		||||
        this.replacementTextIsLink = replacementTextIsLink;
 | 
			
		||||
        mTheme = theme;
 | 
			
		||||
        mDrawable = drawable;
 | 
			
		||||
        mAlignment = alignment;
 | 
			
		||||
        mReplacementTextIsLink = replacementTextIsLink;
 | 
			
		||||
 | 
			
		||||
        // additionally set intrinsic bounds if empty
 | 
			
		||||
        final Rect rect = drawable.getBounds();
 | 
			
		||||
@ -68,13 +68,13 @@ public class AsyncDrawableSpan extends ReplacementSpan {
 | 
			
		||||
            @IntRange(from = 0) int end,
 | 
			
		||||
            @Nullable Paint.FontMetricsInt fm) {
 | 
			
		||||
 | 
			
		||||
        // if we have no async drawable result - we will just render text
 | 
			
		||||
        // if we have no async drawable result - we will just render mText
 | 
			
		||||
 | 
			
		||||
        final int size;
 | 
			
		||||
 | 
			
		||||
        if (drawable.hasResult()) {
 | 
			
		||||
        if (mDrawable.hasResult()) {
 | 
			
		||||
 | 
			
		||||
            final Rect rect = drawable.getBounds();
 | 
			
		||||
            final Rect rect = mDrawable.getBounds();
 | 
			
		||||
 | 
			
		||||
            if (fm != null) {
 | 
			
		||||
                fm.ascent = -rect.bottom;
 | 
			
		||||
@ -89,11 +89,11 @@ public class AsyncDrawableSpan extends ReplacementSpan {
 | 
			
		||||
        } else {
 | 
			
		||||
 | 
			
		||||
            // we will apply style here in case if theme modifies textSize or style (affects metrics)
 | 
			
		||||
            if (replacementTextIsLink) {
 | 
			
		||||
                theme.applyLinkStyle(paint);
 | 
			
		||||
            if (mReplacementTextIsLink) {
 | 
			
		||||
                mTheme.applyLinkStyle(paint);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // NB, no specific text handling (no new lines, etc)
 | 
			
		||||
            // NB, no specific mText handling (no new lines, etc)
 | 
			
		||||
            size = (int) (paint.measureText(text, start, end) + .5F);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -112,10 +112,10 @@ public class AsyncDrawableSpan extends ReplacementSpan {
 | 
			
		||||
            int bottom,
 | 
			
		||||
            @NonNull Paint paint) {
 | 
			
		||||
 | 
			
		||||
        this.lastKnownDrawX = (int) (x + .5F);
 | 
			
		||||
        this.lastKnownDrawY = y;
 | 
			
		||||
        mLastKnownDrawX = (int) (x + .5F);
 | 
			
		||||
        mLastKnownDrawY = y;
 | 
			
		||||
 | 
			
		||||
        final AsyncDrawable drawable = this.drawable;
 | 
			
		||||
        final AsyncDrawable drawable = mDrawable;
 | 
			
		||||
 | 
			
		||||
        if (drawable.hasResult()) {
 | 
			
		||||
 | 
			
		||||
@ -124,9 +124,9 @@ public class AsyncDrawableSpan extends ReplacementSpan {
 | 
			
		||||
            final int save = canvas.save();
 | 
			
		||||
            try {
 | 
			
		||||
                final int translationY;
 | 
			
		||||
                if (ALIGN_CENTER == alignment) {
 | 
			
		||||
                if (ALIGN_CENTER == mAlignment) {
 | 
			
		||||
                    translationY = b - ((bottom - top - drawable.getBounds().height()) / 2);
 | 
			
		||||
                } else if (ALIGN_BASELINE == alignment) {
 | 
			
		||||
                } else if (ALIGN_BASELINE == mAlignment) {
 | 
			
		||||
                    translationY = b - paint.getFontMetricsInt().descent;
 | 
			
		||||
                } else {
 | 
			
		||||
                    translationY = b;
 | 
			
		||||
@ -142,24 +142,24 @@ public class AsyncDrawableSpan extends ReplacementSpan {
 | 
			
		||||
            // let's focus on main functionality and then think of it
 | 
			
		||||
 | 
			
		||||
            final float textY = CanvasUtils.textCenterY(top, bottom, paint);
 | 
			
		||||
            if (replacementTextIsLink) {
 | 
			
		||||
                theme.applyLinkStyle(paint);
 | 
			
		||||
            if (mReplacementTextIsLink) {
 | 
			
		||||
                mTheme.applyLinkStyle(paint);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // NB, no specific text handling (no new lines, etc)
 | 
			
		||||
            // NB, no specific mText handling (no new lines, etc)
 | 
			
		||||
            canvas.drawText(text, start, end, x, textY, paint);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public AsyncDrawable getDrawable() {
 | 
			
		||||
        return drawable;
 | 
			
		||||
        return mDrawable;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int lastKnownDrawX() {
 | 
			
		||||
        return lastKnownDrawX;
 | 
			
		||||
        return mLastKnownDrawX;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int lastKnownDrawY() {
 | 
			
		||||
        return lastKnownDrawY;
 | 
			
		||||
        return mLastKnownDrawY;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -9,19 +9,19 @@ import android.text.style.LeadingMarginSpan;
 | 
			
		||||
 | 
			
		||||
public class BlockQuoteSpan implements LeadingMarginSpan {
 | 
			
		||||
 | 
			
		||||
    private final SpannableTheme theme;
 | 
			
		||||
    private final Rect rect = ObjectsPool.rect();
 | 
			
		||||
    private final Paint paint = ObjectsPool.paint();
 | 
			
		||||
    private final int indent;
 | 
			
		||||
    private final SpannableTheme mTheme;
 | 
			
		||||
    private final Rect mRect = ObjectsPool.rect();
 | 
			
		||||
    private final Paint mPaint = ObjectsPool.paint();
 | 
			
		||||
    private final int mIndent;
 | 
			
		||||
 | 
			
		||||
    public BlockQuoteSpan(@NonNull SpannableTheme theme, int indent) {
 | 
			
		||||
        this.theme = theme;
 | 
			
		||||
        this.indent = indent;
 | 
			
		||||
        mTheme = theme;
 | 
			
		||||
        mIndent = indent;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int getLeadingMargin(boolean first) {
 | 
			
		||||
        return theme.getBlockMargin();
 | 
			
		||||
        return mTheme.getBlockMargin();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@ -39,13 +39,13 @@ public class BlockQuoteSpan implements LeadingMarginSpan {
 | 
			
		||||
            boolean first,
 | 
			
		||||
            Layout layout) {
 | 
			
		||||
 | 
			
		||||
        final int width = theme.getBlockQuoteWidth();
 | 
			
		||||
        final int width = mTheme.getBlockQuoteWidth();
 | 
			
		||||
 | 
			
		||||
        theme.applyBlockQuoteStyle(paint);
 | 
			
		||||
        mTheme.applyBlockQuoteStyle(mPaint);
 | 
			
		||||
 | 
			
		||||
        final int left = theme.getBlockMargin() * (indent - 1);
 | 
			
		||||
        rect.set(left, top, left + width, bottom);
 | 
			
		||||
        final int left = mTheme.getBlockMargin() * (mIndent - 1);
 | 
			
		||||
        mRect.set(left, top, left + width, bottom);
 | 
			
		||||
 | 
			
		||||
        c.drawRect(rect, paint);
 | 
			
		||||
        c.drawRect(mRect, mPaint);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -11,27 +11,27 @@ import android.text.style.LeadingMarginSpan;
 | 
			
		||||
 | 
			
		||||
public class BulletListItemSpan implements LeadingMarginSpan {
 | 
			
		||||
 | 
			
		||||
    private SpannableTheme theme;
 | 
			
		||||
    private SpannableTheme mTheme;
 | 
			
		||||
 | 
			
		||||
    private final Paint paint = ObjectsPool.paint();
 | 
			
		||||
    private final RectF circle = ObjectsPool.rectF();
 | 
			
		||||
    private final Rect rectangle = ObjectsPool.rect();
 | 
			
		||||
    private final Paint mPaint = ObjectsPool.paint();
 | 
			
		||||
    private final RectF mCircle = ObjectsPool.rectF();
 | 
			
		||||
    private final Rect mRectangle = ObjectsPool.rect();
 | 
			
		||||
 | 
			
		||||
    private final int blockIndent;
 | 
			
		||||
    private final int level;
 | 
			
		||||
    private final int mBlockIndent;
 | 
			
		||||
    private final int mLevel;
 | 
			
		||||
 | 
			
		||||
    public BulletListItemSpan(
 | 
			
		||||
            @NonNull SpannableTheme theme,
 | 
			
		||||
            @IntRange(from = 0) int blockIndent,
 | 
			
		||||
            @IntRange(from = 0) int level) {
 | 
			
		||||
        this.theme = theme;
 | 
			
		||||
        this.blockIndent = blockIndent;
 | 
			
		||||
        this.level = level;
 | 
			
		||||
        mTheme = theme;
 | 
			
		||||
        mBlockIndent = blockIndent;
 | 
			
		||||
        mLevel = level;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int getLeadingMargin(boolean first) {
 | 
			
		||||
        return theme.getBlockMargin();
 | 
			
		||||
        return mTheme.getBlockMargin();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@ -42,44 +42,44 @@ public class BulletListItemSpan implements LeadingMarginSpan {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        paint.set(p);
 | 
			
		||||
        mPaint.set(p);
 | 
			
		||||
 | 
			
		||||
        theme.applyListItemStyle(paint);
 | 
			
		||||
        mTheme.applyListItemStyle(mPaint);
 | 
			
		||||
 | 
			
		||||
        final int save = c.save();
 | 
			
		||||
        try {
 | 
			
		||||
 | 
			
		||||
            final int width = theme.getBlockMargin();
 | 
			
		||||
            final int width = mTheme.getBlockMargin();
 | 
			
		||||
            final int height = bottom - top;
 | 
			
		||||
 | 
			
		||||
            final int side = theme.getBulletWidth(bottom - top);
 | 
			
		||||
            final int side = mTheme.getBulletWidth(bottom - top);
 | 
			
		||||
 | 
			
		||||
            final int marginLeft = (width - side) / 2;
 | 
			
		||||
            final int marginTop = (height - side) / 2;
 | 
			
		||||
 | 
			
		||||
            final int l = (width * (blockIndent - 1)) + marginLeft;
 | 
			
		||||
            final int l = (width * (mBlockIndent - 1)) + marginLeft;
 | 
			
		||||
            final int t = top + marginTop;
 | 
			
		||||
            final int r = l + side;
 | 
			
		||||
            final int b = t + side;
 | 
			
		||||
 | 
			
		||||
            if (level == 0
 | 
			
		||||
                    || level == 1) {
 | 
			
		||||
            if (mLevel == 0
 | 
			
		||||
                    || mLevel == 1) {
 | 
			
		||||
 | 
			
		||||
                circle.set(l, t, r, b);
 | 
			
		||||
                mCircle.set(l, t, r, b);
 | 
			
		||||
 | 
			
		||||
                final Paint.Style style = level == 0
 | 
			
		||||
                final Paint.Style style = mLevel == 0
 | 
			
		||||
                        ? Paint.Style.FILL
 | 
			
		||||
                        : Paint.Style.STROKE;
 | 
			
		||||
                paint.setStyle(style);
 | 
			
		||||
                mPaint.setStyle(style);
 | 
			
		||||
 | 
			
		||||
                c.drawOval(circle, paint);
 | 
			
		||||
                c.drawOval(mCircle, mPaint);
 | 
			
		||||
            } else {
 | 
			
		||||
 | 
			
		||||
                rectangle.set(l, t, r, b);
 | 
			
		||||
                mRectangle.set(l, t, r, b);
 | 
			
		||||
 | 
			
		||||
                paint.setStyle(Paint.Style.FILL);
 | 
			
		||||
                mPaint.setStyle(Paint.Style.FILL);
 | 
			
		||||
 | 
			
		||||
                c.drawRect(rectangle, paint);
 | 
			
		||||
                c.drawRect(mRectangle, mPaint);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        } finally {
 | 
			
		||||
 | 
			
		||||
@ -11,15 +11,15 @@ import android.text.style.MetricAffectingSpan;
 | 
			
		||||
 | 
			
		||||
public class CodeSpan extends MetricAffectingSpan implements LeadingMarginSpan {
 | 
			
		||||
 | 
			
		||||
    private final SpannableTheme theme;
 | 
			
		||||
    private final Rect rect = ObjectsPool.rect();
 | 
			
		||||
    private final Paint paint = ObjectsPool.paint();
 | 
			
		||||
    private final SpannableTheme mTheme;
 | 
			
		||||
    private final Rect mRect = ObjectsPool.rect();
 | 
			
		||||
    private final Paint mPaint = ObjectsPool.paint();
 | 
			
		||||
 | 
			
		||||
    private final boolean multiline;
 | 
			
		||||
    private final boolean mMultiline;
 | 
			
		||||
 | 
			
		||||
    public CodeSpan(@NonNull SpannableTheme theme, boolean multiline) {
 | 
			
		||||
        this.theme = theme;
 | 
			
		||||
        this.multiline = multiline;
 | 
			
		||||
        mTheme = theme;
 | 
			
		||||
        mMultiline = multiline;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@ -30,31 +30,31 @@ public class CodeSpan extends MetricAffectingSpan implements LeadingMarginSpan {
 | 
			
		||||
    @Override
 | 
			
		||||
    public void updateDrawState(TextPaint ds) {
 | 
			
		||||
        apply(ds);
 | 
			
		||||
        if (!multiline) {
 | 
			
		||||
            ds.bgColor = theme.getCodeBackgroundColor(ds);
 | 
			
		||||
        if (!mMultiline) {
 | 
			
		||||
            ds.bgColor = mTheme.getCodeBackgroundColor(ds);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void apply(TextPaint p) {
 | 
			
		||||
        theme.applyCodeTextStyle(p);
 | 
			
		||||
        mTheme.applyCodeTextStyle(p);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int getLeadingMargin(boolean first) {
 | 
			
		||||
        return multiline ? theme.getCodeMultilineMargin() : 0;
 | 
			
		||||
        return mMultiline ? mTheme.getCodeMultilineMargin() : 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void drawLeadingMargin(Canvas c, Paint p, int x, int dir, int top, int baseline, int bottom, CharSequence text, int start, int end, boolean first, Layout layout) {
 | 
			
		||||
 | 
			
		||||
        if (multiline) {
 | 
			
		||||
        if (mMultiline) {
 | 
			
		||||
 | 
			
		||||
            paint.setStyle(Paint.Style.FILL);
 | 
			
		||||
            paint.setColor(theme.getCodeBackgroundColor(p));
 | 
			
		||||
            mPaint.setStyle(Paint.Style.FILL);
 | 
			
		||||
            mPaint.setColor(mTheme.getCodeBackgroundColor(p));
 | 
			
		||||
 | 
			
		||||
            rect.set(x, top, c.getWidth(), bottom);
 | 
			
		||||
            mRect.set(x, top, c.getWidth(), bottom);
 | 
			
		||||
 | 
			
		||||
            c.drawRect(rect, paint);
 | 
			
		||||
            c.drawRect(mRect, mPaint);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -12,16 +12,16 @@ import android.text.style.MetricAffectingSpan;
 | 
			
		||||
 | 
			
		||||
public class HeadingSpan extends MetricAffectingSpan implements LeadingMarginSpan {
 | 
			
		||||
 | 
			
		||||
    private final SpannableTheme theme;
 | 
			
		||||
    private final Rect rect = ObjectsPool.rect();
 | 
			
		||||
    private final Paint paint = ObjectsPool.paint();
 | 
			
		||||
    private final int level;
 | 
			
		||||
    private final int textLength;
 | 
			
		||||
    private final SpannableTheme mTheme;
 | 
			
		||||
    private final Rect mRect = ObjectsPool.rect();
 | 
			
		||||
    private final Paint mPaint = ObjectsPool.paint();
 | 
			
		||||
    private final int mLevel;
 | 
			
		||||
    private final int mTextLength;
 | 
			
		||||
 | 
			
		||||
    public HeadingSpan(@NonNull SpannableTheme theme, @IntRange(from = 1, to = 6) int level, @IntRange(from = 0) int textLength) {
 | 
			
		||||
        this.theme = theme;
 | 
			
		||||
        this.level = level;
 | 
			
		||||
        this.textLength = textLength;
 | 
			
		||||
        mTheme = theme;
 | 
			
		||||
        mLevel = level;
 | 
			
		||||
        mTextLength = textLength;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@ -35,7 +35,7 @@ public class HeadingSpan extends MetricAffectingSpan implements LeadingMarginSpa
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void apply(TextPaint paint) {
 | 
			
		||||
        theme.applyHeadingTextStyle(paint, level);
 | 
			
		||||
        mTheme.applyHeadingTextStyle(paint, mLevel);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@ -47,19 +47,19 @@ public class HeadingSpan extends MetricAffectingSpan implements LeadingMarginSpa
 | 
			
		||||
    @Override
 | 
			
		||||
    public void drawLeadingMargin(Canvas c, Paint p, int x, int dir, int top, int baseline, int bottom, CharSequence text, int start, int end, boolean first, Layout layout) {
 | 
			
		||||
 | 
			
		||||
        if (level == 1
 | 
			
		||||
                || level == 2) {
 | 
			
		||||
        if (mLevel == 1
 | 
			
		||||
                || mLevel == 2) {
 | 
			
		||||
 | 
			
		||||
            if ((start + textLength) == end) {
 | 
			
		||||
                paint.set(p);
 | 
			
		||||
            if ((start + mTextLength) == end) {
 | 
			
		||||
                mPaint.set(p);
 | 
			
		||||
 | 
			
		||||
                theme.applyHeadingBreakStyle(paint);
 | 
			
		||||
                mTheme.applyHeadingBreakStyle(mPaint);
 | 
			
		||||
 | 
			
		||||
                final float height = paint.getStrokeWidth();
 | 
			
		||||
                final float height = mPaint.getStrokeWidth();
 | 
			
		||||
                final int b = (int) (bottom - height + .5F);
 | 
			
		||||
 | 
			
		||||
                rect.set(x, b, c.getWidth(), bottom);
 | 
			
		||||
                c.drawRect(rect, paint);
 | 
			
		||||
                mRect.set(x, b, c.getWidth(), bottom);
 | 
			
		||||
                c.drawRect(mRect, mPaint);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -11,23 +11,23 @@ public class LinkSpan extends ClickableSpan {
 | 
			
		||||
        void resolve(View view, @NonNull String link);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private final SpannableTheme theme;
 | 
			
		||||
    private final String link;
 | 
			
		||||
    private final Resolver resolver;
 | 
			
		||||
    private final SpannableTheme mTheme;
 | 
			
		||||
    private final String mLink;
 | 
			
		||||
    private final Resolver mResolver;
 | 
			
		||||
 | 
			
		||||
    public LinkSpan(@NonNull SpannableTheme theme, @NonNull String link, @NonNull Resolver resolver) {
 | 
			
		||||
        this.theme = theme;
 | 
			
		||||
        this.link = link;
 | 
			
		||||
        this.resolver = resolver;
 | 
			
		||||
        mTheme = theme;
 | 
			
		||||
        mLink = link;
 | 
			
		||||
        mResolver = resolver;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onClick(View widget) {
 | 
			
		||||
        resolver.resolve(widget, link);
 | 
			
		||||
        mResolver.resolve(widget, mLink);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void updateDrawState(TextPaint ds) {
 | 
			
		||||
        theme.applyLinkStyle(ds);
 | 
			
		||||
        mTheme.applyLinkStyle(ds);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -9,23 +9,23 @@ import android.text.style.LeadingMarginSpan;
 | 
			
		||||
 | 
			
		||||
public class OrderedListItemSpan implements LeadingMarginSpan {
 | 
			
		||||
 | 
			
		||||
    private final SpannableTheme theme;
 | 
			
		||||
    private final String number;
 | 
			
		||||
    private final int blockIndent;
 | 
			
		||||
    private final SpannableTheme mTheme;
 | 
			
		||||
    private final String mNumber;
 | 
			
		||||
    private final int mBlockIndent;
 | 
			
		||||
 | 
			
		||||
    public OrderedListItemSpan(
 | 
			
		||||
            @NonNull SpannableTheme theme,
 | 
			
		||||
            @NonNull String number,
 | 
			
		||||
            @IntRange(from = 0) int blockIndent
 | 
			
		||||
    ) {
 | 
			
		||||
        this.theme = theme;
 | 
			
		||||
        this.number = number;
 | 
			
		||||
        this.blockIndent = blockIndent;
 | 
			
		||||
        mTheme = theme;
 | 
			
		||||
        mNumber = number;
 | 
			
		||||
        mBlockIndent = blockIndent;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int getLeadingMargin(boolean first) {
 | 
			
		||||
        return theme.getBlockMargin();
 | 
			
		||||
        return mTheme.getBlockMargin();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@ -36,14 +36,14 @@ public class OrderedListItemSpan implements LeadingMarginSpan {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        theme.applyListItemStyle(p);
 | 
			
		||||
        mTheme.applyListItemStyle(p);
 | 
			
		||||
 | 
			
		||||
        final int width = theme.getBlockMargin();
 | 
			
		||||
        final int numberWidth = (int) (p.measureText(number) + .5F);
 | 
			
		||||
        final int numberX = (width * blockIndent) - numberWidth;
 | 
			
		||||
        final int width = mTheme.getBlockMargin();
 | 
			
		||||
        final int numberWidth = (int) (p.measureText(mNumber) + .5F);
 | 
			
		||||
        final int numberX = (width * mBlockIndent) - numberWidth;
 | 
			
		||||
 | 
			
		||||
        final float numberY = CanvasUtils.textCenterY(top, bottom, p);
 | 
			
		||||
 | 
			
		||||
        c.drawText(number, numberX, numberY, p);
 | 
			
		||||
        c.drawText(mNumber, numberX, numberY, p);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -122,128 +122,128 @@ public class SpannableTheme {
 | 
			
		||||
 | 
			
		||||
    protected static final int TABLE_ODD_ROW_DEF_ALPHA = 22;
 | 
			
		||||
 | 
			
		||||
    protected final int linkColor;
 | 
			
		||||
    protected final int mLinkColor;
 | 
			
		||||
 | 
			
		||||
    // used in quote, lists
 | 
			
		||||
    protected final int blockMargin;
 | 
			
		||||
    protected final int mBlockMargin;
 | 
			
		||||
 | 
			
		||||
    // by default it's 1/4th of `blockMargin`
 | 
			
		||||
    protected final int blockQuoteWidth;
 | 
			
		||||
    // by default it's 1/4th of `mBlockMargin`
 | 
			
		||||
    protected final int mBlockQuoteWidth;
 | 
			
		||||
 | 
			
		||||
    // by default it's text color with `BLOCK_QUOTE_DEF_COLOR_ALPHA` applied alpha
 | 
			
		||||
    protected final int blockQuoteColor;
 | 
			
		||||
    // by default it's mText color with `BLOCK_QUOTE_DEF_COLOR_ALPHA` applied alpha
 | 
			
		||||
    protected final int mBlockQuoteColor;
 | 
			
		||||
 | 
			
		||||
    // by default uses text color (applied for un-ordered lists & ordered (bullets & numbers)
 | 
			
		||||
    protected final int listItemColor;
 | 
			
		||||
    // by default uses mText color (applied for un-ordered lists & ordered (bullets & numbers)
 | 
			
		||||
    protected final int mListItemColor;
 | 
			
		||||
 | 
			
		||||
    // by default the stroke color of a paint object
 | 
			
		||||
    protected final int bulletListItemStrokeWidth;
 | 
			
		||||
    protected final int mBulletListItemStrokeWidth;
 | 
			
		||||
 | 
			
		||||
    // width of bullet, by default min(blockMargin, height) / 2
 | 
			
		||||
    protected final int bulletWidth;
 | 
			
		||||
    // width of bullet, by default min(mBlockMargin, height) / 2
 | 
			
		||||
    protected final int mBulletWidth;
 | 
			
		||||
 | 
			
		||||
    // by default - main text color
 | 
			
		||||
    protected final int codeTextColor;
 | 
			
		||||
    // by default - main mText color
 | 
			
		||||
    protected final int mCodeTextColor;
 | 
			
		||||
 | 
			
		||||
    // by default 0.1 alpha of textColor/codeTextColor
 | 
			
		||||
    protected final int codeBackgroundColor;
 | 
			
		||||
    // by default 0.1 alpha of textColor/mCodeTextColor
 | 
			
		||||
    protected final int mCodeBackgroundColor;
 | 
			
		||||
 | 
			
		||||
    // by default `width` of a space char... it's fun and games, but span doesn't have access to paint in `getLeadingMargin`
 | 
			
		||||
    // so, we need to set this value explicitly (think of an utility method, that takes TextView/TextPaint and measures space char)
 | 
			
		||||
    protected final int codeMultilineMargin;
 | 
			
		||||
    protected final int mCodeMultilineMargin;
 | 
			
		||||
 | 
			
		||||
    // by default Typeface.MONOSPACE
 | 
			
		||||
    protected final Typeface codeTypeface;
 | 
			
		||||
    protected final Typeface mCodeTypeface;
 | 
			
		||||
 | 
			
		||||
    // by default a bit (how much?!) smaller than normal text
 | 
			
		||||
    // by default a bit (how much?!) smaller than normal mText
 | 
			
		||||
    // applied ONLY if default typeface was used, otherwise, not applied
 | 
			
		||||
    protected final int codeTextSize;
 | 
			
		||||
    protected final int mCodeTextSize;
 | 
			
		||||
 | 
			
		||||
    // by default paint.getStrokeWidth
 | 
			
		||||
    protected final int headingBreakHeight;
 | 
			
		||||
    protected final int mHeadingBreakHeight;
 | 
			
		||||
 | 
			
		||||
    // by default, text color with `HEADING_DEF_BREAK_COLOR_ALPHA` applied alpha
 | 
			
		||||
    protected final int headingBreakColor;
 | 
			
		||||
    // by default, mText color with `HEADING_DEF_BREAK_COLOR_ALPHA` applied alpha
 | 
			
		||||
    protected final int mHeadingBreakColor;
 | 
			
		||||
 | 
			
		||||
    // by default `SCRIPT_DEF_TEXT_SIZE_RATIO`
 | 
			
		||||
    protected final float scriptTextSizeRatio;
 | 
			
		||||
    protected final float mScriptTextSizeRatio;
 | 
			
		||||
 | 
			
		||||
    // by default textColor with `THEMATIC_BREAK_DEF_ALPHA` applied alpha
 | 
			
		||||
    protected final int thematicBreakColor;
 | 
			
		||||
    protected final int mThematicBreakColor;
 | 
			
		||||
 | 
			
		||||
    // by default paint.strokeWidth
 | 
			
		||||
    protected final int thematicBreakHeight;
 | 
			
		||||
    protected final int mThematicBreakHeight;
 | 
			
		||||
 | 
			
		||||
    // by default 0
 | 
			
		||||
    protected final int tableCellPadding;
 | 
			
		||||
    protected final int mTableCellPadding;
 | 
			
		||||
 | 
			
		||||
    // by default paint.color * TABLE_BORDER_DEF_ALPHA
 | 
			
		||||
    protected final int tableBorderColor;
 | 
			
		||||
    protected final int mTableBorderColor;
 | 
			
		||||
 | 
			
		||||
    protected final int tableBorderWidth;
 | 
			
		||||
    protected final int mTableBorderWidth;
 | 
			
		||||
 | 
			
		||||
    // by default paint.color * TABLE_ODD_ROW_DEF_ALPHA
 | 
			
		||||
    protected final int tableOddRowBackgroundColor;
 | 
			
		||||
    protected final int mTableOddRowBackgroundColor;
 | 
			
		||||
 | 
			
		||||
    // drawable that will be used to render checkbox (should be stateful)
 | 
			
		||||
    // TaskListDrawable can be used
 | 
			
		||||
    protected final Drawable taskListDrawable;
 | 
			
		||||
    protected final Drawable mTaskListDrawable;
 | 
			
		||||
 | 
			
		||||
    protected SpannableTheme(@NonNull Builder builder) {
 | 
			
		||||
        this.linkColor = builder.linkColor;
 | 
			
		||||
        this.blockMargin = builder.blockMargin;
 | 
			
		||||
        this.blockQuoteWidth = builder.blockQuoteWidth;
 | 
			
		||||
        this.blockQuoteColor = builder.blockQuoteColor;
 | 
			
		||||
        this.listItemColor = builder.listItemColor;
 | 
			
		||||
        this.bulletListItemStrokeWidth = builder.bulletListItemStrokeWidth;
 | 
			
		||||
        this.bulletWidth = builder.bulletWidth;
 | 
			
		||||
        this.codeTextColor = builder.codeTextColor;
 | 
			
		||||
        this.codeBackgroundColor = builder.codeBackgroundColor;
 | 
			
		||||
        this.codeMultilineMargin = builder.codeMultilineMargin;
 | 
			
		||||
        this.codeTypeface = builder.codeTypeface;
 | 
			
		||||
        this.codeTextSize = builder.codeTextSize;
 | 
			
		||||
        this.headingBreakHeight = builder.headingBreakHeight;
 | 
			
		||||
        this.headingBreakColor = builder.headingBreakColor;
 | 
			
		||||
        this.scriptTextSizeRatio = builder.scriptTextSizeRatio;
 | 
			
		||||
        this.thematicBreakColor = builder.thematicBreakColor;
 | 
			
		||||
        this.thematicBreakHeight = builder.thematicBreakHeight;
 | 
			
		||||
        this.tableCellPadding = builder.tableCellPadding;
 | 
			
		||||
        this.tableBorderColor = builder.tableBorderColor;
 | 
			
		||||
        this.tableBorderWidth = builder.tableBorderWidth;
 | 
			
		||||
        this.tableOddRowBackgroundColor = builder.tableOddRowBackgroundColor;
 | 
			
		||||
        this.taskListDrawable = builder.taskListDrawable;
 | 
			
		||||
        mLinkColor = builder.linkColor;
 | 
			
		||||
        mBlockMargin = builder.blockMargin;
 | 
			
		||||
        mBlockQuoteWidth = builder.blockQuoteWidth;
 | 
			
		||||
        mBlockQuoteColor = builder.blockQuoteColor;
 | 
			
		||||
        mListItemColor = builder.listItemColor;
 | 
			
		||||
        mBulletListItemStrokeWidth = builder.bulletListItemStrokeWidth;
 | 
			
		||||
        mBulletWidth = builder.bulletWidth;
 | 
			
		||||
        mCodeTextColor = builder.codeTextColor;
 | 
			
		||||
        mCodeBackgroundColor = builder.codeBackgroundColor;
 | 
			
		||||
        mCodeMultilineMargin = builder.codeMultilineMargin;
 | 
			
		||||
        mCodeTypeface = builder.codeTypeface;
 | 
			
		||||
        mCodeTextSize = builder.codeTextSize;
 | 
			
		||||
        mHeadingBreakHeight = builder.headingBreakHeight;
 | 
			
		||||
        mHeadingBreakColor = builder.headingBreakColor;
 | 
			
		||||
        mScriptTextSizeRatio = builder.scriptTextSizeRatio;
 | 
			
		||||
        mThematicBreakColor = builder.thematicBreakColor;
 | 
			
		||||
        mThematicBreakHeight = builder.thematicBreakHeight;
 | 
			
		||||
        mTableCellPadding = builder.tableCellPadding;
 | 
			
		||||
        mTableBorderColor = builder.tableBorderColor;
 | 
			
		||||
        mTableBorderWidth = builder.tableBorderWidth;
 | 
			
		||||
        mTableOddRowBackgroundColor = builder.tableOddRowBackgroundColor;
 | 
			
		||||
        mTaskListDrawable = builder.taskListDrawable;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public void applyLinkStyle(@NonNull Paint paint) {
 | 
			
		||||
        paint.setUnderlineText(true);
 | 
			
		||||
        if (linkColor != 0) {
 | 
			
		||||
            // by default we will be using text color
 | 
			
		||||
            paint.setColor(linkColor);
 | 
			
		||||
        if (mLinkColor != 0) {
 | 
			
		||||
            // by default we will be using mText color
 | 
			
		||||
            paint.setColor(mLinkColor);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void applyBlockQuoteStyle(@NonNull Paint paint) {
 | 
			
		||||
        final int color;
 | 
			
		||||
        if (blockQuoteColor == 0) {
 | 
			
		||||
        if (mBlockQuoteColor == 0) {
 | 
			
		||||
            color = ColorUtils.applyAlpha(paint.getColor(), BLOCK_QUOTE_DEF_COLOR_ALPHA);
 | 
			
		||||
        } else {
 | 
			
		||||
            color = blockQuoteColor;
 | 
			
		||||
            color = mBlockQuoteColor;
 | 
			
		||||
        }
 | 
			
		||||
        paint.setStyle(Paint.Style.FILL);
 | 
			
		||||
        paint.setColor(color);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int getBlockMargin() {
 | 
			
		||||
        return blockMargin;
 | 
			
		||||
        return mBlockMargin;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int getBlockQuoteWidth() {
 | 
			
		||||
        final int out;
 | 
			
		||||
        if (blockQuoteWidth == 0) {
 | 
			
		||||
            out = (int) (blockMargin * .25F + .5F);
 | 
			
		||||
        if (mBlockQuoteWidth == 0) {
 | 
			
		||||
            out = (int) (mBlockMargin * .25F + .5F);
 | 
			
		||||
        } else {
 | 
			
		||||
            out = blockQuoteWidth;
 | 
			
		||||
            out = mBlockQuoteWidth;
 | 
			
		||||
        }
 | 
			
		||||
        return out;
 | 
			
		||||
    }
 | 
			
		||||
@ -251,28 +251,28 @@ public class SpannableTheme {
 | 
			
		||||
    public void applyListItemStyle(@NonNull Paint paint) {
 | 
			
		||||
 | 
			
		||||
        final int color;
 | 
			
		||||
        if (listItemColor != 0) {
 | 
			
		||||
            color = listItemColor;
 | 
			
		||||
        if (mListItemColor != 0) {
 | 
			
		||||
            color = mListItemColor;
 | 
			
		||||
        } else {
 | 
			
		||||
            color = paint.getColor();
 | 
			
		||||
        }
 | 
			
		||||
        paint.setColor(color);
 | 
			
		||||
 | 
			
		||||
        if (bulletListItemStrokeWidth != 0) {
 | 
			
		||||
            paint.setStrokeWidth(bulletListItemStrokeWidth);
 | 
			
		||||
        if (mBulletListItemStrokeWidth != 0) {
 | 
			
		||||
            paint.setStrokeWidth(mBulletListItemStrokeWidth);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int getBulletWidth(int height) {
 | 
			
		||||
 | 
			
		||||
        final int min = Math.min(blockMargin, height) / 2;
 | 
			
		||||
        final int min = Math.min(mBlockMargin, height) / 2;
 | 
			
		||||
 | 
			
		||||
        final int width;
 | 
			
		||||
        if (bulletWidth == 0
 | 
			
		||||
                || bulletWidth > min) {
 | 
			
		||||
        if (mBulletWidth == 0
 | 
			
		||||
                || mBulletWidth > min) {
 | 
			
		||||
            width = min;
 | 
			
		||||
        } else {
 | 
			
		||||
            width = bulletWidth;
 | 
			
		||||
            width = mBulletWidth;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return width;
 | 
			
		||||
@ -280,27 +280,27 @@ public class SpannableTheme {
 | 
			
		||||
 | 
			
		||||
    public void applyCodeTextStyle(@NonNull Paint paint) {
 | 
			
		||||
 | 
			
		||||
        if (codeTextColor != 0) {
 | 
			
		||||
            paint.setColor(codeTextColor);
 | 
			
		||||
        if (mCodeTextColor != 0) {
 | 
			
		||||
            paint.setColor(mCodeTextColor);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // custom typeface was set
 | 
			
		||||
        if (codeTypeface != null) {
 | 
			
		||||
        if (mCodeTypeface != null) {
 | 
			
		||||
 | 
			
		||||
            paint.setTypeface(codeTypeface);
 | 
			
		||||
            paint.setTypeface(mCodeTypeface);
 | 
			
		||||
 | 
			
		||||
            // please note that we won't be calculating textSize
 | 
			
		||||
            // (like we do when no Typeface is provided), if it's some specific typeface
 | 
			
		||||
            // we would confuse users about textSize
 | 
			
		||||
            if (codeTextSize != 0) {
 | 
			
		||||
                paint.setTextSize(codeTextSize);
 | 
			
		||||
            if (mCodeTextSize != 0) {
 | 
			
		||||
                paint.setTextSize(mCodeTextSize);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        } else {
 | 
			
		||||
            paint.setTypeface(Typeface.MONOSPACE);
 | 
			
		||||
            final float textSize;
 | 
			
		||||
            if (codeTextSize != 0) {
 | 
			
		||||
                textSize = codeTextSize;
 | 
			
		||||
            if (mCodeTextSize != 0) {
 | 
			
		||||
                textSize = mCodeTextSize;
 | 
			
		||||
            } else {
 | 
			
		||||
                textSize = paint.getTextSize() * CODE_DEF_TEXT_SIZE_RATIO;
 | 
			
		||||
            }
 | 
			
		||||
@ -309,13 +309,13 @@ public class SpannableTheme {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int getCodeMultilineMargin() {
 | 
			
		||||
        return codeMultilineMargin;
 | 
			
		||||
        return mCodeMultilineMargin;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int getCodeBackgroundColor(@NonNull Paint paint) {
 | 
			
		||||
        final int color;
 | 
			
		||||
        if (codeBackgroundColor != 0) {
 | 
			
		||||
            color = codeBackgroundColor;
 | 
			
		||||
        if (mCodeBackgroundColor != 0) {
 | 
			
		||||
            color = mCodeBackgroundColor;
 | 
			
		||||
        } else {
 | 
			
		||||
            color = ColorUtils.applyAlpha(paint.getColor(), CODE_DEF_BACKGROUND_COLOR_ALPHA);
 | 
			
		||||
        }
 | 
			
		||||
@ -329,25 +329,25 @@ public class SpannableTheme {
 | 
			
		||||
 | 
			
		||||
    public void applyHeadingBreakStyle(@NonNull Paint paint) {
 | 
			
		||||
        final int color;
 | 
			
		||||
        if (headingBreakColor != 0) {
 | 
			
		||||
            color = headingBreakColor;
 | 
			
		||||
        if (mHeadingBreakColor != 0) {
 | 
			
		||||
            color = mHeadingBreakColor;
 | 
			
		||||
        } else {
 | 
			
		||||
            color = ColorUtils.applyAlpha(paint.getColor(), HEADING_DEF_BREAK_COLOR_ALPHA);
 | 
			
		||||
        }
 | 
			
		||||
        paint.setColor(color);
 | 
			
		||||
        paint.setStyle(Paint.Style.FILL);
 | 
			
		||||
        if (headingBreakHeight != 0) {
 | 
			
		||||
        if (mHeadingBreakHeight != 0) {
 | 
			
		||||
            //noinspection SuspiciousNameCombination
 | 
			
		||||
            paint.setStrokeWidth(headingBreakHeight);
 | 
			
		||||
            paint.setStrokeWidth(mHeadingBreakHeight);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void applySuperScriptStyle(@NonNull TextPaint paint) {
 | 
			
		||||
        final float ratio;
 | 
			
		||||
        if (Float.compare(scriptTextSizeRatio, .0F) == 0) {
 | 
			
		||||
        if (Float.compare(mScriptTextSizeRatio, .0F) == 0) {
 | 
			
		||||
            ratio = SCRIPT_DEF_TEXT_SIZE_RATIO;
 | 
			
		||||
        } else {
 | 
			
		||||
            ratio = scriptTextSizeRatio;
 | 
			
		||||
            ratio = mScriptTextSizeRatio;
 | 
			
		||||
        }
 | 
			
		||||
        paint.setTextSize(paint.getTextSize() * ratio);
 | 
			
		||||
        paint.baselineShift += (int) (paint.ascent() / 2);
 | 
			
		||||
@ -355,10 +355,10 @@ public class SpannableTheme {
 | 
			
		||||
 | 
			
		||||
    public void applySubScriptStyle(@NonNull TextPaint paint) {
 | 
			
		||||
        final float ratio;
 | 
			
		||||
        if (Float.compare(scriptTextSizeRatio, .0F) == 0) {
 | 
			
		||||
        if (Float.compare(mScriptTextSizeRatio, .0F) == 0) {
 | 
			
		||||
            ratio = SCRIPT_DEF_TEXT_SIZE_RATIO;
 | 
			
		||||
        } else {
 | 
			
		||||
            ratio = scriptTextSizeRatio;
 | 
			
		||||
            ratio = mScriptTextSizeRatio;
 | 
			
		||||
        }
 | 
			
		||||
        paint.setTextSize(paint.getTextSize() * ratio);
 | 
			
		||||
        paint.baselineShift -= (int) (paint.ascent() / 2);
 | 
			
		||||
@ -366,35 +366,35 @@ public class SpannableTheme {
 | 
			
		||||
 | 
			
		||||
    public void applyThematicBreakStyle(@NonNull Paint paint) {
 | 
			
		||||
        final int color;
 | 
			
		||||
        if (thematicBreakColor != 0) {
 | 
			
		||||
            color = thematicBreakColor;
 | 
			
		||||
        if (mThematicBreakColor != 0) {
 | 
			
		||||
            color = mThematicBreakColor;
 | 
			
		||||
        } else {
 | 
			
		||||
            color = ColorUtils.applyAlpha(paint.getColor(), THEMATIC_BREAK_DEF_ALPHA);
 | 
			
		||||
        }
 | 
			
		||||
        paint.setColor(color);
 | 
			
		||||
        paint.setStyle(Paint.Style.FILL);
 | 
			
		||||
 | 
			
		||||
        if (thematicBreakHeight != 0) {
 | 
			
		||||
        if (mThematicBreakHeight != 0) {
 | 
			
		||||
            //noinspection SuspiciousNameCombination
 | 
			
		||||
            paint.setStrokeWidth(thematicBreakHeight);
 | 
			
		||||
            paint.setStrokeWidth(mThematicBreakHeight);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int tableCellPadding() {
 | 
			
		||||
        return tableCellPadding;
 | 
			
		||||
        return mTableCellPadding;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void applyTableBorderStyle(@NonNull Paint paint) {
 | 
			
		||||
 | 
			
		||||
        final int color;
 | 
			
		||||
        if (tableBorderColor == 0) {
 | 
			
		||||
        if (mTableBorderColor == 0) {
 | 
			
		||||
            color = ColorUtils.applyAlpha(paint.getColor(), TABLE_BORDER_DEF_ALPHA);
 | 
			
		||||
        } else {
 | 
			
		||||
            color = tableBorderColor;
 | 
			
		||||
            color = mTableBorderColor;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (tableBorderWidth != 0) {
 | 
			
		||||
            paint.setStrokeWidth(tableBorderWidth);
 | 
			
		||||
        if (mTableBorderWidth != 0) {
 | 
			
		||||
            paint.setStrokeWidth(mTableBorderWidth);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        paint.setColor(color);
 | 
			
		||||
@ -403,10 +403,10 @@ public class SpannableTheme {
 | 
			
		||||
 | 
			
		||||
    public void applyTableOddRowStyle(@NonNull Paint paint) {
 | 
			
		||||
        final int color;
 | 
			
		||||
        if (tableOddRowBackgroundColor == 0) {
 | 
			
		||||
        if (mTableOddRowBackgroundColor == 0) {
 | 
			
		||||
            color = ColorUtils.applyAlpha(paint.getColor(), TABLE_ODD_ROW_DEF_ALPHA);
 | 
			
		||||
        } else {
 | 
			
		||||
            color = tableOddRowBackgroundColor;
 | 
			
		||||
            color = mTableOddRowBackgroundColor;
 | 
			
		||||
        }
 | 
			
		||||
        paint.setColor(color);
 | 
			
		||||
        paint.setStyle(Paint.Style.FILL);
 | 
			
		||||
@ -418,7 +418,7 @@ public class SpannableTheme {
 | 
			
		||||
     */
 | 
			
		||||
    @Nullable
 | 
			
		||||
    public Drawable getTaskListDrawable() {
 | 
			
		||||
        return taskListDrawable;
 | 
			
		||||
        return mTaskListDrawable;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static class Builder {
 | 
			
		||||
@ -450,28 +450,28 @@ public class SpannableTheme {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Builder(@NonNull SpannableTheme theme) {
 | 
			
		||||
            this.linkColor = theme.linkColor;
 | 
			
		||||
            this.blockMargin = theme.blockMargin;
 | 
			
		||||
            this.blockQuoteWidth = theme.blockQuoteWidth;
 | 
			
		||||
            this.blockQuoteColor = theme.blockQuoteColor;
 | 
			
		||||
            this.listItemColor = theme.listItemColor;
 | 
			
		||||
            this.bulletListItemStrokeWidth = theme.bulletListItemStrokeWidth;
 | 
			
		||||
            this.bulletWidth = theme.bulletWidth;
 | 
			
		||||
            this.codeTextColor = theme.codeTextColor;
 | 
			
		||||
            this.codeBackgroundColor = theme.codeBackgroundColor;
 | 
			
		||||
            this.codeMultilineMargin = theme.codeMultilineMargin;
 | 
			
		||||
            this.codeTypeface = theme.codeTypeface;
 | 
			
		||||
            this.codeTextSize = theme.codeTextSize;
 | 
			
		||||
            this.headingBreakHeight = theme.headingBreakHeight;
 | 
			
		||||
            this.headingBreakColor = theme.headingBreakColor;
 | 
			
		||||
            this.scriptTextSizeRatio = theme.scriptTextSizeRatio;
 | 
			
		||||
            this.thematicBreakColor = theme.thematicBreakColor;
 | 
			
		||||
            this.thematicBreakHeight = theme.thematicBreakHeight;
 | 
			
		||||
            this.tableCellPadding = theme.tableCellPadding;
 | 
			
		||||
            this.tableBorderColor = theme.tableBorderColor;
 | 
			
		||||
            this.tableBorderWidth = theme.tableBorderWidth;
 | 
			
		||||
            this.tableOddRowBackgroundColor = theme.tableOddRowBackgroundColor;
 | 
			
		||||
            this.taskListDrawable = theme.taskListDrawable;
 | 
			
		||||
            linkColor = theme.mLinkColor;
 | 
			
		||||
            blockMargin = theme.mBlockMargin;
 | 
			
		||||
            blockQuoteWidth = theme.mBlockQuoteWidth;
 | 
			
		||||
            blockQuoteColor = theme.mBlockQuoteColor;
 | 
			
		||||
            listItemColor = theme.mListItemColor;
 | 
			
		||||
            bulletListItemStrokeWidth = theme.mBulletListItemStrokeWidth;
 | 
			
		||||
            bulletWidth = theme.mBulletWidth;
 | 
			
		||||
            codeTextColor = theme.mCodeTextColor;
 | 
			
		||||
            codeBackgroundColor = theme.mCodeBackgroundColor;
 | 
			
		||||
            codeMultilineMargin = theme.mCodeMultilineMargin;
 | 
			
		||||
            codeTypeface = theme.mCodeTypeface;
 | 
			
		||||
            codeTextSize = theme.mCodeTextSize;
 | 
			
		||||
            headingBreakHeight = theme.mHeadingBreakHeight;
 | 
			
		||||
            headingBreakColor = theme.mHeadingBreakColor;
 | 
			
		||||
            scriptTextSizeRatio = theme.mScriptTextSizeRatio;
 | 
			
		||||
            thematicBreakColor = theme.mThematicBreakColor;
 | 
			
		||||
            thematicBreakHeight = theme.mThematicBreakHeight;
 | 
			
		||||
            tableCellPadding = theme.mTableCellPadding;
 | 
			
		||||
            tableBorderColor = theme.mTableBorderColor;
 | 
			
		||||
            tableBorderWidth = theme.mTableBorderWidth;
 | 
			
		||||
            tableOddRowBackgroundColor = theme.mTableOddRowBackgroundColor;
 | 
			
		||||
            taskListDrawable = theme.mTaskListDrawable;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @NonNull
 | 
			
		||||
@ -624,14 +624,14 @@ public class SpannableTheme {
 | 
			
		||||
 | 
			
		||||
    private static class Dip {
 | 
			
		||||
 | 
			
		||||
        private final float density;
 | 
			
		||||
        private final float mDensity;
 | 
			
		||||
 | 
			
		||||
        Dip(@NonNull Context context) {
 | 
			
		||||
            this.density = context.getResources().getDisplayMetrics().density;
 | 
			
		||||
            mDensity = context.getResources().getDisplayMetrics().density;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        int toPx(int dp) {
 | 
			
		||||
            return (int) (dp * density + .5F);
 | 
			
		||||
            return (int) (dp * mDensity + .5F);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -6,10 +6,10 @@ import android.text.style.MetricAffectingSpan;
 | 
			
		||||
 | 
			
		||||
public class SubScriptSpan extends MetricAffectingSpan {
 | 
			
		||||
 | 
			
		||||
    private final SpannableTheme theme;
 | 
			
		||||
    private final SpannableTheme mTheme;
 | 
			
		||||
 | 
			
		||||
    public SubScriptSpan(@NonNull SpannableTheme theme) {
 | 
			
		||||
        this.theme = theme;
 | 
			
		||||
        mTheme = theme;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@ -23,6 +23,6 @@ public class SubScriptSpan extends MetricAffectingSpan {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void apply(TextPaint paint) {
 | 
			
		||||
        theme.applySubScriptStyle(paint);
 | 
			
		||||
        mTheme.applySubScriptStyle(paint);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -6,10 +6,10 @@ import android.text.style.MetricAffectingSpan;
 | 
			
		||||
 | 
			
		||||
public class SuperScriptSpan extends MetricAffectingSpan {
 | 
			
		||||
 | 
			
		||||
    private final SpannableTheme theme;
 | 
			
		||||
    private final SpannableTheme mTheme;
 | 
			
		||||
 | 
			
		||||
    public SuperScriptSpan(@NonNull SpannableTheme theme) {
 | 
			
		||||
        this.theme = theme;
 | 
			
		||||
        mTheme = theme;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@ -23,6 +23,6 @@ public class SuperScriptSpan extends MetricAffectingSpan {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void apply(TextPaint paint) {
 | 
			
		||||
        theme.applySuperScriptStyle(paint);
 | 
			
		||||
        mTheme.applySuperScriptStyle(paint);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -35,57 +35,57 @@ public class TableRowSpan extends ReplacementSpan {
 | 
			
		||||
 | 
			
		||||
    public static class Cell {
 | 
			
		||||
 | 
			
		||||
        final int alignment;
 | 
			
		||||
        final CharSequence text;
 | 
			
		||||
        final int mAlignment;
 | 
			
		||||
        final CharSequence mText;
 | 
			
		||||
 | 
			
		||||
        public Cell(@Alignment int alignment, CharSequence text) {
 | 
			
		||||
            this.alignment = alignment;
 | 
			
		||||
            this.text = text;
 | 
			
		||||
            mAlignment = alignment;
 | 
			
		||||
            mText = text;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Alignment
 | 
			
		||||
        public int alignment() {
 | 
			
		||||
            return alignment;
 | 
			
		||||
            return mAlignment;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public CharSequence text() {
 | 
			
		||||
            return text;
 | 
			
		||||
            return mText;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public String toString() {
 | 
			
		||||
            return "Cell{" +
 | 
			
		||||
                    "alignment=" + alignment +
 | 
			
		||||
                    ", text=" + text +
 | 
			
		||||
                    "mAlignment=" + mAlignment +
 | 
			
		||||
                    ", mText=" + mText +
 | 
			
		||||
                    '}';
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private final SpannableTheme theme;
 | 
			
		||||
    private final List<Cell> cells;
 | 
			
		||||
    private final List<StaticLayout> layouts;
 | 
			
		||||
    private final TextPaint textPaint;
 | 
			
		||||
    private final boolean header;
 | 
			
		||||
    private final boolean odd;
 | 
			
		||||
    private final SpannableTheme mTheme;
 | 
			
		||||
    private final List<Cell> mCells;
 | 
			
		||||
    private final List<StaticLayout> mLayouts;
 | 
			
		||||
    private final TextPaint mTextPaint;
 | 
			
		||||
    private final boolean mHeader;
 | 
			
		||||
    private final boolean mOdd;
 | 
			
		||||
 | 
			
		||||
    private final Rect rect = ObjectsPool.rect();
 | 
			
		||||
    private final Paint paint = ObjectsPool.paint();
 | 
			
		||||
    private final Rect mRect = ObjectsPool.rect();
 | 
			
		||||
    private final Paint mPaint = ObjectsPool.paint();
 | 
			
		||||
 | 
			
		||||
    private int width;
 | 
			
		||||
    private int height;
 | 
			
		||||
    private Invalidator invalidator;
 | 
			
		||||
    private int mWidth;
 | 
			
		||||
    private int mHeight;
 | 
			
		||||
    private Invalidator mInvalidator;
 | 
			
		||||
 | 
			
		||||
    public TableRowSpan(
 | 
			
		||||
            @NonNull SpannableTheme theme,
 | 
			
		||||
            @NonNull List<Cell> cells,
 | 
			
		||||
            boolean header,
 | 
			
		||||
            boolean odd) {
 | 
			
		||||
        this.theme = theme;
 | 
			
		||||
        this.cells = cells;
 | 
			
		||||
        this.layouts = new ArrayList<>(cells.size());
 | 
			
		||||
        this.textPaint = new TextPaint();
 | 
			
		||||
        this.header = header;
 | 
			
		||||
        this.odd = odd;
 | 
			
		||||
        mTheme = theme;
 | 
			
		||||
        mCells = cells;
 | 
			
		||||
        mLayouts = new ArrayList<>(cells.size());
 | 
			
		||||
        mTextPaint = new TextPaint();
 | 
			
		||||
        mHeader = header;
 | 
			
		||||
        mOdd = odd;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@ -97,14 +97,14 @@ public class TableRowSpan extends ReplacementSpan {
 | 
			
		||||
            @Nullable Paint.FontMetricsInt fm) {
 | 
			
		||||
 | 
			
		||||
        // it's our absolute requirement to have width of the canvas here... because, well, it changes
 | 
			
		||||
        // the way we draw text. So, if we do not know the width of canvas we cannot correctly measure our text
 | 
			
		||||
        // the way we draw mText. So, if we do not know the width of canvas we cannot correctly measure our mText
 | 
			
		||||
 | 
			
		||||
        if (layouts.size() > 0) {
 | 
			
		||||
        if (mLayouts.size() > 0) {
 | 
			
		||||
 | 
			
		||||
            if (fm != null) {
 | 
			
		||||
 | 
			
		||||
                int max = 0;
 | 
			
		||||
                for (StaticLayout layout : layouts) {
 | 
			
		||||
                for (StaticLayout layout : mLayouts) {
 | 
			
		||||
                    final int height = layout.getHeight();
 | 
			
		||||
                    if (height > max) {
 | 
			
		||||
                        max = height;
 | 
			
		||||
@ -112,10 +112,10 @@ public class TableRowSpan extends ReplacementSpan {
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // we store actual height
 | 
			
		||||
                height = max;
 | 
			
		||||
                mHeight = max;
 | 
			
		||||
 | 
			
		||||
                // but apply height with padding
 | 
			
		||||
                final int padding = theme.tableCellPadding() * 2;
 | 
			
		||||
                final int padding = mTheme.tableCellPadding() * 2;
 | 
			
		||||
 | 
			
		||||
                fm.ascent = -(max + padding);
 | 
			
		||||
                fm.descent = 0;
 | 
			
		||||
@ -125,7 +125,7 @@ public class TableRowSpan extends ReplacementSpan {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return width;
 | 
			
		||||
        return mWidth;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@ -141,46 +141,46 @@ public class TableRowSpan extends ReplacementSpan {
 | 
			
		||||
            @NonNull Paint paint) {
 | 
			
		||||
 | 
			
		||||
        if (recreateLayouts(canvas.getWidth())) {
 | 
			
		||||
            width = canvas.getWidth();
 | 
			
		||||
            textPaint.set(paint);
 | 
			
		||||
            mWidth = canvas.getWidth();
 | 
			
		||||
            mTextPaint.set(paint);
 | 
			
		||||
            makeNewLayouts();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        int maxHeight = 0;
 | 
			
		||||
 | 
			
		||||
        final int padding = theme.tableCellPadding();
 | 
			
		||||
        final int padding = mTheme.tableCellPadding();
 | 
			
		||||
 | 
			
		||||
        final int size = layouts.size();
 | 
			
		||||
        final int size = mLayouts.size();
 | 
			
		||||
 | 
			
		||||
        final int w = width / size;
 | 
			
		||||
        final int w = mWidth / size;
 | 
			
		||||
 | 
			
		||||
        // feels like magic...
 | 
			
		||||
        final int heightDiff = (bottom - top - height) / 4;
 | 
			
		||||
        final int heightDiff = (bottom - top - mHeight) / 4;
 | 
			
		||||
 | 
			
		||||
        if (odd) {
 | 
			
		||||
        if (mOdd) {
 | 
			
		||||
            final int save = canvas.save();
 | 
			
		||||
            try {
 | 
			
		||||
                rect.set(0, 0, width, bottom - top);
 | 
			
		||||
                theme.applyTableOddRowStyle(this.paint);
 | 
			
		||||
                mRect.set(0, 0, mWidth, bottom - top);
 | 
			
		||||
                mTheme.applyTableOddRowStyle(mPaint);
 | 
			
		||||
                canvas.translate(x, top - heightDiff);
 | 
			
		||||
                canvas.drawRect(rect, this.paint);
 | 
			
		||||
                canvas.drawRect(mRect, mPaint);
 | 
			
		||||
            } finally {
 | 
			
		||||
                canvas.restoreToCount(save);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        rect.set(0, 0, w, bottom - top);
 | 
			
		||||
        mRect.set(0, 0, w, bottom - top);
 | 
			
		||||
 | 
			
		||||
        theme.applyTableBorderStyle(this.paint);
 | 
			
		||||
        mTheme.applyTableBorderStyle(mPaint);
 | 
			
		||||
 | 
			
		||||
        StaticLayout layout;
 | 
			
		||||
        for (int i = 0; i < size; i++) {
 | 
			
		||||
            layout = layouts.get(i);
 | 
			
		||||
            layout = mLayouts.get(i);
 | 
			
		||||
            final int save = canvas.save();
 | 
			
		||||
            try {
 | 
			
		||||
 | 
			
		||||
                canvas.translate(x + (i * w), top - heightDiff);
 | 
			
		||||
                canvas.drawRect(rect, this.paint);
 | 
			
		||||
                canvas.drawRect(mRect, mPaint);
 | 
			
		||||
 | 
			
		||||
                canvas.translate(padding, padding + heightDiff);
 | 
			
		||||
                layout.draw(canvas);
 | 
			
		||||
@ -194,40 +194,40 @@ public class TableRowSpan extends ReplacementSpan {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (height != maxHeight) {
 | 
			
		||||
            if (invalidator != null) {
 | 
			
		||||
                invalidator.invalidate();
 | 
			
		||||
        if (mHeight != maxHeight) {
 | 
			
		||||
            if (mInvalidator != null) {
 | 
			
		||||
                mInvalidator.invalidate();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private boolean recreateLayouts(int newWidth) {
 | 
			
		||||
        return width != newWidth;
 | 
			
		||||
        return mWidth != newWidth;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void makeNewLayouts() {
 | 
			
		||||
 | 
			
		||||
        textPaint.setFakeBoldText(header);
 | 
			
		||||
        mTextPaint.setFakeBoldText(mHeader);
 | 
			
		||||
 | 
			
		||||
        final int columns = cells.size();
 | 
			
		||||
        final int padding = theme.tableCellPadding() * 2;
 | 
			
		||||
        final int w = (width / columns) - padding;
 | 
			
		||||
        final int columns = mCells.size();
 | 
			
		||||
        final int padding = mTheme.tableCellPadding() * 2;
 | 
			
		||||
        final int w = (mWidth / columns) - padding;
 | 
			
		||||
 | 
			
		||||
        this.layouts.clear();
 | 
			
		||||
        mLayouts.clear();
 | 
			
		||||
        Cell cell;
 | 
			
		||||
        StaticLayout layout;
 | 
			
		||||
        for (int i = 0, size = cells.size(); i < size; i++) {
 | 
			
		||||
            cell = cells.get(i);
 | 
			
		||||
        for (int i = 0, size = mCells.size(); i < size; i++) {
 | 
			
		||||
            cell = mCells.get(i);
 | 
			
		||||
            layout = new StaticLayout(
 | 
			
		||||
                    cell.text,
 | 
			
		||||
                    textPaint,
 | 
			
		||||
                    cell.mText,
 | 
			
		||||
                    mTextPaint,
 | 
			
		||||
                    w,
 | 
			
		||||
                    alignment(cell.alignment),
 | 
			
		||||
                    alignment(cell.mAlignment),
 | 
			
		||||
                    1.F,
 | 
			
		||||
                    .0F,
 | 
			
		||||
                    false
 | 
			
		||||
            );
 | 
			
		||||
            layouts.add(layout);
 | 
			
		||||
            mLayouts.add(layout);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -249,7 +249,7 @@ public class TableRowSpan extends ReplacementSpan {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public TableRowSpan invalidator(Invalidator invalidator) {
 | 
			
		||||
        this.invalidator = invalidator;
 | 
			
		||||
        mInvalidator = invalidator;
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -24,25 +24,25 @@ public class TaskListDrawable extends Drawable {
 | 
			
		||||
    private static final Point POINT_1 = new Point(7.F / 18, 12.5F / 18);
 | 
			
		||||
    private static final Point POINT_2 = new Point(15.25F / 18, 4.75F / 18);
 | 
			
		||||
 | 
			
		||||
    private final int checkedFillColor;
 | 
			
		||||
    private final int normalOutlineColor;
 | 
			
		||||
    private final int mCheckedFillColor;
 | 
			
		||||
    private final int mNormalOutlineColor;
 | 
			
		||||
 | 
			
		||||
    private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
 | 
			
		||||
    private final RectF rectF = new RectF();
 | 
			
		||||
    private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
 | 
			
		||||
    private final RectF mRectF = new RectF();
 | 
			
		||||
 | 
			
		||||
    private final Paint checkMarkPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
 | 
			
		||||
    private final Path checkMarkPath = new Path();
 | 
			
		||||
    private final Paint mCheckMarkPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
 | 
			
		||||
    private final Path mCheckMarkPath = new Path();
 | 
			
		||||
 | 
			
		||||
    private boolean isChecked;
 | 
			
		||||
    private boolean mIsChecked;
 | 
			
		||||
 | 
			
		||||
    // unfortunately we cannot rely on TextView to be LAYER_TYPE_SOFTWARE
 | 
			
		||||
    // if we could we would draw our checkMarkPath with PorterDuff.CLEAR
 | 
			
		||||
    // if we could we would draw our mCheckMarkPath with PorterDuff.CLEAR
 | 
			
		||||
    public TaskListDrawable(@ColorInt int checkedFillColor, @ColorInt int normalOutlineColor, @ColorInt int checkMarkColor) {
 | 
			
		||||
        this.checkedFillColor = checkedFillColor;
 | 
			
		||||
        this.normalOutlineColor = normalOutlineColor;
 | 
			
		||||
        mCheckedFillColor = checkedFillColor;
 | 
			
		||||
        mNormalOutlineColor = normalOutlineColor;
 | 
			
		||||
 | 
			
		||||
        checkMarkPaint.setColor(checkMarkColor);
 | 
			
		||||
        checkMarkPaint.setStyle(Paint.Style.STROKE);
 | 
			
		||||
        mCheckMarkPaint.setColor(checkMarkColor);
 | 
			
		||||
        mCheckMarkPaint.setStyle(Paint.Style.STROKE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@ -56,16 +56,16 @@ public class TaskListDrawable extends Drawable {
 | 
			
		||||
        final float stroke = min / 8;
 | 
			
		||||
 | 
			
		||||
        final float side = min - stroke;
 | 
			
		||||
        rectF.set(0, 0, side, side);
 | 
			
		||||
        mRectF.set(0, 0, side, side);
 | 
			
		||||
 | 
			
		||||
        paint.setStrokeWidth(stroke);
 | 
			
		||||
        checkMarkPaint.setStrokeWidth(stroke);
 | 
			
		||||
        mPaint.setStrokeWidth(stroke);
 | 
			
		||||
        mCheckMarkPaint.setStrokeWidth(stroke);
 | 
			
		||||
 | 
			
		||||
        checkMarkPath.reset();
 | 
			
		||||
        mCheckMarkPath.reset();
 | 
			
		||||
 | 
			
		||||
        POINT_0.moveTo(checkMarkPath, side);
 | 
			
		||||
        POINT_1.lineTo(checkMarkPath, side);
 | 
			
		||||
        POINT_2.lineTo(checkMarkPath, side);
 | 
			
		||||
        POINT_0.moveTo(mCheckMarkPath, side);
 | 
			
		||||
        POINT_1.lineTo(mCheckMarkPath, side);
 | 
			
		||||
        POINT_2.lineTo(mCheckMarkPath, side);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@ -74,32 +74,32 @@ public class TaskListDrawable extends Drawable {
 | 
			
		||||
        final Paint.Style style;
 | 
			
		||||
        final int color;
 | 
			
		||||
 | 
			
		||||
        if (isChecked) {
 | 
			
		||||
        if (mIsChecked) {
 | 
			
		||||
            style = Paint.Style.FILL_AND_STROKE;
 | 
			
		||||
            color = checkedFillColor;
 | 
			
		||||
            color = mCheckedFillColor;
 | 
			
		||||
        } else {
 | 
			
		||||
            style = Paint.Style.STROKE;
 | 
			
		||||
            color = normalOutlineColor;
 | 
			
		||||
            color = mNormalOutlineColor;
 | 
			
		||||
        }
 | 
			
		||||
        paint.setStyle(style);
 | 
			
		||||
        paint.setColor(color);
 | 
			
		||||
        mPaint.setStyle(style);
 | 
			
		||||
        mPaint.setColor(color);
 | 
			
		||||
 | 
			
		||||
        final Rect bounds = getBounds();
 | 
			
		||||
 | 
			
		||||
        final float left = (bounds.width() - rectF.width()) / 2;
 | 
			
		||||
        final float top = (bounds.height() - rectF.height()) / 2;
 | 
			
		||||
        final float left = (bounds.width() - mRectF.width()) / 2;
 | 
			
		||||
        final float top = (bounds.height() - mRectF.height()) / 2;
 | 
			
		||||
 | 
			
		||||
        final float radius = rectF.width() / 8;
 | 
			
		||||
        final float radius = mRectF.width() / 8;
 | 
			
		||||
 | 
			
		||||
        final int save = canvas.save();
 | 
			
		||||
        try {
 | 
			
		||||
 | 
			
		||||
            canvas.translate(left, top);
 | 
			
		||||
 | 
			
		||||
            canvas.drawRoundRect(rectF, radius, radius, paint);
 | 
			
		||||
            canvas.drawRoundRect(mRectF, radius, radius, mPaint);
 | 
			
		||||
 | 
			
		||||
            if (isChecked) {
 | 
			
		||||
                canvas.drawPath(checkMarkPath, checkMarkPaint);
 | 
			
		||||
            if (mIsChecked) {
 | 
			
		||||
                canvas.drawPath(mCheckMarkPath, mCheckMarkPaint);
 | 
			
		||||
            }
 | 
			
		||||
        } finally {
 | 
			
		||||
            canvas.restoreToCount(save);
 | 
			
		||||
@ -108,12 +108,12 @@ public class TaskListDrawable extends Drawable {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void setAlpha(@IntRange(from = 0, to = 255) int alpha) {
 | 
			
		||||
        paint.setAlpha(alpha);
 | 
			
		||||
        mPaint.setAlpha(alpha);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void setColorFilter(@Nullable ColorFilter colorFilter) {
 | 
			
		||||
        paint.setColorFilter(colorFilter);
 | 
			
		||||
        mPaint.setColorFilter(colorFilter);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@ -150,10 +150,10 @@ public class TaskListDrawable extends Drawable {
 | 
			
		||||
            checked = false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        final boolean result = checked != isChecked;
 | 
			
		||||
        final boolean result = checked != mIsChecked;
 | 
			
		||||
        if (result) {
 | 
			
		||||
            invalidateSelf();
 | 
			
		||||
            isChecked = checked;
 | 
			
		||||
            mIsChecked = checked;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return result;
 | 
			
		||||
 | 
			
		||||
@ -12,21 +12,21 @@ import android.text.style.LeadingMarginSpan;
 | 
			
		||||
 */
 | 
			
		||||
public class TaskListSpan implements LeadingMarginSpan {
 | 
			
		||||
 | 
			
		||||
    private final SpannableTheme theme;
 | 
			
		||||
    private final int blockIndent;
 | 
			
		||||
    private final int start;
 | 
			
		||||
    private final boolean isDone;
 | 
			
		||||
    private final SpannableTheme mTheme;
 | 
			
		||||
    private final int mBlockIndent;
 | 
			
		||||
    private final int mStart;
 | 
			
		||||
    private final boolean mIsDone;
 | 
			
		||||
 | 
			
		||||
    public TaskListSpan(@NonNull SpannableTheme theme, int blockIndent, int start, boolean isDone) {
 | 
			
		||||
        this.theme = theme;
 | 
			
		||||
        this.blockIndent = blockIndent;
 | 
			
		||||
        this.start = start;
 | 
			
		||||
        this.isDone = isDone;
 | 
			
		||||
        mTheme = theme;
 | 
			
		||||
        mBlockIndent = blockIndent;
 | 
			
		||||
        mStart = start;
 | 
			
		||||
        mIsDone = isDone;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int getLeadingMargin(boolean first) {
 | 
			
		||||
        return theme.getBlockMargin() * blockIndent;
 | 
			
		||||
        return mTheme.getBlockMargin() * mBlockIndent;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@ -36,7 +36,7 @@ public class TaskListSpan implements LeadingMarginSpan {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        final Drawable drawable = theme.getTaskListDrawable();
 | 
			
		||||
        final Drawable drawable = mTheme.getTaskListDrawable();
 | 
			
		||||
        if (drawable == null) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
@ -44,7 +44,7 @@ public class TaskListSpan implements LeadingMarginSpan {
 | 
			
		||||
        final int save = c.save();
 | 
			
		||||
        try {
 | 
			
		||||
 | 
			
		||||
            final int width = theme.getBlockMargin();
 | 
			
		||||
            final int width = mTheme.getBlockMargin();
 | 
			
		||||
            final int height = bottom - top;
 | 
			
		||||
 | 
			
		||||
            final int w = (int) (width * .75F + .5F);
 | 
			
		||||
@ -54,7 +54,7 @@ public class TaskListSpan implements LeadingMarginSpan {
 | 
			
		||||
 | 
			
		||||
            if (drawable.isStateful()) {
 | 
			
		||||
                final int[] state;
 | 
			
		||||
                if (isDone) {
 | 
			
		||||
                if (mIsDone) {
 | 
			
		||||
                    state = new int[]{android.R.attr.state_checked};
 | 
			
		||||
                } else {
 | 
			
		||||
                    state = new int[0];
 | 
			
		||||
@ -62,7 +62,7 @@ public class TaskListSpan implements LeadingMarginSpan {
 | 
			
		||||
                drawable.setState(state);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            final int l = (width * (blockIndent - 1)) + ((width - w) / 2);
 | 
			
		||||
            final int l = (width * (mBlockIndent - 1)) + ((width - w) / 2);
 | 
			
		||||
            final int t = top + ((height - h) / 2);
 | 
			
		||||
 | 
			
		||||
            c.translate(l, t);
 | 
			
		||||
 | 
			
		||||
@ -9,12 +9,12 @@ import android.text.style.LeadingMarginSpan;
 | 
			
		||||
 | 
			
		||||
public class ThematicBreakSpan implements LeadingMarginSpan {
 | 
			
		||||
 | 
			
		||||
    private final SpannableTheme theme;
 | 
			
		||||
    private final Rect rect = ObjectsPool.rect();
 | 
			
		||||
    private final Paint paint = ObjectsPool.paint();
 | 
			
		||||
    private final SpannableTheme mTheme;
 | 
			
		||||
    private final Rect mRect = ObjectsPool.rect();
 | 
			
		||||
    private final Paint mPaint = ObjectsPool.paint();
 | 
			
		||||
 | 
			
		||||
    public ThematicBreakSpan(@NonNull SpannableTheme theme) {
 | 
			
		||||
        this.theme = theme;
 | 
			
		||||
        mTheme = theme;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@ -27,13 +27,13 @@ public class ThematicBreakSpan implements LeadingMarginSpan {
 | 
			
		||||
 | 
			
		||||
        final int middle = top + ((bottom - top) / 2);
 | 
			
		||||
 | 
			
		||||
        paint.set(p);
 | 
			
		||||
        theme.applyThematicBreakStyle(paint);
 | 
			
		||||
        mPaint.set(p);
 | 
			
		||||
        mTheme.applyThematicBreakStyle(mPaint);
 | 
			
		||||
 | 
			
		||||
        final int height = (int) (paint.getStrokeWidth() + .5F);
 | 
			
		||||
        final int height = (int) (mPaint.getStrokeWidth() + .5F);
 | 
			
		||||
        final int halfHeight = (int) (height / 2.F + .5F);
 | 
			
		||||
 | 
			
		||||
        rect.set(x, middle - halfHeight, c.getWidth(), middle + halfHeight);
 | 
			
		||||
        c.drawRect(rect, paint);
 | 
			
		||||
        mRect.set(x, middle - halfHeight, c.getWidth(), middle + halfHeight);
 | 
			
		||||
        c.drawRect(mRect, mPaint);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -23,22 +23,22 @@ import java.util.regex.Pattern;
 | 
			
		||||
@SuppressWarnings("WeakerAccess")
 | 
			
		||||
class TaskListBlockParser extends AbstractBlockParser {
 | 
			
		||||
 | 
			
		||||
    private static final Pattern PATTERN = Pattern.compile("\\s*-\\s+\\[(x|X|\\s)\\]\\s+(.*)");
 | 
			
		||||
    private static final Pattern PATTERN = Pattern.compile("\\s*-\\s+\\[(x|X|\\s)]\\s+(.*)");
 | 
			
		||||
 | 
			
		||||
    private final TaskListBlock block = new TaskListBlock();
 | 
			
		||||
    private final TaskListBlock mBlock = new TaskListBlock();
 | 
			
		||||
 | 
			
		||||
    private final List<Item> items = new ArrayList<>(3);
 | 
			
		||||
    private final List<Item> mItems = new ArrayList<>(3);
 | 
			
		||||
 | 
			
		||||
    private int indent = 0;
 | 
			
		||||
 | 
			
		||||
    TaskListBlockParser(@NonNull String startLine, int startIndent) {
 | 
			
		||||
        items.add(new Item(startLine, startIndent));
 | 
			
		||||
        mItems.add(new Item(startLine, startIndent));
 | 
			
		||||
        indent = startIndent;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Block getBlock() {
 | 
			
		||||
        return block;
 | 
			
		||||
        return mBlock;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@ -69,7 +69,7 @@ class TaskListBlockParser extends AbstractBlockParser {
 | 
			
		||||
    @Override
 | 
			
		||||
    public void addLine(CharSequence line) {
 | 
			
		||||
        if (length(line) > 0) {
 | 
			
		||||
            items.add(new Item(line.toString(), indent));
 | 
			
		||||
            mItems.add(new Item(line.toString(), indent));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -80,7 +80,7 @@ class TaskListBlockParser extends AbstractBlockParser {
 | 
			
		||||
 | 
			
		||||
        TaskListItem listItem;
 | 
			
		||||
 | 
			
		||||
        for (Item item : items) {
 | 
			
		||||
        for (Item item : mItems) {
 | 
			
		||||
            matcher = PATTERN.matcher(item.line);
 | 
			
		||||
            if (!matcher.matches()) {
 | 
			
		||||
                continue;
 | 
			
		||||
@ -89,7 +89,7 @@ class TaskListBlockParser extends AbstractBlockParser {
 | 
			
		||||
                    .done(isDone(matcher.group(1)))
 | 
			
		||||
                    .indent(item.indent / 2);
 | 
			
		||||
            inlineParser.parse(matcher.group(2), listItem);
 | 
			
		||||
            block.appendChild(listItem);
 | 
			
		||||
            mBlock.appendChild(listItem);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -8,24 +8,24 @@ import org.commonmark.node.CustomNode;
 | 
			
		||||
@SuppressWarnings("WeakerAccess")
 | 
			
		||||
public class TaskListItem extends CustomNode {
 | 
			
		||||
 | 
			
		||||
    private boolean done;
 | 
			
		||||
    private int indent;
 | 
			
		||||
    private boolean mDone;
 | 
			
		||||
    private int mIndent;
 | 
			
		||||
 | 
			
		||||
    public boolean done() {
 | 
			
		||||
        return done;
 | 
			
		||||
        return mDone;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public TaskListItem done(boolean done) {
 | 
			
		||||
        this.done = done;
 | 
			
		||||
        mDone = done;
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int indent() {
 | 
			
		||||
        return indent;
 | 
			
		||||
        return mIndent;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public TaskListItem indent(int indent) {
 | 
			
		||||
        this.indent = indent;
 | 
			
		||||
        mIndent = indent;
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user