Files
stock-pignon/app/src/main/java/com/stock/pignon/MainActivity.java

417 lines
15 KiB
Java

// MainActivity.java
package com.stock.pignon;
import android.graphics.Color;
import android.graphics.drawable.GradientDrawable;
import android.os.Bundle;
import android.util.Log;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.GridLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.FrameLayout;
import android.widget.TextView;
import android.os.Environment;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import androidx.appcompat.app.AppCompatActivity;
import java.util.List;
/**
* Main Activity: Central Dashboard for the application.
* Manages category navigation and item selection within a single screen.
*/
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
// UI components
private LinearLayout cartList;
private LinearLayout homeLayout;
private FrameLayout categoryItemsLayout;
private LinearLayout categoriesLayout;
private GridLayout gridPieces;
private ImageView mainImage;
// Server component
private ControlServer server;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Hide Android bar
View decorView = getWindow().getDecorView();
int uiOptions = View.SYSTEM_UI_FLAG_LOW_PROFILE | View.SYSTEM_UI_FLAG_FULLSCREEN;
decorView.setSystemUiVisibility(uiOptions);
// View Binding
cartList = findViewById(R.id.cartList);
homeLayout = findViewById(R.id.homeLayout);
categoryItemsLayout = findViewById(R.id.categoryItemsLayout);
categoriesLayout = findViewById(R.id.categoriesLayout);
gridPieces = findViewById(R.id.gridPieces);
mainImage = findViewById(R.id.mainImage);
// Copy assets to sd card if not founded
copyAssetsIfEmpty();
// Get data from sd card
DataLoader.loadData();
// Initial UI Setup
setupCategoriesMenu();
loadMainImage();
// Initial cart visual sync
CartViewHelper.updateCartView(cartList, this);
// Action bar
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayShowHomeEnabled(true);
getSupportActionBar().setTitle(" 🚲 Atelier du Pignon - Gestion du stock à prix libre");
}
// Get app version
String versionName;
try {
versionName = "App v" + getPackageManager().getPackageInfo(getPackageName(), 0).versionName;
} catch (Exception e) {
versionName = ""; // Fallback
}
// Launch server
server = new ControlServer(this,8080);
try {
server.start();
String url = "http://" + getDeviceIP() + ":8080";
// Print URL in action bar for user
if (getSupportActionBar() != null) {
getSupportActionBar().setSubtitle("Serveur en ligne : " + url + " - " + versionName);
}
Log.i(TAG, "Server started on : " + url);
} catch (IOException e) {
if (getSupportActionBar() != null) {
getSupportActionBar().setSubtitle("Erreur serveur" + " - " + versionName);
}
}
// Back button (blue)
Button btnBack = findViewById(R.id.btnBackFromCategory);
if (btnBack != null) {
btnBack.setOnClickListener(v -> showHome());
}
// Empty button (red)
Button btnClear = findViewById(R.id.clearCartBtn);
if (btnClear != null) {
btnClear.setOnClickListener(v -> CartActionHelper.emptyCart(cartList, this));
}
// Validate button (green)
Button btnValidate = findViewById(R.id.validateCartBtn);
if (btnValidate != null) {
btnValidate.setOnClickListener(v -> CartActionHelper.validateCart(cartList, this));
}
}
@Override
protected void onResume() {
super.onResume();
// Ensure cart is up to date if returning to the activity
CartViewHelper.updateCartView(cartList, this);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (server != null) {
server.stop();
Log.i(TAG, "Server stopped.");
}
}
/**
* Builds the left sidebar with category buttons using DataLoader's cache.
*/
private void setupCategoriesMenu() {
List<Category> categories = DataLoader.getCategories();
if (categories == null || categories.isEmpty()) {
Log.w(TAG, "No categories found to display.");
return;
}
categoriesLayout.removeAllViews();
for (Category category : categories) {
Button btn = createCategoryButton(category);
// On click, we show the items for this specific category
btn.setOnClickListener(v -> showCategory(category.getName()));
categoriesLayout.addView(btn);
}
}
/**
* Styles category buttons based on the new Category getters.
*/
private Button createCategoryButton(Category category) {
Button btn = new Button(this);
btn.setText(category.getName());
btn.setTextSize(TypedValue.COMPLEX_UNIT_SP, 24);
float density = getResources().getDisplayMetrics().density;
float radiusPx = 3 * density;
// Apply background color with transparency (70% alpha)
if (category.getBgColor() != null) {
try {
int color = Color.parseColor(category.getBgColor());
int alpha = (int) (0.7 * 255);
color = (color & 0x00FFFFFF) | (alpha << 24);
GradientDrawable shape = new GradientDrawable();
shape.setColor(color);
shape.setCornerRadius(radiusPx);
btn.setBackground(shape);
} catch (Exception e) {
Log.e(TAG, "Error parsing bgColor for: " + category.getName());
}
}
// Apply text color
if (category.getTextColor() != null) {
try {
btn.setTextColor(Color.parseColor(category.getTextColor()));
} catch (Exception e) {
Log.e(TAG, "Error parsing textColor for: " + category.getName());
}
}
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
);
params.setMargins(0, 5, 0, 5);
btn.setLayoutParams(params);
return btn;
}
/**
* Switches center view to display the grid of items.
* Uses DataLoader.getItemsForCategory() to get the merged list.
*/
private void showCategory(String categoryName) {
homeLayout.setVisibility(View.GONE);
categoryItemsLayout.setVisibility(View.VISIBLE);
gridPieces.removeAllViews();
// Calculate item width for a 4-column grid
int gridWidth = getResources().getDisplayMetrics().widthPixels - dpToPx(370);
int itemWidth = gridWidth / 4;
// Fetch the PRE-MERGED list (globals + specifics) from DataLoader
List<Item> itemsToDisplay = DataLoader.getItemsForCategory(categoryName);
for (Item item : itemsToDisplay) {
View itemView = LayoutInflater.from(this).inflate(R.layout.item_piece, gridPieces, false);
// Apply width to the inflated view
GridLayout.LayoutParams params = (GridLayout.LayoutParams) itemView.getLayoutParams();
if (params == null) {
params = new GridLayout.LayoutParams();
}
params.width = itemWidth;
itemView.setLayoutParams(params);
bindItemToView(itemView, item);
gridPieces.addView(itemView);
}
}
/**
* Connects Item data to the inflated layout piece.
*/
private void bindItemToView(View itemView, Item item) {
TextView nom = itemView.findViewById(R.id.itemName);
TextView prix = itemView.findViewById(R.id.itemPrice);
ImageView img = itemView.findViewById(R.id.itemImage);
TextView qtyView = itemView.findViewById(R.id.qtyView);
nom.setText(item.getName());
prix.setText(getString(R.string.price_range, item.getMinPrice(), item.getMaxPrice(), ""));
// Use your existing ImageLoader helper
ImageLoader.loadImage(img, item.getImage(),200,200);
// Sync visual quantity with CartManager
CartItem existing = CartManager.getItemByName(item.getName());
int currentQty = (existing != null) ? existing.getQuantity() : 0;
qtyView.setText(String.valueOf(currentQty));
// Listeners for + and - buttons
itemView.findViewById(R.id.plusBtn).setOnClickListener(v -> updateItemQty(item, qtyView, 1));
itemView.findViewById(R.id.minusBtn).setOnClickListener(v -> updateItemQty(item, qtyView, -1));
}
/**
* Updates the local counter and the global cart state simultaneously.
*/
private void updateItemQty(Item item, TextView qtyView, int delta) {
int current = Integer.parseInt(qtyView.getText().toString());
int next = Math.max(0, current + delta);
qtyView.setText(String.valueOf(next));
// Central data update
CartManager.addOrUpdateItem(item.getName(), item.getMinPrice(), item.getMaxPrice(), next, item.getImage());
// Refresh sidebar cart list
CartViewHelper.updateCartView(cartList, this);
}
public void showHome() {
categoryItemsLayout.setVisibility(View.GONE);
homeLayout.setVisibility(View.VISIBLE);
}
private void loadMainImage() {
// Fallback for home screen image
ImageLoader.loadImage(mainImage, "_velo", 800,800);
}
private int dpToPx(int dp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
}
private void copyAssetsIfEmpty() {
File rootDir = new File(Environment.getExternalStorageDirectory(), Config.EXTERNAL_DIR_NAME);
// Create root folder if not found
if (!rootDir.exists()) {
if (!rootDir.mkdirs()) {
Log.e("MainActivity", "Can't create root dir." + rootDir.getAbsolutePath());
return;
}
}
// Check pieces.json
File inputJson = new File(rootDir, Config.INPUT_JSON_NAME);
if (!inputJson.exists()) {
Log.i("MainActivity", "pieces.json not found, copying it...");
copyFileFromAssets(Config.INPUT_JSON_NAME, inputJson);
} else {
Log.d("MainActivity", "Keep existing pieces.json");
}
// Check stock.json and stock.csv output files to avoid control server error
checkOrCreateEmptyFile(new File(rootDir, Config.OUTPUT_JSON_NAME), "[]");
checkOrCreateEmptyFile(new File(rootDir, Config.OUTPUT_CSV_NAME), "");
// Check images folder
File imgDir = new File(rootDir, Config.IMAGES_SUBDIR_NAME);
if (!imgDir.exists()) {
if (!imgDir.mkdirs()) {
Log.e("MainActivity", "Can't create images dir.");
return;
}
}
// Copy images only if not found
String[] filesInImgDir = imgDir.list();
if (filesInImgDir == null || filesInImgDir.length == 0) {
Log.i("MainActivity", "Images folder empty. Installing images...");
copyFolderFromAssets(Config.IMAGES_SUBDIR_NAME, imgDir);
} else {
Log.d("MainActivity", "Keep existing images.");
}
}
/**
* Create a file with default content if not found
*/
private void checkOrCreateEmptyFile(File file, String defaultContent) {
if (!file.exists()) {
try (FileOutputStream fos = new FileOutputStream(file)) {
fos.write(defaultContent.getBytes());
Log.d(TAG, "Initialisation de : " + file.getName());
} catch (IOException e) {
Log.e(TAG, "Erreur lors de l'initialisation de " + file.getName(), e);
}
}
}
/**
* Copy folder from assets
*/
private void copyFolderFromAssets(String assetDirName, File destDir) {
try {
String[] files = getAssets().list(assetDirName);
if (files != null) {
// For every image
for (String fileName : files) {
// Create source path
String assetPath = assetDirName + "/" + fileName;
// Create dest path
File destFile = new File(destDir, fileName);
// Copy
copyFileFromAssets(assetPath, destFile);
}
}
} catch (IOException e) {
Log.e("MainActivity", "Listing assets error: " + assetDirName, e);
}
}
/**
* Copy file from assets
*/
private void copyFileFromAssets(String assetName, File destFile) {
// Optimized read
// Try-with-resources ensures streams are automatically closed, avoid memory leaks
try (InputStream in = getAssets().open(assetName);
OutputStream out = new FileOutputStream(destFile)) {
byte[] buffer = new byte[8192];
int read;
while ((read = in.read(buffer)) != -1) {
out.write(buffer, 0, read);
}
Log.d("MainActivity", "Success : " + assetName + " copied.");
} catch (IOException e) {
Log.e("MainActivity", "Can't copy asset: " + assetName, e);
}
}
/**
* Get device IP to print it to users for easy remote control
*/
private String getDeviceIP() {
try {
java.util.Enumeration<java.net.NetworkInterface> interfaces = java.net.NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements()) {
java.net.NetworkInterface iface = interfaces.nextElement();
java.util.Enumeration<java.net.InetAddress> addresses = iface.getInetAddresses();
while (addresses.hasMoreElements()) {
java.net.InetAddress addr = addresses.nextElement();
if (!addr.isLoopbackAddress() && addr instanceof java.net.Inet4Address) {
return addr.getHostAddress();
}
}
}
} catch (Exception e) {
Log.e(TAG, "IP error", e);
}
return "127.0.0.1";
}
}