From 0dfc9684647293196ec70b2353d038bbf0f0de17 Mon Sep 17 00:00:00 2001 From: Dimitry Ivanov Date: Thu, 18 May 2017 16:44:41 +0300 Subject: [PATCH] Working with sample app --- README.md | 109 +++++++ app/build.gradle | 2 + app/src/main/AndroidManifest.xml | 53 +++- app/src/main/assets/scrollable.md | 282 ------------------ app/src/main/assets/test.md | 114 ------- .../java/ru/noties/markwon/ActivityScope.java | 7 + app/src/main/java/ru/noties/markwon/App.java | 23 ++ .../java/ru/noties/markwon/AppBarItem.java | 41 +++ .../java/ru/noties/markwon/AppComponent.java | 11 + .../java/ru/noties/markwon/AppModule.java | 66 ++++ .../noties/markwon/AsyncDrawableLoader.java | 91 ++++-- .../noties/markwon/AsyncDrawableTarget.java | 81 +++++ .../java/ru/noties/markwon/MainActivity.java | 146 +++++---- .../markwon/MainActivitySubcomponent.java | 9 + .../ru/noties/markwon/MarkdownLoader.java | 193 ++++++++++++ .../ru/noties/markwon/MarkdownRenderer.java | 60 ++++ .../ru/noties/markwon/TextViewTarget.java | 74 ----- .../main/java/ru/noties/markwon/Themes.java | 46 +++ .../java/ru/noties/markwon/UrlProvider.java | 8 + .../ru/noties/markwon/UrlProviderImpl.java | 42 +++ .../main/java/ru/noties/markwon/Views.java | 36 +++ .../main/res/drawable/bg_app_bar_shadow.xml | 11 + .../res/drawable/ic_app_bar_theme_dark.xml | 9 + .../res/drawable/ic_app_bar_theme_light.xml | 9 + app/src/main/res/layout/activity_main.xml | 100 ++++++- app/src/main/res/mipmap-hdpi/ic_launcher.png | Bin 3418 -> 2561 bytes app/src/main/res/mipmap-mdpi/ic_launcher.png | Bin 2206 -> 1369 bytes app/src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 4842 -> 3211 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 7718 -> 5946 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 10486 -> 7441 bytes app/src/main/res/values-v21/styles.xml | 10 +- app/src/main/res/values/styles.xml | 14 +- .../ru/noties/markwon/DrawablesScheduler.java | 81 +++-- .../main/java/ru/noties/markwon/Markwon.java | 84 ++++-- .../noties/markwon/spans/AsyncDrawable.java | 7 + 35 files changed, 1180 insertions(+), 639 deletions(-) create mode 100644 README.md delete mode 100644 app/src/main/assets/scrollable.md delete mode 100644 app/src/main/assets/test.md create mode 100644 app/src/main/java/ru/noties/markwon/ActivityScope.java create mode 100644 app/src/main/java/ru/noties/markwon/App.java create mode 100644 app/src/main/java/ru/noties/markwon/AppBarItem.java create mode 100644 app/src/main/java/ru/noties/markwon/AppComponent.java create mode 100644 app/src/main/java/ru/noties/markwon/AppModule.java create mode 100644 app/src/main/java/ru/noties/markwon/AsyncDrawableTarget.java create mode 100644 app/src/main/java/ru/noties/markwon/MainActivitySubcomponent.java create mode 100644 app/src/main/java/ru/noties/markwon/MarkdownLoader.java create mode 100644 app/src/main/java/ru/noties/markwon/MarkdownRenderer.java delete mode 100644 app/src/main/java/ru/noties/markwon/TextViewTarget.java create mode 100644 app/src/main/java/ru/noties/markwon/Themes.java create mode 100644 app/src/main/java/ru/noties/markwon/UrlProvider.java create mode 100644 app/src/main/java/ru/noties/markwon/UrlProviderImpl.java create mode 100644 app/src/main/java/ru/noties/markwon/Views.java create mode 100644 app/src/main/res/drawable/bg_app_bar_shadow.xml create mode 100644 app/src/main/res/drawable/ic_app_bar_theme_dark.xml create mode 100644 app/src/main/res/drawable/ic_app_bar_theme_light.xml diff --git a/README.md b/README.md new file mode 100644 index 00000000..84051ecf --- /dev/null +++ b/README.md @@ -0,0 +1,109 @@ +# Markwon +Android library for rendering markdown as system-native Spannables. Based on [commonmark-java][commonmark-java] + +--- + +## Supported markdown features: +* Emphasis (`*`, `_`) +* Strong emphasis (`**`, `__`) +* Strike-through (`~~`) +* Headers (`#{1,6}`) +* Links (`[]()` && `[][]`) +* Images (_requires special handling_) +* Thematic break (`---`, `***`, `___`) +* Quotes & nested quotes (`>{1,}`) +* Ordered & non-ordered lists & nested ones +* Inline code +* Code blocks +* Small subset of inline-html (which is rendered by this library): +* * Emphasis (``, ``, ``, ``) +* * Strong emphasis (``, ``) +* * SuperScript (``) +* * SubScript (``) +* * Underline (``) +* * Strike-through (``, ``, ``) + * other inline html is rendered via (`Html.fromHtml(...)`) + +### Emphasis +*Lorem ipsum dolor sit amet* +_Lorem ipsum dolor sit amet_ +Lorem ipsum dolor sit amet +Lorem ipsum dolor sit amet +Lorem ipsum dolor sit amet +Lorem ipsum dolor sit amet + +### Strong emphasis +**Lorem ipsum dolor sit amet** +__Lorem ipsum dolor sit amet__ +Lorem ipsum dolor sit amet +Lorem ipsum dolor sit amet + +### Strike-through +~~Lorem ipsum dolor sit amet~~ +Lorem ipsum dolor sit amet +Lorem ipsum dolor sit amet +Lorem ipsum dolor sit amet + +--- +# Header 1 +## Header 2 +### Header 3 +#### Header 4 +##### Header 5 +###### Header 6 +--- + +### Links +[click me](https://github.com) +[click me][1] +[click me][github] +click me + +### Images +// todo, normal ones & svg & gif + +### Thematic break +--- +*** +___ + +### Quotes +> Lorem ipsum dolor sit amet +>> Lorem ipsum dolor sit amet +>>> Lorem ipsum dolor sit amet + +### Ordered lists +1. Lorem ipsum dolor sit amet +2. Lorem ipsum dolor sit amet + 1. Lorem ipsum dolor sit amet + 1. Lorem ipsum dolor sit amet + 2. Lorem ipsum dolor sit amet +3. Lorem ipsum dolor sit amet + +### Non-ordered lists +* Lorem ipsum dolor sit amet + * Lorem ipsum dolor sit amet + * * Lorem ipsum dolor sit amet +* * * * Lorem ipsum dolor sit amet +* * Lorem ipsum dolor sit amet +* Lorem ipsum dolor sit amet + +### Inline code +`Lorem` ipsum dolor sit amet +Lorem `ipsum` dolor sit amet +Lorem ipsum `dolor` sit amet +Lorem ipsum dolor `sit` amet +Lorem ipsum dolor sit `amet` + +### Code block +// todo syntax higlight +``` +Lorem ipsum dolor sit amet +Lorem ipsum dolor sit amet +Lorem ipsum dolor sit amet +``` + + +[1]: https://github.com +[github]: https://github.com +[commonmark-java]: https://github.com/atlassian/commonmark-java \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 8078c22c..608dece2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -21,4 +21,6 @@ dependencies { compile 'com.caverock:androidsvg:1.2.1' compile 'pl.droidsonroids.gif:android-gif-drawable:1.2.7' compile 'com.squareup.okhttp3:okhttp:3.8.0' + compile 'com.google.dagger:dagger:2.10' + annotationProcessor 'com.google.dagger:dagger-compiler:2.10' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 016c3e5d..bea26fe9 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,22 +1,67 @@ - - + + android:theme="@style/AppThemeLight"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/assets/scrollable.md b/app/src/main/assets/scrollable.md deleted file mode 100644 index b6b67ecb..00000000 --- a/app/src/main/assets/scrollable.md +++ /dev/null @@ -1,282 +0,0 @@ -![logo](https://github.com/noties/Scrollable/raw/master/art/scrollable_big_logo.png) - -[![Maven Central](https://img.shields.io/maven-central/v/ru.noties/scrollable.svg)](http://search.maven.org/#search|ga|1|g%3A%22ru.noties%22%20AND%20a%3A%22scrollable%22) - ---- - -**Scrollable** is a library for an Android application to implement various scrolling technicks. It's all started with **scrolling tabs**, but now much more can be done with it. Scrollable supports all scrolling and non-scrolling views, including: **RecyclerView**, **ScrollView**, **ListView**, **WebView**, etc and any combination of those inside a **ViewPager**. Library is designed to let developer implement desired effect without imposing one solution. Library is small and has no dependencies. - -## Preview - -All GIFs here are taken from `sample` application module. - - -colorful_sample custom_overscroll_sample dialog_sample - -*Serving suggestion - -## Installation -```groovy -compile 'ru.noties:scrollable:1.3.0` -``` - -## Usage - -To start using this library `ScrollableLayout` must be aded to your layout. - -```xml - - - - - - - - - - -``` - -Please note, that `ScrollableLayout` positions its children like vertical `LinearLayout`, but measures them like a `FrameLayout`. It is crucial that scrolling content holder dimentions must be set to `match_parent` (minus possible *sticky* view that should be extracted from it, for example, by specifying `android:layoutMarginTop="height_of_sticky_view"`). - -Next, `ScrollableLayout` must be initialized in code: - -```java -final ScrollableLayout scrollableLayout = findView(R.id.scrollable_layout); - -// this listener is absolute minimum that is required for `ScrollableLayout` to function -scrollableLayout.setCanScrollVerticallyDelegate(new CanScrollVerticallyDelegate() { - @Override - public boolean canScrollVertically(int direction) { - // Obtain a View that is a scroll container (RecyclerView, ListView, ScrollView, WebView, etc) - // and call its `canScrollVertically(int) method. - // Please note, that if `ViewPager is used, currently displayed View must be obtained - // because `ViewPager` doesn't delegate `canScrollVertically` method calls to it's children - final View view = getCurrentView(); - return view.canScrollVertically(direction); - } -}); -``` - - -### Draggable View - -This is a View, that can be *dragged* to change `ScrollableLayout` scroll state. For example, to expand header if tabs are dragged. To add this simply call: -```java -// Please note that `tabsLayout` must be a child (direct or indirect) of a ScrollableLayout -scrollableLayout.setDraggableView(tabsLayout); -``` - - -### OnScrollChangedListener - -In order to apply custom logic for different scroll state of a `ScrollableLayout` `OnScrollChangedListener` can be used (to change color of a header, create parallax effect, sticky tabs, etc) - -```java -scrollableLayout.addOnScrollChangedListener(new OnScrollChangedListener() { - @Override - public void onScrollChanged(int y, int oldY, int maxY) { - - // `ratio` of current scroll state (from 0.0 to 1.0) - // 0.0 - means fully expanded - // 1.0 - means fully collapsed - final float ratio = (float) y / maxY; - - // for example, we can hide header, if we are collapsed - // and show it when we are expanded (plus intermediate state) - header.setAlpha(1.F - ratio); - - // to create a `sticky` effect for tabs this calculation can be used: - final float tabsTranslationY; - if (y < maxY) { - // natural position - tabsTranslationY = .0F; - } else { - // sticky position - tabsTranslationY = y - maxY; - } - tabsLayout.setTranslationY(tabsTranslationY); - } -}); -``` - - -### OnFlingOverListener - -To *continue* a fling event for a scrolling container `OnFlingOverListener` can be used. - -```java -scrollableLayout.setOnFlingOverListener(new OnFlingOverListener() { - @Override - public void onFlingOver(int y, long duration) { - recyclerView.smoothScrollBy(0, y); - } -}); -``` - - -### OverScrollListener - -To create custom *overscroll* handler (for example, like in `SwipeRefreshLayout` for loading, or to zoom-in header when cannot scroll further) `OverScrollListener` can be used - -```java -scrollableLayout.setOverScrollListener(new OverScrollListener() { - @Override - public void onOverScrolled(ScrollableLayout layout, int overScrollY) { - - } - - @Override - public boolean hasOverScroll(ScrollableLayout layout, int overScrollY) { - return false; - } - - @Override - public void onCancelled(ScrollableLayout layout) { - - } - - @Override - public void clear() { - - } -}); -``` - -`OverScrollListener` gives you full controll of overscrolling, but it implies a lot of handling. For a simple case `OverScrollListenerBase` can be used - -```java -scrollableLayout.setOverScrollListener(new OverScrollListenerBase() { - @Override - protected void onRatioChanged(ScrollableLayout layout, float ratio) { - - } -}); -``` - -For example, this is `onRatioChanged` method from `ZoomInHeaderOverScrollListener` from `sample` application: -```java -@Override -protected void onRatioChanged(ScrollableLayout layout, float ratio) { - final float scale = 1.F + (.33F * ratio); - mHeader.setScaleX(scale); - mHeader.setScaleY(scale); - - final int headerHeight = mHeader.getHeight(); - mContent.setTranslationY(((headerHeight * scale) - headerHeight) / 2.F); -} -``` - -### Scrolling Header - -There is support for scrolling header. This means that if header can scroll, it will scroll first to the final position and only after that scroll event will be redirected. There are no extra steps to enable this feature if scrolling header is the first view in `ScrollableLayout`. Otherwise a XML attribute `app:scrollable_scrollingHeaderId` can be used, it accepts an id of a view. - - -## Various customizations - -### CloseUpAlgorithm - -In order to *close-up* `ScrollableLayout` (do not leave in intermediate state, allow only two scrolling states: collapsed & expanded, etc), `CloseUpAlgorithm` can be used. Its signature is as follows: - -```java -public interface CloseUpAlgorithm { - - int getFlingFinalY(ScrollableLayout layout, boolean isScrollingBottom, int nowY, int suggestedY, int maxY); - - int getIdleFinalY(ScrollableLayout layout, int nowY, int maxY); -} -``` - -And usage is like this: - -```java -scrollableLayout.setCloseUpAlgorithm(new MyCloseUpAlgorithm()); -``` - -Library provides a `DefaultCloseUpAlgorithm` for a most common usage (to allow `ScrollableLayout` only 2 scrolling states: collapsed and expanded). It can be set via java code: `scrollableLayout.setCloseUpAlgorithm(new DefaultCloseUpAlgorithm())` and via XML with `app:scrollable_defaultCloseUp="true"`. - -Also, there is an option to set duration after which CloseUpAlgorithm should be evaluated (idle state - no touch events). Java: `scrollableLayout.setConsiderIdleMillis(100L)` and XML: `app:scrollable_considerIdleMillis="100"`. `100L` is the default value and may be omitted. - -If *close-up* need to have different animation times, `CloseUpIdleAnimationTime` can be used. Its signature: - -```java -public interface CloseUpIdleAnimationTime { - long compute(ScrollableLayout layout, int nowY, int endY, int maxY); -} -``` -If animation time is constant (do not depend on current scroll state), `SimpleCloseUpIdleAnimationTime` can be used. Java: `scrollableLayout.setCloseUpIdleAnimationTime(new SimpleCloseUpIdleAnimationTime(200L))`, XML: `app:app:scrollable_closeUpAnimationMillis="200"`. `200L` is default value and can be omitted. - -If one want to get control of `ValueAnimator` that is used to animate between scroll states, `CloseUpAnimatorConfigurator` can be used. Its signature: - -```java -public interface CloseUpAnimatorConfigurator { - // when called will already have a duration set - void configure(ValueAnimator animator); -} -``` - -If only `Interpolator` must be configured, a `InterpolatorCloseUpAnimatorConfigurator` can be used. Java: `scrollableLayout.setCloseAnimatorConfigurator(new InterpolatorCloseUpAnimatorConfigurator(interpolator))`, XML: `app:scrollable_closeUpAnimatorInterpolator="app:scrollable_closeUpAnimatorInterpolator="@android:interpolator/decelerate_cubic"` - - -### Auto Max Scroll - -If you layout has a header with dynamic height, or it's height should be obtained at runtime, there is an option to automatically obtain it. Java: `scrollableLayout.setAutoMaxScroll(true)`, XML: `app:scrollable_autoMaxScroll="true"`. With this option there is no need manually set `maxScrollY`. Please note, that if not specified explicitly this option will be set to `true` if `maxScroll` option is not set (equals `0`). Also, if at runtime called `scrollableLayout.setMaxScroll(int)`, `autoMaxScroll` if set to `true`, will be set to `false`. - -By default the first View will be used to calculate height, but if different one must be used, there is an option to specify `id` of this view. XML: `app:scrollable_autoMaxScrollViewId="@id/header"` - - -### Disable Handling - -If `ScrollableLayout` must not evaluate its scrolling logic (skip all touch events), `scrollableLayout.setSelfUpdateScroll(boolean)` can be used. Pass `true` to disable all handling, `false` to enable it. - - -### Animate Scroll - -To animate scroll state of a `ScrollableLayout`, `animateScroll(int)` can be used: - -```java -// returns ValueAnimator, that can be configured as desired -// `0` - expand fully -// `scrollableLayout.getMaxScroll()` - collapse -scrollableLayout.animateScroll(0) - .setDuration(250L) - .start(); -``` - -Please note that `ScrollableLayout` caches returned `ValueAnimator` and reuses it. First of all because it doesn't make sense to have two different scrolling animations on one `ScrollableLayout`. So, it's advisable to clear all possible custom state before running animation (just like `View` handles `ViewPropertyAnimator`) - - -## License - -``` - Copyright 2015 Dimitry Ivanov (mail@dimitryivanov.ru) - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -``` diff --git a/app/src/main/assets/test.md b/app/src/main/assets/test.md deleted file mode 100644 index b0f1dd9e..00000000 --- a/app/src/main/assets/test.md +++ /dev/null @@ -1,114 +0,0 @@ -![logo](https://github.com/noties/Scrollable/raw/master/art/scrollable_big_logo.png) - -# Hello! - -**bold *italic*** _just italic_ - -[![Maven Central](https://img.shields.io/maven-central/v/ru.noties/scrollable.svg)](http://search.maven.org/#search|ga|1|g%3A%22ru.noties%22%20AND%20a%3A%22scrollable%22) - -> Quote ->> Second Quote ->>> Third one, yuhuu! - -`can a code have **markdown?**` so good it doesn't - -

Yo! -Omg - - -ddffdg -

- -1. First -2. Second -3. Third -* Interesting -4. Forth - -## Unordered list - -* first -* second -* * second first -* * second __second__ jks8feif fdsuif yuweru sdfoisdfu wutwe iower wtew ruweir weoir wutywr wer woeirwr wieyriow eryowe rwyeor oweryower o -* * * hm, is it actually a thing? -* * * * and this?! -* * * * * omg -* third `and some code` - - -1. okay -2. okay 2 - 1. okay again - * it's also here - 2. and this - 3. and that - 1. Another one nested this time and a lot of text here, well, at least some to check how multiline will be handled - 1. And it goes on and on - 2. And it goes on and on - 3. And it goes on and on - 4. And it goes on and on - 5. And it goes on and on - 6. And it goes on and on - 7. And it goes on and on - 8. And it goes on and on - 9. And it goes on and on - 10. And it goes on and on - 11. And it goes on and on - 12. And it goes on and on - 13. And it goes on and on - 14. And it goes on and on - 15. And it goes on and on - 16. And it goes on and on - 17. And it goes on and on - 18. And it goes on and on - 19. And it goes on and on - 20. And it goes on and on - 21. And it goes on and on - 22. And it goes on and on - 23. And it goes on and on - 24. And it goes on and on - 25. And it goes on and on - 26. And it goes on and on - 27. And it goes on and on - 28. And it goes on and on - 29. And it goes on and on - 30. And it goes on and on - 31. And it goes on and on - 32. And it goes on and on - 333333333. And it goes on and on - - -### Quoted list - -> * first -> * second -> * third -> * * third first ->> * yo #1 ->> * yo #2 - - -jo - - -#### Code block - -```java -final String s = "this id code block"; -s.length() > 0; -``` ---- -okay, have a good day! - -Yo**2**2242 - -To compare~~13~~ - -~~Just strike it~~ - -



- -RED - -**PS** additional text to check if this view scrolls gracefully, sofihweo fwfw fuwf weyf pwefiowef twe weuifphw efwepfuwoefh wfypiwe fuwoef wiefg wtefw uf ywfyw fweouf wpfyw fwfe# \ No newline at end of file diff --git a/app/src/main/java/ru/noties/markwon/ActivityScope.java b/app/src/main/java/ru/noties/markwon/ActivityScope.java new file mode 100644 index 00000000..de2920a7 --- /dev/null +++ b/app/src/main/java/ru/noties/markwon/ActivityScope.java @@ -0,0 +1,7 @@ +package ru.noties.markwon; + +import javax.inject.Scope; + +@Scope +@interface ActivityScope { +} diff --git a/app/src/main/java/ru/noties/markwon/App.java b/app/src/main/java/ru/noties/markwon/App.java new file mode 100644 index 00000000..6b185d78 --- /dev/null +++ b/app/src/main/java/ru/noties/markwon/App.java @@ -0,0 +1,23 @@ +package ru.noties.markwon; + +import android.app.Application; +import android.content.Context; +import android.support.annotation.NonNull; + +public class App extends Application { + + private AppComponent component; + + @Override + public void onCreate() { + super.onCreate(); + + component = DaggerAppComponent.builder() + .appModule(new AppModule(this)) + .build(); + } + + public static AppComponent component(@NonNull Context context) { + return ((App) context.getApplicationContext()).component; + } +} diff --git a/app/src/main/java/ru/noties/markwon/AppBarItem.java b/app/src/main/java/ru/noties/markwon/AppBarItem.java new file mode 100644 index 00000000..c4960a2c --- /dev/null +++ b/app/src/main/java/ru/noties/markwon/AppBarItem.java @@ -0,0 +1,41 @@ +package ru.noties.markwon; + +import android.support.annotation.NonNull; +import android.text.TextUtils; +import android.view.View; +import android.widget.TextView; + +abstract class AppBarItem { + + static class State { + final String title; + final String subtitle; + + State(String title, String subtitle) { + this.title = title; + this.subtitle = subtitle; + } + } + + static class Renderer { + + final TextView title; + final TextView subtitle; + + Renderer(@NonNull View view, @NonNull View.OnClickListener themeChangeClicked) { + this.title = Views.findView(view, R.id.app_bar_title); + this.subtitle = Views.findView(view, R.id.app_bar_subtitle); + view.findViewById(R.id.app_bar_theme_changer) + .setOnClickListener(themeChangeClicked); + } + + void render(@NonNull State state) { + title.setText(state.title); + subtitle.setText(state.subtitle); + Views.setVisible(subtitle, !TextUtils.isEmpty(state.subtitle)); + } + } + + private AppBarItem() { + } +} diff --git a/app/src/main/java/ru/noties/markwon/AppComponent.java b/app/src/main/java/ru/noties/markwon/AppComponent.java new file mode 100644 index 00000000..1ff78c29 --- /dev/null +++ b/app/src/main/java/ru/noties/markwon/AppComponent.java @@ -0,0 +1,11 @@ +package ru.noties.markwon; + +import javax.inject.Singleton; + +import dagger.Component; + +@Component(modules = AppModule.class) +@Singleton +interface AppComponent { + MainActivitySubcomponent mainActivitySubcomponent(); +} diff --git a/app/src/main/java/ru/noties/markwon/AppModule.java b/app/src/main/java/ru/noties/markwon/AppModule.java new file mode 100644 index 00000000..d9868304 --- /dev/null +++ b/app/src/main/java/ru/noties/markwon/AppModule.java @@ -0,0 +1,66 @@ +package ru.noties.markwon; + +import android.content.Context; +import android.content.res.Resources; +import android.os.Handler; +import android.os.Looper; + +import com.squareup.picasso.Picasso; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import javax.inject.Singleton; + +import dagger.Module; +import dagger.Provides; +import okhttp3.OkHttpClient; + +@Module +class AppModule { + + private final App app; + + AppModule(App app) { + this.app = app; + } + + @Provides + Context context() { + return app.getApplicationContext(); + } + + @Provides + Resources resources() { + return app.getResources(); + } + + @Provides + @Singleton + OkHttpClient client() { + return new OkHttpClient(); + } + + @Singleton + @Provides + ExecutorService executorService() { + return Executors.newCachedThreadPool(); + } + + @Singleton + @Provides + Handler mainThread() { + return new Handler(Looper.getMainLooper()); + } + + @Singleton + @Provides + UrlProvider urlProvider() { + return new UrlProviderImpl(); + } + + @Provides + Picasso picasso(Context context) { + return Picasso.with(context); + } +} diff --git a/app/src/main/java/ru/noties/markwon/AsyncDrawableLoader.java b/app/src/main/java/ru/noties/markwon/AsyncDrawableLoader.java index 4a3f931f..d349e498 100644 --- a/app/src/main/java/ru/noties/markwon/AsyncDrawableLoader.java +++ b/app/src/main/java/ru/noties/markwon/AsyncDrawableLoader.java @@ -1,70 +1,82 @@ package ru.noties.markwon; -import android.graphics.Picture; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; -import android.graphics.drawable.PictureDrawable; -import android.net.Uri; import android.support.annotation.NonNull; -import android.widget.TextView; import com.caverock.androidsvg.SVG; import com.squareup.picasso.Picasso; -import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import java.util.concurrent.Future; -import okhttp3.OkHttpClient; +import javax.inject.Inject; + import pl.droidsonroids.gif.GifDrawable; import ru.noties.debug.Debug; import ru.noties.markwon.spans.AsyncDrawable; +@ActivityScope public class AsyncDrawableLoader implements AsyncDrawable.Loader { - private final TextView view; - private final Picasso picasso; - private final OkHttpClient client; - private final ExecutorService executorService; - private final Map> requests; + @Inject + Resources resources; + + @Inject + Picasso picasso; + + @Inject + ExecutorService executorService; + + private final Map> requests = new HashMap<>(3); + private final CopyOnWriteArrayList targets = new CopyOnWriteArrayList<>(); // sh*t.. - public AsyncDrawableLoader(TextView view) { - this.view = view; - this.picasso = new Picasso.Builder(view.getContext()) - .listener(new Picasso.Listener() { - @Override - public void onImageLoadFailed(Picasso picasso, Uri uri, Exception exception) { - Debug.e(exception, picasso, uri); - } - }) - .build(); - this.client = new OkHttpClient(); - this.executorService = Executors.newCachedThreadPool(); - this.requests = new HashMap<>(3); + @Inject + public AsyncDrawableLoader() { } @Override public void load(@NonNull String destination, @NonNull AsyncDrawable drawable) { - Debug.i("destination: %s", destination); - if (destination.endsWith(".svg")) { // load svg requests.put(destination, loadSvg(destination, drawable)); } else if (destination.endsWith(".gif")) { requests.put(destination, loadGif(destination, drawable)); } else { + + final Drawable error = new ColorDrawable(0xFFff0000); + final Drawable placeholder = new ColorDrawable(0xFF00ff00); + error.setBounds(0, 0, 100, 100); + placeholder.setBounds(0, 0, 50, 50); + + final AsyncDrawableTarget target = new AsyncDrawableTarget(resources, drawable, new AsyncDrawableTarget.DoneListener() { + @Override + public void onLoadingDone(AsyncDrawableTarget target) { + targets.remove(target); + } + }); + + targets.add(target); + picasso .load(destination) .tag(destination) - .into(new TextViewTarget(view, drawable)); + .placeholder(placeholder) + .error(error) + .into(target); } } @@ -73,7 +85,7 @@ public class AsyncDrawableLoader implements AsyncDrawable.Loader { Debug.i("destination: %s", destination); picasso.cancelTag(destination); - final Future future = requests.get(destination); + final Future future = requests.remove(destination); if (future != null) { future.cancel(true); } @@ -84,12 +96,27 @@ public class AsyncDrawableLoader implements AsyncDrawable.Loader { @Override public void run() { try { + final URL url = new URL(destination); final HttpURLConnection connection = (HttpURLConnection) url.openConnection(); final InputStream inputStream = connection.getInputStream(); + final SVG svg = SVG.getFromInputStream(inputStream); - final Picture picture = svg.renderToPicture(); - final Drawable drawable = new PictureDrawable(picture); + final float w = svg.getDocumentWidth(); + final float h = svg.getDocumentHeight(); + Debug.i("w: %s, h: %s", w, h); + + final float density = resources.getDisplayMetrics().density; + Debug.i(density); + + final int width = (int) (w * density + .5F); + final int height = (int) (h * density + .5F); + final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_4444); + final Canvas canvas = new Canvas(bitmap); + canvas.scale(density, density); + svg.renderToCanvas(canvas); + + final Drawable drawable = new BitmapDrawable(resources, bitmap); drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); asyncDrawable.setResult(drawable); @@ -105,9 +132,11 @@ public class AsyncDrawableLoader implements AsyncDrawable.Loader { @Override public void run() { try { + final URL url = new URL(destination); final HttpURLConnection connection = (HttpURLConnection) url.openConnection(); final InputStream inputStream = connection.getInputStream(); + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); final byte[] buffer = new byte[1024 * 8]; int read; diff --git a/app/src/main/java/ru/noties/markwon/AsyncDrawableTarget.java b/app/src/main/java/ru/noties/markwon/AsyncDrawableTarget.java new file mode 100644 index 00000000..1de0eaa1 --- /dev/null +++ b/app/src/main/java/ru/noties/markwon/AsyncDrawableTarget.java @@ -0,0 +1,81 @@ +package ru.noties.markwon; + +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; + +import com.squareup.picasso.Picasso; +import com.squareup.picasso.Target; + +import ru.noties.markwon.spans.AsyncDrawable; + +public class AsyncDrawableTarget implements Target { + + interface DoneListener { + void onLoadingDone(AsyncDrawableTarget target); + } + + private final Resources resources; + private final AsyncDrawable asyncDrawable; + private final DoneListener listener; + + public AsyncDrawableTarget(Resources resources, AsyncDrawable asyncDrawable, DoneListener listener) { + this.resources = resources; + this.asyncDrawable = asyncDrawable; + this.listener = listener; + } + + @Override + public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) { + if (bitmap != null) { + final Drawable drawable = new BitmapDrawable(resources, bitmap); + drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); + asyncDrawable.setResult(drawable); + } + notifyDone(); + } + + @Override + public void onBitmapFailed(Drawable errorDrawable) { + if (errorDrawable != null) { + asyncDrawable.setResult(errorDrawable); + } + notifyDone(); + } + + @Override + public void onPrepareLoad(Drawable placeHolderDrawable) { + if (placeHolderDrawable != null) { + asyncDrawable.setResult(placeHolderDrawable); + } + } + + private void notifyDone() { + if (listener != null) { + listener.onLoadingDone(this); + } + } +// +// private void attach() { +// +// // amazing stuff here, in order to keep this target alive (picasso stores target in a WeakReference) +// // we need to do this +// +// //noinspection unchecked +// List list = (List) view.getTag(R.id.amazing); +// if (list == null) { +// list = new ArrayList<>(2); +// view.setTag(R.id.amazing, list); +// } +// list.add(this); +// } +// +// private void detach() { +// //noinspection unchecked +// final List list = (List) view.getTag(R.id.amazing); +// if (list != null) { +// list.remove(this); +// } +// } +} diff --git a/app/src/main/java/ru/noties/markwon/MainActivity.java b/app/src/main/java/ru/noties/markwon/MainActivity.java index 96e811a1..bda7c876 100644 --- a/app/src/main/java/ru/noties/markwon/MainActivity.java +++ b/app/src/main/java/ru/noties/markwon/MainActivity.java @@ -1,90 +1,110 @@ package ru.noties.markwon; import android.app.Activity; +import android.content.Intent; +import android.net.Uri; import android.os.Bundle; -import android.os.SystemClock; -import android.text.method.LinkMovementMethod; +import android.view.View; import android.widget.TextView; -import java.io.IOException; -import java.io.InputStream; -import java.util.Scanner; +import javax.inject.Inject; import ru.noties.debug.AndroidLogDebugOutput; import ru.noties.debug.Debug; -import ru.noties.markwon.spans.AsyncDrawable; public class MainActivity extends Activity { - // markdown, mdown, mkdn, mdwn, mkd, md - // markdown, mdown, mkdn, mkd, md, text - static { Debug.init(new AndroidLogDebugOutput(true)); } -// private List targets = new ArrayList<>(); + @Inject + MarkdownLoader markdownLoader; + + @Inject + MarkdownRenderer markdownRenderer; + + @Inject + Themes themes; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + + App.component(this) + .mainActivitySubcomponent() + .inject(this); + + themes.apply(this); + + // how can we obtain SpannableConfiguration after theme was applied? + setContentView(R.layout.activity_main); - final TextView textView = (TextView) findViewById(R.id.activity_main); - final AsyncDrawable.Loader loader = new AsyncDrawableLoader(textView); - - new Thread(new Runnable() { + final AppBarItem.Renderer appBarRenderer + = new AppBarItem.Renderer(findViewById(R.id.app_bar), new View.OnClickListener() { @Override - public void run() { - InputStream stream = null; - Scanner scanner = null; - String md = null; - try { - stream = getAssets().open("scrollable.md"); -// stream = getAssets().open("test.md"); - scanner = new Scanner(stream).useDelimiter("\\A"); - if (scanner.hasNext()) { - md = scanner.next(); - } - } catch (Throwable t) { - Debug.e(t); - } finally { - if (stream != null) { - try { - stream.close(); - } catch (IOException e) { - } - } - if (scanner != null) { - scanner.close(); - } - } - - if (md != null) { - - final long start = SystemClock.uptimeMillis(); - - final SpannableConfiguration configuration = SpannableConfiguration.builder(MainActivity.this) - .asyncDrawableLoader(loader) - .build(); - - final CharSequence text = Markwon.markdown(configuration, md); - - final long end = SystemClock.uptimeMillis(); - Debug.i("Rendered: %d ms, length: %d", end - start, text.length()); - - textView.post(new Runnable() { - @Override - public void run() { - // NB! LinkMovementMethod forces frequent updates... - textView.setMovementMethod(LinkMovementMethod.getInstance()); - textView.setText(text); - Markwon.scheduleDrawables(textView); - } - }); - } + public void onClick(View v) { + themes.toggle(); + recreate(); } - }).start(); + }); + + final TextView textView = Views.findView(this, R.id.text); + final View progress = findViewById(R.id.progress); + + appBarRenderer.render(appBarState()); + + markdownLoader.load(uri(), new MarkdownLoader.OnMarkdownTextLoaded() { + @Override + public void apply(String text) { + markdownRenderer.render(MainActivity.this, text, new MarkdownRenderer.MarkdownReadyListener() { + @Override + public void onMarkdownReady(CharSequence markdown) { + Markwon.setText(textView, markdown); + Views.setVisible(progress, false); + } + }); + } + }); + } + + private AppBarItem.State appBarState() { + + final String title; + final String subtitle; + + // two possible states: just opened from launcher (no subtitle) + // opened to display external resource (subtitle as a path/url/whatever) + + final Uri uri = uri(); + + Debug.i(uri); + + if (uri != null) { + title = uri.getLastPathSegment(); + subtitle = uri.toString(); + } else { + title = getString(R.string.app_name); + subtitle = null; + } + + return new AppBarItem.State(title, subtitle); + } + + private Uri uri() { + final Intent intent = getIntent(); + return intent != null + ? intent.getData() + : null; + } + + @Override + protected void onDestroy() { + super.onDestroy(); + + markdownLoader.cancel(); + markdownRenderer.cancel(); } } diff --git a/app/src/main/java/ru/noties/markwon/MainActivitySubcomponent.java b/app/src/main/java/ru/noties/markwon/MainActivitySubcomponent.java new file mode 100644 index 00000000..077fcd31 --- /dev/null +++ b/app/src/main/java/ru/noties/markwon/MainActivitySubcomponent.java @@ -0,0 +1,9 @@ +package ru.noties.markwon; + +import dagger.Subcomponent; + +@Subcomponent +@ActivityScope +interface MainActivitySubcomponent { + void inject(MainActivity activity); +} diff --git a/app/src/main/java/ru/noties/markwon/MarkdownLoader.java b/app/src/main/java/ru/noties/markwon/MarkdownLoader.java new file mode 100644 index 00000000..0c9f10db --- /dev/null +++ b/app/src/main/java/ru/noties/markwon/MarkdownLoader.java @@ -0,0 +1,193 @@ +package ru.noties.markwon; + +import android.content.ContentResolver; +import android.content.Context; +import android.net.Uri; +import android.os.Handler; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.text.TextUtils; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; + +import javax.inject.Inject; + +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; +import ru.noties.debug.Debug; + +@ActivityScope +public class MarkdownLoader { + + public interface OnMarkdownTextLoaded { + void apply(String text); + } + + @Inject + Context context; + + @Inject + ExecutorService service; + + @Inject + Handler handler; + + @Inject + OkHttpClient client; + + @Inject + UrlProvider urlProvider; + + private Future task; + + @Inject + MarkdownLoader() { + } + + public void load(@Nullable final Uri uri, @NonNull final OnMarkdownTextLoaded loaded) { + cancel(); + task = service.submit(new Runnable() { + @Override + public void run() { + try { + deliver(loaded, text(uri)); + } catch (Throwable t) { + Debug.e(t); + } + } + }); + } + + public void cancel() { + if (task != null) { + task.cancel(true); + task = null; + } + } + + private void deliver(@NonNull final OnMarkdownTextLoaded loaded, final String text) { + if (task != null + && !task.isCancelled()) { + handler.post(new Runnable() { + @Override + public void run() { + loaded.apply(text); + } + }); + } + } + + private String text(@Nullable Uri uri) { + final String out; + if (uri == null) { + out = loadReadMe(); + } else { + out = loadExternalResource(uri); + } + return out; + } + + private String loadReadMe() { + InputStream stream = null; + try { + stream = context.getAssets().open("README.md"); + } catch (IOException e) { + Debug.e(e); + } + return readStream(stream); + } + + private String loadExternalResource(@NonNull Uri uri) { + final String out; + final String scheme = uri.getScheme(); + if (!TextUtils.isEmpty(scheme) && ContentResolver.SCHEME_FILE.equals(scheme)) { + out = loadExternalFile(uri); + } else { + out = loadExternalUrl(uri); + } + return out; + } + + private String loadExternalFile(@NonNull Uri uri) { + InputStream stream = null; + try { + stream = new FileInputStream(new File(uri.getPath())); + } catch (FileNotFoundException e) { + Debug.e(e); + } + return readStream(stream); + } + + private String loadExternalUrl(@NonNull Uri uri) { + + final String url = urlProvider.provide(uri); + + final Request request = new Request.Builder() + .url(url) + .build(); + + Response response = null; + try { + response = client.newCall(request).execute(); + } catch (IOException e) { + Debug.e(e); + } + + final ResponseBody body = response != null + ? response.body() + : null; + + String out = null; + + if (body != null) { + try { + out = body.string(); + } catch (IOException e) { + Debug.e(e); + } + } + + return out; + } + + private static String readStream(@Nullable InputStream inputStream) { + + String out = null; + + if (inputStream != null) { + BufferedReader reader = null; + try { + reader = new BufferedReader(new InputStreamReader(inputStream)); + final StringBuilder builder = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + builder.append(line) + .append('\n'); + } + out = builder.toString(); + } catch (IOException e) { + Debug.e(e); + } finally { + if (reader != null) { + try { + reader.close(); + } catch (IOException e) { + // no op + } + } + } + } + + return out; + } +} diff --git a/app/src/main/java/ru/noties/markwon/MarkdownRenderer.java b/app/src/main/java/ru/noties/markwon/MarkdownRenderer.java new file mode 100644 index 00000000..b6f51e67 --- /dev/null +++ b/app/src/main/java/ru/noties/markwon/MarkdownRenderer.java @@ -0,0 +1,60 @@ +package ru.noties.markwon; + +import android.content.Context; +import android.os.Handler; +import android.support.annotation.NonNull; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; + +import javax.inject.Inject; + +@ActivityScope +public class MarkdownRenderer { + + interface MarkdownReadyListener { + void onMarkdownReady(CharSequence markdown); + } + + @Inject + AsyncDrawableLoader loader; + + @Inject + ExecutorService service; + + @Inject + Handler handler; + + private Future task; + + @Inject + MarkdownRenderer() { + } + + public void render(@NonNull final Context context, @NonNull final String markdown, @NonNull final MarkdownReadyListener listener) { + cancel(); + task = service.submit(new Runnable() { + @Override + public void run() { + final SpannableConfiguration configuration = SpannableConfiguration.builder(context) + .asyncDrawableLoader(loader) + .build(); + final CharSequence text = Markwon.markdown(configuration, markdown); + handler.post(new Runnable() { + @Override + public void run() { + listener.onMarkdownReady(text); + } + }); + task = null; + } + }); + } + + public void cancel() { + if (task != null) { + task.cancel(true); + task = null; + } + } +} diff --git a/app/src/main/java/ru/noties/markwon/TextViewTarget.java b/app/src/main/java/ru/noties/markwon/TextViewTarget.java deleted file mode 100644 index c491beee..00000000 --- a/app/src/main/java/ru/noties/markwon/TextViewTarget.java +++ /dev/null @@ -1,74 +0,0 @@ -package ru.noties.markwon; - -import android.graphics.Bitmap; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.widget.TextView; - -import com.squareup.picasso.Picasso; -import com.squareup.picasso.Target; - -import java.util.ArrayList; -import java.util.List; - -import ru.noties.markwon.spans.AsyncDrawable; - -public class TextViewTarget implements Target { - - private final TextView view; - private final AsyncDrawable asyncDrawable; - - public TextViewTarget(TextView view, AsyncDrawable asyncDrawable) { - this.view = view; - this.asyncDrawable = asyncDrawable; - - attach(); - } - - @Override - public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) { - if (bitmap != null) { - final Drawable drawable = new BitmapDrawable(view.getResources(), bitmap); - drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); - asyncDrawable.setResult(drawable); - } - detach(); - } - - @Override - public void onBitmapFailed(Drawable errorDrawable) { - if (errorDrawable != null) { - asyncDrawable.setResult(errorDrawable); - } - detach(); - } - - @Override - public void onPrepareLoad(Drawable placeHolderDrawable) { - if (placeHolderDrawable != null) { - asyncDrawable.setResult(placeHolderDrawable); - } - } - - private void attach() { - - // amazing stuff here, in order to keep this target alive (picasso stores target in a WeakReference) - // we need to do this - - //noinspection unchecked - List list = (List) view.getTag(R.id.amazing); - if (list == null) { - list = new ArrayList<>(2); - view.setTag(R.id.amazing, list); - } - list.add(this); - } - - private void detach() { - //noinspection unchecked - final List list = (List) view.getTag(R.id.amazing); - if (list != null) { - list.remove(this); - } - } -} diff --git a/app/src/main/java/ru/noties/markwon/Themes.java b/app/src/main/java/ru/noties/markwon/Themes.java new file mode 100644 index 00000000..05e51747 --- /dev/null +++ b/app/src/main/java/ru/noties/markwon/Themes.java @@ -0,0 +1,46 @@ +package ru.noties.markwon; + +import android.content.Context; +import android.content.SharedPreferences; +import android.support.annotation.NonNull; + +import javax.inject.Inject; +import javax.inject.Singleton; + +@Singleton +public class Themes { + + private static final String PREF_NAME = "theme"; + private static final String KEY_THEME_DARK = "key.tD"; + + private SharedPreferences preferences; + + @Inject + Themes(Context context) { + this.preferences = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE); + } + + public void apply(@NonNull Context context) { + final boolean dark = preferences.getBoolean(KEY_THEME_DARK, false); + // we have only 2 themes and Light one is default, so no need to apply it + final int theme; + if (dark) { + theme = R.style.AppThemeBaseDark; + } else { + theme = R.style.AppThemeBaseLight; + } + + final Context appContext = context.getApplicationContext(); + if (appContext != context) { + appContext.setTheme(theme); + } + context.setTheme(theme); + } + + public void toggle() { + final boolean newValue = !preferences.getBoolean(KEY_THEME_DARK, false); + preferences.edit() + .putBoolean(KEY_THEME_DARK, newValue) + .apply(); + } +} diff --git a/app/src/main/java/ru/noties/markwon/UrlProvider.java b/app/src/main/java/ru/noties/markwon/UrlProvider.java new file mode 100644 index 00000000..82dc87c2 --- /dev/null +++ b/app/src/main/java/ru/noties/markwon/UrlProvider.java @@ -0,0 +1,8 @@ +package ru.noties.markwon; + +import android.net.Uri; +import android.support.annotation.NonNull; + +public interface UrlProvider { + String provide(@NonNull Uri uri); +} diff --git a/app/src/main/java/ru/noties/markwon/UrlProviderImpl.java b/app/src/main/java/ru/noties/markwon/UrlProviderImpl.java new file mode 100644 index 00000000..d935454e --- /dev/null +++ b/app/src/main/java/ru/noties/markwon/UrlProviderImpl.java @@ -0,0 +1,42 @@ +package ru.noties.markwon; + +import android.net.Uri; +import android.support.annotation.NonNull; + +import java.util.List; + +class UrlProviderImpl implements UrlProvider { + + private static final String GITHUB = "github.com"; + + @Override + public String provide(@NonNull Uri uri) { + + // hm... github, even having a README.md in path will return rendered HTML + + if (GITHUB.equals(uri.getAuthority())) { + final List segments = uri.getPathSegments(); + if (segments != null + && segments.contains("blob")) { + // we need to modify the final uri + final Uri.Builder builder = new Uri.Builder() + .scheme(uri.getScheme()) + .authority(uri.getAuthority()) + .fragment(uri.getFragment()) + .query(uri.getQuery()); + for (String segment: segments) { + final String part; + if ("blob".equals(segment)) { + part = "raw"; + } else { + part = segment; + } + builder.appendPath(part); + } + uri = builder.build(); + } + } + + return uri.toString(); + } +} diff --git a/app/src/main/java/ru/noties/markwon/Views.java b/app/src/main/java/ru/noties/markwon/Views.java new file mode 100644 index 00000000..57c283ca --- /dev/null +++ b/app/src/main/java/ru/noties/markwon/Views.java @@ -0,0 +1,36 @@ +package ru.noties.markwon; + +import android.app.Activity; +import android.support.annotation.IdRes; +import android.support.annotation.IntDef; +import android.support.annotation.NonNull; +import android.view.View; + +public abstract class Views { + + @IntDef({View.INVISIBLE, View.GONE}) + @interface NotVisible {} + + public static V findView(@NonNull View view, @IdRes int id) { + //noinspection unchecked + return (V) view.findViewById(id); + } + + public static V findView(@NonNull Activity activity, @IdRes int id) { + //noinspection unchecked + return (V) activity.findViewById(id); + } + + public static void setVisible(@NonNull View view, boolean visible) { + setVisible(view, visible, View.GONE); + } + + public static void setVisible(@NonNull View view, boolean visible, @NotVisible int notVisible) { + final int visibility = visible + ? View.VISIBLE + : notVisible; + view.setVisibility(visibility); + } + + private Views() {} +} diff --git a/app/src/main/res/drawable/bg_app_bar_shadow.xml b/app/src/main/res/drawable/bg_app_bar_shadow.xml new file mode 100644 index 00000000..70124dc0 --- /dev/null +++ b/app/src/main/res/drawable/bg_app_bar_shadow.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_app_bar_theme_dark.xml b/app/src/main/res/drawable/ic_app_bar_theme_dark.xml new file mode 100644 index 00000000..c3c5060d --- /dev/null +++ b/app/src/main/res/drawable/ic_app_bar_theme_dark.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_app_bar_theme_light.xml b/app/src/main/res/drawable/ic_app_bar_theme_light.xml new file mode 100644 index 00000000..b09b244b --- /dev/null +++ b/app/src/main/res/drawable/ic_app_bar_theme_light.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 3dacf3f3..b24d050e 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,19 +1,97 @@ - - + + + + + + + + + + + + + android:orientation="vertical"> - + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png index cde69bcccec65160d92116f20ffce4fce0b5245c..cac0e405a8e026ffc9ba032d6b4d29a89f2044fc 100644 GIT binary patch delta 2552 zcmX9=2{=@J7iWmEjj_Zu1~clbN%G2`H`^d&X-JkR%S+a>G?JyRWnzS~M6acYgeJ?- zL>k0kFcQg&Y_Ba_$P(XV`~E%Oea`dz&vWm&_nz}R=l6TgS9FYI>QP9b!`#@=?oQtF zNVw%CXF2{K8((Z(&Bht}oJue8sH`reA4F%-rdx80|hx$w~ZxJ{QD*>%xK zky4@qMSzl}@1jVlwu2Xw8n&g<>Y}?m_QJ;JXa0mN8@S8#l6`innr3oS^MuaMF;_<; z59$(-5Qpu$zy73Y7n=R!x6gFdk2=8#c-%)$)fXeQixb*Yek6xhhHkEUe9Y1CPqr@E z8Mt2KMW@qg%gf76^xE3LwAbhRirmeeokyfu3#1Ttr!B24FDL3i{m=Fk87N=F|DV4f zagrNDX1gYQaw>v!o(Aq5r2Qenu4anIyT|`W-AYuDsj>=vza2SSZ1H4&a^)qr#sj2k zMOoQGz7iX8sz?4%$J1IXE33#c=fDrQWsKmQ&dDI{1R2&~v|bEP&P#Xt6?rkjyg!x+eZ{>(a~LVYyy)zu9>UhnrQCF zt#AW*>vS(C$8vkTq+q~AqwMYXW78hv2T{=0cc<>Z&+rr%7ss{x`}q-VY|h{>U%o8g z=Ii5Q5FXAcnEx4}BFvM(Y`*GjYYR&>j_ohG7}*-EjVbU&+fxr`XJ^AjMDBNbqE$3K zu|<896dA3;#zr|Mwt})Uf**n5VycwBer;@M$emC;;P=ivGb_vE>l@$Fii-FFKNHB> z8lil(o;S;j=0B*6&%9u4Y%FZ%;)1~-5?DQ*U0sAbceFiKk33oEFEJ{%jd4dw9zHzz zA%@52#*MbI_hC5k#30VPSFd_XOA8@SACgnIVtUa2{qNdzUBpjC41)jm%4lL~DINs; z`V~=Xr>L%_B`Q1rrR)-gLisTI4!RW#UVrUW!#6xU-0P!`Of+_^C!Ivg$hgxSQ!_IU z(HxUsyf`*9H)r&mXi(Dc&RL&Ly-;jsVKH6b;i>BWb8(;mFKB3Jh!ngJoXpJ3xFE~p z60II*cwyXk&dC82myqcD@})JD1qN9qbkXkE@#BSsg)r4@;S(31L9J75Nn$7z#VS|x zIh}r3L`1~Q%#17lQ^J+<_VI?uw7fjwa{Hu?4l~|Ehj3b2Ve8-%fPz^yLAzJvx_3nIqdD}9{wY4=ObwK;pH{s0)U7s92 zc6E?81{4bth6DrzKFrx7(}$Sm*h4}f04XPjc<1ua zBuQ$r_qkDNdHJkPsXp=MO=U6x2avt~=~+sGs63)B?6o5ersmz#+Ld`q7j{NC4s;o# z7li_4!9_()gMw6ETII$M4LN=vuHgrB4GOOsS?*mR0YE6p$;ObQBdVpPrEV-WKVP({ zPn%Sbmxm$8$1CXR$$5EsIo95k4G9UU`iq6=mgvh@T+7PJ@JdQ# z+cNUCN){*#n`V}po<2p3yE#^WKU>Y4fJI`Jl!V*b+D2PeCz?d>6Jp-HQGZcf9K)J+ z?|OVPP6rR2JSouJ+-zoT-TpCG3zyMJBod=%W<1GcvfJ`kx=OZXDV1tOBB_HUQ?s)Z z4tG&Bx_0r*?Z8v&-Px~3)p@#XX9*do(Mg%uSDz&jls^v>2Yk5&I~-kF;Vgk@Be zmPXrC?~mPNWu9`tW^@(|F7SqXa#UhbHT}g^N3Y?Rh3;inSl)BI*DT0wv@Y<9K2$YnTp8iVrAI+fF z)u8}#zfty4Cn{zFnE=ok3wSrzhlRE{pe`T++`@^5odQd3Ls9P4n$y9 zMLpHgX&D)Dsj0{xoT%2ewq^jhA6?<-Q63u`8y{a^!z)+NFD)qYLa54Q(Wi6~uu5FG=SNv>!DyJ3HByB;C>7O*X(r z13OsU5y^Y0zQ>5Fs3o9!>P5;-MTf|$BcX4^ynP1s-D%4Mgw_ogQp(V4UmD)e;pG6GJAorLt#-Y6Io#>+654&suyReJ9~TLxru3#($^5?!j?waB51{NcV6Vm&04+=Eq zy1N|NCNI@7BD86a4g90aJBHDK%7?9QwcZu%7|Hm3^J2Hd6#qI8Cx*fErN4heKJa4C z$imW6B6-ns|j-d(FaaBy{<_%rb0=iISNSFb*` zfOCWvwsB{1D@Aa2*6EBRDea3ZD=VvrG649~PS5)MoE)8}$aU{p0f!WW}j2Rb`D|H)>vKPfv^A5+C*-4t*$rnQ~XXe+n?mTZX) zCjWrrE(zeo36ub`>D0AT6AfEjQF`zoDkwo$tP4-0l5GW({mq|6i!Ua`k?n|2=GwE@2%>;sTesiM4SB H!7cWGkG0GA literal 3418 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 VFwL6zQVak9002ovPDHLkV1nV~3I+fG diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png index bfa42f0e7b91d006d22352c9ff2f134e504e3c1d..c71913957a7d10b49e27e10c56fbe6a1c9564435 100644 GIT binary patch literal 3211 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 diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index 324e72cdd7480cb983fa1bcc7ce686e51ef87fe7..5fda70fed23c644b2b29735e075cf3ba22fd4d95 100644 GIT binary patch literal 5946 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 diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png index aee44e138434630332d88b1680f33c4b24c70ab3..510044e8fe5a6c4c68dbc9438512196968956182 100644 GIT binary patch literal 7441 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 literal 10486 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 - + + \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 5b267e56..b8632f76 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -1,8 +1,16 @@ - + + + +