diff --git a/.travis.yml b/.travis.yml
index fce9c94b..5e3f5d44 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -11,3 +11,6 @@ android:
- android-27
script: cd Clover && ./gradlew build --console plain -x lint
+
+notifications:
+ email: false
diff --git a/Clover/app/src/main/AndroidManifest.xml b/Clover/app/src/main/AndroidManifest.xml
index b0ce7abb..c0c9ddd2 100644
--- a/Clover/app/src/main/AndroidManifest.xml
+++ b/Clover/app/src/main/AndroidManifest.xml
@@ -16,6 +16,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
-->
@@ -23,7 +24,9 @@ along with this program. If not, see .
-
+
* All {@code final} fields are thread-safe.
@@ -99,12 +97,13 @@ public class Post {
public final List repliesFrom = new ArrayList<>();
// These members may only mutate on the main thread.
- private boolean sticky = false;
- private boolean closed = false;
- private boolean archived = false;
- private int replies = -1;
- private int imagesCount = -1;
- private int uniqueIps = -1;
+ private boolean sticky;
+ private boolean closed;
+ private boolean archived;
+ private int replies;
+ private int imagesCount;
+ private int uniqueIps;
+ private long lastModified;
private String title = "";
private Post(Builder builder) {
@@ -116,6 +115,7 @@ public class Post {
replies = builder.replies;
imagesCount = builder.imagesCount;
uniqueIps = builder.uniqueIps;
+ lastModified = builder.lastModified;
sticky = builder.sticky;
closed = builder.closed;
archived = builder.archived;
@@ -214,6 +214,16 @@ public class Post {
this.uniqueIps = uniqueIps;
}
+ @MainThread
+ public long getLastModified() {
+ return lastModified;
+ }
+
+ @MainThread
+ public void setLastModified(long lastModified) {
+ this.lastModified = lastModified;
+ }
+
@MainThread
public String getTitle() {
return title;
@@ -246,19 +256,16 @@ public class Post {
public boolean sticky;
public boolean closed;
public boolean archived;
+ public long lastModified = -1L;
public String subject = "";
public String name = "";
public CharSequence comment = "";
public String tripcode = "";
- public long unixTimestampSeconds = -1;
+ public long unixTimestampSeconds = -1L;
public List images;
- public String countryCode;
- public String countryName;
- public HttpUrl countryUrl;
-
public List httpIcons;
public String posterId = "";
@@ -324,6 +331,11 @@ public class Post {
return this;
}
+ public Builder lastModified(long lastModified) {
+ this.lastModified = lastModified;
+ return this;
+ }
+
public Builder closed(boolean closed) {
this.closed = closed;
return this;
diff --git a/Clover/app/src/main/java/org/floens/chan/core/presenter/SiteSetupPresenter.java b/Clover/app/src/main/java/org/floens/chan/core/presenter/SiteSetupPresenter.java
index 534a51f6..e05ac5fb 100644
--- a/Clover/app/src/main/java/org/floens/chan/core/presenter/SiteSetupPresenter.java
+++ b/Clover/app/src/main/java/org/floens/chan/core/presenter/SiteSetupPresenter.java
@@ -1,8 +1,8 @@
package org.floens.chan.core.presenter;
import org.floens.chan.core.database.DatabaseManager;
-import org.floens.chan.core.settings.Setting;
import org.floens.chan.core.site.Site;
+import org.floens.chan.core.site.SiteSetting;
import java.util.List;
@@ -29,7 +29,7 @@ public class SiteSetupPresenter {
callback.showLogin();
}
- List> settings = site.settings();
+ List settings = site.settings();
if (!settings.isEmpty()) {
callback.showSettings(settings);
}
@@ -57,6 +57,6 @@ public class SiteSetupPresenter {
void setIsLoggedIn(boolean isLoggedIn);
- void showSettings(List> settings);
+ void showSettings(List settings);
}
}
diff --git a/Clover/app/src/main/java/org/floens/chan/core/site/Site.java b/Clover/app/src/main/java/org/floens/chan/core/site/Site.java
index a40557a6..a85acd1d 100644
--- a/Clover/app/src/main/java/org/floens/chan/core/site/Site.java
+++ b/Clover/app/src/main/java/org/floens/chan/core/site/Site.java
@@ -21,12 +21,11 @@ import org.floens.chan.core.model.Post;
import org.floens.chan.core.model.json.site.SiteConfig;
import org.floens.chan.core.model.orm.Board;
import org.floens.chan.core.model.orm.Loadable;
-import org.floens.chan.core.settings.Setting;
import org.floens.chan.core.settings.json.JsonSettings;
-import org.floens.chan.core.site.parser.ChanReader;
import org.floens.chan.core.site.http.DeleteRequest;
import org.floens.chan.core.site.http.LoginRequest;
import org.floens.chan.core.site.http.Reply;
+import org.floens.chan.core.site.parser.ChanReader;
import java.util.List;
@@ -150,7 +149,7 @@ public interface Site {
boolean boardFeature(BoardFeature boardFeature, Board board);
- List> settings();
+ List settings();
SiteEndpoints endpoints();
diff --git a/Clover/app/src/main/java/org/floens/chan/core/site/SiteBase.java b/Clover/app/src/main/java/org/floens/chan/core/site/SiteBase.java
index 6042e8cb..454627a0 100644
--- a/Clover/app/src/main/java/org/floens/chan/core/site/SiteBase.java
+++ b/Clover/app/src/main/java/org/floens/chan/core/site/SiteBase.java
@@ -25,7 +25,6 @@ import org.floens.chan.core.database.LoadableProvider;
import org.floens.chan.core.manager.BoardManager;
import org.floens.chan.core.model.json.site.SiteConfig;
import org.floens.chan.core.model.orm.Board;
-import org.floens.chan.core.settings.Setting;
import org.floens.chan.core.settings.SettingProvider;
import org.floens.chan.core.settings.json.JsonSettings;
import org.floens.chan.core.settings.json.JsonSettingsProvider;
@@ -99,7 +98,7 @@ public abstract class SiteBase implements Site {
}
@Override
- public List> settings() {
+ public List settings() {
return new ArrayList<>();
}
diff --git a/Clover/app/src/main/java/org/floens/chan/core/site/SiteSetting.java b/Clover/app/src/main/java/org/floens/chan/core/site/SiteSetting.java
new file mode 100644
index 00000000..17b94c21
--- /dev/null
+++ b/Clover/app/src/main/java/org/floens/chan/core/site/SiteSetting.java
@@ -0,0 +1,51 @@
+/*
+ * Clover - 4chan browser https://github.com/Floens/Clover/
+ * Copyright (C) 2014 Floens
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.floens.chan.core.site;
+
+import org.floens.chan.core.settings.OptionsSetting;
+import org.floens.chan.core.settings.Setting;
+
+import java.util.List;
+
+/**
+ * Hacky stuff to give the site settings a good UI.
+ */
+public class SiteSetting {
+ public enum Type {
+ OPTIONS
+ }
+
+ public final String name;
+ public final Type type;
+ public final Setting> setting;
+
+ public List optionNames;
+
+ private SiteSetting(String name, Type type, Setting> setting) {
+ this.name = name;
+ this.type = type;
+ this.setting = setting;
+ }
+
+ public static SiteSetting forOption(OptionsSetting> options, String name,
+ List optionNames) {
+ SiteSetting setting = new SiteSetting(name, Type.OPTIONS, options);
+ setting.optionNames = optionNames;
+ return setting;
+ }
+}
diff --git a/Clover/app/src/main/java/org/floens/chan/core/site/common/FutabaChanReader.java b/Clover/app/src/main/java/org/floens/chan/core/site/common/FutabaChanReader.java
index 82563f27..29851612 100644
--- a/Clover/app/src/main/java/org/floens/chan/core/site/common/FutabaChanReader.java
+++ b/Clover/app/src/main/java/org/floens/chan/core/site/common/FutabaChanReader.java
@@ -193,6 +193,9 @@ public class FutabaChanReader implements ChanReader {
case "unique_ips":
builder.uniqueIps(reader.nextInt());
break;
+ case "last_modified":
+ builder.lastModified(reader.nextLong());
+ break;
case "id":
builder.posterId(reader.nextString());
break;
@@ -253,6 +256,7 @@ public class FutabaChanReader implements ChanReader {
op.replies(builder.replies);
op.images(builder.imagesCount);
op.uniqueIps(builder.uniqueIps);
+ op.lastModified(builder.lastModified);
queue.setOp(op);
}
diff --git a/Clover/app/src/main/java/org/floens/chan/core/site/common/vichan/VichanApi.java b/Clover/app/src/main/java/org/floens/chan/core/site/common/vichan/VichanApi.java
index 29261fb2..382147ba 100644
--- a/Clover/app/src/main/java/org/floens/chan/core/site/common/vichan/VichanApi.java
+++ b/Clover/app/src/main/java/org/floens/chan/core/site/common/vichan/VichanApi.java
@@ -171,6 +171,9 @@ public class VichanApi extends CommonSite.CommonApi {
case "unique_ips":
builder.uniqueIps(reader.nextInt());
break;
+ case "last_modified":
+ builder.lastModified(reader.nextLong());
+ break;
case "id":
builder.posterId(reader.nextString());
break;
@@ -228,6 +231,7 @@ public class VichanApi extends CommonSite.CommonApi {
op.replies(builder.replies);
op.images(builder.imagesCount);
op.uniqueIps(builder.uniqueIps);
+ op.lastModified(builder.lastModified);
queue.setOp(op);
}
diff --git a/Clover/app/src/main/java/org/floens/chan/core/site/loader/ChanThreadLoader.java b/Clover/app/src/main/java/org/floens/chan/core/site/loader/ChanThreadLoader.java
index 90910abd..a197639d 100644
--- a/Clover/app/src/main/java/org/floens/chan/core/site/loader/ChanThreadLoader.java
+++ b/Clover/app/src/main/java/org/floens/chan/core/site/loader/ChanThreadLoader.java
@@ -310,6 +310,7 @@ public class ChanThreadLoader implements Response.ErrorListener, Response.Listen
realOp.setReplies(fakeOp.replies);
realOp.setImagesCount(fakeOp.imagesCount);
realOp.setUniqueIps(fakeOp.uniqueIps);
+ realOp.setLastModified(fakeOp.lastModified);
} else {
Logger.e(TAG, "Thread has no op!");
}
diff --git a/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4.java b/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4.java
index 50aa614d..fc4a3ed3 100644
--- a/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4.java
+++ b/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4.java
@@ -26,7 +26,6 @@ import org.floens.chan.core.model.orm.Board;
import org.floens.chan.core.model.orm.Loadable;
import org.floens.chan.core.settings.OptionSettingItem;
import org.floens.chan.core.settings.OptionsSetting;
-import org.floens.chan.core.settings.Setting;
import org.floens.chan.core.settings.SettingProvider;
import org.floens.chan.core.settings.SharedPreferencesSettingProvider;
import org.floens.chan.core.settings.StringSetting;
@@ -38,6 +37,7 @@ import org.floens.chan.core.site.SiteBase;
import org.floens.chan.core.site.SiteEndpoints;
import org.floens.chan.core.site.SiteIcon;
import org.floens.chan.core.site.SiteRequestModifier;
+import org.floens.chan.core.site.SiteSetting;
import org.floens.chan.core.site.SiteUrlHandler;
import org.floens.chan.core.site.common.CommonReplyHttpCall;
import org.floens.chan.core.site.common.FutabaChanReader;
@@ -478,14 +478,18 @@ public class Chan4 extends SiteBase {
public void initializeSettings() {
super.initializeSettings();
- captchaType = new OptionsSetting<>(settingsProvider, "preference_captcha_type",
+ captchaType = new OptionsSetting<>(settingsProvider,
+ "preference_captcha_type",
CaptchaType.class, CaptchaType.V2NOJS);
}
@Override
- public List> settings() {
+ public List settings() {
return Arrays.asList(
- captchaType
+ SiteSetting.forOption(
+ captchaType,
+ "Captcha type",
+ Arrays.asList("Javascript", "Noscript"))
);
}
diff --git a/Clover/app/src/main/java/org/floens/chan/ui/adapter/PostsFilter.java b/Clover/app/src/main/java/org/floens/chan/ui/adapter/PostsFilter.java
index 3c5bb1cf..be0fafc3 100644
--- a/Clover/app/src/main/java/org/floens/chan/ui/adapter/PostsFilter.java
+++ b/Clover/app/src/main/java/org/floens/chan/ui/adapter/PostsFilter.java
@@ -35,33 +35,20 @@ import javax.inject.Inject;
import static org.floens.chan.Chan.inject;
public class PostsFilter {
- public static final Comparator IMAGE_COMPARATOR = new Comparator() {
- @Override
- public int compare(Post lhs, Post rhs) {
- return rhs.getImagesCount() - lhs.getImagesCount();
- }
- };
+ private static final Comparator IMAGE_COMPARATOR =
+ (lhs, rhs) -> rhs.getImagesCount() - lhs.getImagesCount();
- public static final Comparator REPLY_COMPARATOR = new Comparator() {
- @Override
- public int compare(Post lhs, Post rhs) {
- return rhs.getReplies() - lhs.getReplies();
- }
- };
+ private static final Comparator REPLY_COMPARATOR =
+ (lhs, rhs) -> rhs.getReplies() - lhs.getReplies();
- public static final Comparator NEWEST_COMPARATOR = new Comparator() {
- @Override
- public int compare(Post lhs, Post rhs) {
- return (int) (rhs.time - lhs.time);
- }
- };
+ private static final Comparator NEWEST_COMPARATOR =
+ (lhs, rhs) -> (int) (rhs.time - lhs.time);
- public static final Comparator OLDEST_COMPARATOR = new Comparator() {
- @Override
- public int compare(Post lhs, Post rhs) {
- return (int) (lhs.time - rhs.time);
- }
- };
+ private static final Comparator OLDEST_COMPARATOR =
+ (lhs, rhs) -> (int) (lhs.time - rhs.time);
+
+ private static final Comparator MODIFIED_COMPARATOR =
+ (lhs, rhs) -> (int) (rhs.getLastModified() - lhs.getLastModified());
@Inject
DatabaseManager databaseManager;
@@ -99,6 +86,9 @@ public class PostsFilter {
case OLDEST:
Collections.sort(posts, OLDEST_COMPARATOR);
break;
+ case MODIFIED:
+ Collections.sort(posts, MODIFIED_COMPARATOR);
+ break;
}
}
@@ -149,7 +139,8 @@ public class PostsFilter {
REPLY("reply"),
IMAGE("image"),
NEWEST("newest"),
- OLDEST("oldest");
+ OLDEST("oldest"),
+ MODIFIED("modified");
public String name;
diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/ArchiveController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/ArchiveController.java
index 3ffae763..9c97c786 100644
--- a/Clover/app/src/main/java/org/floens/chan/ui/controller/ArchiveController.java
+++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/ArchiveController.java
@@ -115,6 +115,7 @@ public class ArchiveController extends Controller implements ArchivePresenter.Ca
@Override
public void onSearchEntered(String entered) {
+ presenter.onSearchEntered(entered);
}
@Override
diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/BrowseController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/BrowseController.java
index 7e9e6799..a929ac51 100644
--- a/Clover/app/src/main/java/org/floens/chan/ui/controller/BrowseController.java
+++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/BrowseController.java
@@ -290,6 +290,9 @@ public class BrowseController extends ThreadController implements
case OLDEST:
nameId = R.string.order_oldest;
break;
+ case MODIFIED:
+ nameId = R.string.order_modified;
+ break;
}
String name = getString(nameId);
diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/SiteSetupController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/SiteSetupController.java
index a7a88e37..ec686253 100644
--- a/Clover/app/src/main/java/org/floens/chan/ui/controller/SiteSetupController.java
+++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/SiteSetupController.java
@@ -18,12 +18,13 @@
package org.floens.chan.ui.controller;
import android.content.Context;
+import android.support.annotation.NonNull;
import org.floens.chan.R;
import org.floens.chan.core.presenter.SiteSetupPresenter;
import org.floens.chan.core.settings.OptionsSetting;
-import org.floens.chan.core.settings.Setting;
import org.floens.chan.core.site.Site;
+import org.floens.chan.core.site.SiteSetting;
import org.floens.chan.ui.settings.LinkSettingView;
import org.floens.chan.ui.settings.ListSettingView;
import org.floens.chan.ui.settings.SettingsController;
@@ -98,21 +99,25 @@ public class SiteSetupController extends SettingsController implements SiteSetup
}
@Override
- public void showSettings(List> settings) {
+ public void showSettings(List settings) {
SettingsGroup group = new SettingsGroup("Additional settings");
- for (Setting> setting : settings) {
- if (setting instanceof OptionsSetting) {
- OptionsSetting optionsSetting = (OptionsSetting) setting;
+ for (SiteSetting setting : settings) {
+ if (setting.type == SiteSetting.Type.OPTIONS) {
+
+ // Turn the SiteSetting for a list of options into a proper setting with a
+ // name and a list of options, both given in the SiteSetting.
+ OptionsSetting optionsSetting = (OptionsSetting) setting.setting;
List> items = new ArrayList<>();
- for (Enum anEnum : optionsSetting.getItems()) {
- items.add(new ListSettingView.Item<>(anEnum.name(), anEnum));
+ Enum[] settingItems = optionsSetting.getItems();
+ for (int i = 0; i < settingItems.length; i++) {
+ String name = setting.optionNames.get(i);
+ Enum anEnum = settingItems[i];
+ items.add(new ListSettingView.Item<>(name, anEnum));
}
- String name = optionsSetting.getItems()[0].getDeclaringClass().getSimpleName();
- ListSettingView> v = new ListSettingView(this,
- optionsSetting, name, items);
+ ListSettingView> v = getListSettingView(setting, optionsSetting, items);
group.add(v);
}
@@ -121,6 +126,17 @@ public class SiteSetupController extends SettingsController implements SiteSetup
groups.add(group);
}
+ @SuppressWarnings("unchecked")
+ @NonNull
+ private ListSettingView> getListSettingView(
+ SiteSetting setting,
+ OptionsSetting optionsSetting,
+ List> items) {
+ // we know it's an enum
+ return (ListSettingView>) new ListSettingView(this,
+ optionsSetting, setting.name, items);
+ }
+
@Override
public void showLogin() {
SettingsGroup login = new SettingsGroup(R.string.setup_site_group_login);
diff --git a/Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadLayout.java b/Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadLayout.java
index e8065498..0e4e6668 100644
--- a/Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadLayout.java
+++ b/Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadLayout.java
@@ -472,8 +472,8 @@ public class ThreadLayout extends CoordinatorLayout implements
public void showNewPostsNotification(boolean show, int more) {
if (show) {
if (!threadListLayout.scrolledToBottom()) {
- String text = getContext().getString(R.string.thread_new_posts,
- more, getContext().getResources().getQuantityString(R.plurals.posts, more, more));
+ String text = getContext().getResources()
+ .getQuantityString(R.plurals.thread_new_posts, more, more);
newPostsNotification = Snackbar.make(this, text, Snackbar.LENGTH_LONG);
newPostsNotification.setAction(R.string.thread_new_posts_goto, new OnClickListener() {
diff --git a/Clover/app/src/main/res/layout/controller_save_location.xml b/Clover/app/src/main/res/layout/controller_save_location.xml
index a1a0ccd3..bf19677c 100644
--- a/Clover/app/src/main/res/layout/controller_save_location.xml
+++ b/Clover/app/src/main/res/layout/controller_save_location.xml
@@ -55,7 +55,7 @@ along with this program. If not, see .
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
- android:text="@string/up"
+ android:text="@string/setting_folder_navigate_up"
android:textSize="18sp" />
diff --git a/Clover/app/src/main/res/values/strings.xml b/Clover/app/src/main/res/values/strings.xml
index f41ab367..4b4cf5d5 100644
--- a/Clover/app/src/main/res/values/strings.xml
+++ b/Clover/app/src/main/res/values/strings.xml
@@ -20,7 +20,6 @@ along with this program. If not, see .
Add
Close
Back
- Up
OK
Exit
Delete
@@ -117,6 +116,7 @@ Re-enable this permission in the app settings if you permanently disabled it."
Image count
Newest
Oldest
+ Latest reply
Search
Found %1$d %2$s for "%3$s"
@@ -149,7 +149,10 @@ Re-enable this permission in the app settings if you permanently disabled it."
Retry
Archived
Closed
- %1$d new %2$s
+
+ - %d new post
+ - %d new posts
+
View
Please select a thread
Scroll to top/bottom
@@ -296,7 +299,7 @@ Re-enable this permission in the app settings if you permanently disabled it."
Error posting: %s
Post successful
Type the text
- >
+ >
[s]
Delete your post?
@@ -522,6 +525,7 @@ Re-enable this permission in the app settings if you permanently disabled it."
Re-enable this permission in the app settings if you permanently disabled it."
Choose
+ Up
diff --git a/README.md b/README.md
index 3a0d9487..abb6beb4 100644
--- a/README.md
+++ b/README.md
@@ -1,33 +1,31 @@
-# Clover - 4chan browser for Android
+# Clover - imageboard browser for Android
-Clover is a fast Android app for browsing [4chan](https://www.4chan.org/).
-Clover adds inline replying, thread watching, notifications, themes, pass support, filters and a whole lot more. Clover is licensed under the GPL and will always be free.
+[](https://travis-ci.org/Floens/Clover)
+[](https://crowdin.com/project/clover)
-[Join the Slack!](https://join.slack.com/t/uchan/shared_invite/enQtMjkyOTM3NDczNTcxLTNkMzljNDUyNjkzNjEwOTNkZTljZWQ3ZDNmNWUyMTY2YTAwNzBhNmI3YTg1YmNjMDQxZTgzMTM2YzE2YzRhMGI)
-[IRC](https://webchat.freenode.net/?url=irc:///#Clover)
-[Clover website](http://floens.github.io/Clover/)
-[Clover dev builds](https://floens.github.io/Clover/#dev)
-[F-Droid](https://floens.github.io/Clover/#fdroid)
-[APK releases](https://floens.github.io/Clover/#releases)
+[Website](http://floens.github.io/Clover/)
+[APK releases](https://floens.github.io/Clover/#releases) | [Development APK releases](https://floens.github.io/Clover/#dev) | [F-Droid](https://floens.github.io/Clover/#fdroid)
+[Telegram](https://t.me/cloverapp) | [Slack](https://join.slack.com/t/uchan/shared_invite/enQtMjkyOTM3NDczNTcxLTNkMzljNDUyNjkzNjEwOTNkZTljZWQ3ZDNmNWUyMTY2YTAwNzBhNmI3YTg1YmNjMDQxZTgzMTM2YzE2YzRhMGI) | [IRC](https://webchat.freenode.net/?url=irc:///#Clover)
[Donate](https://floens.github.io/Clover/#donate)
+Clover is a fast Android app for browsing imageboards, such as 4chan and 8chan. It adds inline replying, thread watching, notifications, themes, pass support, filters and a whole lot more. Clover is licensed under the GPL and will always be free.
## Issues and features
-Issues can be reported at the [Slack](https://join.slack.com/t/uchan/shared_invite/enQtMjkyOTM3NDczNTcxLTNkMzljNDUyNjkzNjEwOTNkZTljZWQ3ZDNmNWUyMTY2YTAwNzBhNmI3YTg1YmNjMDQxZTgzMTM2YzE2YzRhMGI) or here at the [Github Issues page](https://github.com/Floens/Clover/issues). Please search before reporting an issue to avoid duplicates!
-
-Also take a look at the [Trello board](https://trello.com/b/V6gclKvM/clover) to see if your issue or feature has already been considered.
+Issues can be reported at one of the contact places noted above or here at the [Issues page](https://github.com/Floens/Clover/issues). Please search before reporting an issue to avoid duplicates!
## Contributing
-Contributing to Clover is appreciated, there's always stuff to do or bugs to fix. I keep the todo list of Clover at the
-[Clover Trello board](https://trello.com/b/V6gclKvM/clover).
-[Make a new theme](https://github.com/Floens/Clover/wiki/Adding-a-new-theme)
-
+For first-time contributors, the issues with the label [good first issue](https://github.com/Floens/Clover/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) are a great place to start.
-## Building Clover
See the [Clover setup guide](https://github.com/Floens/Clover/wiki/Building-Clover) for a guide on building Clover.
+We have a spacial guide for [making new themes](https://github.com/Floens/Clover/wiki/Adding-a-new-theme)
+
+
+## Translations
+We use crowdin for crowdsourcing translations of the English strings to other languages.
+[Help us with translating at crowdin.com here](https://crowdin.com/project/clover)
+
## License
-* Clover is [GPLv3](https://github.com/Floens/Clover/blob/master/COPYING.txt)
-* [Licenses of the used libraries](https://github.com/Floens/Clover/blob/dev/Clover/app/src/main/assets/html/licenses.html).
+Clover is [GPLv3](https://github.com/Floens/Clover/blob/master/COPYING.txt), [licenses of the used libraries](https://github.com/Floens/Clover/blob/dev/Clover/app/src/main/assets/html/licenses.html).