diff --git a/Clover/Clover.iml b/Clover/Clover.iml deleted file mode 100644 index d9c631dd..00000000 --- a/Clover/Clover.iml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/Clover/app/app.iml b/Clover/app/app.iml deleted file mode 100644 index 00846781..00000000 --- a/Clover/app/app.iml +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Clover/app/src/main/java/org/floens/chan/controller/NavigationController.java b/Clover/app/src/main/java/org/floens/chan/controller/NavigationController.java index 46ba2598..1061be0d 100644 --- a/Clover/app/src/main/java/org/floens/chan/controller/NavigationController.java +++ b/Clover/app/src/main/java/org/floens/chan/controller/NavigationController.java @@ -145,6 +145,8 @@ public abstract class NavigationController extends Controller implements Control for (Controller controller : controllerList) { controller.onConfigurationChanged(newConfig); } + + toolbar.onConfigurationChanged(newConfig); } public void initWithController(final Controller controller) { diff --git a/Clover/app/src/main/java/org/floens/chan/core/presenter/ThreadPresenter.java b/Clover/app/src/main/java/org/floens/chan/core/presenter/ThreadPresenter.java index 6410d16f..7ddf4af0 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/presenter/ThreadPresenter.java +++ b/Clover/app/src/main/java/org/floens/chan/core/presenter/ThreadPresenter.java @@ -51,19 +51,27 @@ public class ThreadPresenter implements ChanLoader.ChanLoaderCallback, PostAdapt } public void bindLoadable(Loadable loadable) { - if (!loadable.equals(this.loadable)) { - if (this.loadable != null) { - unbindLoadable(); - } + if (chanLoader == null) { + if (!loadable.equals(this.loadable)) { + if (this.loadable != null) { + unbindLoadable(); + } - this.loadable = loadable; + this.loadable = loadable; - chanLoader = LoaderPool.getInstance().obtain(loadable, this); + chanLoader = LoaderPool.getInstance().obtain(loadable, this); + } } } public void unbindLoadable() { - threadPresenterCallback.showLoading(); + if (chanLoader != null) { + LoaderPool.getInstance().release(chanLoader, this); + chanLoader = null; + loadable = null; + + threadPresenterCallback.showLoading(); + } } public void requestData() { 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 fdfc7287..2e937ce7 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 @@ -18,10 +18,19 @@ package org.floens.chan.ui.controller; import android.content.Context; - +import android.graphics.Typeface; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.TextView; + +import org.floens.chan.ChanApplication; import org.floens.chan.R; import org.floens.chan.chan.ChanUrls; import org.floens.chan.controller.Controller; +import org.floens.chan.core.manager.BoardManager; +import org.floens.chan.core.model.Board; import org.floens.chan.core.model.Loadable; import org.floens.chan.ui.layout.ThreadLayout; import org.floens.chan.ui.toolbar.ToolbarMenu; @@ -33,7 +42,7 @@ import org.floens.chan.utils.AndroidUtils; import java.util.ArrayList; import java.util.List; -public class BrowseController extends Controller implements ToolbarMenuItem.ToolbarMenuItemCallback, ThreadLayout.ThreadLayoutCallback { +public class BrowseController extends Controller implements ToolbarMenuItem.ToolbarMenuItemCallback, ThreadLayout.ThreadLayoutCallback, ToolbarMenuSubMenu.ToolbarMenuItemSubMenuCallback, BoardManager.BoardChangeListener { private static final int REFRESH_ID = 1; private static final int POST_ID = 2; private static final int SEARCH_ID = 101; @@ -41,6 +50,7 @@ public class BrowseController extends Controller implements ToolbarMenuItem.Tool private static final int SETTINGS_ID = 103; private ThreadLayout threadLayout; + private List boardItems; public BrowseController(Context context) { super(context); @@ -50,6 +60,12 @@ public class BrowseController extends Controller implements ToolbarMenuItem.Tool public void onCreate() { super.onCreate(); + ChanApplication.getBoardManager().addListener(this); + + navigationItem.middleMenu = new ToolbarMenuSubMenu(context); + navigationItem.middleMenu.setCallback(this); + loadBoards(); + navigationItem.title = "Hello world"; ToolbarMenu menu = new ToolbarMenu(context); navigationItem.menu = menu; @@ -72,13 +88,14 @@ public class BrowseController extends Controller implements ToolbarMenuItem.Tool view = threadLayout; - Loadable loadable = new Loadable("g"); - loadable.mode = Loadable.Mode.CATALOG; - loadable.generateTitle(); - navigationItem.title = loadable.title; + loadBoard(ChanApplication.getBoardManager().getSavedBoards().get(0)); + } - threadLayout.getPresenter().bindLoadable(loadable); - threadLayout.getPresenter().requestData(); + @Override + public void onDestroy() { + super.onDestroy(); + + ChanApplication.getBoardManager().removeListener(this); } @Override @@ -110,10 +127,112 @@ public class BrowseController extends Controller implements ToolbarMenuItem.Tool } } + @Override + public void onSubMenuItemClicked(ToolbarMenuSubMenu menu, ToolbarMenuSubItem item) { + if (menu == navigationItem.middleMenu) { + if (item instanceof ToolbarMenuSubItemBoard) { + loadBoard(((ToolbarMenuSubItemBoard) item).board); + navigationController.toolbar.updateNavigation(); + } else { + // TODO start board editor + } + } + } + @Override public void openThread(Loadable threadLoadable) { ViewThreadController viewThreadController = new ViewThreadController(context); viewThreadController.setLoadable(threadLoadable); navigationController.pushController(viewThreadController); } + + @Override + public void onBoardsChanged() { + loadBoards(); + } + + private void loadBoard(Board board) { + Loadable loadable = new Loadable(board.value); + loadable.mode = Loadable.Mode.CATALOG; + loadable.generateTitle(); + navigationItem.title = board.key; + + threadLayout.getPresenter().unbindLoadable(); + threadLayout.getPresenter().bindLoadable(loadable); + threadLayout.getPresenter().requestData(); + + for (ToolbarMenuSubItem item : boardItems) { + if (((ToolbarMenuSubItemBoard) item).board == board) { + navigationItem.middleMenu.setSelectedItem(item); + } + } + } + + private void loadBoards() { + List boards = ChanApplication.getBoardManager().getSavedBoards(); + boardItems = new ArrayList<>(); + for (Board board : boards) { + ToolbarMenuSubItem item = new ToolbarMenuSubItemBoard(board); + boardItems.add(item); + } + + navigationItem.middleMenu.setItems(boardItems); + navigationItem.middleMenu.setAdapter(new BoardsAdapter(context, boardItems)); + } + + private static class ToolbarMenuSubItemBoard extends ToolbarMenuSubItem { + public Board board; + + public ToolbarMenuSubItemBoard(Board board) { + super(board.id, board.key); + this.board = board; + } + } + + private static class BoardsAdapter extends BaseAdapter { + private final Context context; + private List items; + + public BoardsAdapter(Context context, List items) { + this.context = context; + this.items = items; + } + + @Override + public View getDropDownView(int position, View convertView, ViewGroup parent) { + TextView textView = (TextView) LayoutInflater.from(context).inflate(R.layout.toolbar_menu_item, parent, false); + textView.setText(getItem(position)); + if (position < items.size()) { + textView.setTypeface(AndroidUtils.ROBOTO_MEDIUM); + } else { + textView.setTypeface(AndroidUtils.ROBOTO_MEDIUM, Typeface.ITALIC); + } + + return textView; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + return getDropDownView(position, convertView, parent); + } + + @Override + public int getCount() { + return items.size() + 1; + } + + @Override + public String getItem(int position) { + if (position >= 0 && position < items.size()) { + return items.get(position).getText(); + } else { + return context.getString(R.string.board_select_add); + } + } + + @Override + public long getItemId(int position) { + return position; + } + } } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/drawable/DropdownArrowDrawable.java b/Clover/app/src/main/java/org/floens/chan/ui/drawable/DropdownArrowDrawable.java new file mode 100644 index 00000000..a93161c3 --- /dev/null +++ b/Clover/app/src/main/java/org/floens/chan/ui/drawable/DropdownArrowDrawable.java @@ -0,0 +1,84 @@ +package org.floens.chan.ui.drawable; + +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PixelFormat; +import android.graphics.drawable.Drawable; + +import static org.floens.chan.utils.AndroidUtils.dp; + +public class DropdownArrowDrawable extends Drawable { + private Paint paint = new Paint(); + private Path path = new Path(); + private int width; + private int height; + + public DropdownArrowDrawable() { + width = dp(12); + height = dp(6); + + paint.setStyle(Paint.Style.FILL); + paint.setColor(0xffffffff); + + path.moveTo(0, 0); + path.lineTo(width, 0); + path.lineTo(width / 2, height); + path.lineTo(0, 0); + path.close(); + } + + @Override + public void draw(Canvas canvas) { + canvas.drawPath(path, paint); + } + + @Override + public int getIntrinsicWidth() { + return width; + } + + @Override + public int getIntrinsicHeight() { + return height; + } + + @Override + protected boolean onStateChange(int[] states) { + boolean pressed = false; + for (int state : states) { + if (state == android.R.attr.state_pressed) { + pressed = true; + } + } + int color = pressed ? 0x88ffffff : 0xffffffff; + if (color != paint.getColor()) { + paint.setColor(color); + invalidateSelf(); + return true; + } else { + return false; + } + } + + @Override + public boolean isStateful() { + return true; + } + + @Override + public void setAlpha(int alpha) { + paint.setAlpha(alpha); + } + + @Override + public void setColorFilter(ColorFilter cf) { + paint.setColorFilter(cf); + } + + @Override + public int getOpacity() { + return PixelFormat.TRANSLUCENT; + } +} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/toolbar/NavigationItem.java b/Clover/app/src/main/java/org/floens/chan/ui/toolbar/NavigationItem.java index 34fbb2bb..a71618eb 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/toolbar/NavigationItem.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/toolbar/NavigationItem.java @@ -24,4 +24,5 @@ public class NavigationItem { public ToolbarMenu menu; public boolean hasBack = true; public LinearLayout view; + public ToolbarMenuSubMenu middleMenu; } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/toolbar/Toolbar.java b/Clover/app/src/main/java/org/floens/chan/ui/toolbar/Toolbar.java index 5ed44359..99c4f992 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/toolbar/Toolbar.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/toolbar/Toolbar.java @@ -23,12 +23,13 @@ import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.content.Context; -import android.graphics.Color; +import android.content.res.Configuration; +import android.graphics.drawable.Drawable; import android.os.Build; -import android.text.TextUtils; +import android.support.v4.view.GravityCompat; import android.util.AttributeSet; -import android.util.TypedValue; import android.view.Gravity; +import android.view.LayoutInflater; import android.view.View; import android.view.animation.DecelerateInterpolator; import android.widget.FrameLayout; @@ -38,6 +39,7 @@ import android.widget.TextView; import org.floens.chan.R; import org.floens.chan.ui.drawable.ArrowMenuDrawable; +import org.floens.chan.ui.drawable.DropdownArrowDrawable; import org.floens.chan.utils.AndroidUtils; import java.util.ArrayList; @@ -70,6 +72,10 @@ public class Toolbar extends LinearLayout implements View.OnClickListener { init(); } + public void updateNavigation() { + setNavigationItem(false, false, navigationItem); + } + public void setNavigationItem(final boolean animate, final boolean pushing, final NavigationItem item) { if (item.menu != null) { AndroidUtils.waitForMeasure(this, new AndroidUtils.OnMeasuredCallback() { @@ -100,6 +106,9 @@ public class Toolbar extends LinearLayout implements View.OnClickListener { arrowMenuDrawable.setProgress(progress); } + public void onConfigurationChanged(Configuration newConfig) { + } + private void init() { setOrientation(HORIZONTAL); @@ -128,12 +137,19 @@ public class Toolbar extends LinearLayout implements View.OnClickListener { } private void setNavigationItemView(boolean animate, boolean pushing, NavigationItem toItem) { + final NavigationItem fromItem = navigationItem; + + if (!animate) { + if (fromItem != null) { + removeNavigationItem(fromItem); + } + setArrowMenuProgress(toItem.hasBack ? 1f : 0f); + } + toItem.view = createNavigationItemView(toItem); navigationItemContainer.addView(toItem.view, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); - final NavigationItem fromItem = navigationItem; - final int duration = 300; final int offset = dp(16); @@ -191,12 +207,6 @@ public class Toolbar extends LinearLayout implements View.OnClickListener { set.setStartDelay(pushing ? 0 : 100); set.playTogether(animations); set.start(); - } else { - // No animation - if (fromItem != null) { - removeNavigationItem(fromItem); - } - setArrowMenuProgress(toItem.hasBack ? 1f : 0f); } navigationItem = toItem; @@ -208,26 +218,42 @@ public class Toolbar extends LinearLayout implements View.OnClickListener { item.view = null; } - private LinearLayout createNavigationItemView(NavigationItem item) { - LinearLayout wrapper = new LinearLayout(getContext()); - - TextView titleView = new TextView(getContext()); - titleView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20); -// titleView.setTextColor(Color.argb((int)(0.87 * 255.0), 0, 0, 0)); - titleView.setTextColor(Color.argb(255, 255, 255, 255)); - titleView.setGravity(Gravity.CENTER_VERTICAL); - titleView.setSingleLine(true); - titleView.setLines(1); - titleView.setEllipsize(TextUtils.TruncateAt.END); - titleView.setPadding(dp(16), 0, 0, 0); + private LinearLayout createNavigationItemView(final NavigationItem item) { + LinearLayout wrapper = (LinearLayout) LayoutInflater.from(getContext()).inflate(R.layout.toolbar_menu, null); + wrapper.setGravity(Gravity.CENTER_VERTICAL); + + final TextView titleView = (TextView) wrapper.findViewById(R.id.title); titleView.setTypeface(AndroidUtils.ROBOTO_MEDIUM); titleView.setText(item.title); - wrapper.addView(titleView, new LayoutParams(0, LayoutParams.MATCH_PARENT, 1f)); + // black: titleView.setTextColor(Color.argb((int)(0.87 * 255.0), 0, 0, 0)); + + if (item.middleMenu != null) { + item.middleMenu.setAnchor(titleView, GravityCompat.END | Gravity.TOP, dp(5), dp(5)); // TODO gravity left doesn't work + + Drawable drawable = new DropdownArrowDrawable(); + titleView.setCompoundDrawablesWithIntrinsicBounds(null, null, drawable, null); + + titleView.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + item.middleMenu.show(); + } + }); + } if (item.menu != null) { wrapper.addView(item.menu, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT)); } + AndroidUtils.waitForMeasure(titleView, new AndroidUtils.OnMeasuredCallback() { + @Override + public void onMeasured(View view, int width, int height) { + if (item.middleMenu != null) { + item.middleMenu.setPopupWidth(Math.max(dp(150), titleView.getWidth())); + } + } + }); + return wrapper; } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/toolbar/ToolbarMenuItem.java b/Clover/app/src/main/java/org/floens/chan/ui/toolbar/ToolbarMenuItem.java index 0865df4c..08709e03 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/toolbar/ToolbarMenuItem.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/toolbar/ToolbarMenuItem.java @@ -91,7 +91,7 @@ public class ToolbarMenuItem implements View.OnClickListener, ToolbarMenuSubMenu } @Override - public void onSubMenuItemClicked(ToolbarMenuSubItem item) { + public void onSubMenuItemClicked(ToolbarMenuSubMenu menu, ToolbarMenuSubItem item) { callback.onSubMenuItemClicked(this, item); } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/toolbar/ToolbarMenuSubMenu.java b/Clover/app/src/main/java/org/floens/chan/ui/toolbar/ToolbarMenuSubMenu.java index 992521f8..674f7123 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/toolbar/ToolbarMenuSubMenu.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/toolbar/ToolbarMenuSubMenu.java @@ -18,6 +18,7 @@ package org.floens.chan.ui.toolbar; import android.content.Context; +import android.support.v4.view.GravityCompat; import android.support.v7.widget.ListPopupWindow; import android.view.Gravity; import android.view.LayoutInflater; @@ -26,6 +27,7 @@ import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.widget.AdapterView; import android.widget.ArrayAdapter; +import android.widget.ListAdapter; import android.widget.PopupWindow; import android.widget.TextView; @@ -39,43 +41,101 @@ import static org.floens.chan.utils.AndroidUtils.dp; public class ToolbarMenuSubMenu { private final Context context; - private final View anchor; + private View anchor; + private int anchorGravity; + private int anchorOffsetX; + private int anchorOffsetY; + private int popupWidth = -1; private List items; + private ToolbarMenuSubItem selectedItem; + private ListAdapter adapter; private ViewTreeObserver.OnGlobalLayoutListener globalLayoutListener; + private ListPopupWindow popupWindow; private ToolbarMenuItemSubMenuCallback callback; public ToolbarMenuSubMenu(Context context, View anchor, List items) { this.context = context; this.anchor = anchor; + anchorGravity = Gravity.RIGHT | Gravity.TOP; + anchorOffsetX = -dp(5); + anchorOffsetY = dp(5); this.items = items; } + public ToolbarMenuSubMenu(Context context) { + this.context = context; + } + + public void setAnchor(View anchor, int anchorGravity, int anchorOffsetX, int anchorOffsetY) { + this.anchor = anchor; + this.anchorGravity = anchorGravity; + this.anchorOffsetX = anchorOffsetX; + this.anchorOffsetY = anchorOffsetY; + } + + public void setPopupWidth(int width) { + this.popupWidth = width; + if (popupWindow != null) { + popupWindow.setContentWidth(popupWidth); + } + } + + public void setItems(List items) { + this.items = items; + if (popupWindow != null) { + popupWindow.dismiss(); + } + } + + public void setSelectedItem(ToolbarMenuSubItem item) { + this.selectedItem = item; + } + + public void setAdapter(ListAdapter adapter) { + this.adapter = adapter; + } + public void setCallback(ToolbarMenuItemSubMenuCallback callback) { this.callback = callback; } public void show() { - final ListPopupWindow popupWindow = new ListPopupWindow(context); + popupWindow = new ListPopupWindow(context); popupWindow.setAnchorView(anchor); popupWindow.setModal(true); - popupWindow.setDropDownGravity(Gravity.RIGHT | Gravity.TOP); - popupWindow.setVerticalOffset(-anchor.getHeight() + dp(5)); - popupWindow.setHorizontalOffset(-dp(5)); - popupWindow.setContentWidth(dp(3 * 56)); + popupWindow.setDropDownGravity(GravityCompat.END | Gravity.TOP); + popupWindow.setVerticalOffset(-anchor.getHeight() + anchorOffsetY); + popupWindow.setHorizontalOffset(anchorOffsetX); + if (popupWidth > 0) { + popupWindow.setContentWidth(popupWidth); + } else { + popupWindow.setContentWidth(dp(3 * 56)); + } List stringItems = new ArrayList<>(items.size()); - for (ToolbarMenuSubItem item : items) { - stringItems.add(item.getText()); + int selectedPosition = 0; + for (int i = 0; i < items.size(); i++) { + stringItems.add(items.get(i).getText()); + if (items.get(i) == selectedItem) { + selectedPosition = i; + } + } + + if (adapter != null) { + popupWindow.setAdapter(adapter); + } else { + popupWindow.setAdapter(new SubMenuArrayAdapter(context, R.layout.toolbar_menu_item, stringItems)); } - popupWindow.setAdapter(new SubMenuArrayAdapter(context, R.layout.toolbar_menu_item, stringItems)); popupWindow.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { if (position >= 0 && position < items.size()) { - callback.onSubMenuItemClicked(items.get(position)); + callback.onSubMenuItemClicked(ToolbarMenuSubMenu.this, items.get(position)); popupWindow.dismiss(); + } else { + callback.onSubMenuItemClicked(ToolbarMenuSubMenu.this, null); } } }); @@ -98,14 +158,16 @@ public class ToolbarMenuSubMenu { anchor.getViewTreeObserver().removeGlobalOnLayoutListener(globalLayoutListener); } globalLayoutListener = null; + popupWindow = null; } }); popupWindow.show(); + popupWindow.setSelection(selectedPosition); } public interface ToolbarMenuItemSubMenuCallback { - public void onSubMenuItemClicked(ToolbarMenuSubItem item); + public void onSubMenuItemClicked(ToolbarMenuSubMenu menu, ToolbarMenuSubItem item); } private static class SubMenuArrayAdapter extends ArrayAdapter { diff --git a/Clover/app/src/main/res/layout/toolbar_menu.xml b/Clover/app/src/main/res/layout/toolbar_menu.xml new file mode 100644 index 00000000..eff0b3e7 --- /dev/null +++ b/Clover/app/src/main/res/layout/toolbar_menu.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + diff --git a/Clover/app/src/main/res/layout/toolbar_menu_item.xml b/Clover/app/src/main/res/layout/toolbar_menu_item.xml index fb2fdc46..d0f10f7e 100644 --- a/Clover/app/src/main/res/layout/toolbar_menu_item.xml +++ b/Clover/app/src/main/res/layout/toolbar_menu_item.xml @@ -6,4 +6,7 @@ android:paddingLeft="16dp" android:paddingRight="16dp" android:textColor="#ff000000" - android:textSize="16sp" /> + android:textSize="16sp" + android:singleLine="true" + android:lines="1" + android:ellipsize="end" />