mirror of
https://github.com/TimeCrafters/TimeCraftersConfigurationTool.git
synced 2025-12-16 13:32:35 +00:00
Added a view to main activity layout to host Snackbars, Configurations menu is mostly functional, misc.
This commit is contained in:
@@ -28,6 +28,7 @@ dependencies {
|
|||||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||||
implementation 'com.google.android.material:material:1.0.0'
|
implementation 'com.google.android.material:material:1.0.0'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||||
|
implementation 'androidx.coordinatorlayout:coordinatorlayout:1.1.0'
|
||||||
implementation 'androidx.vectordrawable:vectordrawable:1.1.0'
|
implementation 'androidx.vectordrawable:vectordrawable:1.1.0'
|
||||||
implementation 'androidx.navigation:navigation-fragment:2.1.0'
|
implementation 'androidx.navigation:navigation-fragment:2.1.0'
|
||||||
implementation 'androidx.navigation:navigation-ui:2.1.0'
|
implementation 'androidx.navigation:navigation-ui:2.1.0'
|
||||||
|
|||||||
@@ -33,11 +33,17 @@ import java.io.FileReader;
|
|||||||
import java.io.FileWriter;
|
import java.io.FileWriter;
|
||||||
import java.io.FilenameFilter;
|
import java.io.FilenameFilter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.nio.file.StandardCopyOption;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
public class Backend {
|
public class Backend {
|
||||||
private static final String TAG = "Backend";
|
private static final String TAG = "Backend";
|
||||||
|
static private HashMap<String, Object> storage = new HashMap<>();
|
||||||
static private Backend instance;
|
static private Backend instance;
|
||||||
private TACNET tacnet;
|
private TACNET tacnet;
|
||||||
private Server server;
|
private Server server;
|
||||||
@@ -46,6 +52,10 @@ public class Backend {
|
|||||||
private Settings settings;
|
private Settings settings;
|
||||||
private boolean configChanged, settingsChanged;
|
private boolean configChanged, settingsChanged;
|
||||||
|
|
||||||
|
public static HashMap<String, Object> getStorage() {
|
||||||
|
return storage;
|
||||||
|
}
|
||||||
|
|
||||||
public Backend() {
|
public Backend() {
|
||||||
if (Backend.instance() != null) {
|
if (Backend.instance() != null) {
|
||||||
throw(new RuntimeException("Backend instance already exists!"));
|
throw(new RuntimeException("Backend instance already exists!"));
|
||||||
@@ -114,10 +124,14 @@ public class Backend {
|
|||||||
saveConfig();
|
saveConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isConfigChanged() { return configChanged; }
|
public boolean hasConfigChanged() { return configChanged; }
|
||||||
|
|
||||||
|
public String configPath(String name) {
|
||||||
|
return TAC.CONFIGS_PATH + File.separator + name + ".json";
|
||||||
|
}
|
||||||
|
|
||||||
public void loadConfig(String name) {
|
public void loadConfig(String name) {
|
||||||
String path = "" + TAC.CONFIGS_PATH + File.separator + name + ".json";
|
String path = configPath(name);
|
||||||
File file = new File(path);
|
File file = new File(path);
|
||||||
|
|
||||||
if (file.exists() && file.isFile()) {
|
if (file.exists() && file.isFile()) {
|
||||||
@@ -129,12 +143,38 @@ public class Backend {
|
|||||||
public boolean saveConfig() {
|
public boolean saveConfig() {
|
||||||
if (config == null) { return false; }
|
if (config == null) { return false; }
|
||||||
|
|
||||||
final String path = "" + TAC.CONFIGS_PATH + File.separator + getConfig().getName() + ".json";
|
final String path = configPath(getConfig().getName());
|
||||||
configChanged = false;
|
configChanged = false;
|
||||||
|
|
||||||
return writeToFile(path, gsonForConfig().toJson(config));
|
return writeToFile(path, gsonForConfig().toJson(config));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean moveConfig(String oldName, String newName) {
|
||||||
|
final String oldPath = configPath(oldName);
|
||||||
|
final String newPath = configPath(newName);
|
||||||
|
|
||||||
|
final File oldFile = new File(oldPath);
|
||||||
|
final File newFile = new File(newPath);
|
||||||
|
|
||||||
|
if (!oldFile.exists() || !oldFile.isFile()) {
|
||||||
|
Log.e(TAG, "moveConfig: Can not move config file \"" + oldPath + "\" does not exists!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newFile.exists() && newFile.isFile()) {
|
||||||
|
Log.e(TAG, "moveConfig: Config file \"" + newPath + "\" already exists!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return oldFile.renameTo(newFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean deleteConfig(String name) {
|
||||||
|
File file = new File(configPath(name));
|
||||||
|
|
||||||
|
return file.delete();
|
||||||
|
}
|
||||||
|
|
||||||
public void uploadConfig() {
|
public void uploadConfig() {
|
||||||
if (config != null && tacnet.isConnected()) {
|
if (config != null && tacnet.isConnected()) {
|
||||||
String json = "";
|
String json = "";
|
||||||
@@ -149,7 +189,7 @@ public class Backend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void writeNewConfig(String name) {
|
public void writeNewConfig(String name) {
|
||||||
String path = TAC.CONFIGS_PATH + File.separator + name + ".json";
|
String path = configPath(name);
|
||||||
File file = new File(path);
|
File file = new File(path);
|
||||||
|
|
||||||
Config config = new Config(name);
|
Config config = new Config(name);
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import java.util.regex.Pattern;
|
|||||||
|
|
||||||
public class ConfigurationDialog extends TimeCraftersDialog {
|
public class ConfigurationDialog extends TimeCraftersDialog {
|
||||||
private static final String TAG = "ConfigurationDialog";
|
private static final String TAG = "ConfigurationDialog";
|
||||||
|
private String configName;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
@@ -31,7 +32,14 @@ public class ConfigurationDialog extends TimeCraftersDialog {
|
|||||||
final Button cancel = view.findViewById(R.id.cancel);
|
final Button cancel = view.findViewById(R.id.cancel);
|
||||||
final Button mutate = view.findViewById(R.id.mutate);
|
final Button mutate = view.findViewById(R.id.mutate);
|
||||||
|
|
||||||
|
if (getArguments() != null) {
|
||||||
|
configName = getArguments().getString("config_name");
|
||||||
|
title.setText("Editing " + configName);
|
||||||
|
name.setText(configName);
|
||||||
|
mutate.setText(getResources().getString(R.string.dialog_update));
|
||||||
|
} else {
|
||||||
title.setText("Create Configuration");
|
title.setText("Create Configuration");
|
||||||
|
}
|
||||||
cancel.setOnClickListener(new View.OnClickListener() {
|
cancel.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
@@ -39,16 +47,20 @@ public class ConfigurationDialog extends TimeCraftersDialog {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// mutate.setText(getResources().getString(R.string.dialog_update));
|
|
||||||
mutate.setOnClickListener(new View.OnClickListener() {
|
mutate.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
String configName = name.getText().toString();
|
final String newConfigName = name.getText().toString();
|
||||||
|
|
||||||
if (isValid(configName)) {
|
if (isValid(newConfigName)) {
|
||||||
Backend.instance().writeNewConfig(configName);
|
if (configName != null) {
|
||||||
|
Backend.instance().moveConfig(configName, newConfigName);
|
||||||
|
} else {
|
||||||
|
Backend.instance().writeNewConfig(newConfigName);
|
||||||
|
}
|
||||||
dismiss();
|
dismiss();
|
||||||
} else {
|
} else {
|
||||||
|
// TODO: Show friendly error message
|
||||||
Log.d(TAG, "onClick: InValid");
|
Log.d(TAG, "onClick: InValid");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
package org.timecrafters.TimeCraftersConfigurationTool.dialogs;
|
package org.timecrafters.TimeCraftersConfigurationTool.dialogs;
|
||||||
|
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.Typeface;
|
||||||
|
import android.graphics.drawable.ColorDrawable;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@@ -10,25 +15,30 @@ import android.widget.TextView;
|
|||||||
|
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||||
|
|
||||||
|
import com.google.android.material.resources.TextAppearance;
|
||||||
|
|
||||||
import org.timecrafters.TimeCraftersConfigurationTool.R;
|
import org.timecrafters.TimeCraftersConfigurationTool.R;
|
||||||
|
import org.timecrafters.TimeCraftersConfigurationTool.backend.Backend;
|
||||||
import org.timecrafters.TimeCraftersConfigurationTool.library.TimeCraftersDialog;
|
import org.timecrafters.TimeCraftersConfigurationTool.library.TimeCraftersDialog;
|
||||||
|
|
||||||
public class ConfirmationDialog extends TimeCraftersDialog {
|
public class ConfirmationDialog extends TimeCraftersDialog {
|
||||||
private String title, message;
|
private String title, message;
|
||||||
private Runnable action;
|
private Runnable action;
|
||||||
|
|
||||||
public ConfirmationDialog() {}
|
|
||||||
|
|
||||||
public ConfirmationDialog(String title, String message, Runnable action) {
|
|
||||||
this.title = title;
|
|
||||||
this.message = message;
|
|
||||||
this.action = action;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
View root = super.onCreateView(inflater, container, savedInstanceState);
|
View root = super.onCreateView(inflater, container, savedInstanceState);
|
||||||
|
|
||||||
|
if (getArguments() != null) {
|
||||||
|
this.title = getArguments().getString("title", "Are You Sure?");
|
||||||
|
this.message = getArguments().getString("message", "");
|
||||||
|
|
||||||
|
final String actionKey = getArguments().getString("action", null);
|
||||||
|
if (actionKey != null && Backend.getStorage().containsKey(actionKey)) {
|
||||||
|
this.action = (Runnable) Backend.getStorage().get(actionKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final TextView title = root.findViewById(R.id.dialogTitle);
|
final TextView title = root.findViewById(R.id.dialogTitle);
|
||||||
final ConstraintLayout titlebar = root.findViewById(R.id.titlebar);
|
final ConstraintLayout titlebar = root.findViewById(R.id.titlebar);
|
||||||
final LinearLayout view = root.findViewById(R.id.dialogContent);
|
final LinearLayout view = root.findViewById(R.id.dialogContent);
|
||||||
@@ -37,13 +47,29 @@ public class ConfirmationDialog extends TimeCraftersDialog {
|
|||||||
final Button cancel = root.findViewById(R.id.cancel);
|
final Button cancel = root.findViewById(R.id.cancel);
|
||||||
final Button confirm = root.findViewById(R.id.confirm);
|
final Button confirm = root.findViewById(R.id.confirm);
|
||||||
|
|
||||||
|
if (getArguments() != null && getArguments().getBoolean("extreme_danger", false)) {
|
||||||
|
titlebar.setBackgroundColor(getResources().getColor(R.color.dialogError));
|
||||||
|
getDialog().getWindow().setDimAmount(0.8f);
|
||||||
|
cancel.setTypeface(cancel.getTypeface(), Typeface.BOLD);
|
||||||
|
} else {
|
||||||
titlebar.setBackgroundColor(getResources().getColor(R.color.dialogAlert));
|
titlebar.setBackgroundColor(getResources().getColor(R.color.dialogAlert));
|
||||||
|
}
|
||||||
title.setText(this.title);
|
title.setText(this.title);
|
||||||
messageView.setText(message);
|
messageView.setText(message);
|
||||||
|
|
||||||
|
cancel.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
dismiss();
|
||||||
|
}
|
||||||
|
});
|
||||||
confirm.setOnClickListener(new View.OnClickListener() {
|
confirm.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
|
if (action != null) {
|
||||||
|
action.run();
|
||||||
|
}
|
||||||
|
|
||||||
dismiss();
|
dismiss();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -104,7 +104,12 @@ public class GroupsFragment extends TimeCraftersFragment {
|
|||||||
delete.setOnClickListener(new View.OnClickListener() {
|
delete.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
ConfirmationDialog dialog = new ConfirmationDialog("Are you sure?", "Really delete " + group.name + "?", null);
|
ConfirmationDialog dialog = new ConfirmationDialog();
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putString("title", "Are you sure?");
|
||||||
|
bundle.putString("message", "Delete group " + group.name + "?");
|
||||||
|
dialog.setArguments(bundle);
|
||||||
|
|
||||||
dialog.show(getFragmentManager(), null);
|
dialog.show(getFragmentManager(), null);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,28 +1,38 @@
|
|||||||
package org.timecrafters.TimeCraftersConfigurationTool.ui.settings.configurations;
|
package org.timecrafters.TimeCraftersConfigurationTool.ui.settings.configurations;
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
|
import android.widget.ImageButton;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import android.widget.ScrollView;
|
import android.widget.ScrollView;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||||
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
|
|
||||||
import org.timecrafters.TimeCraftersConfigurationTool.R;
|
import org.timecrafters.TimeCraftersConfigurationTool.R;
|
||||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.Backend;
|
import org.timecrafters.TimeCraftersConfigurationTool.backend.Backend;
|
||||||
import org.timecrafters.TimeCraftersConfigurationTool.dialogs.ConfigurationDialog;
|
import org.timecrafters.TimeCraftersConfigurationTool.dialogs.ConfigurationDialog;
|
||||||
|
import org.timecrafters.TimeCraftersConfigurationTool.dialogs.ConfirmationDialog;
|
||||||
import org.timecrafters.TimeCraftersConfigurationTool.library.TimeCraftersFragment;
|
import org.timecrafters.TimeCraftersConfigurationTool.library.TimeCraftersFragment;
|
||||||
|
|
||||||
public class ConfigurationsFragment extends TimeCraftersFragment {
|
public class ConfigurationsFragment extends TimeCraftersFragment {
|
||||||
|
private LayoutInflater inflater;
|
||||||
|
private LinearLayout configsContainer;
|
||||||
|
private View root;
|
||||||
|
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater,
|
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||||
ViewGroup container, Bundle savedInstanceState) {
|
ViewGroup container, Bundle savedInstanceState) {
|
||||||
final View root = inflater.inflate(R.layout.fragment_configuration, container, false);
|
this.inflater = inflater;
|
||||||
|
this.root = inflater.inflate(R.layout.fragment_configuration, container, false);
|
||||||
final ScrollView scrollview = root.findViewById(R.id.scrollview);
|
final ScrollView scrollview = root.findViewById(R.id.scrollview);
|
||||||
final LinearLayout configsContainer = root.findViewById(R.id.container);
|
configsContainer = root.findViewById(R.id.container);
|
||||||
final FloatingActionButton actionButton = root.findViewById(R.id.actionButton);
|
final FloatingActionButton actionButton = root.findViewById(R.id.actionButton);
|
||||||
|
|
||||||
floatingActionButtonAutoHide(actionButton, scrollview);
|
floatingActionButtonAutoHide(actionButton, scrollview);
|
||||||
@@ -34,8 +44,16 @@ public class ConfigurationsFragment extends TimeCraftersFragment {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
populateConfigFiles();
|
||||||
|
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void populateConfigFiles() {
|
||||||
|
configsContainer.removeAllViews();
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (String configFile : Backend.instance().configsList()) {
|
for (final String configFile : Backend.instance().configsList()) {
|
||||||
final String config = configFile.replace(".json", "");
|
final String config = configFile.replace(".json", "");
|
||||||
View view = inflater.inflate(R.layout.fragment_part_configuration, null);
|
View view = inflater.inflate(R.layout.fragment_part_configuration, null);
|
||||||
|
|
||||||
@@ -45,21 +63,62 @@ public class ConfigurationsFragment extends TimeCraftersFragment {
|
|||||||
view.setBackgroundColor(getResources().getColor(R.color.list_odd));
|
view.setBackgroundColor(getResources().getColor(R.color.list_odd));
|
||||||
}
|
}
|
||||||
|
|
||||||
Button configName = view.findViewById(R.id.name);
|
final Button configName = view.findViewById(R.id.name);
|
||||||
|
final ImageButton rename = view.findViewById(R.id.rename);
|
||||||
|
final ImageButton delete = view.findViewById(R.id.delete);
|
||||||
configName.setText(config);
|
configName.setText(config);
|
||||||
configName.setOnClickListener(new View.OnClickListener() {
|
configName.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
|
if (Backend.instance().getSettings().config.equals(config)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Backend.instance().getSettings().config = config;
|
Backend.instance().getSettings().config = config;
|
||||||
Backend.instance().loadConfig(config);
|
Backend.instance().loadConfig(config);
|
||||||
Backend.instance().saveSettings();
|
Backend.instance().saveSettings();
|
||||||
|
|
||||||
|
View snackbarHost = getActivity().findViewById(R.id.snackbar_host);
|
||||||
|
Snackbar.make(snackbarHost, "Loaded config: " + config, Snackbar.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
rename.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
ConfigurationDialog dialog = new ConfigurationDialog();
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putString("config_name", config);
|
||||||
|
dialog.setArguments(bundle);
|
||||||
|
dialog.show(getFragmentManager(), null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
delete.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
ConfirmationDialog dialog = new ConfirmationDialog();
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
final String actionKey = "delete_configuration";
|
||||||
|
bundle.putString("title", "Are you sure?");
|
||||||
|
bundle.putString("message", "Destroy configuration " + config + "?");
|
||||||
|
bundle.putString("action", actionKey);
|
||||||
|
bundle.putBoolean("extreme_danger", true);
|
||||||
|
Runnable action = new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Backend.instance().deleteConfig(config);
|
||||||
|
}
|
||||||
|
} ;
|
||||||
|
Backend.getStorage().put(actionKey, action);
|
||||||
|
dialog.setArguments(bundle);
|
||||||
|
|
||||||
|
dialog.show(getFragmentManager(), null);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
configsContainer.addView(view);
|
configsContainer.addView(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
return root;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,13 @@
|
|||||||
app:layout_constraintVertical_bias="0.0"
|
app:layout_constraintVertical_bias="0.0"
|
||||||
app:navGraph="@navigation/mobile_navigation" />
|
app:navGraph="@navigation/mobile_navigation" />
|
||||||
|
|
||||||
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
|
android:id="@+id/snackbar_host"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/nav_host_fragment"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/nav_host_fragment" />
|
||||||
|
|
||||||
<com.google.android.material.bottomnavigation.BottomNavigationView
|
<com.google.android.material.bottomnavigation.BottomNavigationView
|
||||||
android:id="@+id/nav_view"
|
android:id="@+id/nav_view"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
|
|||||||
Reference in New Issue
Block a user