Fix issue #28 & #61

This commit is contained in:
Thomas 2022-05-18 11:42:45 +02:00
parent 4725ca6ce7
commit e063eec767
11 changed files with 616 additions and 216 deletions

View file

@ -136,6 +136,7 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
private Pinned pinned;
public static boolean show_boosts, show_replies, show_art_nsfw;
public static String regex_home, regex_local, regex_public;
private BottomMenu bottomMenu;
private final BroadcastReceiver broadcast_data = new BroadcastReceiver() {
@Override
@ -145,6 +146,51 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
if (b.getBoolean(Helper.RECEIVE_REDRAW_TOPBAR, false)) {
List<MastodonList> mastodonLists = (List<MastodonList>) b.getSerializable(Helper.RECEIVE_MASTODON_LIST);
redrawPinned(mastodonLists);
}
if (b.getBoolean(Helper.RECEIVE_REDRAW_BOTTOM, false)) {
bottomMenu = new BottomMenu(BaseMainActivity.this).hydrate(account, binding.bottomNavView);
if (bottomMenu != null) {
//ManageClick on bottom menu items
if (binding.bottomNavView.findViewById(R.id.nav_home) != null) {
binding.bottomNavView.findViewById(R.id.nav_home).setOnLongClickListener(view -> {
int position = BottomMenu.getPosition(bottomMenu, R.id.nav_home);
if (position >= 0) {
manageFilters(position);
}
return false;
});
}
if (binding.bottomNavView.findViewById(R.id.nav_local) != null) {
binding.bottomNavView.findViewById(R.id.nav_local).setOnLongClickListener(view -> {
int position = BottomMenu.getPosition(bottomMenu, R.id.nav_local);
if (position >= 0) {
manageFilters(position);
}
return false;
});
}
if (binding.bottomNavView.findViewById(R.id.nav_public) != null) {
binding.bottomNavView.findViewById(R.id.nav_public).setOnLongClickListener(view -> {
int position = BottomMenu.getPosition(bottomMenu, R.id.nav_public);
if (position >= 0) {
manageFilters(position);
}
return false;
});
}
binding.bottomNavView.setOnItemSelectedListener(item -> {
int itemId = item.getItemId();
int position = BottomMenu.getPosition(bottomMenu, itemId);
if (position >= 0) {
if (binding.viewPager.getCurrentItem() == position) {
scrollToTop();
} else {
binding.viewPager.setCurrentItem(position);
}
}
return true;
});
}
} else if (b.getBoolean(Helper.RECEIVE_RECREATE_ACTIVITY, false)) {
Cyanea.getInstance().edit().apply().recreate(BaseMainActivity.this);
} else if (b.getBoolean(Helper.RECEIVE_NEW_MESSAGE, false)) {
@ -179,8 +225,6 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
private void mamageNewIntent(Intent intent) {
if (intent == null)
return;
String action = intent.getAction();
String type = intent.getType();
Bundle extras = intent.getExtras();
String userIdIntent, instanceIntent;
if (extras != null && extras.containsKey(Helper.INTENT_ACTION)) {
@ -251,59 +295,10 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
binding.tabLayout.setTabIconTint(ThemeHelper.getColorStateList(BaseMainActivity.this));
binding.compose.setOnClickListener(v -> startActivity(new Intent(this, ComposeActivity.class)));
headerMenuOpen = false;
new BottomMenu(BaseMainActivity.this).hydrate(account, binding.bottomNavView);
binding.bottomNavView.setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
binding.navView.setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
//ManageClick on bottom menu items
binding.bottomNavView.findViewById(R.id.nav_home).setOnLongClickListener(view -> {
manageFilters(0);
return false;
});
binding.bottomNavView.findViewById(R.id.nav_local).setOnLongClickListener(view -> {
manageFilters(1);
return false;
});
binding.bottomNavView.findViewById(R.id.nav_public).setOnLongClickListener(view -> {
manageFilters(2);
return false;
});
binding.bottomNavView.setOnItemSelectedListener(item -> {
int itemId = item.getItemId();
if (itemId == R.id.nav_home) {
if (binding.viewPager.getCurrentItem() == 0) {
scrollToTop();
} else {
binding.viewPager.setCurrentItem(0);
}
} else if (itemId == R.id.nav_local) {
if (binding.viewPager.getCurrentItem() == 1) {
scrollToTop();
} else {
binding.viewPager.setCurrentItem(1);
}
} else if (itemId == R.id.nav_public) {
if (binding.viewPager.getCurrentItem() == 2) {
scrollToTop();
} else {
binding.viewPager.setCurrentItem(2);
}
} else if (itemId == R.id.nav_notifications) {
if (binding.viewPager.getCurrentItem() == 3) {
scrollToTop();
} else {
binding.viewPager.setCurrentItem(3);
}
} else if (itemId == R.id.nav_privates) {
if (binding.viewPager.getCurrentItem() == 4) {
scrollToTop();
} else {
binding.viewPager.setCurrentItem(4);
}
}
return true;
});
// Passing each menu ID as a set of Ids because each
@ -567,6 +562,51 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
finish();
return;
}
bottomMenu = new BottomMenu(BaseMainActivity.this).hydrate(account, binding.bottomNavView);
if (bottomMenu != null) {
//ManageClick on bottom menu items
if (binding.bottomNavView.findViewById(R.id.nav_home) != null) {
binding.bottomNavView.findViewById(R.id.nav_home).setOnLongClickListener(view -> {
int position = BottomMenu.getPosition(bottomMenu, R.id.nav_home);
if (position >= 0) {
manageFilters(position);
}
return false;
});
}
if (binding.bottomNavView.findViewById(R.id.nav_local) != null) {
binding.bottomNavView.findViewById(R.id.nav_local).setOnLongClickListener(view -> {
int position = BottomMenu.getPosition(bottomMenu, R.id.nav_local);
if (position >= 0) {
manageFilters(position);
}
return false;
});
}
if (binding.bottomNavView.findViewById(R.id.nav_public) != null) {
binding.bottomNavView.findViewById(R.id.nav_public).setOnLongClickListener(view -> {
int position = BottomMenu.getPosition(bottomMenu, R.id.nav_public);
if (position >= 0) {
manageFilters(position);
}
return false;
});
}
binding.bottomNavView.setOnItemSelectedListener(item -> {
int itemId = item.getItemId();
int position = BottomMenu.getPosition(bottomMenu, itemId);
if (position >= 0) {
if (binding.viewPager.getCurrentItem() == position) {
scrollToTop();
} else {
binding.viewPager.setCurrentItem(position);
}
}
return true;
});
}
currentInstance = account.instance;
currentUserID = account.user_id;
show_boosts = sharedpreferences.getBoolean(getString(R.string.SET_SHOW_BOOSTS) + currentUserID + currentInstance, true);
@ -611,11 +651,11 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
.observe(this, pinned -> {
this.pinned = pinned;
//First it's taken from db (last stored values)
PinnedTimelineHelper.redrawTopBarPinned(BaseMainActivity.this, binding, pinned, null);
PinnedTimelineHelper.redrawTopBarPinned(BaseMainActivity.this, binding, pinned, bottomMenu, null);
//Fetch remote lists for the authenticated account and update them
new ViewModelProvider(BaseMainActivity.this).get(TimelinesVM.class).getLists(currentInstance, currentToken)
.observe(this, mastodonLists ->
PinnedTimelineHelper.redrawTopBarPinned(BaseMainActivity.this, binding, pinned, mastodonLists)
PinnedTimelineHelper.redrawTopBarPinned(BaseMainActivity.this, binding, pinned, bottomMenu, mastodonLists)
);
});
};
@ -850,7 +890,7 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
.observe(this, pinned -> {
this.pinned = pinned;
//First it's taken from db (last stored values)
PinnedTimelineHelper.redrawTopBarPinned(BaseMainActivity.this, binding, pinned, mastodonLists);
PinnedTimelineHelper.redrawTopBarPinned(BaseMainActivity.this, binding, pinned, bottomMenu, mastodonLists);
binding.viewPager.setCurrentItem(currentItem);
});
}

View file

@ -15,6 +15,7 @@ package app.fedilab.android.activities;
* see <http://www.gnu.org/licenses>. */
import static app.fedilab.android.helper.PinnedTimelineHelper.sortMenuItem;
import static app.fedilab.android.helper.PinnedTimelineHelper.sortPositionAsc;
import android.content.Intent;
@ -47,6 +48,7 @@ import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
import app.fedilab.android.R;
import app.fedilab.android.client.entities.BottomMenu;
import app.fedilab.android.client.entities.InstanceSocial;
import app.fedilab.android.client.entities.Pinned;
import app.fedilab.android.client.entities.Timeline;
@ -60,6 +62,7 @@ import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.helper.itemtouchhelper.OnStartDragListener;
import app.fedilab.android.helper.itemtouchhelper.OnUndoListener;
import app.fedilab.android.helper.itemtouchhelper.SimpleItemTouchHelperCallback;
import app.fedilab.android.ui.drawer.ReorderBottomMenuAdapter;
import app.fedilab.android.ui.drawer.ReorderTabAdapter;
import app.fedilab.android.viewmodel.mastodon.InstanceSocialVM;
import app.fedilab.android.viewmodel.mastodon.ReorderVM;
@ -75,12 +78,23 @@ public class ReorderTimelinesActivity extends BaseActivity implements OnStartDra
private ItemTouchHelper touchHelper;
private ReorderTabAdapter adapter;
private ReorderTabAdapter reorderTabAdapter;
private ReorderBottomMenuAdapter reorderBottomMenuAdapter;
private boolean searchInstanceRunning;
private String oldSearch;
private Pinned pinned;
private BottomMenu bottomMenu;
private ActivityReorderTabsBinding binding;
private boolean changes;
private boolean bottomChanges;
public void setChanges(boolean changes) {
this.changes = changes;
}
public void setBottomChanges(boolean bottomChanges) {
this.bottomChanges = bottomChanges;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -95,6 +109,7 @@ public class ReorderTimelinesActivity extends BaseActivity implements OnStartDra
}
changes = false;
bottomChanges = false;
ReorderVM reorderVM = new ViewModelProvider(ReorderTimelinesActivity.this).get(ReorderVM.class);
reorderVM.getPinned().observe(ReorderTimelinesActivity.this, _pinned -> {
this.pinned = _pinned;
@ -103,16 +118,31 @@ public class ReorderTimelinesActivity extends BaseActivity implements OnStartDra
this.pinned.pinnedTimelines = new ArrayList<>();
}
sortPositionAsc(this.pinned.pinnedTimelines);
adapter = new ReorderTabAdapter(this.pinned, ReorderTimelinesActivity.this, ReorderTimelinesActivity.this);
reorderTabAdapter = new ReorderTabAdapter(this.pinned, ReorderTimelinesActivity.this, ReorderTimelinesActivity.this);
ItemTouchHelper.Callback callback =
new SimpleItemTouchHelperCallback(adapter);
new SimpleItemTouchHelperCallback(reorderTabAdapter);
touchHelper = new ItemTouchHelper(callback);
touchHelper.attachToRecyclerView(binding.lvReorderTabs);
binding.lvReorderTabs.setAdapter(adapter);
binding.lvReorderTabs.setAdapter(reorderTabAdapter);
LinearLayoutManager mLayoutManager = new LinearLayoutManager(ReorderTimelinesActivity.this);
binding.lvReorderTabs.setLayoutManager(mLayoutManager);
});
reorderVM.getBottomMenu().observe(ReorderTimelinesActivity.this, _bottomMenu -> {
this.bottomMenu = _bottomMenu;
if (this.bottomMenu == null) {
this.bottomMenu = new BottomMenu(getApplicationContext()).defaultBottomMenu();
this.bottomMenu.bottom_menu = new ArrayList<>();
}
sortMenuItem(this.bottomMenu.bottom_menu);
reorderBottomMenuAdapter = new ReorderBottomMenuAdapter(this.bottomMenu, ReorderTimelinesActivity.this);
ItemTouchHelper.Callback callback =
new SimpleItemTouchHelperCallback(reorderBottomMenuAdapter);
touchHelper = new ItemTouchHelper(callback);
touchHelper.attachToRecyclerView(binding.lvReorderBottom);
binding.lvReorderBottom.setAdapter(reorderBottomMenuAdapter);
LinearLayoutManager mLayoutManager = new LinearLayoutManager(ReorderTimelinesActivity.this);
binding.lvReorderBottom.setLayoutManager(mLayoutManager);
});
}
@Override
@ -209,7 +239,7 @@ public class ReorderTimelinesActivity extends BaseActivity implements OnStartDra
try {
new Pinned(ReorderTimelinesActivity.this).updatePinned(pinned);
changes = true;
adapter.notifyItemInserted(pinned.pinnedTimelines.size());
reorderTabAdapter.notifyItemInserted(pinned.pinnedTimelines.size());
} catch (DBException e) {
e.printStackTrace();
}
@ -299,6 +329,13 @@ public class ReorderTimelinesActivity extends BaseActivity implements OnStartDra
intentBD.putExtras(b);
LocalBroadcastManager.getInstance(ReorderTimelinesActivity.this).sendBroadcast(intentBD);
}
if (bottomChanges) {
Bundle b = new Bundle();
b.putBoolean(Helper.RECEIVE_REDRAW_BOTTOM, true);
Intent intentBD = new Intent(Helper.BROADCAST_DATA);
intentBD.putExtras(b);
LocalBroadcastManager.getInstance(ReorderTimelinesActivity.this).sendBroadcast(intentBD);
}
}
@Override
@ -329,7 +366,7 @@ public class ReorderTimelinesActivity extends BaseActivity implements OnStartDra
pinned.pinnedTimelines.get(i).position -= 1;
}
pinned.pinnedTimelines.remove(pinnedTimeline);
adapter.notifyItemRemoved(position);
reorderTabAdapter.notifyItemRemoved(position);
try {
new Pinned(ReorderTimelinesActivity.this).updatePinned(pinned);
changes = true;
@ -341,7 +378,7 @@ public class ReorderTimelinesActivity extends BaseActivity implements OnStartDra
handler.postDelayed(runnable, 4000);
binding.undoAction.setOnClickListener(v -> {
pinned.pinnedTimelines.add(position, pinnedTimeline);
adapter.notifyItemInserted(position);
reorderTabAdapter.notifyItemInserted(position);
binding.undoContainer.setVisibility(View.GONE);
handler.removeCallbacks(runnable);
});

View file

@ -20,6 +20,8 @@ import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.view.Menu;
import androidx.annotation.IdRes;
import com.google.android.material.bottomnavigation.BottomNavigationView;
import com.google.gson.Gson;
import com.google.gson.annotations.SerializedName;
@ -31,6 +33,7 @@ import java.util.List;
import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R;
import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.exception.DBException;
import app.fedilab.android.sqlite.Sqlite;
@ -89,59 +92,62 @@ public class BottomMenu implements Serializable {
}
}
public void hydrate(Account account, BottomNavigationView bottomNavigationView) {
public static int getPosition(BottomMenu bottomMenu, @IdRes int idRes) {
for (MenuItem menuItem : bottomMenu.bottom_menu) {
if (idRes == R.id.nav_home && menuItem.item_menu_type == ItemMenuType.HOME) {
return menuItem.position;
} else if (idRes == R.id.nav_local && menuItem.item_menu_type == ItemMenuType.LOCAL) {
return menuItem.position;
} else if (idRes == R.id.nav_public && menuItem.item_menu_type == ItemMenuType.PUBLIC) {
return menuItem.position;
} else if (idRes == R.id.nav_notifications && menuItem.item_menu_type == ItemMenuType.NOTIFICATION) {
return menuItem.position;
} else if (idRes == R.id.nav_privates && menuItem.item_menu_type == ItemMenuType.DIRECT) {
return menuItem.position;
}
}
return -1;
}
public static ItemMenuType getType(BottomMenu bottomMenu, int position) {
if (bottomMenu == null || bottomMenu.bottom_menu == null || bottomMenu.bottom_menu.size() < position) {
return null;
}
return bottomMenu.bottom_menu.get(position).item_menu_type;
}
public BottomMenu hydrate(Account account, BottomNavigationView bottomNavigationView) {
bottomNavigationView.getMenu().clear();
BottomMenu bottomMenu;
try {
bottomMenu = getBottomMenu(account);
bottomMenu = getAllBottomMenu(account);
} catch (DBException e) {
bottomMenu = defaultBottomMenu();
}
for (BottomMenu.MenuItem menuItem : bottomMenu.bottom_menu) {
if (menuItem.visible) {
switch (menuItem.item_menu_type) {
case HOME:
bottomNavigationView.getMenu().add(Menu.NONE, R.id.nav_home, menuItem.position, context.getString(R.string.home_menu)).setIcon(R.drawable.ic_baseline_home_24);
break;
case LOCAL:
bottomNavigationView.getMenu().add(Menu.NONE, R.id.nav_local, menuItem.position, context.getString(R.string.local_menu)).setIcon(R.drawable.ic_baseline_people_alt_24);
break;
case PUBLIC:
bottomNavigationView.getMenu().add(Menu.NONE, R.id.nav_public, menuItem.position, context.getString(R.string.v_public)).setIcon(R.drawable.ic_baseline_public_24);
break;
case NOTIFICATION:
bottomNavigationView.getMenu().add(Menu.NONE, R.id.nav_notifications, menuItem.position, context.getString(R.string.notifications)).setIcon(R.drawable.ic_baseline_notifications_24);
break;
case DIRECT:
bottomNavigationView.getMenu().add(Menu.NONE, R.id.nav_privates, menuItem.position, context.getString(R.string.v_private)).setIcon(R.drawable.ic_baseline_mail_24);
break;
}
android.view.MenuItem menuItemLoop = null;
switch (menuItem.item_menu_type) {
case HOME:
menuItemLoop = bottomNavigationView.getMenu().add(Menu.NONE, R.id.nav_home, menuItem.position, context.getString(R.string.home_menu)).setIcon(R.drawable.ic_baseline_home_24);
break;
case LOCAL:
menuItemLoop = bottomNavigationView.getMenu().add(Menu.NONE, R.id.nav_local, menuItem.position, context.getString(R.string.local_menu)).setIcon(R.drawable.ic_baseline_people_alt_24);
break;
case PUBLIC:
menuItemLoop = bottomNavigationView.getMenu().add(Menu.NONE, R.id.nav_public, menuItem.position, context.getString(R.string.v_public)).setIcon(R.drawable.ic_baseline_public_24);
break;
case NOTIFICATION:
menuItemLoop = bottomNavigationView.getMenu().add(Menu.NONE, R.id.nav_notifications, menuItem.position, context.getString(R.string.notifications)).setIcon(R.drawable.ic_baseline_notifications_24);
break;
case DIRECT:
menuItemLoop = bottomNavigationView.getMenu().add(Menu.NONE, R.id.nav_privates, menuItem.position, context.getString(R.string.v_private)).setIcon(R.drawable.ic_baseline_mail_24);
break;
}
if (menuItemLoop != null && !menuItem.visible) {
menuItemLoop.setVisible(false);
}
}
}
/**
* Insert or update instance
*
* @param bottomMenu {@link BottomMenu}
* @return long - db id
* @throws DBException exception with database
*/
public long insertOrUpdate(BottomMenu bottomMenu) throws DBException {
if (db == null) {
throw new DBException("db is null. Wrong initialization.");
}
if (bottomMenu == null) {
return -1;
}
boolean exists = bottomMenuExists(bottomMenu);
long idReturned;
if (exists) {
idReturned = updateBottomMenu(bottomMenu);
} else {
idReturned = insertBottomMenu(bottomMenu);
}
return idReturned;
return bottomMenu;
}
/**
@ -192,13 +198,60 @@ public class BottomMenu implements Serializable {
}
}
/**
* Insert or update instance
*
* @param bottomMenu {@link BottomMenu}
* @return long - db id
* @throws DBException exception with database
*/
public long insertOrUpdate(BottomMenu bottomMenu) throws DBException {
if (db == null) {
throw new DBException("db is null. Wrong initialization.");
}
if (bottomMenu == null) {
return -1;
}
if (bottomMenu.user_id == null) {
bottomMenu.user_id = MainActivity.currentUserID;
bottomMenu.instance = MainActivity.currentInstance;
}
boolean exists = bottomMenuExists(bottomMenu);
long idReturned;
if (exists) {
idReturned = updateBottomMenu(bottomMenu);
} else {
idReturned = insertBottomMenu(bottomMenu);
}
return idReturned;
}
/**
* Returns the bottom menu for an account
*
* @param account Account
* @return Pinned - {@link BottomMenu}
* @return BottomMenu - {@link BottomMenu}
*/
private BottomMenu getBottomMenu(Account account) throws DBException {
public BottomMenu getAllBottomMenu(Account account) throws DBException {
if (db == null) {
throw new DBException("db is null. Wrong initialization.");
}
try {
Cursor c = db.query(Sqlite.TABLE_BOTTOM_MENU, null, Sqlite.COL_INSTANCE + " = '" + account.instance + "' AND " + Sqlite.COL_USER_ID + " = '" + account.user_id + "'", null, null, null, Sqlite.COL_ID + " DESC", "1");
return cursorToBottomMenu(c);
} catch (Exception e) {
e.printStackTrace();
return defaultBottomMenu();
}
}
/**
* Returns the bottom menu for an account
*
* @param account Account
* @return BottomMenu - {@link BottomMenu}
*/
public BottomMenu getBottomMenu(Account account) throws DBException {
if (db == null) {
throw new DBException("db is null. Wrong initialization.");
}
@ -207,9 +260,12 @@ public class BottomMenu implements Serializable {
BottomMenu bottomMenu = cursorToBottomMenu(c);
List<MenuItem> menuItemList = new ArrayList<>();
if (bottomMenu != null) {
int inc = 0;
for (MenuItem menuItem : bottomMenu.bottom_menu) {
if (menuItem.visible) {
menuItemList.add(menuItem.position, menuItem);
menuItem.position = inc;
menuItemList.add(menuItem);
inc++;
}
}
bottomMenu.bottom_menu = menuItemList;
@ -219,11 +275,24 @@ public class BottomMenu implements Serializable {
}
return bottomMenu;
} catch (Exception e) {
e.printStackTrace();
return defaultBottomMenu();
}
}
private BottomMenu defaultBottomMenu() {
public boolean bottomMenuExists(BottomMenu bottomMenu) throws DBException {
if (db == null) {
throw new DBException("db is null. Wrong initialization.");
}
Cursor mCount = db.rawQuery("select count(*) from " + Sqlite.TABLE_BOTTOM_MENU
+ " where " + Sqlite.COL_INSTANCE + " = '" + bottomMenu.instance + "' AND " + Sqlite.COL_USER_ID + " = '" + bottomMenu.user_id + "'", null);
mCount.moveToFirst();
int count = mCount.getInt(0);
mCount.close();
return (count > 0);
}
public BottomMenu defaultBottomMenu() {
BottomMenu bottomMenu = new BottomMenu();
bottomMenu.bottom_menu = new ArrayList<>();
MenuItem menuItemHome = new MenuItem();
@ -254,18 +323,6 @@ public class BottomMenu implements Serializable {
return bottomMenu;
}
public boolean bottomMenuExists(BottomMenu bottomMenu) throws DBException {
if (db == null) {
throw new DBException("db is null. Wrong initialization.");
}
Cursor mCount = db.rawQuery("select count(*) from " + Sqlite.TABLE_BOTTOM_MENU
+ " where " + Sqlite.COL_INSTANCE + " = '" + bottomMenu.instance + "' AND " + Sqlite.COL_USER_ID + " = '" + bottomMenu.user_id + "'", null);
mCount.moveToFirst();
int count = mCount.getInt(0);
mCount.close();
return (count > 0);
}
/**
* Restore pinned from db
*
@ -325,10 +382,10 @@ public class BottomMenu implements Serializable {
public static class MenuItem {
@SerializedName("position")
int position;
public int position;
@SerializedName("item_menu_type")
ItemMenuType item_menu_type;
public ItemMenuType item_menu_type;
@SerializedName("visible")
boolean visible;
public boolean visible;
}
}

View file

@ -172,6 +172,25 @@ public class Pinned implements Serializable {
}
}
/**
* Returns the pinned timeline for an account
*
* @param account Account
* @return Pinned - {@link Pinned}
*/
public Pinned getAllPinned(Account account) throws DBException {
if (db == null) {
throw new DBException("db is null. Wrong initialization.");
}
try {
Cursor c = db.query(Sqlite.TABLE_PINNED_TIMELINES, null, Sqlite.COL_INSTANCE + " = '" + account.instance + "' AND " + Sqlite.COL_USER_ID + " = '" + account.user_id + "'", null, null, null, Sqlite.COL_UPDATED_AT + " DESC", "1");
return cursorToPined(c);
} catch (Exception e) {
return null;
}
}
public boolean pinnedExist(Pinned pinned) throws DBException {
if (db == null) {
throw new DBException("db is null. Wrong initialization.");

View file

@ -163,6 +163,8 @@ public class Helper {
public static final String BROADCAST_DATA = "BROADCAST_DATA";
public static final String RECEIVE_REDRAW_TOPBAR = "RECEIVE_REDRAW_TOPBAR";
public static final String RECEIVE_REDRAW_BOTTOM = "RECEIVE_REDRAW_BOTTOM";
public static final String RECEIVE_STATUS_ACTION = "RECEIVE_STATUS_ACTION";
public static final String RECEIVE_RECREATE_ACTIVITY = "RECEIVE_RECREATE_ACTIVITY";

View file

@ -43,6 +43,7 @@ import java.util.List;
import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R;
import app.fedilab.android.client.entities.BottomMenu;
import app.fedilab.android.client.entities.Pinned;
import app.fedilab.android.client.entities.Timeline;
import app.fedilab.android.client.entities.app.PinnedTimeline;
@ -63,7 +64,12 @@ public class PinnedTimelineHelper {
Collections.sort(pinnedTimelineList, (obj1, obj2) -> Integer.compare(obj1.position, obj2.position));
}
public synchronized static void redrawTopBarPinned(BaseMainActivity activity, ActivityMainBinding activityMainBinding, Pinned pinned, List<MastodonList> mastodonLists) {
public static void sortMenuItem(List<BottomMenu.MenuItem> menuItemList) {
//noinspection ComparatorCombinators
Collections.sort(menuItemList, (obj1, obj2) -> Integer.compare(obj1.position, obj2.position));
}
public synchronized static void redrawTopBarPinned(BaseMainActivity activity, ActivityMainBinding activityMainBinding, Pinned pinned, BottomMenu bottomMenu, List<MastodonList> mastodonLists) {
//Values must be initialized if there is no records in db
if (pinned == null) {
pinned = new Pinned();
@ -93,16 +99,16 @@ public class PinnedTimelineHelper {
if (!present) {
pinnedToRemove.add(pinnedTimeline);
needRedraw = true; //Something changed, redraw must be done
try {
new Pinned(activity).updatePinned(pinned);
} catch (DBException e) {
e.printStackTrace();
}
}
}
}
if (pinnedToRemove.size() > 0) {
pinned.pinnedTimelines.removeAll(pinnedToRemove);
try {
new Pinned(activity).updatePinned(pinned);
} catch (DBException e) {
e.printStackTrace();
}
}
for (MastodonList mastodonList : mastodonLists) {
@ -197,7 +203,7 @@ public class PinnedTimelineHelper {
activityMainBinding.viewPager.clearOnPageChangeListeners();
activityMainBinding.tabLayout.clearOnTabSelectedListeners();
FedilabPageAdapter fedilabPageAdapter = new FedilabPageAdapter(activity.getSupportFragmentManager(), pinned);
FedilabPageAdapter fedilabPageAdapter = new FedilabPageAdapter(activity.getSupportFragmentManager(), pinned, bottomMenu);
activityMainBinding.viewPager.setAdapter(fedilabPageAdapter);
activityMainBinding.viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(activityMainBinding.tabLayout));
activityMainBinding.viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {

View file

@ -0,0 +1,184 @@
package app.fedilab.android.ui.drawer;
/* Copyright 2022 Thomas Schneider
*
* This file is a part of Fedilab
*
* 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.
*
* Fedilab 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 Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
import android.annotation.SuppressLint;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.ViewGroup;
import android.widget.Toast;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
import app.fedilab.android.R;
import app.fedilab.android.activities.ReorderTimelinesActivity;
import app.fedilab.android.client.entities.BottomMenu;
import app.fedilab.android.databinding.DrawerReorderBinding;
import app.fedilab.android.exception.DBException;
import app.fedilab.android.helper.itemtouchhelper.ItemTouchHelperAdapter;
import app.fedilab.android.helper.itemtouchhelper.ItemTouchHelperViewHolder;
import app.fedilab.android.helper.itemtouchhelper.OnStartDragListener;
import es.dmoral.toasty.Toasty;
/**
* Simple RecyclerView.Adapter that implements {@link ItemTouchHelperAdapter} to respond to move and
* dismiss events from a {@link androidx.recyclerview.widget.ItemTouchHelper}.
*
* @author Paul Burke (ipaulpro)
*/
public class ReorderBottomMenuAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements ItemTouchHelperAdapter {
private final OnStartDragListener mDragStartListener;
private final BottomMenu bottomMenu;
private Context context;
public ReorderBottomMenuAdapter(BottomMenu bottomMenu, OnStartDragListener dragStartListener) {
this.mDragStartListener = dragStartListener;
this.bottomMenu = bottomMenu;
}
@NotNull
@Override
public ReorderViewHolder onCreateViewHolder(@NotNull ViewGroup parent, int viewType) {
context = parent.getContext();
DrawerReorderBinding itemBinding = DrawerReorderBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
return new ReorderViewHolder(itemBinding);
}
@SuppressLint("ClickableViewAccessibility")
@Override
public void onBindViewHolder(@NotNull final RecyclerView.ViewHolder viewHolder, int position) {
ReorderViewHolder holder = (ReorderViewHolder) viewHolder;
String title = "";
switch (bottomMenu.bottom_menu.get(position).item_menu_type) {
case HOME:
holder.binding.icon.setImageResource(R.drawable.ic_baseline_home_24);
title = context.getString(R.string.home_menu);
break;
case LOCAL:
holder.binding.icon.setImageResource(R.drawable.ic_baseline_people_alt_24);
title = context.getString(R.string.local);
break;
case PUBLIC:
holder.binding.icon.setImageResource(R.drawable.ic_baseline_public_24);
title = context.getString(R.string.v_public);
break;
case NOTIFICATION:
holder.binding.icon.setImageResource(R.drawable.ic_baseline_notifications_24);
title = context.getString(R.string.notifications);
break;
case DIRECT:
holder.binding.icon.setImageResource(R.drawable.ic_baseline_mail_24);
title = context.getString(R.string.v_private);
break;
}
holder.binding.text.setText(title);
if (bottomMenu.bottom_menu.get(position).visible) {
holder.binding.hide.setImageResource(R.drawable.ic_baseline_visibility_24);
} else {
holder.binding.hide.setImageResource(R.drawable.ic_baseline_visibility_off_24);
}
holder.binding.hide.setOnClickListener(v -> {
bottomMenu.bottom_menu.get(position).visible = !bottomMenu.bottom_menu.get(position).visible;
if (bottomMenu.bottom_menu.get(position).visible) {
holder.binding.hide.setImageResource(R.drawable.ic_baseline_visibility_24);
} else {
holder.binding.hide.setImageResource(R.drawable.ic_baseline_visibility_off_24);
}
new Thread(() -> {
try {
new BottomMenu(context).insertOrUpdate(bottomMenu);
((ReorderTimelinesActivity) context).setBottomChanges(true);
} catch (DBException e) {
e.printStackTrace();
}
}).start();
});
// Start a drag whenever the handle view it touched
holder.binding.handle.setOnTouchListener((v, event) -> {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
mDragStartListener.onStartDrag(holder);
return true;
}
return false;
});
}
@Override
public void onItemDismiss(int position) {
BottomMenu.MenuItem menuItem = bottomMenu.bottom_menu.get(position);
notifyItemChanged(position);
Toasty.info(context, context.getString(R.string.warning_main_timeline), Toast.LENGTH_SHORT).show();
}
@Override
public boolean onItemMove(int fromPosition, int toPosition) {
Collections.swap(bottomMenu.bottom_menu, fromPosition, toPosition);
//update position value
for (int j = 0; j < bottomMenu.bottom_menu.size(); j++) {
bottomMenu.bottom_menu.get(j).position = j;
}
notifyItemMoved(fromPosition, toPosition);
try {
new BottomMenu(context).insertOrUpdate(bottomMenu);
((ReorderTimelinesActivity) context).setBottomChanges(true);
} catch (DBException e) {
e.printStackTrace();
}
return true;
}
@Override
public int getItemCount() {
return bottomMenu.bottom_menu.size();
}
/**
* Simple example of a view holder that implements {@link ItemTouchHelperViewHolder} and has a
* "handle" view that initiates a drag event when touched.
*/
public class ReorderViewHolder extends RecyclerView.ViewHolder implements ItemTouchHelperViewHolder {
DrawerReorderBinding binding;
ReorderViewHolder(DrawerReorderBinding itemView) {
super(itemView.getRoot());
binding = itemView;
}
@Override
public void onItemSelected() {
itemView.setBackgroundColor(ContextCompat.getColor(context, R.color.mastodonC3));
}
@Override
public void onItemClear() {
itemView.setBackgroundColor(0);
}
}
}

View file

@ -30,6 +30,7 @@ import org.jetbrains.annotations.NotNull;
import java.util.Collections;
import app.fedilab.android.R;
import app.fedilab.android.activities.ReorderTimelinesActivity;
import app.fedilab.android.client.entities.Pinned;
import app.fedilab.android.client.entities.Timeline;
import app.fedilab.android.client.entities.app.PinnedTimeline;
@ -121,14 +122,11 @@ public class ReorderTabAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
holder.binding.hide.setOnClickListener(v -> {
pinned.pinnedTimelines.get(position).displayed = !pinned.pinnedTimelines.get(position).displayed;
if (pinned.pinnedTimelines.get(position).displayed) {
holder.binding.hide.setImageResource(R.drawable.ic_baseline_visibility_24);
} else {
holder.binding.hide.setImageResource(R.drawable.ic_baseline_visibility_off_24);
}
notifyItemChanged(position);
new Thread(() -> {
try {
new Pinned(context).updatePinned(pinned);
((ReorderTimelinesActivity) context).setChanges(true);
} catch (DBException e) {
e.printStackTrace();
}
@ -169,6 +167,7 @@ public class ReorderTabAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
notifyItemMoved(fromPosition, toPosition);
try {
new Pinned(context).updatePinned(pinned);
((ReorderTimelinesActivity) context).setChanges(true);
} catch (DBException e) {
e.printStackTrace();
}

View file

@ -22,6 +22,7 @@ import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentStatePagerAdapter;
import app.fedilab.android.client.entities.BottomMenu;
import app.fedilab.android.client.entities.Pinned;
import app.fedilab.android.client.entities.Timeline;
import app.fedilab.android.client.entities.app.PinnedTimeline;
@ -34,11 +35,13 @@ public class FedilabPageAdapter extends FragmentStatePagerAdapter {
public static final int BOTTOM_TIMELINE_COUNT = 5; //home, local, public, notification, DM
private final Pinned pinned;
private final BottomMenu bottomMenu;
private Fragment mCurrentFragment;
public FedilabPageAdapter(FragmentManager fm, Pinned pinned) {
public FedilabPageAdapter(FragmentManager fm, Pinned pinned, BottomMenu bottomMenu) {
super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
this.pinned = pinned;
this.bottomMenu = bottomMenu;
}
public Fragment getCurrentFragment() {
@ -62,39 +65,49 @@ public class FedilabPageAdapter extends FragmentStatePagerAdapter {
@NonNull
@Override
public Fragment getItem(int position) {
FragmentMastodonTimeline fragment = new FragmentMastodonTimeline();
Bundle bundle = new Bundle();
//Position 3 is for notifications
if (position == 3) {
return new FragmentNotificationContainer();
} else if (position == 4) { //Position 4 is for DM
return new FragmentMastodonConversation();
} else {
FragmentMastodonTimeline fragment = new FragmentMastodonTimeline();
Bundle bundle = new Bundle();
if (position == 0) { //Home timeline
bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, Timeline.TimeLineEnum.HOME);
} else if (position == 1) { //Local timeline
bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, Timeline.TimeLineEnum.LOCAL);
} else if (position == 2) { //Public timeline
bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, Timeline.TimeLineEnum.PUBLIC);
} else { //Pinned timeline
int pinnedPosition = position - BOTTOM_TIMELINE_COUNT; //Real position has an offset.
PinnedTimeline pinnedTimeline = pinned.pinnedTimelines.get(pinnedPosition);
bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, pinnedTimeline.type);
if (pinnedTimeline.type == Timeline.TimeLineEnum.LIST) {
bundle.putString(Helper.ARG_LIST_ID, pinnedTimeline.mastodonList.id);
} else if (pinnedTimeline.type == Timeline.TimeLineEnum.TAG) {
bundle.putSerializable(Helper.ARG_TAG_TIMELINE, pinnedTimeline.tagTimeline);
} else if (pinnedTimeline.type == Timeline.TimeLineEnum.REMOTE) {
String instance = pinnedTimeline.remoteInstance.host;
bundle.putString(Helper.ARG_REMOTE_INSTANCE, instance);
if (position < 5) {
if (bottomMenu != null) {
BottomMenu.ItemMenuType type = BottomMenu.getType(bottomMenu, position);
if (type == null) {
return fragment;
}
if (type == BottomMenu.ItemMenuType.NOTIFICATION) {
return new FragmentNotificationContainer();
} else if (type == BottomMenu.ItemMenuType.DIRECT) {
return new FragmentMastodonConversation();
}
if (type == BottomMenu.ItemMenuType.HOME) { //Home timeline
bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, Timeline.TimeLineEnum.HOME);
} else if (type == BottomMenu.ItemMenuType.LOCAL) { //Local timeline
bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, Timeline.TimeLineEnum.LOCAL);
} else if (type == BottomMenu.ItemMenuType.PUBLIC) { //Public timeline
bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, Timeline.TimeLineEnum.PUBLIC);
}
} else {
return fragment;
}
bundle.putString(Helper.ARG_VIEW_MODEL_KEY, "FEDILAB_" + position);
fragment.setArguments(bundle);
return fragment;
} else {
int pinnedPosition = position - BOTTOM_TIMELINE_COUNT; //Real position has an offset.
PinnedTimeline pinnedTimeline = pinned.pinnedTimelines.get(pinnedPosition);
bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, pinnedTimeline.type);
if (pinnedTimeline.type == Timeline.TimeLineEnum.LIST) {
bundle.putString(Helper.ARG_LIST_ID, pinnedTimeline.mastodonList.id);
} else if (pinnedTimeline.type == Timeline.TimeLineEnum.TAG) {
bundle.putSerializable(Helper.ARG_TAG_TIMELINE, pinnedTimeline.tagTimeline);
} else if (pinnedTimeline.type == Timeline.TimeLineEnum.REMOTE) {
String instance = pinnedTimeline.remoteInstance.host;
bundle.putString(Helper.ARG_REMOTE_INSTANCE, instance);
}
}
bundle.putString(Helper.ARG_VIEW_MODEL_KEY, "FEDILAB_" + position);
fragment.setArguments(bundle);
return fragment;
}
@Override

View file

@ -28,6 +28,7 @@ import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.client.entities.BottomMenu;
import app.fedilab.android.client.entities.Pinned;
import app.fedilab.android.client.mastodon.MastodonSearchService;
import app.fedilab.android.client.mastodon.entities.Results;
@ -48,6 +49,7 @@ public class ReorderVM extends AndroidViewModel {
.build();
private MutableLiveData<Results> resultsMutableLiveData;
private MutableLiveData<Pinned> pinnedMutableLiveData;
private MutableLiveData<BottomMenu> bottomMenuMutableLiveData;
public ReorderVM(@NonNull Application application) {
super(application);
@ -67,7 +69,7 @@ public class ReorderVM extends AndroidViewModel {
new Thread(() -> {
Pinned pinned = null;
try {
pinned = new Pinned(getApplication().getApplicationContext()).getPinned(BaseMainActivity.accountWeakReference.get());
pinned = new Pinned(getApplication().getApplicationContext()).getAllPinned(BaseMainActivity.accountWeakReference.get());
} catch (DBException e) {
e.printStackTrace();
}
@ -80,6 +82,23 @@ public class ReorderVM extends AndroidViewModel {
}
public LiveData<BottomMenu> getBottomMenu() {
bottomMenuMutableLiveData = new MutableLiveData<>();
new Thread(() -> {
BottomMenu bottomMenu = null;
try {
bottomMenu = new BottomMenu(getApplication().getApplicationContext()).getAllBottomMenu(BaseMainActivity.accountWeakReference.get());
} catch (DBException e) {
e.printStackTrace();
}
Handler mainHandler = new Handler(Looper.getMainLooper());
BottomMenu finalBottomMenu = bottomMenu;
Runnable myRunnable = () -> bottomMenuMutableLiveData.setValue(finalBottomMenu);
mainHandler.post(myRunnable);
}).start();
return bottomMenuMutableLiveData;
}
/**
* Search for content in accounts, statuses and hashtags with API v2
*

View file

@ -14,60 +14,84 @@
You should have received a copy of the GNU General Public License along with Fedilab; if not,
see <http://www.gnu.org/licenses>.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<ScrollView 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">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/lv_reorder_bottom"
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="none" />
android:layout_height="wrap_content">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/lv_reorder_tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="none" />
<RelativeLayout
android:id="@+id/undo_container"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_alignParentBottom="true"
android:layout_marginBottom="0dp"
android:background="@color/cyanea_primary_reference"
android:visibility="gone">
<TextView
android:id="@+id/undo_message"
android:layout_width="wrap_content"
<androidx.cardview.widget.CardView
android:id="@+id/lv_reorder_bottom_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_centerInParent="true"
android:layout_marginStart="10dp"
android:paddingLeft="20dp"
android:paddingTop="5dp"
android:paddingRight="20dp"
android:paddingBottom="5dp"
android:textColor="@color/cyanea_accent_dark_reference"
android:textSize="14sp" />
android:layout_marginTop="20dp"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/undo_action"
android:layout_width="wrap_content"
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/lv_reorder_bottom"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="none" />
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerInParent="true"
android:layout_marginEnd="10dp"
android:paddingLeft="20dp"
android:paddingTop="5dp"
android:paddingRight="20dp"
android:paddingBottom="5dp"
android:text="@string/undo"
android:textAllCaps="true"
android:textColor="@color/cyanea_accent_dark_reference"
android:textSize="18sp" />
</RelativeLayout>
</RelativeLayout>
android:layout_marginTop="20dp"
app:layout_constraintTop_toBottomOf="@+id/lv_reorder_bottom_container">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/lv_reorder_tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="none" />
</androidx.cardview.widget.CardView>
<RelativeLayout
android:id="@+id/undo_container"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginBottom="0dp"
android:background="@color/cyanea_primary_reference"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent">
<TextView
android:id="@+id/undo_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_centerInParent="true"
android:layout_marginStart="10dp"
android:paddingLeft="20dp"
android:paddingTop="5dp"
android:paddingRight="20dp"
android:paddingBottom="5dp"
android:textColor="@color/cyanea_accent_dark_reference"
android:textSize="14sp" />
<TextView
android:id="@+id/undo_action"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerInParent="true"
android:layout_marginEnd="10dp"
android:paddingLeft="20dp"
android:paddingTop="5dp"
android:paddingRight="20dp"
android:paddingBottom="5dp"
android:text="@string/undo"
android:textAllCaps="true"
android:textColor="@color/cyanea_accent_dark_reference"
android:textSize="18sp" />
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>