Merge pull request #126 from ClaytonWWilson/master

Merge from master
This commit is contained in:
Aaron Sun 2020-11-21 11:33:53 -08:00 committed by GitHub
commit 392a67eb45
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 604 additions and 134 deletions

View File

@ -3,39 +3,48 @@ import java.sql.SQLException;
import java.util.ArrayList;
public class List {
Integer itemID;
Integer listID;
String name;
String owner;
long lastUpdated;
ArrayList<ItemEntry> entries;
boolean shared;
Integer uiPosition;
public List(ResultSet listRow, boolean shared) throws SQLException {
itemID = listRow.getInt("listID");
public List(ResultSet listRow, boolean shared, Integer uiPosition) throws SQLException {
listID = listRow.getInt("listID");
name = listRow.getString("name");
owner = listRow.getString("owner");
lastUpdated = listRow.getTimestamp("lastUpdated").toInstant().toEpochMilli();
entries = new ArrayList<>();
this.shared = shared;
this.uiPosition = uiPosition;
}
@Override
public String toString() {
return "List{" +
"itemID=" + itemID +
"listID=" + listID +
", name='" + name + '\'' +
", owner='" + owner + '\'' +
", lastUpdated=" + lastUpdated +
", entries=" + entries +
", shared=" + shared +
", uiPosition=" + uiPosition +
'}';
}
public Integer getItemID() {
return itemID;
public ItemEntry[] getEntries() {
return entries.toArray(new ItemEntry[entries.size()]);
}
public void setItemID(Integer itemID) {
this.itemID = itemID;
public Integer getListID() {
return listID;
}
public void setListID(Integer listID) {
this.listID = listID;
}
public String getName() {
@ -70,7 +79,14 @@ public class List {
this.shared = shared;
}
public ItemEntry[] getEntries() {
public Integer getUiPosition() {
return uiPosition;
}
public void setUiPosition(Integer uiPosition) {
this.uiPosition = uiPosition;
public ItemEntry[] getEntries() {
return entries.toArray(new ItemEntry[entries.size()]);
}

View File

@ -9,7 +9,8 @@ public class ListAdder implements CallHandler {
private String cognitoID;
private final String LIST_CREATE = "INSERT INTO List (name, owner, lastUpdated) VALUES (?, ?, ?);";
private final String LIST_ACCESS_GRANT = "INSERT INTO ListSharee(listID, userID, permissionLevel) VALUES(?, ?, ?);";
private final String LIST_ACCESS_GRANT = "INSERT INTO ListSharee(listID, userID, permissionLevel, uiPosition) VALUES(?, ?, ?, ?);";
private final String UI_POSITION_CHECK = "SELECT Max(uiPosition) as maxUIPosition FROM ListSharee WHERE userID = ?;";
public ListAdder(Connection connection, String cognitoID) {
this.connection = connection;
@ -17,9 +18,17 @@ public class ListAdder implements CallHandler {
}
public Object conductAction(Map<String, Object> bodyMap, HashMap<String, String> queryString, String cognitoID) throws SQLException {
PreparedStatement statement = connection.prepareStatement(LIST_CREATE, Statement.RETURN_GENERATED_KEYS);
String listName = bodyMap.get("name").toString();//Needs safe checking
PreparedStatement uiPositionCheck = connection.prepareStatement(UI_POSITION_CHECK);
uiPositionCheck.setString(1, cognitoID);
ResultSet uiPositionCheckRS = uiPositionCheck.executeQuery();
int nextPosition = 1;
if (uiPositionCheckRS.next()) {
nextPosition = uiPositionCheckRS.getInt("maxUIPosition") + 1;
}
PreparedStatement statement = connection.prepareStatement(LIST_CREATE, Statement.RETURN_GENERATED_KEYS);
statement.setString(1, listName);
statement.setString(2, cognitoID);
statement.setTimestamp(3, Timestamp.from(Instant.now()));
@ -32,6 +41,7 @@ public class ListAdder implements CallHandler {
accessGrant.setInt(1, newID);
accessGrant.setString(2, cognitoID);
accessGrant.setInt(3, ListPermissions.getAll());
accessGrant.setInt(4, nextPosition);
System.out.println(accessGrant);
accessGrant.executeUpdate();
connection.commit();

View File

@ -12,7 +12,7 @@ public class ListGetter implements CallHandler{
private final String cognitoID;
private final String GET_LIST = "SELECT * FROM List WHERE listID = ?;";
private final String GET_LISTS = "SELECT listID FROM ListSharee WHERE userID = ?;";
private final String GET_LISTS = "SELECT listID FROM ListSharee WHERE userID = ? ORDER BY uiPosition;";
private final String SHARE_CHECK = "SELECT * FROM ListSharee WHERE listID = ?;";
private final String GET_ENTRIES = "SELECT * FROM ListProduct WHERE listID = ?;";
@ -42,6 +42,7 @@ public class ListGetter implements CallHandler{
ResultSet accessResults = checkAccess.executeQuery();
int sharees = 0;
boolean verifiedAccess = false;
int uiPosition = 1;
while ((sharees < 2 && accessResults.next()) || !verifiedAccess) {
int permissionLevel = accessResults.getInt("permissionLevel");
if (accessResults.getString("userID").equals(cognitoID)) {
@ -49,6 +50,7 @@ public class ListGetter implements CallHandler{
if (!ListPermissions.hasPermission(permissionLevel, "Read")) {
throw new AccessControlException("User " + cognitoID + " does not have permission to read list " + id);
}
uiPosition = accessResults.getInt("uiPosition");
}
if (permissionLevel > 0) {
sharees++;
@ -64,7 +66,7 @@ public class ListGetter implements CallHandler{
ResultSet getListResults = getList.executeQuery();
getListResults.first();
System.out.println(getListResults);
List retrievedList = new List(getListResults, shared);
List retrievedList = new List(getListResults, shared, uiPosition);
System.out.println(retrievedList);
PreparedStatement getListEntries = connection.prepareStatement(GET_ENTRIES);
getListEntries.setInt(1, id);

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 ListPUT implements RequestHandler<Map<String,Object>, Object> {
public Object handleRequest(Map<String, Object> inputMap, Context unfilled) {
return BasicHandler.handleRequest(inputMap, unfilled, ListPutter.class);
}
}

View File

@ -0,0 +1,44 @@
import java.security.AccessControlException;
import java.sql.*;
import java.time.Instant;
import java.util.HashMap;
import java.util.Map;
public class ListPutter implements CallHandler {
private final Connection connection;
private final String cognitoID;
private final String ACCESS_CHECK = "SELECT * from ListSharee WHERE userID = ? and listID = ?;";
private final String LIST_RENAME = "UPDATE List SET name = ?, lastUpdated = ? WHERE listID = ?;";
public ListPutter(Connection connection, String cognitoID) {
this.connection = connection;
this.cognitoID = cognitoID;
}
@Override
public Object conductAction(Map<String, Object> bodyMap, HashMap<String, String> queryMap, String cognitoID) throws SQLException {
Integer listID = Integer.parseInt(bodyMap.get("listID").toString());
PreparedStatement accessCheck = connection.prepareStatement(ACCESS_CHECK);
accessCheck.setString(1, cognitoID);
accessCheck.setInt(2, listID);
System.out.println(accessCheck);
ResultSet userLists = accessCheck.executeQuery();
if (!userLists.next()) {
throw new AccessControlException("User does not have access to list");
} else {
if (!ListPermissions.hasPermission(userLists.getInt("permissionLevel"), "Delete")) {
throw new AccessControlException("User " + cognitoID + " does not have permission to edit list " + listID);
}
}
PreparedStatement renameList = connection.prepareStatement(LIST_RENAME);
renameList.setString(1, bodyMap.get("name").toString());
renameList.setTimestamp(2, Timestamp.from(Instant.now()));
renameList.setInt(3, listID);
System.out.println(renameList);
renameList.executeUpdate();
connection.commit();
return null;
}
}

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 ListDuplicatePOST implements RequestHandler<Map<String,Object>, Object>{
public Object handleRequest(Map<String, Object> inputMap, Context unfilled) {
return BasicHandler.handleRequest(inputMap, unfilled, ListDuplicater.class);
}
}

View File

@ -0,0 +1,73 @@
import com.amazonaws.services.lambda.AWSLambdaClientBuilder;
import com.amazonaws.services.lambda.model.InvokeRequest;
import com.amazonaws.services.lambda.model.InvokeResult;
import java.security.AccessControlException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.InputMismatchException;
import java.util.Map;
public class ListDuplicater implements CallHandler {
private Connection connection;
private String cognitoID;
private final String LIST_POPULATE = "INSERT INTO ListProduct(listID, productID, quantity, addedDate, purchased) SELECT ?, productID, quantity, addedDate, purchased FROM ListProduct WHERE listID = ?;";
private final String ACCESS_CHECK = "SELECT * from ListSharee WHERE userID = ? and listID = ?;";
public ListDuplicater(Connection connection, String cognitoID) {
this.connection = connection;
this.cognitoID = cognitoID;
}
public Object conductAction(Map<String, Object> bodyMap, HashMap<String, String> queryString, String cognitoID) throws SQLException {
String listName = bodyMap.get("name").toString();//Needs safe checking
Integer copyListID = (Integer) bodyMap.get("listID");
PreparedStatement accessCheck = connection.prepareStatement(ACCESS_CHECK);
accessCheck.setString(1, cognitoID);
accessCheck.setInt(2, copyListID);
ResultSet access = accessCheck.executeQuery();
if (access.next()) {
if (!ListPermissions.hasPermission(access.getInt("permissionLevel"), "Read")) {
throw new AccessControlException("User " + cognitoID + " does not have write permissions for list " + copyListID);
}
} else {
throw new AccessControlException("User " + cognitoID + " does not have any permissions to access list " + copyListID);
}
InvokeRequest invokeRequest = new InvokeRequest();
invokeRequest.setFunctionName("ListPOST");
invokeRequest.setPayload("{" +
" \"body\": {" +
" \"name\": \"" + listName + "\"" +
" }," +
" \"params\": {" +
" \"querystring\": {" +
" }" +
" }," +
" \"context\": {" +
" \"sub\": \"" + cognitoID + "\"" +
" }" +
"}");
System.out.println(invokeRequest);
InvokeResult listCreateResult = AWSLambdaClientBuilder.defaultClient().invoke(invokeRequest);
if (listCreateResult.getStatusCode() != 200) {
throw new InputMismatchException("Could not find specified user to share with");
}
Integer newListID = Integer.parseInt(new String(listCreateResult.getPayload().array()).replace("\"", ""));
PreparedStatement populateList = connection.prepareStatement(LIST_POPULATE);
populateList.setInt(1, newListID);
populateList.setInt(2, copyListID);
System.out.println(populateList);
populateList.executeUpdate();
connection.commit();
return newListID;
}
}

View File

@ -0,0 +1,70 @@
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 ListRepositionActor implements CallHandler {
private Connection connection;
private String cognitoID;
public ListRepositionActor(Connection connection, String cognitoID) {
this.connection = connection;
this.cognitoID = cognitoID;
}
final private String GET_PRIOR_POSITION = "SELECT uiPosition FROM ListSharee WHERE userID = ? AND listID = ?;";
final private String SET_NEW_POSITION = "UPDATE ListSharee SET uiPosition = ? WHERE userID = ? AND listID = ?;";
final private String DECREMENT_HIGHER_POSITIONS = "UPDATE ListSharee SET uiPosition = uiPosition - 1 WHERE uiPosition > ? AND userID = ?;";
final private String INCREMENT_GEQ_POSITIONS = "UPDATE ListSharee SET uiPosition = uiPosition + 1 WHERE uiPosition >= ? AND userID = ?;";
public Object conductAction(Map<String, Object> bodyMap, HashMap<String, String> queryString, String cognitoID) throws SQLException {
Integer listID = (Integer) bodyMap.get("listID");
Integer newPosition = (Integer) bodyMap.get("newPosition");
PreparedStatement getPriorPosition = connection.prepareStatement(GET_PRIOR_POSITION);
getPriorPosition.setString(1, cognitoID);
getPriorPosition.setInt(2, listID);
ResultSet priorPositionRS = getPriorPosition.executeQuery();
if (!priorPositionRS.next()) {
throw new IllegalArgumentException("Bad listID for user");
}
Integer priorPosition = priorPositionRS.getInt("uiPosition");
PreparedStatement openNewPosition = connection.prepareStatement(INCREMENT_GEQ_POSITIONS);
if (newPosition.equals(priorPosition)) {
return null;
}
if (newPosition < priorPosition) {
openNewPosition.setInt(1, newPosition);
} else {
openNewPosition.setInt(1, newPosition + 1);
}
openNewPosition.setString(2, cognitoID);
System.out.println(openNewPosition);
openNewPosition.executeUpdate();
PreparedStatement fillPriorPosition = connection.prepareStatement(DECREMENT_HIGHER_POSITIONS);
fillPriorPosition.setInt(1, priorPosition);
fillPriorPosition.setString(2, cognitoID);
System.out.println(fillPriorPosition);
fillPriorPosition.executeUpdate();
PreparedStatement setNewPosition = connection.prepareStatement(SET_NEW_POSITION);
setNewPosition.setInt(1, newPosition);
setNewPosition.setString(2, cognitoID);
setNewPosition.setInt(3, listID);
System.out.println(setNewPosition);
setNewPosition.executeUpdate();
connection.commit();
return null;
}
}

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 ListRepositionPUT implements RequestHandler<Map<String,Object>, Object> {
public Object handleRequest(Map<String, Object> inputMap, Context unfilled) {
return BasicHandler.handleRequest(inputMap, unfilled, ListRepositionActor.class);
}
}

View File

@ -22,9 +22,9 @@ public class ListSharer implements CallHandler {
}
final private String CHECK_ACCESS = "SELECT * from ListSharee WHERE listID = ? AND userID = ?;";
private final String UI_POSITION_CHECK = "SELECT Max(uiPosition) as maxUIPosition FROM ListSharee WHERE userID = ?;";
final private String SHARE_LIST = "INSERT INTO ListSharee(listID, userID, permissionLevel, uiPosition) VALUES(?, ?, ?, ?) ON DUPLICATE KEY UPDATE permissionLevel = ?;";
public Object conductAction(Map<String, Object> bodyMap, HashMap<String, String> queryString, String cognitoID) throws SQLException {
PreparedStatement checkAccess = connection.prepareStatement(CHECK_ACCESS);
Integer listID = Integer.parseInt(bodyMap.get("listID").toString());
@ -63,12 +63,23 @@ public class ListSharer implements CallHandler {
// throw new InputMismatchException("The specified user already has access");
// }
PreparedStatement uiPositionCheck = connection.prepareStatement(UI_POSITION_CHECK);
uiPositionCheck.setString(1, shareWithSub);
System.out.println(uiPositionCheck);
ResultSet uiPositionCheckRS = uiPositionCheck.executeQuery();
int nextPosition = 1;
if (uiPositionCheckRS.next()) {
nextPosition = uiPositionCheckRS.getInt("maxUIPosition") + 1;
}
PreparedStatement shareList = connection.prepareStatement(SHARE_LIST);
shareList.setInt(1, listID);
shareList.setString(2, shareWithSub);
Integer permissionLevel = Integer.parseInt(bodyMap.get("permissionLevel").toString());
shareList.setInt(3, permissionLevel);
shareList.setInt(4, permissionLevel);
shareList.setInt(4, nextPosition);
shareList.setInt(5, permissionLevel);
System.out.println(shareList);
shareList.executeUpdate();
connection.commit();
return null;

View File

@ -62,8 +62,12 @@ public class CreateListAddDialogFragment extends DialogFragment {
btnMinus.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (etQuantity.getText().toString().equals("")) {
etQuantity.setText("1");
}
int curQauntity = Integer.parseInt(etQuantity.getText().toString());
if (curQauntity > 0) {
if (curQauntity > 1) {
curQauntity--;
etQuantity.setText(String.format("%d", curQauntity));
}
@ -74,6 +78,10 @@ public class CreateListAddDialogFragment extends DialogFragment {
btnPlus.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (etQuantity.getText().toString().equals("")) {
etQuantity.setText("1");
}
int curQauntity = Integer.parseInt(etQuantity.getText().toString());
curQauntity++;
etQuantity.setText(String.format("%d", curQauntity));

View File

@ -1,27 +1,16 @@
package com.example.listify;
import android.app.Dialog;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import com.amplifyframework.auth.AuthException;
import android.view.View;
import android.widget.*;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import com.bumptech.glide.Glide;
import com.example.listify.data.List;
import com.example.listify.data.ListEntry;
import com.example.listify.model.Product;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import org.json.JSONException;
import java.io.IOException;
@ -215,7 +204,7 @@ public class ItemDetails extends AppCompatActivity implements ListPickerDialogFr
try {
ListEntry entry = new ListEntry(shoppingLists.get(selectedListIndex).getItemID(), curProduct.getItemId(), quantity, Instant.now().toEpochMilli(),false);
ListEntry entry = new ListEntry(shoppingLists.get(selectedListIndex).getListID(), curProduct.getItemId(), quantity, Instant.now().toEpochMilli(),false);
requestor.postObject(entry);
Toast.makeText(this, String.format("%d of Item added to %s", quantity, shoppingLists.get(selectedListIndex).getName()), Toast.LENGTH_LONG).show();
} catch (Exception e) {
@ -239,7 +228,7 @@ public class ItemDetails extends AppCompatActivity implements ListPickerDialogFr
Requestor requestor = new Requestor(am, configs.getProperty("apiKey"));
SynchronousReceiver<Integer> idReceiver = new SynchronousReceiver<>();
com.example.listify.data.List newList = new List(-1, name, "user filled by lambda", Instant.now().toEpochMilli());
com.example.listify.data.List newList = new List(-1, name, "user filled by lambda", Instant.now().toEpochMilli(), -1);
Thread t = new Thread(new Runnable() {
@Override

View File

@ -10,6 +10,8 @@ import android.widget.*;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.example.listify.ui.home.HomeFragment;
import com.bumptech.glide.Glide;
import com.example.listify.data.*;
import org.json.JSONException;
@ -27,6 +29,7 @@ public class ListPage extends AppCompatActivity implements Requestor.Receiver {
ListView listView;
MyAdapter myAdapter;
Requestor requestor;
SwipeRefreshLayout refreshList;
Button incrQuan;
Button decrQuan;
@ -59,7 +62,9 @@ public class ListPage extends AppCompatActivity implements Requestor.Receiver {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list);
final int listID = (int) getIntent().getSerializableExtra("listID");
final int LIST_ID = (int) getIntent().getSerializableExtra("listID");
final String LIST_NAME = (String) getIntent().getSerializableExtra("listName");
setTitle(LIST_NAME);
Properties configs = new Properties();
try {
@ -68,7 +73,7 @@ public class ListPage extends AppCompatActivity implements Requestor.Receiver {
e.printStackTrace();
}
requestor = new Requestor(am, configs.getProperty("apiKey"));
requestor.getObject(Integer.toString(listID), List.class, this);
requestor.getObject(Integer.toString(LIST_ID), List.class, this);
listView = findViewById(R.id.listView);
myAdapter = new MyAdapter(this, pNames, pStores, pPrices, pQuantity, pImages);
@ -77,6 +82,8 @@ public class ListPage extends AppCompatActivity implements Requestor.Receiver {
loadingListItems = findViewById(R.id.progress_loading_list_items);
loadingListItems.setVisibility(View.VISIBLE);
tvTotalPrice = (TextView) findViewById(R.id.total_price);
clearAll = (Button) findViewById(R.id.buttonClear);
clearAll.setOnClickListener(new View.OnClickListener() {
@Override
@ -114,7 +121,8 @@ public class ListPage extends AppCompatActivity implements Requestor.Receiver {
public void onClick(DialogInterface dialog, int which) {
EditText sharedEmailText = (EditText) codeView.findViewById(R.id.editTextTextSharedEmail);
String sharedEmail = sharedEmailText.getText().toString();
ListShare listShare = new ListShare(listID, sharedEmail, "Read, Write, Delete, Share");
ListShare listShare = new ListShare(LIST_ID, sharedEmail, "Read, Write, Delete, Share");
try {
requestor.putObject(listShare);
}
@ -131,6 +139,22 @@ public class ListPage extends AppCompatActivity implements Requestor.Receiver {
dialog.show();
}
});
refreshList = (SwipeRefreshLayout) findViewById(R.id.refresh_list);
refreshList.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
Properties configs = new Properties();
try {
configs = AuthManager.loadProperties(ListPage.this, "android.resource://" + getPackageName() + "/raw/auths.json");
} catch (IOException | JSONException e) {
e.printStackTrace();
}
requestor = new Requestor(am, configs.getProperty("apiKey"));
requestor.getObject(Integer.toString(LIST_ID), List.class, ListPage.this);
}
});
}
@Override
@ -180,6 +204,24 @@ public class ListPage extends AppCompatActivity implements Requestor.Receiver {
@Override
public void acceptDelivery(Object delivered) {
// Clear out old values
runOnUiThread(new Runnable() {
@Override
public void run() {
pNames.clear();
pStores.clear();
pPrices.clear();
pQuantity.clear();
pImages.clear();
totalPriceByStore.clear();
storeID2Name.clear();
storeHeaderIndex.clear();
pListItemPair.clear();
totalPrice = 0;
tvTotalPrice.setText(String.format("$%.2f", totalPrice));
}
});
List list = (List) delivered;
if(list != null) {
@ -260,8 +302,6 @@ public class ListPage extends AppCompatActivity implements Requestor.Receiver {
}
}
tvTotalPrice = (TextView) findViewById(R.id.total_price);
runOnUiThread(new Runnable() {
@Override
public void run() {
@ -271,6 +311,8 @@ public class ListPage extends AppCompatActivity implements Requestor.Receiver {
}
});
}
refreshList.setRefreshing(false);
}
class MyAdapter extends ArrayAdapter<String> {
@ -330,7 +372,7 @@ public class ListPage extends AppCompatActivity implements Requestor.Receiver {
catch (Exception e) {
Log.i("Authentication", e.toString());
}
listView.setAdapter(myAdapter);
myAdapter.notifyDataSetChanged();
}
});
if(Integer.parseInt(pQuantity.get(position)) <= 1) {
@ -365,7 +407,7 @@ public class ListPage extends AppCompatActivity implements Requestor.Receiver {
catch (Exception e) {
Log.i("Authentication", e.toString());
}
listView.setAdapter(myAdapter);
myAdapter.notifyDataSetChanged();
}
});
if(Integer.parseInt(pQuantity.get(position)) > 1) {
@ -391,7 +433,7 @@ public class ListPage extends AppCompatActivity implements Requestor.Receiver {
pImages.remove(position);
requestor.deleteObject(pListItemPair.remove(position));
listView.setAdapter(myAdapter);
myAdapter.notifyDataSetChanged();
}
});

View File

@ -90,8 +90,13 @@ public class ListPickerDialogFragment extends DialogFragment {
btnMinus.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Set to 1 if it is empty
if (etQuantity.getText().toString().equals("")) {
etQuantity.setText("1");
}
int curQauntity = Integer.parseInt(etQuantity.getText().toString());
if (curQauntity > 0) {
if (curQauntity > 1) {
curQauntity--;
etQuantity.setText(String.format("%d", curQauntity));
}
@ -102,6 +107,11 @@ public class ListPickerDialogFragment extends DialogFragment {
btnPlus.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Set to 1 if it is empty
if (etQuantity.getText().toString().equals("")) {
etQuantity.setText("1");
}
int curQauntity = Integer.parseInt(etQuantity.getText().toString());
curQauntity++;
etQuantity.setText(String.format("%d", curQauntity));

View File

@ -17,6 +17,8 @@ import androidx.navigation.ui.AppBarConfiguration;
import androidx.navigation.ui.NavigationUI;
import com.amplifyframework.auth.AuthException;
import com.example.listify.data.List;
import com.example.listify.data.ListDuplicate;
import com.example.listify.data.ListReposition;
import com.example.listify.data.SearchHistory;
import com.example.listify.ui.LoginPage;
import com.google.android.material.navigation.NavigationView;
@ -107,7 +109,10 @@ public class MainActivity extends AppCompatActivity implements CreateListDialogF
SynchronousReceiver<SearchHistory> historyReceiver = new SynchronousReceiver<>();
requestor.getObject("N/A", SearchHistory.class, historyReceiver, historyReceiver);
try {
requestor.putObject(new List(293, "Java.py", "me!", 1));
System.out.println(historyReceiver.await());
requestor.putObject(new ListReposition(291, 1));
requestor.postObject(new ListDuplicate(290, "yet another list"));
} catch (Exception e) {
e.printStackTrace();
}
@ -153,7 +158,7 @@ public class MainActivity extends AppCompatActivity implements CreateListDialogF
DrawerLayout drawer = findViewById(R.id.drawer_layout);
NavigationView navigationView = findViewById(R.id.nav_view);
mAppBarConfiguration = new AppBarConfiguration.Builder(
R.id.nav_home, R.id.nav_profile)
R.id.nav_home, R.id.nav_profile, R.id.nav_logout)
.setDrawerLayout(drawer)
.build();
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
@ -178,20 +183,14 @@ public class MainActivity extends AppCompatActivity implements CreateListDialogF
}
public void onClickSignout(MenuItem m) {
m.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
try {
am.signOutUser();
Intent intent = new Intent(MainActivity.this, com.example.listify.ui.LoginPage.class);
startActivity(intent);
}
catch (Exception e) {
Log.i("Authentication", e.toString());
}
return false;
}
});
try {
am.signOutUser();
Intent intent = new Intent(MainActivity.this, com.example.listify.ui.LoginPage.class);
startActivity(intent);
}
catch (Exception e) {
Log.i("Authentication", e.toString());
}
}
@Override
@ -205,7 +204,7 @@ public class MainActivity extends AppCompatActivity implements CreateListDialogF
Requestor requestor = new Requestor(am, configs.getProperty("apiKey"));
SynchronousReceiver<Integer> idReceiver = new SynchronousReceiver<>();
List newList = new List(-1, name, "user filled by lambda", Instant.now().toEpochMilli());
List newList = new List(-1, name, "user filled by lambda", Instant.now().toEpochMilli(), -1);
try {
requestor.postObject(newList, idReceiver, idReceiver);

View File

@ -13,6 +13,8 @@ import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.SearchView;
import android.widget.TextView;
import com.example.listify.adapter.SearchResultsListAdapter;
import com.example.listify.data.Chain;
import com.example.listify.data.ItemSearch;
@ -28,9 +30,10 @@ import java.util.Properties;
import static com.example.listify.MainActivity.am;
public class SearchResults extends AppCompatActivity implements FilterDialogFragment.OnFilterListener, SortDialogFragment.OnSortListener, Requestor.Receiver {
private ListView listView;
private ListView resultsListView;
private MenuItem filterItem;
private ProgressBar loadingSearch;
private TextView tvNoResults;
private SearchResultsListAdapter searchResultsListAdapter;
private List<Product> resultsProductList = new ArrayList<>();
private List<Product> resultsProductListSorted = new ArrayList<>();
@ -64,6 +67,7 @@ public class SearchResults extends AppCompatActivity implements FilterDialogFrag
setSupportActionBar(toolbar);
loadingSearch = (ProgressBar) findViewById(R.id.progress_loading_search);
tvNoResults = (TextView) findViewById(R.id.tv_search_no_results);
// Back button closes this activity and returns to previous activity (MainActivity)
ImageButton backButton = (ImageButton) findViewById(R.id.backToHomeButton);
@ -95,10 +99,10 @@ public class SearchResults extends AppCompatActivity implements FilterDialogFrag
}
});
ListView listView = (ListView) findViewById(R.id.search_results_list);
resultsListView = (ListView) findViewById(R.id.search_results_list);
searchResultsListAdapter = new SearchResultsListAdapter(this, resultsProductListSorted);
listView.setAdapter(searchResultsListAdapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
resultsListView.setAdapter(searchResultsListAdapter);
resultsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Intent itemDetailsPage = new Intent(SearchResults.this, ItemDetails.class);
@ -207,13 +211,22 @@ public class SearchResults extends AppCompatActivity implements FilterDialogFrag
requestor.getObject(query, ItemSearch.class, this);
}
// TODO: Scroll the list back to the top when a search, sort, or filter is performed
// Sorts the search results
private void sortResults() {
// Reset the filtered list
resultsProductListSorted.clear();
resultsProductListSorted.addAll(resultsProductList);
// Scroll the user back to the top of the results
if (resultsListView != null) {
runOnUiThread(new Runnable() {
@Override
public void run() {
resultsListView.smoothScrollToPosition(0);
}
});
}
// Sort Modes
// 0 default (no sorting)
// 1 itemName
@ -311,10 +324,22 @@ public class SearchResults extends AppCompatActivity implements FilterDialogFrag
}
// This is called after the search results come back from the server
// TODO: Display a "no results" message if nothing is found when searching
@Override
public void acceptDelivery(Object delivered) {
ItemSearch results = (ItemSearch) delivered;
// Display "no results" message if the search returns none
runOnUiThread(new Runnable() {
@Override
public void run() {
if (results.getResults().size() == 0) {
tvNoResults.setVisibility(View.VISIBLE);
} else {
tvNoResults.setVisibility(View.GONE);
}
}
});
try {
HashMap<Integer,String> chainNameMap = new HashMap<>();
for (int i = 0; i < results.getResults().size(); i++) {

View File

@ -35,7 +35,6 @@ public class SortDialogFragment extends DialogFragment {
}
// TODO: Sorting should scroll the user back to the top of the page
@Override
public Dialog onCreateDialog(final Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

View File

@ -10,6 +10,7 @@ import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import com.example.listify.model.Product;
import com.example.listify.R;
@ -55,8 +56,12 @@ public class SearchResultsListAdapter extends BaseAdapter {
TextView itemStore = (TextView) convertView.findViewById(R.id.item_store);
Product product = productList.get(position);
// TODO: If image url is broken, display @drawable/ic_baseline_broken_image_600.xml
Glide.with(activity).load(product.getImageUrl()).into(productImage);
Glide.with(activity)
.applyDefaultRequestOptions(new RequestOptions().placeholder(R.drawable.ic_baseline_image_600).error(R.drawable.ic_baseline_broken_image_600))
.load(product.getImageUrl())
.into(productImage);
if (product.getItemName().length() >= 60) {
itemName.setText(product.getItemName().substring(0, 60) + "...");
} else {

View File

@ -80,7 +80,8 @@ public class ShoppingListsSwipeableAdapter extends BaseAdapter {
holder.frontView = convertView.findViewById(R.id.front_layout);
holder.deleteList = convertView.findViewById(R.id.delete_list);
holder.shareList = convertView.findViewById(R.id.share_list);
holder.textView = (TextView) convertView.findViewById(R.id.shopping_list_name);
holder.listName = (TextView) convertView.findViewById(R.id.shopping_list_name);
holder.itemCount = (TextView) convertView.findViewById(R.id.shopping_list_item_count);
convertView.setTag(holder);
} else {
@ -90,19 +91,22 @@ public class ShoppingListsSwipeableAdapter extends BaseAdapter {
final List curList = lists.get(position);
// Bind the view to the unique list ID
binderHelper.bind(holder.swipeLayout, Integer.toString(curList.getItemID()));
binderHelper.bind(holder.swipeLayout, Integer.toString(curList.getListID()));
if(curList.isShared()) {
holder.textView.setText(curList.getName() + " (shared)");
holder.listName.setText(curList.getName() + " (shared)");
}
else {
holder.textView.setText(curList.getName());
holder.listName.setText(curList.getName());
}
holder.itemCount.setText(String.format("%d items", curList.getEntries().length));
holder.deleteList.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
requestor.deleteObject(Integer.toString(curList.getItemID()), List.class);
requestor.deleteObject(Integer.toString(curList.getListID()), List.class);
}
catch(Exception e) {
e.printStackTrace();
@ -129,7 +133,7 @@ public class ShoppingListsSwipeableAdapter extends BaseAdapter {
public void onClick(DialogInterface dialog, int which) {
EditText sharedEmailText = (EditText) codeView.findViewById(R.id.editTextTextSharedEmail);
String sharedEmail = sharedEmailText.getText().toString();
ListShare listShare = new ListShare(curList.getItemID(), sharedEmail, "Read, Write, Delete, Share");
ListShare listShare = new ListShare(curList.getListID(), sharedEmail, "Read, Write, Delete, Share");
try {
requestor.putObject(listShare);
}
@ -148,7 +152,7 @@ public class ShoppingListsSwipeableAdapter extends BaseAdapter {
Toast.makeText(activity, String.format("Share %s", curList.getName()), Toast.LENGTH_SHORT).show();
// Close the layout
binderHelper.closeLayout(Integer.toString(curList.getItemID()));
binderHelper.closeLayout(Integer.toString(curList.getListID()));
}
});
@ -157,8 +161,10 @@ public class ShoppingListsSwipeableAdapter extends BaseAdapter {
public void onClick(View v) {
Intent listPage = new Intent(activity, ListPage.class);
// Send the list ID
listPage.putExtra("listID", curList.getItemID());
// Send the list ID and list name
listPage.putExtra("listID", curList.getListID());
listPage.putExtra("listName", curList.getName());
activity.startActivity(listPage);
}
});
@ -171,6 +177,7 @@ public class ShoppingListsSwipeableAdapter extends BaseAdapter {
View frontView;
View deleteList;
View shareList;
TextView textView;
TextView listName;
TextView itemCount;
}
}

View File

@ -3,44 +3,47 @@ package com.example.listify.data;
import java.util.Arrays;
public class List {
Integer itemID;
Integer listID;
String name;
String owner;
long lastUpdated;
final ListEntry[] entries;
boolean shared;
Integer uiPosition;
public List(Integer itemID, String name, String owner, long lastUpdated, ListEntry[] entries, boolean shared) {
this.itemID = itemID;
public List(Integer listID, String name, String owner, long lastUpdated, ListEntry[] entries, boolean shared, Integer uiPosition) {
this.listID = listID;
this.name = name;
this.owner = owner;
this.lastUpdated = lastUpdated;
this.entries = entries;
this.shared = false;
this.uiPosition = uiPosition;
}
public List(Integer itemID, String name, String owner, long lastUpdated) {
this(itemID, name, owner, lastUpdated, null, false);
public List(Integer listID, String name, String owner, long lastUpdated, Integer uiPosition) {
this(listID, name, owner, lastUpdated, null, false, uiPosition);
}
@Override
public String toString() {
return "List{" +
"itemID=" + itemID +
"listID=" + listID +
", name='" + name + '\'' +
", owner='" + owner + '\'' +
", lastUpdated=" + lastUpdated +
", entries=" + Arrays.toString(entries) +
", shared=" + shared +
", uiPosition=" + uiPosition +
'}';
}
public Integer getItemID() {
return itemID;
public Integer getListID() {
return listID;
}
public void setItemID(Integer itemID) {
this.itemID = itemID;
public void setListID(Integer listID) {
this.listID = listID;
}
public String getName() {
@ -75,6 +78,13 @@ public class List {
this.shared = shared;
}
public Integer getUiPosition() {
return uiPosition;
}
public void setUiPosition(Integer uiPosition) {
this.uiPosition = uiPosition;
public ListEntry[] getEntries() {
return entries;
}

View File

@ -0,0 +1,37 @@
package com.example.listify.data;
// Info on List to copy
public class ListDuplicate {
Integer listID;
String name;
public ListDuplicate(Integer listID, String name) {
this.listID = listID;
this.name = name;
}
@Override
public String toString() {
return "ListDuplicate{" +
"listID=" + listID +
", name='" + name + '\'' +
'}';
}
public Integer getListID() {
return listID;
}
public void setListID(Integer listID) {
this.listID = listID;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -0,0 +1,35 @@
package com.example.listify.data;
public class ListReposition {
Integer listID;
Integer newPosition;
public ListReposition(Integer listID, Integer newPosition) {
this.listID = listID;
this.newPosition = newPosition;
}
@Override
public String toString() {
return "ListReposition{" +
"listID=" + listID +
", newPosition=" + newPosition +
'}';
}
public Integer getListID() {
return listID;
}
public void setListID(Integer listID) {
this.listID = listID;
}
public Integer getNewPosition() {
return newPosition;
}
public void setNewPosition(Integer newPosition) {
this.newPosition = newPosition;
}
}

View File

@ -6,11 +6,14 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.example.listify.AuthManager;
import com.example.listify.CreateListDialogFragment;
import com.example.listify.LoadingCircleDialog;
@ -37,6 +40,7 @@ public class HomeFragment extends Fragment implements CreateListDialogFragment.O
ListView shoppingListsView;
ProgressBar loadingLists;
TextView emptyMessage;
SwipeRefreshLayout refreshLists;
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_home, container, false);
@ -44,6 +48,7 @@ public class HomeFragment extends Fragment implements CreateListDialogFragment.O
loadingLists = (ProgressBar) root.findViewById(R.id.progress_loading_lists);
loadingLists.setVisibility(View.VISIBLE);
emptyMessage = (TextView) root.findViewById(R.id.textViewEmpty);
refreshLists = (SwipeRefreshLayout) root.findViewById(R.id.refresh_lists);
Properties configs = new Properties();
try {
@ -55,8 +60,8 @@ public class HomeFragment extends Fragment implements CreateListDialogFragment.O
requestor = new Requestor(am, configs.getProperty("apiKey"));
SynchronousReceiver<Integer[]> listIdsReceiver = new SynchronousReceiver<>();
final Requestor.Receiver<Integer[]> recv = this;
requestor.getListOfIds(List.class, recv, null);
// final Requestor.Receiver<Integer[]> recv = this;
requestor.getListOfIds(List.class, this, null);
FloatingActionButton fab = (FloatingActionButton) root.findViewById(R.id.new_list_fab);
@ -70,6 +75,23 @@ public class HomeFragment extends Fragment implements CreateListDialogFragment.O
}
});
refreshLists.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
Properties configs = new Properties();
try {
configs = AuthManager.loadProperties(getContext(), "android.resource://" + getActivity().getPackageName() + "/raw/auths.json");
} catch (IOException | JSONException e) {
e.printStackTrace();
}
requestor = new Requestor(am, configs.getProperty("apiKey"));
SynchronousReceiver<Integer[]> listIdsReceiver = new SynchronousReceiver<>();
requestor.getListOfIds(List.class, HomeFragment.this, null);
}
});
return root;
}
@ -88,7 +110,7 @@ public class HomeFragment extends Fragment implements CreateListDialogFragment.O
Requestor requestor = new Requestor(am, configs.getProperty("apiKey"));
SynchronousReceiver<Integer> idReceiver = new SynchronousReceiver<>();
List newList = new List(-1, name, "user filled by lambda", Instant.now().toEpochMilli());
List newList = new List(-1, name, "user filled by lambda", Instant.now().toEpochMilli() , -1);
try {
requestor.postObject(newList, idReceiver, idReceiver);
@ -101,7 +123,7 @@ public class HomeFragment extends Fragment implements CreateListDialogFragment.O
@Override
public void run() {
try {
newList.setItemID(idReceiver.await());
newList.setListID(idReceiver.await());
} catch (Exception e) {
getActivity().runOnUiThread(new Runnable() {
@Override
@ -130,6 +152,9 @@ public class HomeFragment extends Fragment implements CreateListDialogFragment.O
@Override
public void acceptDelivery(Object delivered) {
// Remove old lists on refresh
shoppingLists.clear();
Integer[] listIds = (Integer[]) delivered;
// Create threads and add them to a list
Thread[] threads = new Thread[listIds.length];
@ -169,16 +194,6 @@ public class HomeFragment extends Fragment implements CreateListDialogFragment.O
@Override
public void run() {
shoppingListsView.setAdapter(shoppingListsSwipeableAdapter);
// shoppingListsView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
// @Override
// public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// Intent listPage = new Intent(getContext(), ListPage.class);
//
// // Send the list ID
// listPage.putExtra("listID", shoppingLists.get(position).getItemID());
// startActivity(listPage);
// }
// });
loadingLists.setVisibility(View.GONE);
if(listIds.length == 0) {
@ -187,5 +202,6 @@ public class HomeFragment extends Fragment implements CreateListDialogFragment.O
}
});
refreshLists.setRefreshing(false);
}
}

View File

@ -1,19 +0,0 @@
package com.example.listify.ui.home;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
public class HomeViewModel extends ViewModel {
private MutableLiveData<String> mText;
public HomeViewModel() {
mText = new MutableLiveData<>();
mText.setValue("This is home fragment");
}
public LiveData<String> getText() {
return mText;
}
}

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M10.09,15.59L11.5,17l5,-5 -5,-5 -1.41,1.41L12.67,11H3v2h9.67l-2.58,2.59zM19,3H5c-1.11,0 -2,0.9 -2,2v4h2V5h14v14H5v-4H3v4c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector android:height="600dp" android:tint="?attr/colorControlNormal"
android:viewportHeight="24" android:viewportWidth="24"
android:width="600dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M21,19V5c0,-1.1 -0.9,-2 -2,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM8.5,13.5l2.5,3.01L14.5,12l4.5,6H5l3.5,-4.5z"/>
</vector>

View File

@ -37,12 +37,19 @@
</LinearLayout>
<ListView
android:id="@+id/listView"
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/refresh_list"
android:layout_width="match_parent"
android:layout_height="600dp"
android:layout_marginTop="50dp"
android:paddingBottom="20dp"/>
android:layout_height="wrap_content">
<ListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="600dp"
android:layout_marginTop="50dp"
android:paddingBottom="20dp"/>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<LinearLayout
android:layout_width="match_parent"

View File

@ -29,4 +29,14 @@
android:divider="@color/list_divider"
android:dividerHeight="1dp"/>
<TextView
android:id="@+id/tv_search_no_results"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="80dp"
android:textSize="20sp"
android:text="No Results"
android:gravity="center"
android:visibility="gone"/>
</RelativeLayout>

View File

@ -38,6 +38,7 @@
android:layout_width="60dp"
android:layout_height="50dp"
android:text="@string/_1"
android:digits="0123456789"
android:inputType="number"/>
<Button

View File

@ -35,6 +35,7 @@
android:layout_width="60dp"
android:layout_height="50dp"
android:text="@string/_1"
android:digits="0123456789"
android:inputType="number"/>
<Button

View File

@ -20,12 +20,18 @@
android:indeterminate="true"
android:visibility="gone"/>
<ListView
android:id="@+id/shopping_lists"
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/refresh_lists"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:divider="@color/list_divider"
android:dividerHeight="1dp"/>
android:layout_height="wrap_content" >
<ListView
android:id="@+id/shopping_lists"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:divider="@color/list_divider"
android:dividerHeight="1dp"/>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/new_list_fab"

View File

@ -40,11 +40,18 @@
android:layout_height="50dp">
<TextView
android:id="@+id/shopping_list_name"
android:layout_width="match_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:textStyle="bold"
android:layout_gravity="center"/>
android:layout_gravity="center_vertical"/>
<TextView
android:id="@+id/shopping_list_item_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:layout_gravity="center_vertical|end"/>
</FrameLayout>
</com.chauthai.swipereveallayout.SwipeRevealLayout>

View File

@ -17,7 +17,8 @@
<item
android:id="@+id/nav_logout"
android:title="Sign out"
android:onClick="onClickSignout" />
android:onClick="onClickSignout"
android:icon="@drawable/ic_baseline_exit_to_app_24"/>
<!-- <item-->
<!-- android:id="@+id/nav_gallery"-->