feat: owner input and csv export

This commit is contained in:
Ninjdai 2024-12-20 14:23:08 +01:00
parent 22eaf7372f
commit 739fad458f
8 changed files with 302 additions and 27 deletions

View File

@ -1,31 +1,46 @@
package dev.ninjdai.balscanner; package dev.ninjdai.balscanner;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
import com.google.android.material.bottomnavigation.BottomNavigationView; import com.google.android.material.bottomnavigation.BottomNavigationView;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.navigation.NavController; import androidx.navigation.NavController;
import androidx.navigation.Navigation; import androidx.navigation.Navigation;
import androidx.navigation.ui.AppBarConfiguration; import androidx.navigation.ui.AppBarConfiguration;
import androidx.navigation.ui.NavigationUI; import androidx.navigation.ui.NavigationUI;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import dev.ninjdai.balscanner.books.Book; import dev.ninjdai.balscanner.books.Book;
import dev.ninjdai.balscanner.books.Owner; import dev.ninjdai.balscanner.books.Owner;
import dev.ninjdai.balscanner.databinding.ActivityMainBinding; import dev.ninjdai.balscanner.databinding.ActivityMainBinding;
import dev.ninjdai.balscanner.ui.home.UsersItemsAdapter;
import dev.ninjdai.balscanner.ui.scanner.ScannerItemsAdapter;
public class MainActivity extends AppCompatActivity { public class MainActivity extends AppCompatActivity {
private ActivityMainBinding binding; private ActivityMainBinding binding;
public static ConcurrentHashMap<Owner, List<Book>> books = new ConcurrentHashMap<>(); public static ConcurrentHashMap<Owner, List<Book>> books = new ConcurrentHashMap<>();
public static Owner avril = new Owner("Avril", "Papillon"); public static Owner current_owner = null;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@ -41,8 +56,5 @@ public class MainActivity extends AppCompatActivity {
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_activity_main); NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_activity_main);
NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration); NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
NavigationUI.setupWithNavController(binding.navView, navController); NavigationUI.setupWithNavController(binding.navView, navController);
books.put(avril, new ArrayList<>());
books.get(avril).add(new Book("Test book", "Albert", "69420", avril, 0));
} }
} }

View File

@ -2,6 +2,6 @@ package dev.ninjdai.balscanner.books
data class Book(val title: String, val author: String, val EAN: String, val owner: Owner, var price: Int) data class Book(val title: String, val author: String, val EAN: String, val owner: Owner, var price: Int)
data class Owner(val firstName: String, val lastName: String) { data class Owner(var firstName: String, var lastName: String, var contact: String) {
val id: String = firstName[0].toString() + lastName[0].toString() val id: String = firstName[0].toString().uppercase() + lastName[0].toString().uppercase()
} }

View File

@ -1,20 +1,49 @@
package dev.ninjdai.balscanner.ui.home; package dev.ninjdai.balscanner.ui.home;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.text.InputType;
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;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.journeyapps.barcodescanner.ScanOptions;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Objects;
import dev.ninjdai.balscanner.MainActivity;
import dev.ninjdai.balscanner.R;
import dev.ninjdai.balscanner.books.Book;
import dev.ninjdai.balscanner.books.Owner;
import dev.ninjdai.balscanner.databinding.FragmentHomeBinding; import dev.ninjdai.balscanner.databinding.FragmentHomeBinding;
public class HomeFragment extends Fragment { public class HomeFragment extends Fragment {
private FragmentHomeBinding binding; private FragmentHomeBinding binding;
private UsersItemsAdapter adapter;
private ActivityResultLauncher<String> documentCreator;
public View onCreateView(@NonNull LayoutInflater inflater, public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) { ViewGroup container, Bundle savedInstanceState) {
@ -24,14 +53,126 @@ public class HomeFragment extends Fragment {
binding = FragmentHomeBinding.inflate(inflater, container, false); binding = FragmentHomeBinding.inflate(inflater, container, false);
View root = binding.getRoot(); View root = binding.getRoot();
final TextView textView = binding.textHome;
homeViewModel.getText().observe(getViewLifecycleOwner(), textView::setText);
return root; return root;
} }
public void updateItems() {
adapter.notifyItemChanged(MainActivity.books.keySet().size() - 1);
}
@SuppressLint("NotifyDataSetChanged")
public void addOwner() {
Owner tmpOwner = new Owner("UNK", "UNK", "");
Activity activity = getActivity();
if (activity==null) return;
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setTitle("Prénom");
final EditText input = new EditText(getActivity());
input.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PERSON_NAME);
builder.setView(input);
builder.setPositiveButton("Suivant", (dialog, which) -> {
tmpOwner.setFirstName(input.getText().toString());
AlertDialog.Builder lastBuilder = new AlertDialog.Builder(activity);
lastBuilder.setTitle("Nom de famille");
final EditText lastInput = new EditText(getActivity());
input.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PERSON_NAME);
lastBuilder.setView(lastInput);
lastBuilder.setPositiveButton("Suivant", (lDialog, lWhich) -> {
tmpOwner.setLastName(lastInput.getText().toString());
AlertDialog.Builder cBuilder = new AlertDialog.Builder(activity);
cBuilder.setTitle("Contact");
final EditText cInput = new EditText(getActivity());
input.setInputType(InputType.TYPE_CLASS_TEXT);
cBuilder.setView(cInput);
cBuilder.setPositiveButton("Valider", (cDialog, cWhich) -> {
tmpOwner.setContact(cInput.getText().toString());
Owner o = new Owner(tmpOwner.getFirstName().strip(), tmpOwner.getLastName().strip(), tmpOwner.getContact().strip());
MainActivity.books.put(o, new ArrayList<>());
MainActivity.current_owner = o;
adapter.setmData(new ArrayList<>(MainActivity.books.keySet()));
adapter.notifyDataSetChanged();
});
cBuilder.setNegativeButton("Annuler", (cDialog, cWhich) -> cDialog.cancel());
cBuilder.show();
});
lastBuilder.setNegativeButton("Annuler", (lDialog, lWhich) -> lDialog.cancel());
lastBuilder.show();
});
builder.setNegativeButton("Annuler", (dialog, which) -> dialog.cancel());
builder.show();
}
@SuppressLint("NotifyDataSetChanged")
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
RecyclerView recyclerView = view.findViewById(R.id.users_items);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
adapter = new UsersItemsAdapter(getContext(), new ArrayList<>(MainActivity.books.keySet()));
adapter.setClickListener((v, position) -> {
Owner o = adapter.getItem(position);
Toast.makeText(getContext(), String.format("Sélectionné %s %s", o.getFirstName(), o.getLastName()), Toast.LENGTH_SHORT).show();
adapter.notifyDataSetChanged();
MainActivity.current_owner = o;
});
adapter.setLongClickListener((v, position) -> {
Toast.makeText(getContext(), "Removed " + adapter.getItem(position), Toast.LENGTH_LONG).show();
adapter.removeItem(position);
adapter.notifyItemRemoved(position);
return true;
});
recyclerView.setAdapter(adapter);
documentCreator = registerForActivityResult(new ActivityResultContracts.CreateDocument("text/csv"), this::onDocumentActivityResult);
Button userButton = view.findViewById(R.id.user_button);
userButton.setOnClickListener(v -> {
addOwner();
});
Button exportButton = view.findViewById(R.id.export_button);
exportButton.setOnClickListener(v -> {
documentCreator.launch("export.csv");
});
}
@Override @Override
public void onDestroyView() { public void onDestroyView() {
super.onDestroyView(); super.onDestroyView();
binding = null; binding = null;
} }
private void onDocumentActivityResult(Uri result) {
if (result == null) {
Toast.makeText(getContext(), "Export annulé", Toast.LENGTH_LONG).show();
return;
}
try {
OutputStream stream = getActivity().getContentResolver().openOutputStream(result);
if (stream==null) {
Toast.makeText(getContext(), "Erreur lors de l'ouverture du fichier", Toast.LENGTH_LONG).show();
return;
}
for (Owner owner: MainActivity.books.keySet()) {
for (Book book: Objects.requireNonNull(MainActivity.books.get(owner))) {
@SuppressLint("DefaultLocale")
String line = String.format(";%s;%s;%s;%d;;%s;;%s;%s%n",
owner.getLastName(), owner.getFirstName(), owner.getContact(), book.getPrice(), book.getEAN(), book.getTitle(), book.getAuthor());
stream.write(line.getBytes(StandardCharsets.UTF_8));
}
}
stream.flush();
stream.close();
} catch (IOException e) {
Log.e("BALScanner", "onDocumentActivityResult", e);
Toast.makeText(getContext(), "Erreur lors de la création du fichier", Toast.LENGTH_LONG).show();
}
}
} }

View File

@ -0,0 +1,106 @@
package dev.ninjdai.balscanner.ui.home;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
import dev.ninjdai.balscanner.MainActivity;
import dev.ninjdai.balscanner.R;
import dev.ninjdai.balscanner.books.Book;
import dev.ninjdai.balscanner.books.Owner;
public class UsersItemsAdapter extends RecyclerView.Adapter<UsersItemsAdapter.ViewHolder> {
private List<Owner> mData;
private final LayoutInflater mInflater;
private ItemClickListener mClickListener;
private ItemLongClickListener mLongClickListener;
// data is passed into the constructor
public UsersItemsAdapter(Context context, List<Owner> data) {
this.mInflater = LayoutInflater.from(context);
this.mData = data;
}
public void setmData(List<Owner> owners) {
mData = owners;
}
// inflates the row layout from xml when needed
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = mInflater.inflate(R.layout.text_row_item, parent, false);
return new ViewHolder(view);
}
// binds the data to the TextView in each row
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
Owner owner = mData.get(position);
holder.myTextView.setText(String.format("%s%s %s (%s) - %s", owner==MainActivity.current_owner ? "> " : "", owner.getFirstName(), owner.getLastName(), owner.getId(), owner.getContact()));
}
// total number of rows
@Override
public int getItemCount() {
return mData.size();
}
// stores and recycles views as they are scrolled off screen
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {
TextView myTextView;
ViewHolder(View itemView) {
super(itemView);
myTextView = itemView.findViewById(R.id.bookText);
itemView.setOnClickListener(this);
itemView.setOnLongClickListener(this);
}
@Override
public void onClick(View view) {
if (mClickListener != null) mClickListener.onItemClick(view, getAdapterPosition());
}
@Override
public boolean onLongClick(View view) {
if (mClickListener != null) return mLongClickListener.onItemLongClick(view, getAdapterPosition());
return false;
}
}
// convenience method for getting data at click position
Owner getItem(int id) {
return mData.get(id);
}
Owner removeItem(int id) {
MainActivity.books.remove(mData.get(id));
return mData.remove(id);
}
// allows clicks events to be caught
void setClickListener(ItemClickListener itemClickListener) {
this.mClickListener = itemClickListener;
}
void setLongClickListener(ItemLongClickListener itemLongClickListener) {
this.mLongClickListener = itemLongClickListener;
}
// parent activity will implement this method to respond to click events
public interface ItemClickListener {
void onItemClick(View view, int position);
}
public interface ItemLongClickListener {
boolean onItemLongClick(View view, int position);
}
}

View File

@ -53,7 +53,7 @@ public class ScannerFragment extends Fragment {
} }
public void updateItems() { public void updateItems() {
adapter.notifyItemChanged(MainActivity.books.get(MainActivity.avril).size()-1); adapter.notifyItemChanged(MainActivity.books.get(MainActivity.current_owner).size()-1);
} }
private void noBookFoundToast() { private void noBookFoundToast() {
@ -73,7 +73,7 @@ public class ScannerFragment extends Fragment {
builder.setPositiveButton("Valider", (dialog, which) -> { builder.setPositiveButton("Valider", (dialog, which) -> {
book.setPrice(Integer.parseInt(input.getText().toString())); book.setPrice(Integer.parseInt(input.getText().toString()));
MainActivity.books.computeIfPresent(MainActivity.avril, ((owner, ownerBooks) -> { MainActivity.books.computeIfPresent(MainActivity.current_owner, ((owner, ownerBooks) -> {
ownerBooks.add(book); ownerBooks.add(book);
return ownerBooks; return ownerBooks;
})); }));
@ -88,9 +88,11 @@ public class ScannerFragment extends Fragment {
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
RecyclerView recyclerView = view.findViewById(R.id.scanner_items); Toast.makeText(getActivity(), String.format("%s %s", MainActivity.current_owner.getFirstName(), MainActivity.current_owner.getLastName()), Toast.LENGTH_SHORT).show();
RecyclerView recyclerView = view.findViewById(R.id.users_items);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
adapter = new ScannerItemsAdapter(getContext(), MainActivity.books.get(MainActivity.avril)); adapter = new ScannerItemsAdapter(getContext(), MainActivity.books.get(MainActivity.current_owner));
adapter.setClickListener((v, position) -> Toast.makeText(getActivity(), "Book: " + adapter.getItem(position), Toast.LENGTH_LONG).show()); adapter.setClickListener((v, position) -> Toast.makeText(getActivity(), "Book: " + adapter.getItem(position), Toast.LENGTH_LONG).show());
adapter.setLongClickListener((v, position) -> { adapter.setLongClickListener((v, position) -> {
@ -125,7 +127,7 @@ public class ScannerFragment extends Fragment {
builder.setView(input); builder.setView(input);
builder.setPositiveButton("Suivant", (dialog, which) -> { builder.setPositiveButton("Suivant", (dialog, which) -> {
Book book = new Book(input.getText().toString(), "Inconnu", ean, MainActivity.avril, 0); Book book = new Book(input.getText().toString().strip(), "Inconnu", ean, MainActivity.current_owner, 0);
addBook(book); addBook(book);
}); });
builder.setNegativeButton("Annuler", (dialog, which) -> dialog.cancel()); builder.setNegativeButton("Annuler", (dialog, which) -> dialog.cancel());
@ -143,7 +145,7 @@ public class ScannerFragment extends Fragment {
if (authorRes != null) author = authorRes.getString("name"); if (authorRes != null) author = authorRes.getString("name");
} }
} }
Book book = new Book(title, author, ean, MainActivity.avril, 0); Book book = new Book(title, author, ean, MainActivity.current_owner, 0);
addBook(book); addBook(book);
} catch (JSONException ex) { } catch (JSONException ex) {
Log.e("App", "Failure", ex); Log.e("App", "Failure", ex);

View File

@ -6,17 +6,29 @@
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context=".ui.home.HomeFragment"> tools:context=".ui.home.HomeFragment">
<TextView <Button
android:id="@+id/text_home" android:id="@+id/user_button"
android:layout_width="match_parent" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp" android:layout_marginEnd="8dp"
android:textAlignment="center" android:layout_marginBottom="108dp"
android:textSize="20sp" android:text="@string/user_new"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent" />
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> <androidx.recyclerview.widget.RecyclerView
android:id="@+id/users_items"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<Button
android:id="@+id/export_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginBottom="68dp"
android:text="@string/export"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -17,7 +17,7 @@
app:layout_constraintEnd_toEndOf="parent" /> app:layout_constraintEnd_toEndOf="parent" />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/scanner_items" android:id="@+id/users_items"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" /> android:layout_height="match_parent" />

View File

@ -5,4 +5,6 @@
<string name="scan_new">Scan</string> <string name="scan_new">Scan</string>
<string name="element_text">element</string> <string name="element_text">element</string>
<string name="user_new">Ajout</string>
<string name="export">Export</string>
</resources> </resources>