diff --git a/Clover/app/build.gradle b/Clover/app/build.gradle index b1923d3f..05075bfa 100644 --- a/Clover/app/build.gradle +++ b/Clover/app/build.gradle @@ -71,12 +71,12 @@ android { } dependencies { - compile 'com.android.support:support-v13:22.2.0' - compile 'com.android.support:appcompat-v7:22.2.0' - compile 'com.android.support:recyclerview-v7:22.2.0' - compile 'com.android.support:cardview-v7:22.2.0' - compile 'com.android.support:support-annotations:22.2.0' - compile 'com.android.support:design:22.2.0' + compile 'com.android.support:support-v13:22.2.1' + compile 'com.android.support:appcompat-v7:22.2.1' + compile 'com.android.support:recyclerview-v7:22.2.1' + compile 'com.android.support:cardview-v7:22.2.1' + compile 'com.android.support:support-annotations:22.2.1' + compile 'com.android.support:design:22.2.1' compile 'org.jsoup:jsoup:1.8.2' compile 'com.j256.ormlite:ormlite-core:4.48' diff --git a/Clover/app/src/main/java/org/floens/chan/chan/ChanLoader.java b/Clover/app/src/main/java/org/floens/chan/chan/ChanLoader.java index 127dca1f..13250ff0 100644 --- a/Clover/app/src/main/java/org/floens/chan/chan/ChanLoader.java +++ b/Clover/app/src/main/java/org/floens/chan/chan/ChanLoader.java @@ -268,10 +268,13 @@ public class ChanLoader implements Response.ErrorListener, Response.Listener posts = thread.posts; + for (int i = 0; i < posts.size(); i++) { + Post sourcePost = posts.get(i); - for (Post replyToSource : thread.posts) { + for (int j = i; j < posts.size(); j++) { + Post replyToSource = posts.get(j); if (replyToSource != sourcePost) { if (replyToSource.repliesTo.contains(sourcePost.no)) { sourcePost.repliesFrom.add(replyToSource.no); @@ -279,6 +282,8 @@ public class ChanLoader implements Response.ErrorListener, Response.Listener. + */ +package org.floens.chan.ui.dialog; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.RectF; +import android.graphics.Shader; +import android.graphics.SweepGradient; +import android.support.annotation.NonNull; +import android.view.MotionEvent; +import android.view.View; + +import static org.floens.chan.utils.AndroidUtils.dp; + +public class ColorPickerView extends View { + private static final int[] COLORS = new int[]{ + 0xFFFF0000, 0xFFFF00FF, 0xFF0000FF, 0xFF00FFFF, 0xFF00FF00, + 0xFFFFFF00, 0xFFFF0000 + }; + + private Paint paint; + private Paint centerPaint; + private int centerRadius; + + public ColorPickerView(Context context) { + super(context); + + centerRadius = dp(32); + + Shader s = new SweepGradient(0, 0, COLORS, null); + + paint = new Paint(Paint.ANTI_ALIAS_FLAG); + paint.setShader(s); + paint.setStyle(Paint.Style.STROKE); + paint.setStrokeWidth(dp(32)); + + centerPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + centerPaint.setStrokeWidth(dp(5)); + } + + public void setColor(int color) { + centerPaint.setColor(color); + } + + public int getColor() { + return centerPaint.getColor(); + } + + @Override + public boolean onTouchEvent(@NonNull MotionEvent event) { + float x = event.getX() - getWidth() / 2f; + float y = event.getY() - getHeight() / 2f; + + switch (event.getAction()) { + case MotionEvent.ACTION_MOVE: + float angle = (float) Math.atan2(y, x); + // need to turn angle [-PI ... PI] into unit [0....1] + float unit = (float) (angle / (2.0 * Math.PI)); + if (unit < 0.0) { + unit += 1.0; + } + centerPaint.setColor(interpColor(COLORS, unit)); + invalidate(); + break; + } + return true; + } + + @Override + protected void onDraw(Canvas canvas) { + float r = Math.min(getWidth() / 2f, getHeight() / 2f) - paint.getStrokeWidth() * 0.5f; + canvas.translate(getWidth() / 2f, getHeight() / 2f); + canvas.drawOval(new RectF(-r, -r, r, r), paint); + canvas.drawCircle(0, 0, centerRadius, centerPaint); + } + + private int interpColor(int colors[], float unit) { + if (unit <= 0) { + return colors[0]; + } + if (unit >= 1) { + return colors[colors.length - 1]; + } + + float p = unit * (colors.length - 1); + int i = (int) p; + p -= i; + + // now p is just the fractional part [0...1) and i is the index + int c0 = colors[i]; + int c1 = colors[i + 1]; + int a = ave(Color.alpha(c0), Color.alpha(c1), p); + int r = ave(Color.red(c0), Color.red(c1), p); + int g = ave(Color.green(c0), Color.green(c1), p); + int b = ave(Color.blue(c0), Color.blue(c1), p); + + return Color.argb(a, r, g, b); + } + + private int ave(int s, int d, float p) { + return s + Math.round(p * (d - s)); + } +} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/layout/FilterLayout.java b/Clover/app/src/main/java/org/floens/chan/ui/layout/FilterLayout.java index f39a28a1..3d7ceeac 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/layout/FilterLayout.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/layout/FilterLayout.java @@ -19,13 +19,22 @@ package org.floens.chan.ui.layout; import android.content.Context; import android.content.DialogInterface; +import android.graphics.Typeface; import android.support.v7.app.AlertDialog; +import android.text.Editable; +import android.text.Html; +import android.text.SpannableStringBuilder; import android.text.TextUtils; +import android.text.TextWatcher; +import android.text.style.BackgroundColorSpan; +import android.text.style.StyleSpan; +import android.text.style.TypefaceSpan; import android.util.AttributeSet; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.widget.CheckBox; +import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; @@ -35,6 +44,7 @@ import org.floens.chan.core.manager.BoardManager; import org.floens.chan.core.manager.FilterEngine; import org.floens.chan.core.model.Board; import org.floens.chan.core.model.Filter; +import org.floens.chan.ui.dialog.ColorPickerView; import org.floens.chan.ui.drawable.DropdownArrowDrawable; import org.floens.chan.ui.view.FloatingMenu; import org.floens.chan.ui.view.FloatingMenuItem; @@ -42,19 +52,27 @@ import org.floens.chan.ui.view.FloatingMenuItem; import java.util.ArrayList; import java.util.List; +import static org.floens.chan.ui.theme.ThemeHelper.theme; import static org.floens.chan.utils.AndroidUtils.dp; import static org.floens.chan.utils.AndroidUtils.getAttrColor; import static org.floens.chan.utils.AndroidUtils.getString; -public class FilterLayout extends LinearLayout implements View.OnClickListener, FloatingMenu.FloatingMenuCallback { +public class FilterLayout extends LinearLayout implements View.OnClickListener { private TextView typeText; private TextView boardsSelector; + private boolean patternContainerErrorShowing = false; private TextView pattern; + private TextView patternPreview; + private TextView patternPreviewStatus; private CheckBox enabled; - private CheckBox hide; + private ImageView help; + private TextView actionText; + private LinearLayout colorContainer; + private View colorPreview; private BoardManager boardManager; + private FilterLayoutCallback callback; private Filter filter = new Filter(); private List appliedBoards = new ArrayList<>(); @@ -79,9 +97,49 @@ public class FilterLayout extends LinearLayout implements View.OnClickListener, typeText = (TextView) findViewById(R.id.type); boardsSelector = (TextView) findViewById(R.id.boards); + actionText = (TextView) findViewById(R.id.action); pattern = (TextView) findViewById(R.id.pattern); + pattern.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + filter.compiledPattern = null; + filter.pattern = s.toString(); + updateFilterValidity(); + updatePatternPreview(); + } + + @Override + public void afterTextChanged(Editable s) { + } + }); + patternPreview = (TextView) findViewById(R.id.pattern_preview); + patternPreview.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + updatePatternPreview(); + } + + @Override + public void afterTextChanged(Editable s) { + } + }); + patternPreviewStatus = (TextView) findViewById(R.id.pattern_preview_status); enabled = (CheckBox) findViewById(R.id.enabled); - hide = (CheckBox) findViewById(R.id.hide); + help = (ImageView) findViewById(R.id.help); + theme().helpDrawable.apply(help); + help.setOnClickListener(this); + colorContainer = (LinearLayout) findViewById(R.id.color_container); + colorContainer.setOnClickListener(this); + colorPreview = findViewById(R.id.color_preview); + typeText.setOnClickListener(this); typeText.setCompoundDrawablesWithIntrinsicBounds(null, null, new DropdownArrowDrawable(dp(12), dp(12), true, getAttrColor(getContext(), R.attr.dropdown_dark_color), getAttrColor(getContext(), R.attr.dropdown_dark_pressed_color)), null); @@ -90,12 +148,15 @@ public class FilterLayout extends LinearLayout implements View.OnClickListener, boardsSelector.setCompoundDrawablesWithIntrinsicBounds(null, null, new DropdownArrowDrawable(dp(12), dp(12), true, getAttrColor(getContext(), R.attr.dropdown_dark_color), getAttrColor(getContext(), R.attr.dropdown_dark_pressed_color)), null); - update(); + actionText.setOnClickListener(this); + actionText.setCompoundDrawablesWithIntrinsicBounds(null, null, new DropdownArrowDrawable(dp(12), dp(12), true, + getAttrColor(getContext(), R.attr.dropdown_dark_color), getAttrColor(getContext(), R.attr.dropdown_dark_pressed_color)), null); } public void setFilter(Filter filter) { this.filter.apply(filter); appliedBoards.clear(); + if (filter.allBoards) { appliedBoards.addAll(boardManager.getSavedBoards()); } else if (!TextUtils.isEmpty(filter.boards)) { @@ -106,12 +167,22 @@ public class FilterLayout extends LinearLayout implements View.OnClickListener, } } } - update(); + + pattern.setText(filter.pattern); + + updateFilterValidity(); + updateCheckboxes(); + updateFilterType(); + updateFilterAction(); + updateBoardsSummary(); + updatePatternPreview(); + } + + public void setCallback(FilterLayoutCallback callback) { + this.callback = callback; } public Filter getFilter() { - filter.pattern = pattern.getText().toString(); - filter.hide = hide.isChecked(); filter.enabled = enabled.isChecked(); filter.boards = ""; @@ -129,7 +200,7 @@ public class FilterLayout extends LinearLayout implements View.OnClickListener, @Override public void onClick(View v) { if (v == typeText) { - List menuItems = new ArrayList<>(2); + List menuItems = new ArrayList<>(6); for (FilterEngine.FilterType filterType : FilterEngine.FilterType.values()) { menuItems.add(new FloatingMenuItem(filterType, filterTypeName(filterType))); @@ -138,7 +209,19 @@ public class FilterLayout extends LinearLayout implements View.OnClickListener, FloatingMenu menu = new FloatingMenu(v.getContext()); menu.setAnchor(v, Gravity.LEFT, -dp(5), -dp(5)); menu.setPopupWidth(dp(150)); - menu.setCallback(this); + menu.setCallback(new FloatingMenu.FloatingMenuCallback() { + @Override + public void onFloatingMenuItemClicked(FloatingMenu menu, FloatingMenuItem item) { + FilterEngine.FilterType type = (FilterEngine.FilterType) item.getId(); + filter.type = type.id; + updateFilterType(); + updatePatternPreview(); + } + + @Override + public void onFloatingMenuDismissed(FloatingMenu menu) { + } + }); menu.setItems(menuItems); menu.show(); } else if (v == boardsSelector) { @@ -151,32 +234,103 @@ public class FilterLayout extends LinearLayout implements View.OnClickListener, .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - setCheckedBoards(boardSelectLayout.getCheckedBoards(), boardSelectLayout.getAllChecked()); - update(); + appliedBoards.clear(); + appliedBoards.addAll(boardSelectLayout.getCheckedBoards()); + filter.allBoards = boardSelectLayout.getAllChecked(); + updateBoardsSummary(); + } + }) + .show(); + } else if (v == actionText) { + List menuItems = new ArrayList<>(6); + + for (FilterEngine.FilterAction action : FilterEngine.FilterAction.values()) { + menuItems.add(new FloatingMenuItem(action, actionName(action))); + } + + FloatingMenu menu = new FloatingMenu(v.getContext()); + menu.setAnchor(v, Gravity.LEFT, -dp(5), -dp(5)); + menu.setPopupWidth(dp(150)); + menu.setCallback(new FloatingMenu.FloatingMenuCallback() { + @Override + public void onFloatingMenuItemClicked(FloatingMenu menu, FloatingMenuItem item) { + FilterEngine.FilterAction action = (FilterEngine.FilterAction) item.getId(); + filter.action = action.id; + updateFilterAction(); + } + + @Override + public void onFloatingMenuDismissed(FloatingMenu menu) { + } + }); + menu.setItems(menuItems); + menu.show(); + } else if (v == help) { + SpannableStringBuilder message = (SpannableStringBuilder) Html.fromHtml(getString(R.string.filter_help)); + TypefaceSpan[] typefaceSpans = message.getSpans(0, message.length(), TypefaceSpan.class); + for (TypefaceSpan span : typefaceSpans) { + if (span.getFamily().equals("monospace")) { + int start = message.getSpanStart(span); + int end = message.getSpanEnd(span); + message.setSpan(new BackgroundColorSpan(0x22000000), start, end, 0); + } + } + + StyleSpan[] styleSpans = message.getSpans(0, message.length(), StyleSpan.class); + for (StyleSpan span : styleSpans) { + if (span.getStyle() == Typeface.ITALIC) { + int start = message.getSpanStart(span); + int end = message.getSpanEnd(span); + message.setSpan(new BackgroundColorSpan(0x22000000), start, end, 0); + } + } + + new AlertDialog.Builder(getContext()) + .setTitle(R.string.filter_help_title) + .setMessage(message) + .setPositiveButton(R.string.ok, null) + .show(); + } else if (v == colorContainer) { + final ColorPickerView colorPickerView = new ColorPickerView(getContext()); + colorPickerView.setColor(filter.color); + + AlertDialog dialog = new AlertDialog.Builder(getContext()) + .setTitle(R.string.filter_color_pick) + .setView(colorPickerView) + .setNegativeButton(R.string.cancel, null) + .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + filter.color = colorPickerView.getColor(); + updateFilterAction(); } }) .show(); + dialog.getWindow().setLayout(dp(300), dp(300)); } } - @Override - public void onFloatingMenuItemClicked(FloatingMenu menu, FloatingMenuItem item) { - FilterEngine.FilterType type = (FilterEngine.FilterType) item.getId(); - filter.type = type.id; - update(); - } + private void updateFilterValidity() { + FilterEngine.FilterType filterType = FilterEngine.FilterType.forId(filter.type); - @Override - public void onFloatingMenuDismissed(FloatingMenu menu) { - } + boolean valid; + if (filterType.isRegex) { + valid = FilterEngine.getInstance().compile(filter.pattern) != null; + } else { + valid = !TextUtils.isEmpty(filter.pattern); + } - private void update() { - pattern.setText(filter.pattern); - hide.setChecked(filter.hide); - enabled.setChecked(filter.enabled); + if (valid != patternContainerErrorShowing) { + patternContainerErrorShowing = valid; + pattern.setError(valid ? null : getString(R.string.filter_invalid_pattern)); + } - typeText.setText(filterTypeName(FilterEngine.FilterType.forId(filter.type))); + if (callback != null) { + callback.setSaveButtonEnabled(valid); + } + } + private void updateBoardsSummary() { String text = getString(R.string.filter_boards) + " ("; if (filter.allBoards) { text += getString(R.string.filter_boards_all); @@ -187,10 +341,30 @@ public class FilterLayout extends LinearLayout implements View.OnClickListener, boardsSelector.setText(text); } - private void setCheckedBoards(List checkedBoards, boolean allChecked) { - appliedBoards.clear(); - appliedBoards.addAll(checkedBoards); - filter.allBoards = allChecked; + private void updateCheckboxes() { + enabled.setChecked(filter.enabled); + } + + private void updateFilterAction() { + FilterEngine.FilterAction action = FilterEngine.FilterAction.forId(filter.action); + actionText.setText(actionName(action)); + colorContainer.setVisibility(action == FilterEngine.FilterAction.COLOR ? VISIBLE : GONE); + if (filter.color == 0) { + filter.color = 0xffff0000; + } + colorPreview.setBackgroundColor(filter.color); + } + + private void updateFilterType() { + FilterEngine.FilterType filterType = FilterEngine.FilterType.forId(filter.type); + typeText.setText(filterTypeName(filterType)); + pattern.setHint(filterType.isRegex ? R.string.filter_pattern_hint_regex : R.string.filter_pattern_hint_exact); + } + + private void updatePatternPreview() { + String text = patternPreview.getText().toString(); + boolean matches = text.length() > 0 && FilterEngine.getInstance().matches(filter, text); + patternPreviewStatus.setText(matches ? R.string.filter_matches : R.string.filter_no_matches); } private String filterTypeName(FilterEngine.FilterType type) { @@ -210,4 +384,18 @@ public class FilterLayout extends LinearLayout implements View.OnClickListener, } return null; } + + private String actionName(FilterEngine.FilterAction action) { + switch (action) { + case HIDE: + return getString(R.string.filter_hide); + case COLOR: + return getString(R.string.filter_color); + } + return null; + } + + public interface FilterLayoutCallback { + void setSaveButtonEnabled(boolean enabled); + } } 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 59daf19a..0217cced 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 @@ -419,6 +419,7 @@ public class ThreadLayout extends CoordinatorLayout implements ThreadPresenter.T private void showReplyButton(boolean show) { if (show != showingReplyButton) { showingReplyButton = show; + replyButton.animate() .setInterpolator(new DecelerateInterpolator(2f)) .setStartDelay(show ? 100 : 0) diff --git a/Clover/app/src/main/java/org/floens/chan/ui/theme/DarkTheme.java b/Clover/app/src/main/java/org/floens/chan/ui/theme/DarkTheme.java index 9598cfcd..9ece2259 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/theme/DarkTheme.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/theme/DarkTheme.java @@ -18,5 +18,6 @@ public class DarkTheme extends Theme { doneDrawable = new ThemeDrawable(R.drawable.ic_done_white_24dp, 1f); historyDrawable = new ThemeDrawable(R.drawable.ic_history_white_24dp, 1f); listAddDrawable = new ThemeDrawable(R.drawable.ic_playlist_add_white_24dp, 1f); + helpDrawable = new ThemeDrawable(R.drawable.ic_help_outline_white_24dp, 1f); } } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/theme/Theme.java b/Clover/app/src/main/java/org/floens/chan/ui/theme/Theme.java index ffc4eb3a..b6c2b406 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/theme/Theme.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/theme/Theme.java @@ -63,6 +63,7 @@ public class Theme { public ThemeDrawable doneDrawable; public ThemeDrawable historyDrawable; public ThemeDrawable listAddDrawable; + public ThemeDrawable helpDrawable; public Theme(String displayName, String name, int resValue, ThemeHelper.PrimaryColor primaryColor) { this.displayName = displayName; @@ -83,6 +84,7 @@ public class Theme { doneDrawable = new ThemeDrawable(R.drawable.ic_done_black_24dp, 0.54f); historyDrawable = new ThemeDrawable(R.drawable.ic_history_black_24dp, 0.54f); listAddDrawable = new ThemeDrawable(R.drawable.ic_playlist_add_black_24dp, 0.54f); + helpDrawable = new ThemeDrawable(R.drawable.ic_help_outline_black_24dp, 0.54f); } private void resolveSpanColors() { diff --git a/Clover/app/src/main/res/drawable-hdpi/ic_help_outline_black_24dp.png b/Clover/app/src/main/res/drawable-hdpi/ic_help_outline_black_24dp.png new file mode 100644 index 00000000..314154a0 Binary files /dev/null and b/Clover/app/src/main/res/drawable-hdpi/ic_help_outline_black_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-hdpi/ic_help_outline_white_24dp.png b/Clover/app/src/main/res/drawable-hdpi/ic_help_outline_white_24dp.png new file mode 100644 index 00000000..4bc2a963 Binary files /dev/null and b/Clover/app/src/main/res/drawable-hdpi/ic_help_outline_white_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-mdpi/ic_help_outline_black_24dp.png b/Clover/app/src/main/res/drawable-mdpi/ic_help_outline_black_24dp.png new file mode 100644 index 00000000..9eb8f36c Binary files /dev/null and b/Clover/app/src/main/res/drawable-mdpi/ic_help_outline_black_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-mdpi/ic_help_outline_white_24dp.png b/Clover/app/src/main/res/drawable-mdpi/ic_help_outline_white_24dp.png new file mode 100644 index 00000000..1e5dde76 Binary files /dev/null and b/Clover/app/src/main/res/drawable-mdpi/ic_help_outline_white_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-xhdpi/ic_help_outline_black_24dp.png b/Clover/app/src/main/res/drawable-xhdpi/ic_help_outline_black_24dp.png new file mode 100644 index 00000000..5fa59ee2 Binary files /dev/null and b/Clover/app/src/main/res/drawable-xhdpi/ic_help_outline_black_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-xhdpi/ic_help_outline_white_24dp.png b/Clover/app/src/main/res/drawable-xhdpi/ic_help_outline_white_24dp.png new file mode 100644 index 00000000..e10918b8 Binary files /dev/null and b/Clover/app/src/main/res/drawable-xhdpi/ic_help_outline_white_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-xxhdpi/ic_help_outline_black_24dp.png b/Clover/app/src/main/res/drawable-xxhdpi/ic_help_outline_black_24dp.png new file mode 100644 index 00000000..dc5cdeeb Binary files /dev/null and b/Clover/app/src/main/res/drawable-xxhdpi/ic_help_outline_black_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-xxhdpi/ic_help_outline_white_24dp.png b/Clover/app/src/main/res/drawable-xxhdpi/ic_help_outline_white_24dp.png new file mode 100644 index 00000000..e9585a04 Binary files /dev/null and b/Clover/app/src/main/res/drawable-xxhdpi/ic_help_outline_white_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-xxxhdpi/ic_help_outline_black_24dp.png b/Clover/app/src/main/res/drawable-xxxhdpi/ic_help_outline_black_24dp.png new file mode 100644 index 00000000..2d3937d0 Binary files /dev/null and b/Clover/app/src/main/res/drawable-xxxhdpi/ic_help_outline_black_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-xxxhdpi/ic_help_outline_white_24dp.png b/Clover/app/src/main/res/drawable-xxxhdpi/ic_help_outline_white_24dp.png new file mode 100644 index 00000000..0f88a236 Binary files /dev/null and b/Clover/app/src/main/res/drawable-xxxhdpi/ic_help_outline_white_24dp.png differ diff --git a/Clover/app/src/main/res/layout/layout_filter.xml b/Clover/app/src/main/res/layout/layout_filter.xml index df327d46..f8cbcec0 100644 --- a/Clover/app/src/main/res/layout/layout_filter.xml +++ b/Clover/app/src/main/res/layout/layout_filter.xml @@ -16,6 +16,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . --> . + + + + + + + + + + + + . + + + android:layout_height="wrap_content" + android:orientation="horizontal"> - + android:drawablePadding="8dp" + android:paddingBottom="4dp" + android:paddingLeft="4dp" + android:paddingTop="4dp" /> - + android:orientation="horizontal"> + + + + + + + + + + + + diff --git a/Clover/app/src/main/res/values/strings.xml b/Clover/app/src/main/res/values/strings.xml index c0e4ae3e..ec777fe5 100644 --- a/Clover/app/src/main/res/values/strings.xml +++ b/Clover/app/src/main/res/values/strings.xml @@ -118,10 +118,45 @@ along with this program. If not, see . Sort A-Z Enabled + Filter + Action Pattern + Test pattern + Exact text + Pattern Boards all - Hide + Hide post + Highlight + Invalid pattern + Test your filter + Does not match + Matches + Pick color + Filter help + + "Filters act on a given pattern and a place to search the pattern.<br> +If the pattern matches then the post can be hidden or highlighted.<br> + +<h4>For tripcodes, names and IDs:</h4> +<p> + It will match the given pattern exact.<br> + <tt>!Ep8pui8Vw2</tt> will match the tripcode <i>!Ep8pui8Vw2</i> but not <i>Ep8pu</i>. +</p> + +<h4>For comments, subjects and filenames:</h4> +<p> + These filters are pattern based, and have three modes:<br> + <br> + 1. The pattern <tt>foo bar</tt> will match text that has any of the words in it. It will match <i>foo</i> or <i>bar</i>, but not <i>foobar</i>. + Placing a * allows any character to be filled in: <tt>f*o</tt> will match both <i>foo</i>, <i>foooo</i> but not <i>foobar</i><br> + <br> + 2. Quoting your pattern with <tt>\"</tt> like <tt>\"foo bar\"</tt> will match the text exact. + <i>foo bar</i> matches but <i>foo</i> does not.<br> + <br> + 3. Regular expressions. <tt>/^>implying/</tt> for example. +</p>" + Tripcode Name