Generate bottom menu programmatically

This commit is contained in:
Thomas 2022-05-17 17:12:48 +02:00
parent 42f950b23b
commit 4725ca6ce7
4 changed files with 357 additions and 3 deletions

View file

@ -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)));

View file

@ -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 <http://www.gnu.org/licenses>. */
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<MenuItem> 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<MenuItem> 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<MenuItem> restoreMenuItemFromString(String serializedMenuItem) {
Gson gson = new Gson();
try {
return gson.fromJson(serializedMenuItem, new TypeToken<List<MenuItem>>() {
}.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<MenuItem> 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;
}
}

View file

@ -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;
}

View file

@ -18,6 +18,12 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/lv_reorder_bottom"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="none" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/lv_reorder_tabs"
android:layout_width="match_parent"