Merge pull request #99 from ClaytonWWilson/master

Merge from master
This commit is contained in:
Aaron Sun 2020-11-03 14:44:51 -08:00 committed by GitHub
commit e29a026954
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 688 additions and 251 deletions

View File

@ -0,0 +1,35 @@
import java.sql.ResultSet;
import java.sql.SQLException;
public class Chain {
String name;
String website;
public Chain (ResultSet chainRow) throws SQLException {
this.name = chainRow.getString("name");
System.out.println(this.name);
this.website = chainRow.getString("website");
System.out.println(this.website);
}
public Chain(String name, String website) {
this.name = name;
this.website = website;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getWebsite() {
return website;
}
public void setWebsite(String website) {
this.website = website;
}
}

View File

@ -0,0 +1,11 @@
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import java.util.Map;
public class ChainGET implements RequestHandler<Map<String,Object>, Object> {
public Object handleRequest(Map<String, Object> inputMap, Context unfilled) {
return BasicHandler.handleRequest(inputMap, unfilled, ChainGetter.class);
}
}

View File

@ -0,0 +1,29 @@
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
public class ChainGetter implements CallHandler {
private final Connection connection;
private final String GET_CHAIN = "SELECT * FROM Chain WHERE chainID = ?;";
public ChainGetter(Connection connection, String cognitoID) {
this.connection = connection;
}
@Override
public Object conductAction(Map<String, Object> bodyMap, HashMap<String, String> queryMap, String cognitoID) throws SQLException {
PreparedStatement statement = connection.prepareStatement(GET_CHAIN);
statement.setInt(1, Integer.parseInt(queryMap.get("id")));
System.out.println(statement);
ResultSet queryResults = statement.executeQuery();
queryResults.first();
System.out.println(queryResults);
Chain retrievedChain = new Chain(queryResults);
System.out.println(retrievedChain);
return retrievedChain;
}
}

View File

@ -12,6 +12,7 @@ public class ListDeleter implements CallHandler {
private final String GET_LISTS = "SELECT * FROM List WHERE (owner = ? AND listID = ?);"; private final String GET_LISTS = "SELECT * FROM List WHERE (owner = ? AND listID = ?);";
private final String DELETE_LIST = "DELETE FROM List WHERE listID = ?;"; private final String DELETE_LIST = "DELETE FROM List WHERE listID = ?;";
private final String DELETE_REQUESTOR_ACCESS = "DELETE FROM ListSharee where listID = ? AND userID = ?;";
private final String DELETE_LIST_ACCESS = "DELETE FROM ListSharee where listID = ?;"; private final String DELETE_LIST_ACCESS = "DELETE FROM ListSharee where listID = ?;";
private final String DELETE_LIST_ENTRIES = "DELETE FROM ListProduct where listID = ?;"; private final String DELETE_LIST_ENTRIES = "DELETE FROM ListProduct where listID = ?;";
@ -23,12 +24,17 @@ public class ListDeleter implements CallHandler {
@Override @Override
public Object conductAction(Map<String, Object> bodyMap, HashMap<String, String> queryMap, String cognitoID) throws SQLException { public Object conductAction(Map<String, Object> bodyMap, HashMap<String, String> queryMap, String cognitoID) throws SQLException {
Integer listID = Integer.parseInt(queryMap.get("id")); Integer listID = Integer.parseInt(queryMap.get("id"));
PreparedStatement cleanRequestorAccess = connection.prepareStatement(DELETE_REQUESTOR_ACCESS);
cleanRequestorAccess.setInt(1, listID);
cleanRequestorAccess.setString(2, cognitoID);
System.out.println(cleanRequestorAccess);
cleanRequestorAccess.executeUpdate();
PreparedStatement accessCheck = connection.prepareStatement(GET_LISTS); PreparedStatement accessCheck = connection.prepareStatement(GET_LISTS);
accessCheck.setString(1, cognitoID); accessCheck.setString(1, cognitoID);
accessCheck.setInt(2, listID); accessCheck.setInt(2, listID);
System.out.println(accessCheck); System.out.println(accessCheck);
ResultSet userLists = accessCheck.executeQuery(); ResultSet userLists = accessCheck.executeQuery();
if (!userLists.next()) { if (!userLists.next()) {
throw new AccessControlException("User does not have access to list"); throw new AccessControlException("User does not have access to list");
} }

View File

@ -0,0 +1,113 @@
package com.example.listify;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ListView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.DialogFragment;
import com.crystal.crystalrangeseekbar.interfaces.OnRangeSeekbarChangeListener;
import com.crystal.crystalrangeseekbar.widgets.CrystalRangeSeekbar;
import com.example.listify.adapter.CheckBoxListViewAdapter;
import java.util.ArrayList;
public class FilterDialogFragment extends DialogFragment {
public interface OnFilterListener {
void sendFilter(ArrayList<String> selectedStores, double minPrice, double maxPrice);
}
public OnFilterListener onFilterListener;
CrystalRangeSeekbar priceSeekbar;
CheckBoxListViewAdapter checkBoxAdapter;
private ArrayList<String> selectedStores;
private ArrayList<String> stores;
private double maxProductPrice; // The highest price on the slider
private double minPrice; // The selected min price
private double maxPrice; // The selected max price
public FilterDialogFragment(ArrayList<String> selectedStores, ArrayList<String> stores, double maxProductPrice, double minPrice, double maxPrice) {
this.selectedStores = selectedStores;
this.stores = stores;
this.maxProductPrice = maxProductPrice;
this.minPrice = minPrice;
this.maxPrice = maxPrice;
}
@Override
public Dialog onCreateDialog(final Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
// Get the layout inflater
LayoutInflater inflater = requireActivity().getLayoutInflater();
// Inflate and set the layout for the dialog
// Pass null as the parent view because its going in the dialog layout
View root = inflater.inflate(R.layout.dialog_filter, null);
builder.setView(root)
// Add action buttons
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
selectedStores = checkBoxAdapter.getChecked();
onFilterListener.sendFilter(selectedStores, priceSeekbar.getSelectedMinValue().doubleValue(), priceSeekbar.getSelectedMaxValue().doubleValue());
}
})
.setNegativeButton("cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
FilterDialogFragment.this.getDialog().cancel();
}
});
ListView storesList = root.findViewById(R.id.store_name_list);
// Create arraylist of stores from search results
ArrayList<String> storeChoices = new ArrayList<>(stores);
// Create adapter and send stores and selected stores
checkBoxAdapter = new CheckBoxListViewAdapter(getActivity(), storeChoices, this.selectedStores);
storesList.setAdapter(checkBoxAdapter);
// Set up the seekbar for price
priceSeekbar = (CrystalRangeSeekbar) root.findViewById(R.id.price_range_seekbar);
final TextView tvMin = (TextView) root.findViewById(R.id.tv_min_price);
final TextView tvMax = (TextView) root.findViewById(R.id.tv_max_price);
priceSeekbar.setMaxValue((float) this.maxProductPrice);
priceSeekbar.setMinStartValue((float) this.minPrice);
priceSeekbar.setMaxStartValue((float) this.maxPrice);
priceSeekbar.apply();
// Update price display
priceSeekbar.setOnRangeSeekbarChangeListener(new OnRangeSeekbarChangeListener() {
@Override
public void valueChanged(Number minValue, Number maxValue) {
tvMin.setText(String.format("$%.2f", minValue.doubleValue()));
tvMax.setText(String.format("$%.2f", maxValue.doubleValue()));
}
});
return builder.create();
}
// Required to extend DialogFragment
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
try {
onFilterListener = (OnFilterListener) getActivity();
} catch (ClassCastException e) {
Log.e("FilterDialogFragment", "onAttach: ClassCastException: " + e.getMessage());
}
}
}

View File

@ -225,6 +225,7 @@ public class ListPage extends AppCompatActivity implements Requestor.Receiver {
ArrayList<String> pQuantity; ArrayList<String> pQuantity;
ArrayList<String> pImages; ArrayList<String> pImages;
MyAdapter (Context c, ArrayList<String> names, ArrayList<String> stores, ArrayList<String> prices, ArrayList<String> quantity, ArrayList<String> images) { MyAdapter (Context c, ArrayList<String> names, ArrayList<String> stores, ArrayList<String> prices, ArrayList<String> quantity, ArrayList<String> images) {
super(c, R.layout.activity_listproductentry, R.id.productView, names); super(c, R.layout.activity_listproductentry, R.id.productView, names);
context = c; context = c;

View File

@ -190,18 +190,6 @@ public class MainActivity extends AppCompatActivity implements CreateListDialogF
return NavigationUI.navigateUp(navController, mAppBarConfiguration) || super.onSupportNavigateUp(); return NavigationUI.navigateUp(navController, mAppBarConfiguration) || super.onSupportNavigateUp();
} }
// This function only exists for the create new list option in hamburger menu
public void onClickCreateList(MenuItem m) {
m.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
CreateListDialogFragment createListDialogFragment = new CreateListDialogFragment();
createListDialogFragment.show(getSupportFragmentManager(), "Create New List");
return false;
}
});
}
public void onClickSignout(MenuItem m) { public void onClickSignout(MenuItem m) {
m.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { m.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override @Override

View File

@ -3,6 +3,8 @@ import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar; import androidx.appcompat.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.EditText; import android.widget.EditText;
@ -23,28 +25,35 @@ import java.util.Properties;
import static com.example.listify.MainActivity.am; import static com.example.listify.MainActivity.am;
public class SearchResults extends AppCompatActivity implements SortDialogFragment.OnSortingListener, Requestor.Receiver { public class SearchResults extends AppCompatActivity implements FilterDialogFragment.OnFilterListener, SortDialogFragment.OnSortListener, Requestor.Receiver {
private ListView listView;
private MenuItem filterItem;
private ProgressBar loadingSearch; private ProgressBar loadingSearch;
private SearchResultsListAdapter searchResultsListAdapter; private SearchResultsListAdapter searchResultsListAdapter;
private List<Product> resultsProductList = new ArrayList<>(); private List<Product> resultsProductList = new ArrayList<>();
private List<Product> resultsProductListSorted = new ArrayList<>(); private List<Product> resultsProductListSorted = new ArrayList<>();
private ArrayList<String> stores = new ArrayList<>(); private ArrayList<String> stores = new ArrayList<>();
private int storeSelection; private ArrayList<String> selectedStores = new ArrayList<>();
private int sortMode; private SortModes sortMode = SortModes.NONE;
private boolean descending; private boolean descending;
private double minPrice = 0; private double minPrice = 0;
private double maxPrice = -1; private double maxPrice = -1;
@Override @Override
public void sendSort(int storeSelection, int sortMode, boolean descending, double minPrice, double maxPrice) { public void sendFilter(ArrayList<String> selectedStores, double minPrice, double maxPrice) {
this.storeSelection = storeSelection; this.selectedStores = selectedStores;
this.sortMode = sortMode;
this.descending = descending;
this.minPrice = minPrice; this.minPrice = minPrice;
this.maxPrice = maxPrice; this.maxPrice = maxPrice;
sortResults(); sortResults();
} }
@Override
public void sendSort(SortModes sortMode, boolean descending) {
this.sortMode = sortMode;
this.descending = descending;
sortResults();
}
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@ -120,22 +129,27 @@ public class SearchResults extends AppCompatActivity implements SortDialogFragme
return false; return false;
} }
}); });
}
// TODO: Change this to a menu in which sort and filter are two different options @Override
// TODO: Sort should be disabled until a search is made public boolean onCreateOptionsMenu(Menu menu) {
// Create a dialog for filtering and sorting search results //Inflate the menu; this adds items to the action bar if it is present.
ImageButton sortButton = (ImageButton) findViewById(R.id.results_sort_button); getMenuInflater().inflate(R.menu.search, menu);
sortButton.setOnClickListener(new View.OnClickListener() { MenuItem sortItem = menu.findItem(R.id.action_sort);
sortItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override @Override
public void onClick(View v) { public boolean onMenuItemClick(MenuItem item) {
// Sort the store list SortDialogFragment sortDialog = new SortDialogFragment(sortMode, descending);
stores.sort(new Comparator<String>() { sortDialog.show(getSupportFragmentManager(), "Sort Dialog");
@Override return false;
public int compare(String o1, String o2) { }
return o1.compareTo(o2); });
}
});
// TODO: Reset upper and lower limits on price filter when a new search is made
filterItem = menu.findItem(R.id.action_filter);
filterItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
// Determine the max price for the price slider // Determine the max price for the price slider
double maxProductPrice; double maxProductPrice;
if (resultsProductList.isEmpty()) { if (resultsProductList.isEmpty()) {
@ -159,10 +173,18 @@ public class SearchResults extends AppCompatActivity implements SortDialogFragme
// Round up to nearest whole number for display on price seekbar // Round up to nearest whole number for display on price seekbar
maxProductPrice = Math.ceil(maxProductPrice); maxProductPrice = Math.ceil(maxProductPrice);
SortDialogFragment sortDialog = new SortDialogFragment(storeSelection, stores, sortMode, descending, maxProductPrice, minPrice, maxPrice); FilterDialogFragment sortDialog = new FilterDialogFragment(selectedStores, stores, maxProductPrice, minPrice, maxPrice);
sortDialog.show(getSupportFragmentManager(), "Sort"); sortDialog.show(getSupportFragmentManager(), "Filter Dialog");
return false;
} }
}); });
// Disable filtering by default until a search is made
if (resultsProductList.isEmpty()) {
filterItem.setEnabled(false);
}
return true;
} }
// Override default phone back button to add animation // Override default phone back button to add animation
@ -200,10 +222,11 @@ public class SearchResults extends AppCompatActivity implements SortDialogFragme
// Sort based on mode // Sort based on mode
switch (this.sortMode) { switch (this.sortMode) {
case 0: case NONE:
// Do nothing // Do nothing
break; break;
case 1:
case NAME:
resultsProductListSorted.sort(new Comparator<Product>() { resultsProductListSorted.sort(new Comparator<Product>() {
@Override @Override
public int compare(Product a, Product b) { public int compare(Product a, Product b) {
@ -212,7 +235,7 @@ public class SearchResults extends AppCompatActivity implements SortDialogFragme
}); });
break; break;
case 2: case PRICE:
resultsProductListSorted.sort(new Comparator<Product>() { resultsProductListSorted.sort(new Comparator<Product>() {
@Override @Override
public int compare(Product a, Product b) { public int compare(Product a, Product b) {
@ -227,7 +250,7 @@ public class SearchResults extends AppCompatActivity implements SortDialogFragme
}); });
break; break;
case 3: case STORE:
resultsProductListSorted.sort(new Comparator<Product>() { resultsProductListSorted.sort(new Comparator<Product>() {
@Override @Override
public int compare(Product a, Product b) { public int compare(Product a, Product b) {
@ -236,7 +259,7 @@ public class SearchResults extends AppCompatActivity implements SortDialogFragme
}); });
break; break;
case 4: case UPC:
resultsProductListSorted.sort(new Comparator<Product>() { resultsProductListSorted.sort(new Comparator<Product>() {
@Override @Override
public int compare(Product a, Product b) { public int compare(Product a, Product b) {
@ -247,7 +270,7 @@ public class SearchResults extends AppCompatActivity implements SortDialogFragme
} }
// Flip the list if descending is selected // Flip the list if descending is selected
if (this.sortMode != 0 & this.descending) { if (this.descending) {
for (int i = 0; i < resultsProductListSorted.size() / 2; i++) { for (int i = 0; i < resultsProductListSorted.size() / 2; i++) {
Product temp = resultsProductListSorted.get(i); Product temp = resultsProductListSorted.get(i);
resultsProductListSorted.set(i, resultsProductListSorted.get(resultsProductListSorted.size() - i - 1)); resultsProductListSorted.set(i, resultsProductListSorted.get(resultsProductListSorted.size() - i - 1));
@ -256,10 +279,10 @@ public class SearchResults extends AppCompatActivity implements SortDialogFragme
} }
// Only keep results that match the current store selection // Only keep results that match the current store selection
if (this.storeSelection != 0) { if (!this.selectedStores.equals(this.stores)) {
ArrayList<Product> temp = new ArrayList<>(); ArrayList<Product> temp = new ArrayList<>();
resultsProductListSorted.forEach(product -> { resultsProductListSorted.forEach(product -> {
if (product.getChainName().equals(this.stores.get(this.storeSelection - 1))) { if (this.selectedStores.contains(product.getChainName())) {
temp.add(product); temp.add(product);
} }
}); });
@ -317,9 +340,25 @@ public class SearchResults extends AppCompatActivity implements SortDialogFragme
} }
} }
// Reset selected stores on search so that every store is selected
this.selectedStores.clear();
this.selectedStores.addAll(stores);
// Add all results to the sorted list // Add all results to the sorted list
resultsProductListSorted.addAll(resultsProductList); resultsProductListSorted.addAll(resultsProductList);
// Filtering should only be allowed if there are items in the results
runOnUiThread(new Runnable() {
@Override
public void run() {
if (resultsProductList.isEmpty()) {
filterItem.setEnabled(false);
} else {
filterItem.setEnabled(true);
}
}
});
// Apply selected sorting to the list // Apply selected sorting to the list
sortResults(); sortResults();

View File

@ -1,56 +1,41 @@
package com.example.listify; package com.example.listify;
import android.app.Dialog; import android.app.Dialog;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log; import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.widget.AdapterView; import android.widget.LinearLayout;
import android.widget.ArrayAdapter;
import android.widget.ImageButton;
import android.widget.Spinner;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.SwitchCompat;
import androidx.fragment.app.DialogFragment; import androidx.fragment.app.DialogFragment;
import com.crystal.crystalrangeseekbar.interfaces.OnRangeSeekbarChangeListener;
import com.crystal.crystalrangeseekbar.widgets.CrystalRangeSeekbar;
import java.util.ArrayList;
public class SortDialogFragment extends DialogFragment { public class SortDialogFragment extends DialogFragment {
public interface OnSortingListener { public interface OnSortListener {
void sendSort(int storeSelection, int sortMode, boolean descending, double minPrice, double maxPrice); void sendSort(SortModes sortMode, boolean descending);
} }
public OnSortingListener onSortingListener; public OnSortListener onSortListener;
CrystalRangeSeekbar priceSeekbar; private SortModes sortMode;
private int storeSelection;
private int sortMode;
private boolean descending; private boolean descending;
private ArrayList<String> stores;
private double maxProductPrice; // The highest price on the slider
private double minPrice; // The selected min price
private double maxPrice; // The selected max price
public SortDialogFragment(int storeSelection, ArrayList<String> stores, int sortMode, boolean descending, double maxProductPrice, double minPrice, double maxPrice) { TextView tvSortNone;
this.storeSelection = storeSelection; TextView tvSortName;
this.stores = stores; TextView tvSortPrice;
TextView tvSortStore;
SwitchCompat swDescending;
public SortDialogFragment(SortModes sortMode, boolean descending) {
this.sortMode = sortMode; this.sortMode = sortMode;
this.descending = descending; this.descending = descending;
this.maxProductPrice = maxProductPrice;
this.minPrice = minPrice;
this.maxPrice = maxPrice;
} }
// TODO: Sorting should scroll the user back to the top of the page
@Override @Override
public Dialog onCreateDialog(final Bundle savedInstanceState) { public Dialog onCreateDialog(final Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
@ -60,122 +45,105 @@ public class SortDialogFragment extends DialogFragment {
// Inflate and set the layout for the dialog // Inflate and set the layout for the dialog
// Pass null as the parent view because its going in the dialog layout // Pass null as the parent view because its going in the dialog layout
View root = inflater.inflate(R.layout.dialog_sort, null); View root = inflater.inflate(R.layout.dialog_sort, null);
builder.setView(root) builder.setView(root);
// Add action buttons
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
onSortingListener.sendSort(storeSelection, sortMode, descending, priceSeekbar.getSelectedMinValue().doubleValue(), priceSeekbar.getSelectedMaxValue().doubleValue());
}
})
.setNegativeButton("cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
SortDialogFragment.this.getDialog().cancel();
}
});
Spinner storeDropdown = (Spinner) root.findViewById(R.id.sort_store_dropdown); tvSortNone = (TextView) root.findViewById(R.id.sort_none);
String[] storeChoices = new String[stores.size() + 1]; tvSortName = (TextView) root.findViewById(R.id.sort_name);
storeChoices[0] = "All"; tvSortPrice = (TextView) root.findViewById(R.id.sort_price);
for (int i = 1; i < stores.size() + 1; i++) { tvSortStore = (TextView) root.findViewById(R.id.sort_store);
storeChoices[i] = stores.get(i - 1); LinearLayout llDescendingContainer = (LinearLayout) root.findViewById(R.id.descending_container);
swDescending = (SwitchCompat) root.findViewById(R.id.switch_descending);
switch (this.sortMode) {
case NONE:
tvSortNone.setBackgroundColor(getResources().getColor(R.color.colorAccent));
break;
case NAME:
tvSortName.setBackgroundColor(getResources().getColor(R.color.colorAccent));
break;
case PRICE:
tvSortPrice.setBackgroundColor(getResources().getColor(R.color.colorAccent));
break;
case STORE:
tvSortStore.setBackgroundColor(getResources().getColor(R.color.colorAccent));
break;
} }
// Create the store selection dropdown if (this.descending) {
ArrayAdapter<String> storeAdapter = new ArrayAdapter<>(root.getContext(), android.R.layout.simple_spinner_dropdown_item, storeChoices); swDescending.setChecked(true);
storeDropdown.setAdapter(storeAdapter);
storeDropdown.setSelection(this.storeSelection);
storeDropdown.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
storeSelection = position;
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
// Change the sort arrow to be pointing up or down based on ascending or descending
final ImageButton sortDirectionButton = root.findViewById(R.id.sort_direction_button);
if (descending) {
sortDirectionButton.setImageResource(R.drawable.ic_baseline_arrow_downward_50);
} else {
sortDirectionButton.setImageResource(R.drawable.ic_baseline_arrow_upward_50);
} }
// Change arrow pointing direction whenever the user clicks the button tvSortNone.setOnClickListener(new View.OnClickListener() {
sortDirectionButton.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
if (descending) { handleClicked(tvSortNone, SortModes.NONE, swDescending.isChecked());
descending = false;
sortDirectionButton.setImageResource(R.drawable.ic_baseline_arrow_upward_50);
} else {
descending = true;
sortDirectionButton.setImageResource(R.drawable.ic_baseline_arrow_downward_50);
}
} }
}); });
// Create the sort mode selection dropdown tvSortName.setOnClickListener(new View.OnClickListener() {
Spinner sortDropdown = (Spinner) root.findViewById(R.id.sort_mode_dropdown);
String[] items = new String[] {"<Default>", "Name", "Price", "Store"};
ArrayAdapter<String> adapter = new ArrayAdapter<>(root.getContext(), android.R.layout.simple_spinner_dropdown_item, items);
sortDropdown.setAdapter(adapter);
sortDropdown.setSelection(this.sortMode);
sortDropdown.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override @Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { public void onClick(View v) {
sortMode = position; handleClicked(tvSortName, SortModes.NAME, swDescending.isChecked());
// Update the sort direction button
if (position == 0) {
sortDirectionButton.setEnabled(false);
} else {
sortDirectionButton.setEnabled(true);
}
} }
@Override
public void onNothingSelected(AdapterView<?> parent) {}
}); });
// Disable the direction button if they have the default sorting mode selected tvSortPrice.setOnClickListener(new View.OnClickListener() {
// Ascending and Descending are mostly irrelevant in the default sort mode
if (sortDropdown.getSelectedItemPosition() == 0) {
sortDirectionButton.setEnabled(false);
}
// Set up the seekbar for price
priceSeekbar = (CrystalRangeSeekbar) root.findViewById(R.id.price_range_seekbar);
final TextView tvMin = (TextView) root.findViewById(R.id.tv_min_price);
final TextView tvMax = (TextView) root.findViewById(R.id.tv_max_price);
priceSeekbar.setMaxValue((float) this.maxProductPrice);
priceSeekbar.setMinStartValue((float) this.minPrice);
priceSeekbar.setMaxStartValue((float) this.maxPrice);
priceSeekbar.apply();
// Update price display
priceSeekbar.setOnRangeSeekbarChangeListener(new OnRangeSeekbarChangeListener() {
@Override @Override
public void valueChanged(Number minValue, Number maxValue) { public void onClick(View v) {
tvMin.setText(String.format("$%.2f", minValue.doubleValue())); handleClicked(tvSortPrice, SortModes.PRICE, swDescending.isChecked());
tvMax.setText(String.format("$%.2f", maxValue.doubleValue())); }
});
tvSortStore.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
handleClicked(tvSortStore, SortModes.STORE, swDescending.isChecked());
}
});
// TODO: set onclick listener for descending switch
llDescendingContainer.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
System.out.println("linear");
swDescending.performClick();
// handleClicked(sortMode, swDescending.isChecked());
}
});
swDescending.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
System.out.println("switch");
descending = swDescending.isChecked();
onSortListener.sendSort(sortMode, swDescending.isChecked());
} }
}); });
return builder.create(); return builder.create();
} }
void handleClicked(TextView tvSelected, SortModes sortMode, boolean descending) {
this.sortMode = sortMode;
this.descending = descending;
tvSortNone.setBackgroundColor(getResources().getColor(R.color.white));
tvSortName.setBackgroundColor(getResources().getColor(R.color.white));
tvSortPrice.setBackgroundColor(getResources().getColor(R.color.white));
tvSortStore.setBackgroundColor(getResources().getColor(R.color.white));
tvSelected.setBackgroundColor(getResources().getColor(R.color.colorAccent));
onSortListener.sendSort(sortMode, descending);
// SortDialogFragment.this.getDialog().cancel();
}
// Required to extend DialogFragment // Required to extend DialogFragment
@Override @Override
public void onAttach(@NonNull Context context) { public void onAttach(@NonNull Context context) {
super.onAttach(context); super.onAttach(context);
try { try {
onSortingListener = (OnSortingListener) getActivity(); onSortListener = (OnSortListener) getActivity();
} catch (ClassCastException e) { } catch (ClassCastException e) {
Log.e("SortDialogFragment", "onAttach: ClassCastException: " + e.getMessage()); Log.e("SortDialogFragment", "onAttach: ClassCastException: " + e.getMessage());
} }

View File

@ -0,0 +1,9 @@
package com.example.listify;
public enum SortModes {
NONE,
NAME,
PRICE,
STORE,
UPC
}

View File

@ -0,0 +1,94 @@
package com.example.listify.adapter;
import android.app.Activity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.TextView;
import com.example.listify.R;
import java.util.ArrayList;
public class CheckBoxListViewAdapter extends BaseAdapter {
private Activity activity;
ArrayList<String> list = new ArrayList<>();
ArrayList<String> checkedList = new ArrayList<>();
public CheckBoxListViewAdapter(Activity activity, ArrayList<String> list, ArrayList<String> checkedList) {
super();
this.activity = activity;
this.list = list;
this.checkedList = checkedList;
}
@Override
public int getCount() {
return list.size();
}
@Override
public String getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
public class ViewHolder {
public TextView label;
public CheckBox checkBox;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder;
LayoutInflater inflator = ((Activity) activity).getLayoutInflater();
if (convertView == null) {
convertView = inflator.inflate(R.layout.filter_store_item, null);
convertView.setSoundEffectsEnabled(false);
holder = new ViewHolder();
holder.label = (TextView) convertView.findViewById(R.id.store_name);
holder.checkBox = (CheckBox)convertView.findViewById(R.id.store_check_box);
holder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
int getPosition = (Integer) buttonView.getTag();
if (isChecked) {
checkedList.add(list.get(getPosition));
} else {
checkedList.remove(list.get(getPosition));
}
}
});
convertView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
System.out.println("clicked");
holder.checkBox.performClick();
}
});
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.checkBox.setTag(position);
holder.label.setText("" + list.get(position));
holder.checkBox.setChecked(checkedList.contains(list.get(position)));
return convertView;
}
public ArrayList<String> getChecked() {
return this.checkedList;
}
}

View File

@ -0,0 +1,27 @@
package com.example.listify.data;
public class Chain {
String name;
String website;
public Chain(String name, String website) {
this.name = name;
this.website = website;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getWebsite() {
return website;
}
public void setWebsite(String website) {
this.website = website;
}
}

View File

@ -23,10 +23,12 @@
android:id="@+id/backToHomeButton" android:id="@+id/backToHomeButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:translationX="-10dp"
android:padding="10dp"
app:srcCompat="@drawable/abc_vector_test" app:srcCompat="@drawable/abc_vector_test"
android:background="@null" android:background="@null"
android:contentDescription="@string/backButton"/> android:contentDescription="@string/backButton"
android:foreground="?android:attr/selectableItemBackgroundBorderless"/>
<SearchView <SearchView
android:id="@+id/searchBar" android:id="@+id/searchBar"
@ -36,14 +38,6 @@
> >
</SearchView> </SearchView>
<ImageButton
android:id="@+id/results_sort_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:contentDescription="@string/sort_button_desc"
android:background="@null"
app:srcCompat="@drawable/ic_baseline_sort_28" />
</androidx.appcompat.widget.Toolbar> </androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>

View File

@ -19,13 +19,15 @@
app:popupTheme="@style/AppTheme.PopupOverlay" > app:popupTheme="@style/AppTheme.PopupOverlay" >
<ImageButton <ImageButton
android:id="@+id/searchButton" android:id="@+id/searchButton"
android:layout_width="30dp" android:layout_width="wrap_content"
android:layout_gravity="end" android:layout_gravity="end"
android:layout_marginEnd="5dp" android:layout_marginEnd="5dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:padding="10dp"
app:srcCompat="@drawable/ic_baseline_search_28" app:srcCompat="@drawable/ic_baseline_search_28"
android:contentDescription="@string/search_button_desc" android:contentDescription="@string/search_button_desc"
android:background="@null"/> android:background="@null"
android:foreground="?android:attr/selectableItemBackgroundBorderless"/>
</androidx.appcompat.widget.Toolbar> </androidx.appcompat.widget.Toolbar>

View File

@ -0,0 +1,62 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_store_selection"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:layout_marginStart="15dp"
android:text="@string/store_selection"
android:textSize="20sp"/>
<ListView
android:id="@+id/store_name_list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:layout_marginBottom="10dp"/>
<TextView
android:id="@+id/tv_price_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginStart="15dp"
android:text="Price"
android:textSize="20sp"/>
<com.crystal.crystalrangeseekbar.widgets.CrystalRangeSeekbar
android:id="@+id/price_range_seekbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="10dp"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_min_price"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginTop="20dp"
android:layout_marginStart="15dp"
android:text="$00.00" />
<TextView
android:id="@+id/tv_max_price"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginTop="20dp"
android:layout_marginEnd="15dp"
android:gravity="end"
android:text="$00.00" />
</LinearLayout>
</LinearLayout>

View File

@ -1,93 +1,104 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:orientation="vertical"
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content"
android:layout_marginHorizontal="30dp">
<!-- I would've use a ListView here, but it doesn't offer enough customization -->
<TextView
android:id="@+id/sort_none"
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="None"
android:textSize="16sp"
android:textColor="@android:color/black"
android:paddingStart="16dp"
android:gravity="center_vertical"
android:clickable="true"
android:focusable="true"
android:foreground="?attr/selectableItemBackgroundBorderless" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/list_divider" />
<TextView <TextView
android:id="@+id/tv_store_selection" android:id="@+id/sort_name"
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="50dp"
android:layout_weight="1" android:text="Name"
android:layout_marginTop="15dp" android:textSize="16sp"
android:layout_marginStart="15dp" android:textColor="@android:color/black"
android:text="@string/store_selection" /> android:paddingStart="16dp"
android:gravity="center_vertical"
android:clickable="true"
android:focusable="true"
android:foreground="?attr/selectableItemBackgroundBorderless"/>
<Spinner <View
android:id="@+id/sort_store_dropdown" android:layout_width="match_parent"
android:layout_width="265dp" android:layout_height="1dp"
android:layout_marginStart="15dp" android:background="@color/list_divider" />
android:layout_height="wrap_content"
android:background="@android:drawable/spinner_dropdown_background"
android:spinnerMode="dropdown"/>
<TextView <TextView
android:id="@+id/tv_sort_by" android:id="@+id/sort_price"
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="50dp"
android:layout_weight="1" android:text="Price"
android:layout_marginTop="20dp" android:textSize="16sp"
android:layout_marginStart="15dp" android:textColor="@android:color/black"
android:text="Sort by" /> android:paddingStart="16dp"
android:gravity="center_vertical"
android:clickable="true"
android:focusable="true"
android:foreground="?attr/selectableItemBackgroundBorderless"/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/list_divider" />
<TextView
android:id="@+id/sort_store"
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="Store"
android:textSize="16sp"
android:textColor="@android:color/black"
android:paddingStart="16dp"
android:gravity="center_vertical"
android:clickable="true"
android:focusable="true"
android:foreground="?attr/selectableItemBackgroundBorderless"/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/list_divider" />
<LinearLayout <LinearLayout
android:id="@+id/descending_container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="50dp"
android:paddingStart="15dp"> android:gravity="center_vertical"
android:clickable="true"
<Spinner android:focusable="true"
android:id="@+id/sort_mode_dropdown" android:foreground="?attr/selectableItemBackgroundBorderless"
android:layout_width="265dp" android:soundEffectsEnabled="false">
android:layout_height="wrap_content"
android:background="@android:drawable/spinner_dropdown_background"
android:spinnerMode="dropdown"/>
<ImageButton
android:id="@+id/sort_direction_button"
android:layout_marginTop="-6dp"
android:layout_width="57dp"
android:layout_height="70dp"
android:background="@null"
android:src="@drawable/ic_baseline_arrow_upward_50"/>
</LinearLayout>
<TextView
android:id="@+id/tv_price_label"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_marginTop="20dp"
android:layout_marginStart="15dp"
android:text="Price" />
<com.crystal.crystalrangeseekbar.widgets.CrystalRangeSeekbar
android:id="@+id/price_range_seekbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="10dp"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView <TextView
android:id="@+id/tv_min_price"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:text="Descending"
android:layout_marginTop="20dp" android:textSize="16sp"
android:layout_marginStart="15dp" android:textColor="@android:color/black"
android:text="$00.00" /> android:paddingStart="16dp"/>
<TextView
android:id="@+id/tv_max_price"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginTop="20dp"
android:layout_marginEnd="15dp"
android:gravity="end"
android:text="$00.00" />
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/switch_descending"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout> </LinearLayout>

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/store_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="New Text"
android:layout_alignParentTop="true"
android:layout_alignParentStart="true"
android:textSize="16sp"
android:layout_marginStart="25dp"/>
<CheckBox
android:id="@+id/store_check_box"
android:checked="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=""
android:focusable="false"
android:gravity="end"
android:layout_marginEnd="20dp"
android:layout_marginBottom="-5dp"
android:layout_alignParentEnd="true" />
</RelativeLayout>

View File

@ -5,7 +5,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:dragEdge="right" app:dragEdge="left"
app:mode="same_level"> app:mode="same_level">
<FrameLayout <FrameLayout

View File

@ -20,6 +20,11 @@
android:id="@+id/nav_lists" android:id="@+id/nav_lists"
android:icon="@drawable/ic_baseline_list_alt_28" android:icon="@drawable/ic_baseline_list_alt_28"
android:title="@string/menu_lists" /> android:title="@string/menu_lists" />
<!-- <item-->
<!-- android:id="@+id/nav_create_list"-->
<!-- android:icon="@drawable/ic_baseline_add_28"-->
<!-- android:title="Create New List"-->
<!-- android:onClick="onClickCreateList" />-->
<item <item
android:id="@+id/nav_logout" android:id="@+id/nav_logout"
android:title="Sign out" android:title="Sign out"

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_sort"
android:orderInCategory="101"
android:title="Sort"
app:showAsAction="never" />
<item
android:id="@+id/action_filter"
android:orderInCategory="101"
android:title="Filter"
app:showAsAction="never" />
</menu>

View File

@ -11,4 +11,5 @@
<color name="list_row_hover_start_color">#ebeef0</color> <color name="list_row_hover_start_color">#ebeef0</color>
<color name="list_row_hover_end_color">#1c9ef4</color> <color name="list_row_hover_end_color">#1c9ef4</color>
<color name="light_gray">#e6e6e6</color> <color name="light_gray">#e6e6e6</color>
<color name="white">#ffffffff</color>
</resources> </resources>