setup flow: delete introduction controllers, new empty layout flow.

instead of immediately guiding the user to the sites setup, show the
empty state first and guide them towards the site setup.

the introduction controllers were a big buggy anyway, and this should
help getting the context of the app right the first time.
also rework some strings to be friendlier and less redundant.
crowdin
Floens 7 years ago
parent 2b6c0c299a
commit 0bdf4b7296
  1. 38
      Clover/app/src/main/java/org/floens/chan/core/presenter/SetupPresenter.java
  2. 25
      Clover/app/src/main/java/org/floens/chan/core/presenter/SitesSetupPresenter.java
  3. 6
      Clover/app/src/main/java/org/floens/chan/core/presenter/ThreadPresenter.java
  4. 11
      Clover/app/src/main/java/org/floens/chan/ui/activity/StartActivity.java
  5. 34
      Clover/app/src/main/java/org/floens/chan/ui/controller/BrowseController.java
  6. 51
      Clover/app/src/main/java/org/floens/chan/ui/controller/IntroController.java
  7. 84
      Clover/app/src/main/java/org/floens/chan/ui/controller/SetupController.java
  8. 49
      Clover/app/src/main/java/org/floens/chan/ui/controller/SitesSetupController.java
  9. 4
      Clover/app/src/main/java/org/floens/chan/ui/controller/ThreadController.java
  10. 6
      Clover/app/src/main/java/org/floens/chan/ui/drawable/DropdownArrowDrawable.java
  11. 44
      Clover/app/src/main/java/org/floens/chan/ui/helper/HintPopup.java
  12. 1
      Clover/app/src/main/java/org/floens/chan/ui/layout/SiteAddLayout.java
  13. 22
      Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadLayout.java
  14. 29
      Clover/app/src/main/java/org/floens/chan/ui/toolbar/ToolbarContainer.java
  15. BIN
      Clover/app/src/main/res/drawable-xxhdpi/logo.png
  16. 90
      Clover/app/src/main/res/layout/controller_intro.xml
  17. 61
      Clover/app/src/main/res/layout/layout_empty_setup.xml
  18. 1
      Clover/app/src/main/res/layout/popup_hint.xml
  19. 1
      Clover/app/src/main/res/layout/popup_hint_top.xml
  20. 20
      Clover/app/src/main/res/values/strings.xml

@ -1,38 +0,0 @@
/*
* 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.presenter;
public class SetupPresenter {
private Callback callback;
public void create(Callback callback) {
this.callback = callback;
callback.moveToIntro();
}
public void startClicked() {
callback.moveToSiteSetup();
}
public interface Callback {
void moveToIntro();
void moveToSiteSetup();
}
}

@ -59,10 +59,8 @@ public class SitesSetupPresenter implements Observer {
updateSitesInUi();
this.callback.setNextAllowed(!sitesShown.isEmpty());
if (sitesShown.isEmpty()) {
callback.presentIntro();
if (sites.getAll().isEmpty()) {
callback.showHint();
}
}
@ -90,12 +88,6 @@ public class SitesSetupPresenter implements Observer {
updateSitesInUi();
}
public void onIntroDismissed() {
if (sitesShown.isEmpty()) {
callback.showHint();
}
}
public void bindAddDialog(AddCallback addCallback) {
this.addCallback = addCallback;
}
@ -104,10 +96,6 @@ public class SitesSetupPresenter implements Observer {
this.addCallback = null;
}
public boolean mayExit() {
return sitesShown.size() > 0;
}
public void onShowDialogClicked() {
callback.showAddDialog();
}
@ -131,16 +119,11 @@ public class SitesSetupPresenter implements Observer {
});
}
public void onDoneClicked() {
}
private void siteAdded(Site site) {
sitesShown.add(site);
saveOrder();
updateSitesInUi();
callback.setNextAllowed(!sitesShown.isEmpty());
}
public void onSiteCellSettingsClicked(Site site) {
@ -172,14 +155,10 @@ public class SitesSetupPresenter implements Observer {
public interface Callback {
void setSites(List<SiteBoardCount> sites);
void presentIntro();
void showHint();
void showAddDialog();
void setNextAllowed(boolean nextAllowed);
void openSiteConfiguration(Site site);
}

@ -102,6 +102,10 @@ public class ThreadPresenter implements ChanThreadLoader.ChanLoaderCallback, Pos
this.threadPresenterCallback = threadPresenterCallback;
}
public void showNoContent() {
threadPresenterCallback.showEmpty();
}
public void bindLoadable(Loadable loadable) {
if (!loadable.equals(this.loadable)) {
if (chanLoader != null) {
@ -751,6 +755,8 @@ public class ThreadPresenter implements ChanThreadLoader.ChanLoaderCallback, Pos
void showLoading();
void showEmpty();
void showPostInfo(String info);
void showPostLinkables(Post post);

@ -50,7 +50,6 @@ import org.floens.chan.core.site.SiteService;
import org.floens.chan.ui.controller.BrowseController;
import org.floens.chan.ui.controller.DoubleNavigationController;
import org.floens.chan.ui.controller.DrawerController;
import org.floens.chan.ui.controller.SitesSetupController;
import org.floens.chan.ui.controller.SplitNavigationController;
import org.floens.chan.ui.controller.StyledToolbarNavigationController;
import org.floens.chan.ui.controller.ThreadSlideController;
@ -158,15 +157,7 @@ public class StartActivity extends AppCompatActivity implements NfcAdapter.Creat
private void restoreFresh() {
if (!siteService.areSitesSetup()) {
SitesSetupController setupController = new SitesSetupController(this);
if (drawerController.childControllers.get(0) instanceof DoubleNavigationController) {
DoubleNavigationController doubleNavigationController =
(DoubleNavigationController) drawerController.childControllers.get(0);
doubleNavigationController.pushController(setupController, false);
} else {
mainNavigationController.pushController(setupController, false);
}
browseController.showSitesNotSetup();
} else {
browseController.loadWithDefaultBoard();
}

@ -35,6 +35,7 @@ import org.floens.chan.core.settings.ChanSettings;
import org.floens.chan.core.site.Site;
import org.floens.chan.ui.adapter.PostsFilter;
import org.floens.chan.ui.helper.BoardHelper;
import org.floens.chan.ui.helper.HintPopup;
import org.floens.chan.ui.layout.BrowseBoardsFloatingMenu;
import org.floens.chan.ui.layout.ThreadLayout;
import org.floens.chan.ui.toolbar.NavigationItem;
@ -92,6 +93,15 @@ public class BrowseController extends ThreadController implements
presenter.destroy();
}
@Override
public void showSitesNotSetup() {
super.showSitesNotSetup();
HintPopup hint = HintPopup.show(context, getToolbar(), R.string.thread_empty_setup_hint);
hint.alignLeft();
hint.wiggle();
}
public void setBoard(Board board) {
presenter.setBoard(board);
}
@ -109,30 +119,6 @@ public class BrowseController extends ThreadController implements
// Toolbar menu
navigation.hasBack = false;
/*search = menu.addItem(new ToolbarMenuItem(context, this, SEARCH_ID, R.drawable.ic_search_white_24dp));
refresh = menu.addItem(new ToolbarMenuItem(context, this, REFRESH_ID, R.drawable.ic_refresh_white_24dp));
// Toolbar overflow
overflow = menu.createOverflow(this);
List<FloatingMenuItem> items = new ArrayList<>();
if (!ChanSettings.enableReplyFab.get()) {
items.add(new FloatingMenuItem(REPLY_ID, R.string.action_reply));
}
viewModeMenuItem = new FloatingMenuItem(VIEW_MODE_ID, postViewMode == ChanSettings.PostViewMode.LIST ?
R.string.action_switch_catalog : R.string.action_switch_board);
items.add(viewModeMenuItem);
archive = new FloatingMenuItem(ARCHIVE_ID, R.string.thread_view_archive);
items.add(archive);
archive.setEnabled(false);
items.add(new FloatingMenuItem(ORDER_ID, R.string.action_order));
items.add(new FloatingMenuItem(OPEN_BROWSER_ID, R.string.action_open_browser));
items.add(new FloatingMenuItem(SHARE_ID, R.string.action_share));
overflow.setSubMenu(new FloatingMenu(context, overflow.getView(), items));*/
NavigationItem.MenuOverflowBuilder overflowBuilder = navigation.buildMenu()
.withItem(R.drawable.ic_search_white_24dp, this::searchClicked)
.withItem(R.drawable.ic_refresh_white_24dp, this::reloadClicked)

@ -1,51 +0,0 @@
/*
* 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.ui.controller;
import android.content.Context;
import android.view.View;
import android.widget.Button;
import org.floens.chan.R;
import org.floens.chan.controller.Controller;
public class IntroController extends Controller implements View.OnClickListener {
private Button start;
public IntroController(Context context) {
super(context);
}
@Override
public void onCreate() {
super.onCreate();
view = inflateRes(R.layout.controller_intro);
start = view.findViewById(R.id.start);
start.setOnClickListener(this);
}
@Override
public void onClick(View v) {
if (v == start) {
((SitesSetupController) presentedByController).onIntroDismissed();
stopPresenting();
}
}
}

@ -1,84 +0,0 @@
/*
* 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.ui.controller;
import android.content.Context;
import android.view.animation.DecelerateInterpolator;
import org.floens.chan.R;
import org.floens.chan.controller.Controller;
import org.floens.chan.controller.transition.FadeInTransition;
import org.floens.chan.core.presenter.SetupPresenter;
import org.floens.chan.ui.theme.ThemeHelper;
import org.floens.chan.ui.toolbar.Toolbar;
public class SetupController extends ToolbarNavigationController implements SetupPresenter.Callback {
private SetupPresenter presenter;
public SetupController(Context context) {
super(context);
}
@Override
public void onCreate() {
super.onCreate();
view = inflateRes(R.layout.controller_navigation_setup);
container = view.findViewById(R.id.container);
toolbar = view.findViewById(R.id.toolbar);
toolbar.setArrowMenuIconShown(false);
toolbar.setBackgroundColor(ThemeHelper.getInstance().getTheme().primaryColor.color);
toolbar.setCallback(new Toolbar.SimpleToolbarCallback() {
@Override
public void onMenuOrBackClicked(boolean isArrow) {
}
});
toolbar.setAlpha(0f);
requireSpaceForToolbar = false;
presenter = new SetupPresenter();
presenter.create(this);
}
public SetupPresenter getPresenter() {
return presenter;
}
@Override
public void moveToIntro() {
replaceController(new IntroController(context), false);
}
@Override
public void moveToSiteSetup() {
SitesSetupController sitesSetupController = new SitesSetupController(context);
sitesSetupController.showDoneCheckmark();
replaceController(sitesSetupController, true);
}
private void replaceController(Controller to, boolean showToolbar) {
if (blockingInput || toolbar.isTransitioning()) return;
boolean animated = getTop() != null;
transition(getTop(), to, true, animated ? new FadeInTransition() : null);
toolbar.animate().alpha(showToolbar ? 1f : 0f)
.setInterpolator(new DecelerateInterpolator(2f)).start();
}
}

@ -68,6 +68,8 @@ public class SitesSetupController extends StyledToolbarNavigationController impl
private RecyclerView sitesRecyclerview;
private FloatingActionButton addButton;
private HintPopup addBoardsHint;
private SitesAdapter sitesAdapter;
private ItemTouchHelper itemTouchHelper;
private List<SiteBoardCount> sites = new ArrayList<>();
@ -143,16 +145,6 @@ public class SitesSetupController extends StyledToolbarNavigationController impl
presenter.destroy();
}
public void showDoneCheckmark() {
navigation.swipeable = false;
navigation.buildMenu()
.withItem(R.drawable.ic_done_white_24dp, (item) -> presenter.onDoneClicked())
.build();
// doneMenuItem.getView().setAlpha(0f);
}
@Override
public void onClick(View v) {
if (v == addButton) {
@ -160,28 +152,11 @@ public class SitesSetupController extends StyledToolbarNavigationController impl
}
}
@Override
public boolean onBack() {
if (presenter.mayExit()) {
return super.onBack();
} else {
return true;
}
}
@Override
public void presentIntro() {
presentController(new IntroController(context), false);
}
public void onIntroDismissed() {
presenter.onIntroDismissed();
}
@Override
public void showHint() {
String s = context.getString(R.string.setup_sites_add_hint);
HintPopup popup = new HintPopup(context, addButton, s, 0, 0, true);
popup.wiggle();
popup.show();
}
@ -226,16 +201,6 @@ public class SitesSetupController extends StyledToolbarNavigationController impl
crossfadeView.toggle(!sites.isEmpty(), true);
}
@Override
public void setNextAllowed(boolean nextAllowed) {
// if (doneMenuItem != null) {
// doneMenuItem.getView().animate().alpha(nextAllowed ? 1f : 0f).start();
// }
if (!nextAllowed) {
navigation.swipeable = false;
}
}
private void onSiteCellSettingsClicked(Site site) {
presenter.onSiteCellSettingsClicked(site);
}
@ -267,6 +232,14 @@ public class SitesSetupController extends StyledToolbarNavigationController impl
String boardsString = context.getResources().getQuantityString(R.plurals.board, boards, boards);
String descriptionText = context.getString(R.string.setup_sites_site_description, boardsString);
holder.description.setText(descriptionText);
if (boards == 0) {
if (addBoardsHint != null) {
addBoardsHint.dismiss();
}
addBoardsHint = HintPopup.show(context, holder.settings, R.string.setup_sites_add_boards_hint);
addBoardsHint.wiggle();
}
}
@Override

@ -103,6 +103,10 @@ public abstract class ThreadController extends Controller implements
EventBus.getDefault().unregister(this);
}
public void showSitesNotSetup() {
threadLayout.getPresenter().showNoContent();
}
public abstract void openPin(Pin pin);
/*

@ -23,6 +23,7 @@ import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
public class DropdownArrowDrawable extends Drawable {
private Paint paint = new Paint();
@ -45,7 +46,7 @@ public class DropdownArrowDrawable extends Drawable {
}
@Override
public void draw(Canvas canvas) {
public void draw(@NonNull Canvas canvas) {
path.rewind();
path.moveTo(0, height / 4);
path.lineTo(width, height / 4);
@ -84,7 +85,9 @@ public class DropdownArrowDrawable extends Drawable {
}
int color = pressed ? pressedColor : this.color;
if (color != paint.getColor()) {
int prevAlpha = paint.getAlpha();
paint.setColor(color);
paint.setAlpha(prevAlpha);
invalidateSelf();
return true;
} else {
@ -100,6 +103,7 @@ public class DropdownArrowDrawable extends Drawable {
@Override
public void setAlpha(int alpha) {
paint.setAlpha(alpha);
invalidateSelf();
}
@Override

@ -17,10 +17,12 @@
*/
package org.floens.chan.ui.helper;
import android.animation.TimeInterpolator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -51,15 +53,18 @@ public class HintPopup {
private TextView textView;
private PopupWindow popupWindow;
private LinearLayout popupView;
private ViewGroup popupView;
private final View anchor;
private String text;
private final int offsetX;
private final int offsetY;
private final boolean top;
private boolean dismissed;
private boolean rightAligned = true;
private boolean wiggle = false;
public HintPopup(Context context, final View anchor, final String text, final int offsetX, final int offsetY, final boolean top) {
public HintPopup(Context context, final View anchor, final String text,
final int offsetX, final int offsetY, final boolean top) {
this.anchor = anchor;
this.text = text;
this.offsetX = offsetX;
@ -71,7 +76,7 @@ public class HintPopup {
@SuppressLint("InflateParams")
private void createView(Context context) {
popupView = (LinearLayout) LayoutInflater.from(context)
popupView = (ViewGroup) LayoutInflater.from(context)
.inflate(top ? R.layout.popup_hint_top : R.layout.popup_hint, null);
textView = popupView.findViewById(R.id.text);
@ -91,15 +96,46 @@ public class HintPopup {
if (!dismissed) {
popupView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
// TODO: cleanup
int xoff = -popupView.getMeasuredWidth() + anchor.getWidth() + offsetX - dp(2);
int xoff;
if (rightAligned) {
xoff = -popupView.getMeasuredWidth() + anchor.getWidth() + offsetX - dp(2);
} else {
xoff = -popupView.getMeasuredWidth() + offsetX - dp(2);
}
int yoff = -dp(25) + offsetY + (top ? -anchor.getHeight() - dp(30) : 0);
popupWindow.showAsDropDown(anchor, xoff, yoff);
if (wiggle) {
TimeInterpolator wiggleInterpolator = input ->
(float) Math.sin(60 * input * 2.0 * Math.PI);
popupView.animate()
.translationY(dp(2))
.setInterpolator(wiggleInterpolator)
.setDuration(60000)
.start();
}
if (!rightAligned) {
View arrow = popupView.findViewById(R.id.arrow);
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) arrow.getLayoutParams();
lp.gravity = Gravity.LEFT;
arrow.setLayoutParams(lp);
}
}
}, 400);
// popupView.postDelayed(popupWindow::dismiss, 5000);
}
public void alignLeft() {
rightAligned = false;
}
public void wiggle() {
wiggle = true;
}
public void dismiss() {
popupWindow.dismiss();
dismissed = true;

@ -35,6 +35,7 @@ public class SiteAddLayout extends LinearLayout implements SitesSetupPresenter.A
urlContainer = findViewById(R.id.url_container);
url = findViewById(R.id.url);
url.setHint(R.string.setup_sites_url_hint);
}
public void setDialog(Dialog dialog) {

@ -70,7 +70,8 @@ import static org.floens.chan.utils.AndroidUtils.fixSnackbarText;
import static org.floens.chan.utils.AndroidUtils.getString;
/**
* Wrapper around ThreadListLayout, so that it cleanly manages between a load bar and the list view.
* Wrapper around ThreadListLayout, so that it cleanly manages between a loading state
* and the recycler view.
*/
public class ThreadLayout extends CoordinatorLayout implements
ThreadPresenter.ThreadPresenterCallback,
@ -78,9 +79,10 @@ public class ThreadLayout extends CoordinatorLayout implements
View.OnClickListener,
ThreadListLayout.ThreadListLayoutCallback {
private enum Visible {
EMPTY,
LOADING,
THREAD,
ERROR;
ERROR
}
@Inject
@ -138,6 +140,9 @@ public class ThreadLayout extends CoordinatorLayout implements
errorText = errorLayout.findViewById(R.id.text);
errorRetryButton = errorLayout.findViewById(R.id.button);
// Inflate empty layout
// View setup
threadListLayout.setCallbacks(presenter, presenter, presenter, presenter, this);
postPopupHelper = new PostPopupHelper(getContext(), presenter, this);
@ -251,6 +256,11 @@ public class ThreadLayout extends CoordinatorLayout implements
switchVisible(Visible.LOADING);
}
@Override
public void showEmpty() {
switchVisible(Visible.EMPTY);
}
public void showPostInfo(String info) {
new AlertDialog.Builder(getContext())
.setTitle(R.string.post_info_title)
@ -538,6 +548,9 @@ public class ThreadLayout extends CoordinatorLayout implements
this.visible = visible;
switch (visible) {
case EMPTY:
loadView.setView(inflateEmptyView());
break;
case LOADING:
View view = loadView.setView(null);
// TODO: cleanup
@ -559,6 +572,11 @@ public class ThreadLayout extends CoordinatorLayout implements
}
}
@SuppressLint("InflateParams")
private View inflateEmptyView() {
return LayoutInflater.from(getContext()).inflate(R.layout.layout_empty_setup, null);
}
@Override
public void presentRepliesController(Controller controller) {
callback.presentRepliesController(controller);

@ -27,7 +27,8 @@ import android.content.Context;
import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.LayoutInflater;
@ -48,6 +49,7 @@ import org.floens.chan.utils.AndroidUtils;
import java.util.HashMap;
import java.util.Map;
import static android.text.TextUtils.isEmpty;
import static org.floens.chan.utils.AndroidUtils.dp;
import static org.floens.chan.utils.AndroidUtils.getAttrColor;
import static org.floens.chan.utils.AndroidUtils.removeFromParentView;
@ -153,7 +155,7 @@ public class ToolbarContainer extends FrameLayout {
titleView.setText(item.title);
}
if (!TextUtils.isEmpty(item.subtitle)) {
if (!isEmpty(item.subtitle)) {
TextView subtitleView = view.findViewById(R.id.subtitle);
if (subtitleView != null) {
subtitleView.setText(item.subtitle);
@ -459,16 +461,33 @@ public class ToolbarContainer extends FrameLayout {
int arrowColor = getAttrColor(getContext(), R.attr.dropdown_light_color);
int arrowPressedColor = getAttrColor(
getContext(), R.attr.dropdown_light_pressed_color);
Drawable drawable = new DropdownArrowDrawable(
final Drawable arrowDrawable = new DropdownArrowDrawable(
dp(12), dp(12), true, arrowColor, arrowPressedColor);
titleView.setCompoundDrawablesWithIntrinsicBounds(
null, null, drawable, null);
null, null, arrowDrawable, null);
titleView.setOnClickListener(v -> item.middleMenu.show(titleView));
// Hide the dropdown arrow if there is no text.
titleView.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) {
}
@Override
public void afterTextChanged(Editable s) {
arrowDrawable.setAlpha(isEmpty(s) ? 0 : 255);
}
});
arrowDrawable.setAlpha(isEmpty(item.title) ? 0 : 255);
}
// Possible subtitle.
TextView subtitleView = menu.findViewById(R.id.subtitle);
if (!TextUtils.isEmpty(item.subtitle)) {
if (!isEmpty(item.subtitle)) {
ViewGroup.LayoutParams titleParams = titleView.getLayoutParams();
titleParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
titleView.setLayoutParams(titleParams);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

@ -1,90 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
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/>.
-->
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?backcolor"
android:orientation="vertical">
<ImageView
android:id="@+id/logo"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_margin="16dp"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_weight="1"
android:gravity="center"
android:src="@drawable/logo"
app:layout_constraintBottom_toTopOf="@+id/guideline"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="1.0" />
<android.support.constraint.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintGuide_percent="0.45" />
<TextView
android:id="@+id/title"
style="@style/TextAppearance.AppCompat.Display1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="@string/intro_title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/guideline" />
<TextView
android:id="@+id/content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="@string/intro_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/title" />
<Button
android:id="@+id/start"
style="@style/Widget.AppCompat.Button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:foreground="?selectableItemBackground"
android:text="@string/intro_start"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</android.support.constraint.ConstraintLayout>

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/header"
style="@style/TextAppearance.AppCompat.Headline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="@string/thread_empty_setup_title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/guideline" />
<android.support.constraint.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.5" />
<TextView
android:id="@+id/body"
style="@style/TextAppearance.AppCompat.Subhead"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="@string/thread_empty_setup_body"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/header" />
<TextView
android:id="@+id/feature"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginEnd="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginStart="8dp"
android:text="@string/thread_empty_setup_feature"
android:textSize="50dp"
app:layout_constraintBottom_toTopOf="@+id/header"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:ignore="SpUsage" />
</android.support.constraint.ConstraintLayout>

@ -27,6 +27,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
android:layout_height="15dp"
android:layout_gravity="right"
android:layout_marginRight="14dp"
android:layout_marginLeft="14dp"
android:background="@drawable/background_hint_arrow" />
<FrameLayout

@ -42,6 +42,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
android:layout_height="15dp"
android:layout_gravity="right"
android:layout_marginRight="14dp"
android:layout_marginLeft="14dp"
android:background="@drawable/background_hint_arrow"
android:rotation="180" />

@ -172,18 +172,20 @@ Re-enable this permission in the app settings if you permanently disabled it."</
<string name="thread_pin_hint">Bookmark this thread</string>
<string name="thread_view_archive">Archive</string>
<string name="intro_title">Clover</string>
<string name="intro_content">Browse your favorite imageboards</string>
<string name="intro_start">Get started</string>
<string name="thread_empty_setup_feature">&#128564;</string>
<string name="thread_empty_setup_title">Nothing to show</string>
<string name="thread_empty_setup_body">Add a site to begin browsing</string>
<string name="thread_empty_setup_hint">Add a site here</string>
<string name="setup_sites_title">Your sites</string>
<string name="setup_sites_title">Setup sites</string>
<string name="setup_sites_empty">No sites added</string>
<string name="setup_sites_add_hint">Add sites</string>
<string name="setup_sites_add_hint">Add a site here</string>
<string name="setup_sites_add_title">Add site</string>
<string name="setup_sites_description">Enter the url of the site you want to browse</string>
<string name="setup_sites_url">Site url</string>
<string name="setup_sites_url_hint">http://</string>
<string name="setup_sites_description">Add a site by its url or name</string>
<string name="setup_sites_url">URL or name</string>
<string name="setup_sites_url_hint">http://example.com</string>
<string name="setup_sites_site_description">%s added</string>
<string name="setup_sites_add_boards_hint">Tap to add boards</string>
<string name="setup_site_title">Configure %s</string>
<string name="setup_site_group_general">General</string>
@ -369,7 +371,7 @@ Re-enable this permission in the app settings if you permanently disabled it."</
<string name="image_save_failed">Saving image failed</string>
<string name="image_save_directory_error">Cannot make save directory</string>
<string name="drawer_sites">Sites</string>
<string name="drawer_sites">Setup sites</string>
<string name="drawer_history">History</string>
<!-- Main settings -->

Loading…
Cancel
Save