diff --git a/Clover/app/src/main/java/org/floens/chan/core/database/DatabaseHelper.java b/Clover/app/src/main/java/org/floens/chan/core/database/DatabaseHelper.java index baaabde6..9d8666e7 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/database/DatabaseHelper.java +++ b/Clover/app/src/main/java/org/floens/chan/core/database/DatabaseHelper.java @@ -45,7 +45,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { private static final String TAG = "DatabaseHelper"; private static final String DATABASE_NAME = "ChanDB"; - private static final int DATABASE_VERSION = 23; + private static final int DATABASE_VERSION = 24; public Dao pinDao; public Dao loadableDao; @@ -238,6 +238,14 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { Logger.e(TAG, "Error upgrading to version 14", e); } } + + if (oldVersion < 24) { + try { + siteDao.executeRawNoArgs("ALTER TABLE site ADD COLUMN \"order\" INTEGER;"); + } catch (SQLException e) { + Logger.e(TAG, "Error upgrading to version 14", e); + } + } } public void reset() { diff --git a/Clover/app/src/main/java/org/floens/chan/core/database/DatabaseSiteManager.java b/Clover/app/src/main/java/org/floens/chan/core/database/DatabaseSiteManager.java index 4c467397..effa2a27 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/database/DatabaseSiteManager.java +++ b/Clover/app/src/main/java/org/floens/chan/core/database/DatabaseSiteManager.java @@ -18,9 +18,13 @@ package org.floens.chan.core.database; +import com.j256.ormlite.stmt.QueryBuilder; + import org.floens.chan.core.model.orm.SiteModel; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.Callable; public class DatabaseSiteManager { @@ -64,4 +68,29 @@ public class DatabaseSiteManager { return site; }; } + + public Callable> getOrdering() { + return () -> { + QueryBuilder q = helper.siteDao.queryBuilder(); + q.selectColumns("id", "order"); + List modelsWithOrder = q.query(); + Map ordering = new HashMap<>(); + for (SiteModel siteModel : modelsWithOrder) { + ordering.put(siteModel.id, siteModel.order); + } + return ordering; + }; + } + + public Callable updateOrdering(final List siteIdsWithCorrectOrder) { + return () -> { + for (int i = 0; i < siteIdsWithCorrectOrder.size(); i++) { + Integer id = siteIdsWithCorrectOrder.get(i); + SiteModel m = helper.siteDao.queryForId(id); + m.order = i; + helper.siteDao.update(m); + } + return null; + }; + } } diff --git a/Clover/app/src/main/java/org/floens/chan/core/manager/BoardManager.java b/Clover/app/src/main/java/org/floens/chan/core/manager/BoardManager.java index fbe44356..6d89e382 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/manager/BoardManager.java +++ b/Clover/app/src/main/java/org/floens/chan/core/manager/BoardManager.java @@ -23,13 +23,14 @@ import org.floens.chan.core.database.DatabaseBoardManager; import org.floens.chan.core.database.DatabaseManager; import org.floens.chan.core.model.orm.Board; import org.floens.chan.core.site.Site; -import org.floens.chan.core.site.Sites; +import org.floens.chan.core.site.SiteService; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Observable; +import java.util.Observer; import javax.inject.Inject; import javax.inject.Singleton; @@ -47,13 +48,15 @@ import javax.inject.Singleton; * favorite board list, along with a {@link Board#order} in which they appear. */ @Singleton -public class BoardManager { +public class BoardManager implements Observer { private static final String TAG = "BoardManager"; private static final Comparator ORDER_SORT = (lhs, rhs) -> lhs.order - rhs.order; private final DatabaseManager databaseManager; private final DatabaseBoardManager databaseBoardManager; + private final SiteService siteService; + private final SiteService.SitesChangedObservable sitesChangedObservable; private final AllBoards allBoardsObservable = new AllBoards(); private final SavedBoards savedBoardsObservable = new SavedBoards(); @@ -62,11 +65,25 @@ public class BoardManager { private final List>> sitesWithSavedBoards = new ArrayList<>(); @Inject - public BoardManager(DatabaseManager databaseManager) { + public BoardManager(DatabaseManager databaseManager, SiteService siteService) { this.databaseManager = databaseManager; + this.siteService = siteService; + + sitesChangedObservable = siteService.getSitesChangedObservable(); + databaseBoardManager = databaseManager.getDatabaseBoardManager(); updateObservables(); + + sitesChangedObservable.addObserver(this); + } + + @Override + public void update(Observable o, Object arg) { + // If the sites changed (added, removed or reordered) we need to reload the boards. + if (o == sitesChangedObservable) { + updateObservables(); + } } public void createAll(List boards) { @@ -126,7 +143,7 @@ public class BoardManager { private void updateObservables() { sitesWithBoards.clear(); - for (Site site : Sites.allSites()) { + for (Site site : siteService.getAllSitesInOrder()) { List all = getSiteBoards(site); sitesWithBoards.add(new Pair<>(site, all)); diff --git a/Clover/app/src/main/java/org/floens/chan/core/model/orm/SiteModel.java b/Clover/app/src/main/java/org/floens/chan/core/model/orm/SiteModel.java index 79252bee..22176cf3 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/model/orm/SiteModel.java +++ b/Clover/app/src/main/java/org/floens/chan/core/model/orm/SiteModel.java @@ -60,6 +60,9 @@ public class SiteModel { @DatabaseField public String userSettings; + @DatabaseField + public int order; + public SiteModel() { } diff --git a/Clover/app/src/main/java/org/floens/chan/core/presenter/BoardsMenuPresenter.java b/Clover/app/src/main/java/org/floens/chan/core/presenter/BoardsMenuPresenter.java index ae12fe52..0b09d853 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/presenter/BoardsMenuPresenter.java +++ b/Clover/app/src/main/java/org/floens/chan/core/presenter/BoardsMenuPresenter.java @@ -118,17 +118,6 @@ public class BoardsMenuPresenter implements Observer { notifyObservers(); } - private boolean shouldShowBoard(String filter, Board board) { - if (filter == null || filter.length() == 0) { - return board.saved; - } - - String fl = filter.toLowerCase(); - return board.code.toLowerCase().contains(fl) || - (board.name != null && board.name.toLowerCase().contains(fl));/* || - (board.description != null && board.description.toLowerCase().contains(fl));*/ - } - public int getCount() { return items.size(); } diff --git a/Clover/app/src/main/java/org/floens/chan/core/presenter/SitesSetupPresenter.java b/Clover/app/src/main/java/org/floens/chan/core/presenter/SitesSetupPresenter.java index 2b9a9bbd..da0899ea 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/presenter/SitesSetupPresenter.java +++ b/Clover/app/src/main/java/org/floens/chan/core/presenter/SitesSetupPresenter.java @@ -21,7 +21,6 @@ package org.floens.chan.core.presenter; import org.floens.chan.core.manager.BoardManager; import org.floens.chan.core.site.Site; import org.floens.chan.core.site.SiteService; -import org.floens.chan.core.site.Sites; import java.util.ArrayList; import java.util.List; @@ -46,7 +45,7 @@ public class SitesSetupPresenter { public void create(Callback callback) { this.callback = callback; - sites.addAll(Sites.allSites()); + sites.addAll(siteService.getAllSitesInOrder()); this.callback.setAddedSites(sites); @@ -61,6 +60,13 @@ public class SitesSetupPresenter { callback.setAddedSites(sites); } + public void move(int from, int to) { + Site item = sites.remove(from); + sites.add(to, item); + saveOrder(); + callback.setAddedSites(sites); + } + public void onIntroDismissed() { if (sites.isEmpty()) { callback.showHint(); @@ -110,8 +116,8 @@ public class SitesSetupPresenter { } private void siteAdded(Site site) { - sites.clear(); - sites.addAll(Sites.allSites()); + sites.add(site); + saveOrder(); callback.setAddedSites(sites); @@ -122,6 +128,10 @@ public class SitesSetupPresenter { callback.openSiteConfiguration(site); } + private void saveOrder() { + siteService.updateOrdering(sites); + } + public interface Callback { void presentIntro(); diff --git a/Clover/app/src/main/java/org/floens/chan/core/site/SiteRepository.java b/Clover/app/src/main/java/org/floens/chan/core/site/SiteRepository.java index 57d36674..80f6aa3c 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/site/SiteRepository.java +++ b/Clover/app/src/main/java/org/floens/chan/core/site/SiteRepository.java @@ -2,10 +2,12 @@ package org.floens.chan.core.site; import org.floens.chan.core.database.DatabaseManager; import org.floens.chan.core.model.json.site.SiteConfig; -import org.floens.chan.core.settings.json.JsonSettings; import org.floens.chan.core.model.orm.SiteModel; +import org.floens.chan.core.settings.json.JsonSettings; +import java.util.ArrayList; import java.util.List; +import java.util.Map; import javax.inject.Inject; @@ -44,4 +46,18 @@ public class SiteRepository { databaseManager.runTaskAsync(databaseManager.getDatabaseSiteManager() .update(siteModel)); } + + public Map getOrdering() { + return databaseManager.runTask(databaseManager.getDatabaseSiteManager().getOrdering()); + } + + public void updateSiteOrderingAsync(List sites, Runnable done) { + List ids = new ArrayList<>(sites.size()); + for (Site site : sites) { + ids.add(site.id()); + } + + databaseManager.runTaskAsync(databaseManager.getDatabaseSiteManager().updateOrdering(ids), + result -> done.run()); + } } diff --git a/Clover/app/src/main/java/org/floens/chan/core/site/SiteService.java b/Clover/app/src/main/java/org/floens/chan/core/site/SiteService.java index 3e5eec8e..7724816b 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/site/SiteService.java +++ b/Clover/app/src/main/java/org/floens/chan/core/site/SiteService.java @@ -21,12 +21,15 @@ package org.floens.chan.core.site; import android.util.Pair; import org.floens.chan.core.model.json.site.SiteConfig; -import org.floens.chan.core.settings.json.JsonSettings; import org.floens.chan.core.model.orm.SiteModel; +import org.floens.chan.core.settings.json.JsonSettings; import org.floens.chan.core.site.sites.chan4.Chan4; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.Map; +import java.util.Observable; import javax.inject.Inject; import javax.inject.Singleton; @@ -47,6 +50,8 @@ public class SiteService { private boolean initialized = false; + private SitesChangedObservable sitesChangedObservable = new SitesChangedObservable(); + @Inject public SiteService(SiteRepository siteRepository, SiteResolver resolver) { @@ -54,6 +59,10 @@ public class SiteService { this.resolver = resolver; } + public SitesChangedObservable getSitesChangedObservable() { + return sitesChangedObservable; + } + public boolean areSitesSetup() { return !Sites.allSites().isEmpty(); } @@ -84,6 +93,8 @@ public class SiteService { loadSites(); callback.onSiteAdded(site); + + sitesChangedObservable.doNotify(); } public void updateUserSettings(Site site, JsonSettings jsonSettings) { @@ -92,6 +103,24 @@ public class SiteService { siteRepository.updateSiteUserSettingsAsync(siteModel, jsonSettings); } + public List getAllSitesInOrder() { + Map ordering = siteRepository.getOrdering(); + + List all = Sites.allSites(); + + Site[] ordered = new Site[all.size()]; + for (Site site : all) { + ordered[ordering.get(site.id())] = site; + } + + return Arrays.asList(ordered); + } + + public void updateOrdering(List sitesInNewOrder) { + siteRepository.updateSiteOrderingAsync(sitesInNewOrder, + () -> sitesChangedObservable.doNotify()); + } + public void initialize() { if (initialized) { throw new IllegalStateException("Already initialized"); @@ -180,4 +209,11 @@ public class SiteService { void onSiteAddFailed(String message); } + + public class SitesChangedObservable extends Observable { + private void doNotify() { + setChanged(); + notifyObservers(); + } + } } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/SitesSetupController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/SitesSetupController.java index a9f12eb4..06ccffc7 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/SitesSetupController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/SitesSetupController.java @@ -21,11 +21,15 @@ package org.floens.chan.ui.controller; import android.annotation.SuppressLint; import android.content.Context; import android.content.DialogInterface; +import android.graphics.drawable.Drawable; import android.support.design.widget.FloatingActionButton; +import android.support.v4.graphics.drawable.DrawableCompat; import android.support.v7.app.AlertDialog; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.helper.ItemTouchHelper; import android.view.LayoutInflater; +import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.Button; @@ -51,9 +55,13 @@ import javax.inject.Inject; import static org.floens.chan.Chan.inject; import static org.floens.chan.ui.theme.ThemeHelper.theme; +import static org.floens.chan.utils.AndroidUtils.getAttrColor; import static org.floens.chan.utils.AndroidUtils.setRoundItemBackground; -public class SitesSetupController extends StyledToolbarNavigationController implements SitesSetupPresenter.Callback, ToolbarMenuItem.ToolbarMenuItemCallback, View.OnClickListener { +public class SitesSetupController extends StyledToolbarNavigationController implements + SitesSetupPresenter.Callback, + ToolbarMenuItem.ToolbarMenuItemCallback, + View.OnClickListener { private static final int DONE_ID = 1; @Inject @@ -66,8 +74,28 @@ public class SitesSetupController extends StyledToolbarNavigationController impl private FloatingActionButton addButton; private SitesAdapter sitesAdapter; + private ItemTouchHelper itemTouchHelper; private List sites = new ArrayList<>(); + private ItemTouchHelper.SimpleCallback touchHelperCallback = new ItemTouchHelper.SimpleCallback( + ItemTouchHelper.UP | ItemTouchHelper.DOWN, + 0 + ) { + @Override + public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { + int from = viewHolder.getAdapterPosition(); + int to = target.getAdapterPosition(); + + presenter.move(from, to); + + return true; + } + + @Override + public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { + } + }; + public SitesSetupController(Context context) { super(context); } @@ -96,6 +124,8 @@ public class SitesSetupController extends StyledToolbarNavigationController impl sitesRecyclerview.setAdapter(sitesAdapter); sitesRecyclerview.addItemDecoration( new DividerItemDecoration(context, DividerItemDecoration.VERTICAL)); + itemTouchHelper = new ItemTouchHelper(touchHelperCallback); + itemTouchHelper.attachToRecyclerView(sitesRecyclerview); addButton.setOnClickListener(this); theme().applyFabColor(addButton); crossfadeView.toggle(false, false); @@ -257,9 +287,11 @@ public class SitesSetupController extends StyledToolbarNavigationController impl private TextView description; private SiteIcon siteIcon; private ImageView settings; + private ImageView reorder; private Site site; + @SuppressLint("ClickableViewAccessibility") public SiteCell(View itemView) { super(itemView); @@ -268,11 +300,24 @@ public class SitesSetupController extends StyledToolbarNavigationController impl text = itemView.findViewById(R.id.text); description = itemView.findViewById(R.id.description); settings = itemView.findViewById(R.id.settings); + reorder = itemView.findViewById(R.id.reorder); // Setup views itemView.setOnClickListener(this); setRoundItemBackground(settings); theme().settingsDrawable.apply(settings); + + Drawable drawable = DrawableCompat.wrap( + context.getResources().getDrawable(R.drawable.ic_reorder_black_24dp)).mutate(); + DrawableCompat.setTint(drawable, getAttrColor(context, R.attr.text_color_hint)); + reorder.setImageDrawable(drawable); + + reorder.setOnTouchListener((v, event) -> { + if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { + itemTouchHelper.startDrag(this); + } + return false; + }); } private void setSite(Site site) { diff --git a/Clover/app/src/main/java/org/floens/chan/ui/layout/BrowseBoardsFloatingMenu.java b/Clover/app/src/main/java/org/floens/chan/ui/layout/BrowseBoardsFloatingMenu.java index a10c29d6..759551ca 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/layout/BrowseBoardsFloatingMenu.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/layout/BrowseBoardsFloatingMenu.java @@ -430,6 +430,7 @@ public class BrowseBoardsFloatingMenu extends FrameLayout implements BoardsMenuP icon = site.icon(); image.setTag(icon); + image.setImageDrawable(null); icon.get((siteIcon, drawable) -> { if (image.getTag() == siteIcon) { image.setImageDrawable(drawable); diff --git a/Clover/app/src/main/res/layout/cell_site.xml b/Clover/app/src/main/res/layout/cell_site.xml index fc9102ea..7190830e 100644 --- a/Clover/app/src/main/res/layout/cell_site.xml +++ b/Clover/app/src/main/res/layout/cell_site.xml @@ -72,4 +72,13 @@ along with this program. If not, see . android:scaleType="center" tools:src="@drawable/ic_settings_black_24dp" /> + + diff --git a/docs/database.txt b/docs/database.txt index 24344de1..58f50cbe 100644 --- a/docs/database.txt +++ b/docs/database.txt @@ -76,3 +76,7 @@ ALTER TABLE threadhide ADD COLUMN site INTEGER default 0; Changes in version 23: ALTER TABLE board ADD COLUMN "archive" INTEGER; + + +Changes in version 24: +ALTER TABLE site ADD COLUMN "order" INTEGER;