Merge branch 'dev' into dev_i10n

dev_i10n
Floens 7 years ago
commit 955b853725
  1. 3
      .travis.yml
  2. 5
      Clover/app/src/main/AndroidManifest.xml
  3. 38
      Clover/app/src/main/java/org/floens/chan/core/model/Post.java
  4. 6
      Clover/app/src/main/java/org/floens/chan/core/presenter/SiteSetupPresenter.java
  5. 5
      Clover/app/src/main/java/org/floens/chan/core/site/Site.java
  6. 3
      Clover/app/src/main/java/org/floens/chan/core/site/SiteBase.java
  7. 51
      Clover/app/src/main/java/org/floens/chan/core/site/SiteSetting.java
  8. 4
      Clover/app/src/main/java/org/floens/chan/core/site/common/FutabaChanReader.java
  9. 4
      Clover/app/src/main/java/org/floens/chan/core/site/common/vichan/VichanApi.java
  10. 1
      Clover/app/src/main/java/org/floens/chan/core/site/loader/ChanThreadLoader.java
  11. 12
      Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4.java
  12. 41
      Clover/app/src/main/java/org/floens/chan/ui/adapter/PostsFilter.java
  13. 1
      Clover/app/src/main/java/org/floens/chan/ui/controller/ArchiveController.java
  14. 3
      Clover/app/src/main/java/org/floens/chan/ui/controller/BrowseController.java
  15. 36
      Clover/app/src/main/java/org/floens/chan/ui/controller/SiteSetupController.java
  16. 4
      Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadLayout.java
  17. 2
      Clover/app/src/main/res/layout/controller_save_location.xml
  18. 10
      Clover/app/src/main/res/values/strings.xml
  19. 36
      README.md

@ -11,3 +11,6 @@ android:
- android-27
script: cd Clover && ./gradlew build --console plain -x lint
notifications:
email: false

@ -16,6 +16,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="org.floens.chan"
android:installLocation="auto">
@ -23,7 +24,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission
android:name="android.permission.WAKE_LOCK"
tools:node="replace" />
<application
android:name=".ChanApplication"

@ -28,8 +28,6 @@ import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import okhttp3.HttpUrl;
/**
* Contains all data needed to represent a single post.<br>
* All {@code final} fields are thread-safe.
@ -99,12 +97,13 @@ public class Post {
public final List<Integer> 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<PostImage> images;
public String countryCode;
public String countryName;
public HttpUrl countryUrl;
public List<PostHttpIcon> 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;

@ -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<Setting<?>> settings = site.settings();
List<SiteSetting> settings = site.settings();
if (!settings.isEmpty()) {
callback.showSettings(settings);
}
@ -57,6 +57,6 @@ public class SiteSetupPresenter {
void setIsLoggedIn(boolean isLoggedIn);
void showSettings(List<Setting<?>> settings);
void showSettings(List<SiteSetting> settings);
}
}

@ -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<Setting<?>> settings();
List<SiteSetting> settings();
SiteEndpoints endpoints();

@ -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<Setting<?>> settings() {
public List<SiteSetting> settings() {
return new ArrayList<>();
}

@ -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 <http://www.gnu.org/licenses/>.
*/
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<String> 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<String> optionNames) {
SiteSetting setting = new SiteSetting(name, Type.OPTIONS, options);
setting.optionNames = optionNames;
return setting;
}
}

@ -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);
}

@ -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);
}

@ -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!");
}

@ -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<Setting<?>> settings() {
public List<SiteSetting> settings() {
return Arrays.asList(
captchaType
SiteSetting.forOption(
captchaType,
"Captcha type",
Arrays.asList("Javascript", "Noscript"))
);
}

@ -35,33 +35,20 @@ import javax.inject.Inject;
import static org.floens.chan.Chan.inject;
public class PostsFilter {
public static final Comparator<Post> IMAGE_COMPARATOR = new Comparator<Post>() {
@Override
public int compare(Post lhs, Post rhs) {
return rhs.getImagesCount() - lhs.getImagesCount();
}
};
private static final Comparator<Post> IMAGE_COMPARATOR =
(lhs, rhs) -> rhs.getImagesCount() - lhs.getImagesCount();
public static final Comparator<Post> REPLY_COMPARATOR = new Comparator<Post>() {
@Override
public int compare(Post lhs, Post rhs) {
return rhs.getReplies() - lhs.getReplies();
}
};
private static final Comparator<Post> REPLY_COMPARATOR =
(lhs, rhs) -> rhs.getReplies() - lhs.getReplies();
public static final Comparator<Post> NEWEST_COMPARATOR = new Comparator<Post>() {
@Override
public int compare(Post lhs, Post rhs) {
return (int) (rhs.time - lhs.time);
}
};
private static final Comparator<Post> NEWEST_COMPARATOR =
(lhs, rhs) -> (int) (rhs.time - lhs.time);
public static final Comparator<Post> OLDEST_COMPARATOR = new Comparator<Post>() {
@Override
public int compare(Post lhs, Post rhs) {
return (int) (lhs.time - rhs.time);
}
};
private static final Comparator<Post> OLDEST_COMPARATOR =
(lhs, rhs) -> (int) (lhs.time - rhs.time);
private static final Comparator<Post> 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;

@ -115,6 +115,7 @@ public class ArchiveController extends Controller implements ArchivePresenter.Ca
@Override
public void onSearchEntered(String entered) {
presenter.onSearchEntered(entered);
}
@Override

@ -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);

@ -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<Setting<?>> settings) {
public void showSettings(List<SiteSetting> 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<ListSettingView.Item<Enum>> 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<ListSettingView.Item<Enum>> 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);

@ -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() {

@ -55,7 +55,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
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" />
</LinearLayout>

@ -20,7 +20,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<string name="add">Add</string>
<string name="close">Close</string>
<string name="back">Back</string>
<string name="up">Up</string>
<string name="ok">OK</string>
<string name="exit">Exit</string>
<string name="delete">Delete</string>
@ -117,6 +116,7 @@ Re-enable this permission in the app settings if you permanently disabled it."</
<string name="order_image">Image count</string>
<string name="order_newest">Newest</string>
<string name="order_oldest">Oldest</string>
<string name="order_modified">Latest reply</string>
<string name="search_hint">Search</string>
<string name="search_results">Found %1$d %2$s for "%3$s"</string>
@ -149,7 +149,10 @@ Re-enable this permission in the app settings if you permanently disabled it."</
<string name="thread_load_failed_retry">Retry</string>
<string name="thread_archived">Archived</string>
<string name="thread_closed">Closed</string>
<string name="thread_new_posts">%1$d new %2$s</string>
<plurals name="thread_new_posts">
<item quantity="one">%d new post</item>
<item quantity="other">%d new posts</item>
</plurals>
<string name="thread_new_posts_goto">View</string>
<string name="thread_empty_select">Please select a thread</string>
<string name="thread_up_down_hint">Scroll to top/bottom</string>
@ -296,7 +299,7 @@ Re-enable this permission in the app settings if you permanently disabled it."</
<string name="reply_error_message">Error posting: %s</string>
<string name="reply_success">Post successful</string>
<string name="reply_captcha_text">Type the text</string>
<string name="reply_comment_button_quote">&gt;</string>
<string name="reply_comment_button_quote" translatable="false">&gt;</string>
<string name="reply_comment_button_spoiler">[s]</string>
<string name="delete_confirm">Delete your post?</string>
@ -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."</string>
<string name="setting_folder_pick_ok">Choose</string>
<string name="setting_folder_navigate_up">Up</string>
<!-- Theme settings -->

@ -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.
[![Build Status](https://travis-ci.org/Floens/Clover.svg?branch=dev)](https://travis-ci.org/Floens/Clover)
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/clover/localized.svg)](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).

Loading…
Cancel
Save