diff --git a/app/src/main/java/app/fedilab/android/BaseMainActivity.java b/app/src/main/java/app/fedilab/android/BaseMainActivity.java index fc6825c3f..eae375ddd 100644 --- a/app/src/main/java/app/fedilab/android/BaseMainActivity.java +++ b/app/src/main/java/app/fedilab/android/BaseMainActivity.java @@ -96,6 +96,7 @@ import app.fedilab.android.activities.SearchResultTabActivity; import app.fedilab.android.activities.SettingsActivity; import app.fedilab.android.broadcastreceiver.NetworkStateReceiver; import app.fedilab.android.client.entities.Account; +import app.fedilab.android.client.entities.BottomMenu; import app.fedilab.android.client.entities.Pinned; import app.fedilab.android.client.entities.app.PinnedTimeline; import app.fedilab.android.client.mastodon.entities.Filter; @@ -250,7 +251,7 @@ 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; - binding.bottomNavView.inflateMenu(R.menu.bottom_nav_menu); + 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))); diff --git a/app/src/main/java/app/fedilab/android/client/entities/BottomMenu.java b/app/src/main/java/app/fedilab/android/client/entities/BottomMenu.java new file mode 100644 index 000000000..9ee4b3b97 --- /dev/null +++ b/app/src/main/java/app/fedilab/android/client/entities/BottomMenu.java @@ -0,0 +1,334 @@ +package app.fedilab.android.client.entities; +/* 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 . */ + +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.view.Menu; + +import com.google.android.material.bottomnavigation.BottomNavigationView; +import com.google.gson.Gson; +import com.google.gson.annotations.SerializedName; +import com.google.gson.reflect.TypeToken; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +import app.fedilab.android.BaseMainActivity; +import app.fedilab.android.R; +import app.fedilab.android.exception.DBException; +import app.fedilab.android.sqlite.Sqlite; + + +public class BottomMenu implements Serializable { + private final SQLiteDatabase db; + + @SerializedName("id") + public long id = -1; + @SerializedName("instance") + public String instance; + @SerializedName("user_id") + public String user_id; + @SerializedName("bottom_menu") + public List bottom_menu; + private Context context; + + public BottomMenu() { + db = null; + } + + public BottomMenu(Context context) { + //Creation of the DB with tables + this.context = context; + this.db = Sqlite.getInstance(context.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); + } + + /** + * Serialized a list of MenuItem class + * + * @param menuItemList List of {@link MenuItem} to serialize + * @return String serialized menuItemList list + */ + public static String menuItemListToStringStorage(List menuItemList) { + Gson gson = new Gson(); + try { + return gson.toJson(menuItemList); + } catch (Exception e) { + return null; + } + } + + /** + * Unserialized a MenuItem List + * + * @param serializedMenuItem String serialized MenuItem list + * @return List of {@link MenuItem} + */ + public static List restoreMenuItemFromString(String serializedMenuItem) { + Gson gson = new Gson(); + try { + return gson.fromJson(serializedMenuItem, new TypeToken>() { + }.getType()); + } catch (Exception e) { + return null; + } + } + + public void hydrate(Account account, BottomNavigationView bottomNavigationView) { + bottomNavigationView.getMenu().clear(); + BottomMenu bottomMenu; + try { + bottomMenu = getBottomMenu(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; + } + } + } + } + + /** + * 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; + } + + /** + * Insert BottomMenu in db + * + * @param bottomMenu {@link BottomMenu} + * @return long - db id + * @throws DBException exception with database + */ + private long insertBottomMenu(BottomMenu bottomMenu) throws DBException { + if (db == null) { + throw new DBException("db is null. Wrong initialization."); + } + ContentValues values = new ContentValues(); + values.put(Sqlite.COL_INSTANCE, BaseMainActivity.currentInstance); + values.put(Sqlite.COL_USER_ID, BaseMainActivity.currentUserID); + values.put(Sqlite.COL_BOTTOM_MENU, menuItemListToStringStorage(bottomMenu.bottom_menu)); + //Inserts bottom + try { + return db.insertOrThrow(Sqlite.TABLE_BOTTOM_MENU, null, values); + } catch (Exception e) { + e.printStackTrace(); + return -1; + } + } + + /** + * update bottomMenu in db + * + * @param bottomMenu {@link BottomMenu} + * @return long - db id + * @throws DBException exception with database + */ + private long updateBottomMenu(BottomMenu bottomMenu) throws DBException { + if (db == null) { + throw new DBException("db is null. Wrong initialization."); + } + ContentValues values = new ContentValues(); + values.put(Sqlite.COL_BOTTOM_MENU, menuItemListToStringStorage(bottomMenu.bottom_menu)); + //Inserts token + try { + return db.update(Sqlite.TABLE_BOTTOM_MENU, + values, Sqlite.COL_INSTANCE + " = ? AND " + Sqlite.COL_USER_ID + " = ?", + new String[]{bottomMenu.instance, bottomMenu.user_id}); + } catch (Exception e) { + e.printStackTrace(); + return -1; + } + } + + /** + * Returns the bottom menu for an account + * + * @param account Account + * @return Pinned - {@link BottomMenu} + */ + private BottomMenu getBottomMenu(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"); + BottomMenu bottomMenu = cursorToBottomMenu(c); + List menuItemList = new ArrayList<>(); + if (bottomMenu != null) { + for (MenuItem menuItem : bottomMenu.bottom_menu) { + if (menuItem.visible) { + menuItemList.add(menuItem.position, menuItem); + } + } + bottomMenu.bottom_menu = menuItemList; + } + if (bottomMenu == null) { + bottomMenu = defaultBottomMenu(); + } + return bottomMenu; + } catch (Exception e) { + return defaultBottomMenu(); + } + } + + private BottomMenu defaultBottomMenu() { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.bottom_menu = new ArrayList<>(); + MenuItem menuItemHome = new MenuItem(); + menuItemHome.position = 0; + menuItemHome.visible = true; + menuItemHome.item_menu_type = ItemMenuType.HOME; + bottomMenu.bottom_menu.add(menuItemHome); + MenuItem menuItemLocal = new MenuItem(); + menuItemLocal.position = 1; + menuItemLocal.visible = true; + menuItemLocal.item_menu_type = ItemMenuType.LOCAL; + bottomMenu.bottom_menu.add(menuItemLocal); + MenuItem menuItemPublic = new MenuItem(); + menuItemPublic.position = 2; + menuItemPublic.visible = true; + menuItemPublic.item_menu_type = ItemMenuType.PUBLIC; + bottomMenu.bottom_menu.add(menuItemPublic); + MenuItem menuItemNotification = new MenuItem(); + menuItemNotification.position = 3; + menuItemNotification.visible = true; + menuItemNotification.item_menu_type = ItemMenuType.NOTIFICATION; + bottomMenu.bottom_menu.add(menuItemNotification); + MenuItem menuItemPrivate = new MenuItem(); + menuItemPrivate.position = 4; + menuItemPrivate.visible = true; + menuItemPrivate.item_menu_type = ItemMenuType.DIRECT; + bottomMenu.bottom_menu.add(menuItemPrivate); + 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 + * + * @param c Cursor + * @return Pinned + */ + private BottomMenu cursorToBottomMenu(Cursor c) { + //No element found + if (c.getCount() == 0) { + c.close(); + return null; + } + //Take the first element + c.moveToFirst(); + BottomMenu bottomMenu = convertCursorToBottomMenu(c); + //Close the cursor + c.close(); + return bottomMenu; + } + + /** + * Read cursor and hydrate without closing it + * + * @param c - Cursor + * @return BottomMenu + */ + private BottomMenu convertCursorToBottomMenu(Cursor c) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.id = c.getInt(c.getColumnIndexOrThrow(Sqlite.COL_ID)); + bottomMenu.instance = c.getString(c.getColumnIndexOrThrow(Sqlite.COL_INSTANCE)); + bottomMenu.user_id = c.getString(c.getColumnIndexOrThrow(Sqlite.COL_USER_ID)); + bottomMenu.bottom_menu = restoreMenuItemFromString(c.getString(c.getColumnIndexOrThrow(Sqlite.COL_BOTTOM_MENU))); + return bottomMenu; + } + + public enum ItemMenuType { + @SerializedName("HOME") + HOME("HOME"), + @SerializedName("DIRECT") + DIRECT("DIRECT"), + @SerializedName("NOTIFICATION") + NOTIFICATION("NOTIFICATION"), + @SerializedName("LOCAL") + LOCAL("LOCAL"), + @SerializedName("PUBLIC") + PUBLIC("PUBLIC"); + private final String value; + + ItemMenuType(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } + + public static class MenuItem { + @SerializedName("position") + int position; + @SerializedName("item_menu_type") + ItemMenuType item_menu_type; + @SerializedName("visible") + boolean visible; + } +} diff --git a/app/src/main/java/app/fedilab/android/sqlite/Sqlite.java b/app/src/main/java/app/fedilab/android/sqlite/Sqlite.java index aef5e8048..5f650e686 100644 --- a/app/src/main/java/app/fedilab/android/sqlite/Sqlite.java +++ b/app/src/main/java/app/fedilab/android/sqlite/Sqlite.java @@ -23,7 +23,7 @@ import android.database.sqlite.SQLiteOpenHelper; public class Sqlite extends SQLiteOpenHelper { - public static final int DB_VERSION = 2; + public static final int DB_VERSION = 3; public static final String DB_NAME = "fedilab_db"; //Table of owned accounts @@ -77,6 +77,10 @@ public class Sqlite extends SQLiteOpenHelper { public static final String TABLE_QUICK_LOAD = "QUICK_LOAD"; public static final String COL_SLUG = "SLUG"; public static final String COL_STATUSES = "STATUSES"; + //Bottom menu + public static final String TABLE_BOTTOM_MENU = "TABLE_BOTTOM_MENU"; + public static final String COL_BOTTOM_MENU = "BOTTOM_MENU"; + private static final String CREATE_TABLE_USER_ACCOUNT = "CREATE TABLE " + TABLE_USER_ACCOUNT + " (" + COL_USER_ID + " TEXT NOT NULL, " @@ -157,6 +161,13 @@ public class Sqlite extends SQLiteOpenHelper { + COL_POSITION + " INTEGER, " + COL_STATUSES + " TEXT NOT NULL)"; + + private static final String CREATE_TABLE_BOTTOM_MENU = "CREATE TABLE IF NOT EXISTS " + TABLE_BOTTOM_MENU + " (" + + COL_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + + COL_INSTANCE + " TEXT NOT NULL, " + + COL_USER_ID + " TEXT NOT NULL, " + + COL_BOTTOM_MENU + " TEXT NOT NULL)"; + public static SQLiteDatabase db; private static Sqlite sInstance; @@ -184,6 +195,7 @@ public class Sqlite extends SQLiteOpenHelper { db.execSQL(CREATE_TABLE_PINNED_TIMELINES); db.execSQL(CREATE_TABLE_SCHEDULE_BOOST); db.execSQL(CREATE_TABLE_QUICK_LOAD); + db.execSQL(CREATE_TABLE_BOTTOM_MENU); } @Override @@ -193,10 +205,11 @@ public class Sqlite extends SQLiteOpenHelper { @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - //noinspection SwitchStatementWithTooFewBranches switch (oldVersion) { case 1: db.execSQL(CREATE_TABLE_QUICK_LOAD); + case 2: + db.execSQL(CREATE_TABLE_BOTTOM_MENU); default: break; } diff --git a/app/src/main/res/layout/activity_reorder_tabs.xml b/app/src/main/res/layout/activity_reorder_tabs.xml index a807eb470..9a0769258 100644 --- a/app/src/main/res/layout/activity_reorder_tabs.xml +++ b/app/src/main/res/layout/activity_reorder_tabs.xml @@ -18,6 +18,12 @@ android:layout_width="match_parent" android:layout_height="match_parent"> + +