mirror of
https://github.com/ClaytonWWilson/Listify.git
synced 2025-12-17 02:58:48 +00:00
Merge branch 'master' into revamped-search-filter
This commit is contained in:
commit
7c9f5cbd5e
3
.gitignore
vendored
3
.gitignore
vendored
@ -90,3 +90,6 @@ Lambdas/Lists/target/classes/META-INF/Lists.kotlin_module
|
|||||||
Listify/app/src/main/res/raw/auths.json
|
Listify/app/src/main/res/raw/auths.json
|
||||||
Lambdas/Lists/target/surefire-reports/TestInputUtils.txt
|
Lambdas/Lists/target/surefire-reports/TestInputUtils.txt
|
||||||
Lambdas/Lists/target/surefire-reports/TEST-TestInputUtils.xml
|
Lambdas/Lists/target/surefire-reports/TEST-TestInputUtils.xml
|
||||||
|
Lambdas/Scraping/scraperConfigs.json
|
||||||
|
Lambdas/Scraping/dbConfigs.json
|
||||||
|
Lambdas/Scraping/artifacts/*
|
||||||
|
|||||||
@ -7,26 +7,24 @@ import java.util.Map;
|
|||||||
|
|
||||||
public class ItemSearcher implements CallHandler {
|
public class ItemSearcher implements CallHandler {
|
||||||
|
|
||||||
DBConnector connector;
|
Connection connection;
|
||||||
String cognitoID;
|
String cognitoID;
|
||||||
|
|
||||||
private final String GET_ITEM_MATCHES = "SELECT * FROM Product WHERE description LIKE ?";
|
private final String GET_ITEM_MATCHES = "SELECT * FROM Product WHERE description LIKE ? LIMIT 100;";
|
||||||
|
|
||||||
public ItemSearcher(DBConnector connector, String cognitoID) {
|
public ItemSearcher(Connection connection, String cognitoID) {
|
||||||
this.connector = connector;
|
this.connection = connection;
|
||||||
this.cognitoID = cognitoID;
|
this.cognitoID = cognitoID;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object conductAction(Map<String, Object> body, HashMap<String, String> queryParams, String s) throws SQLException {
|
public Object conductAction(Map<String, Object> body, HashMap<String, String> queryParams, String s) throws SQLException {
|
||||||
try (Connection connection = connector.getConnection()) {
|
PreparedStatement getItemMatches = connection.prepareStatement(GET_ITEM_MATCHES);
|
||||||
PreparedStatement getItemMatches = connection.prepareStatement(GET_ITEM_MATCHES);
|
getItemMatches.setString(1, "%" + queryParams.get("id") + "%");
|
||||||
getItemMatches.setString(1, "%" + queryParams.get("id") + "%");
|
System.out.println(getItemMatches);
|
||||||
System.out.println(getItemMatches);
|
ResultSet searchResults = getItemMatches.executeQuery();
|
||||||
ResultSet searchResults = getItemMatches.executeQuery();
|
ItemSearch searchResultsObject = new ItemSearch(searchResults);
|
||||||
ItemSearch searchResultsObject = new ItemSearch(searchResults);
|
System.out.println(searchResultsObject);
|
||||||
System.out.println(searchResultsObject);
|
return searchResultsObject;
|
||||||
return searchResultsObject;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,13 +8,15 @@ public class List {
|
|||||||
String owner;
|
String owner;
|
||||||
long lastUpdated;
|
long lastUpdated;
|
||||||
ArrayList<ItemEntry> entries;
|
ArrayList<ItemEntry> entries;
|
||||||
|
boolean shared;
|
||||||
|
|
||||||
public List(ResultSet listRow) throws SQLException {
|
public List(ResultSet listRow, boolean shared) throws SQLException {
|
||||||
itemID = listRow.getInt("listID");
|
itemID = listRow.getInt("listID");
|
||||||
name = listRow.getString("name");
|
name = listRow.getString("name");
|
||||||
owner = listRow.getString("owner");
|
owner = listRow.getString("owner");
|
||||||
lastUpdated = listRow.getTimestamp("lastUpdated").toInstant().toEpochMilli();
|
lastUpdated = listRow.getTimestamp("lastUpdated").toInstant().toEpochMilli();
|
||||||
entries = new ArrayList<>();
|
entries = new ArrayList<>();
|
||||||
|
this.shared = shared;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addItemEntry(ItemEntry entry) {
|
public void addItemEntry(ItemEntry entry) {
|
||||||
@ -67,4 +69,12 @@ public class List {
|
|||||||
public void setLastUpdated(long lastUpdated) {
|
public void setLastUpdated(long lastUpdated) {
|
||||||
this.lastUpdated = lastUpdated;
|
this.lastUpdated = lastUpdated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean getShared() {
|
||||||
|
return shared;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setShared(boolean shared) {
|
||||||
|
this.shared = shared;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,8 @@ public class ListAdder implements CallHandler {
|
|||||||
private Connection connection;
|
private Connection connection;
|
||||||
private String cognitoID;
|
private String cognitoID;
|
||||||
|
|
||||||
private final String LIST_CREATE = "INSERT INTO List (name, owner, lastUpdated) VALUES (?, ?, ?)";
|
private final String LIST_CREATE = "INSERT INTO List (name, owner, lastUpdated) VALUES (?, ?, ?);";
|
||||||
|
private final String LIST_ACCESS_GRANT = "INSERT INTO ListSharee(listID, userID) VALUES(?, ?);";
|
||||||
|
|
||||||
public ListAdder(Connection connection, String cognitoID) {
|
public ListAdder(Connection connection, String cognitoID) {
|
||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
@ -17,7 +18,9 @@ public class ListAdder implements CallHandler {
|
|||||||
|
|
||||||
public Object conductAction(Map<String, Object> bodyMap, HashMap<String, String> queryString, String cognitoID) throws SQLException {
|
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);
|
PreparedStatement statement = connection.prepareStatement(LIST_CREATE, Statement.RETURN_GENERATED_KEYS);
|
||||||
statement.setString(1, bodyMap.get("name").toString());//Needs safe checking
|
|
||||||
|
String listName = bodyMap.get("name").toString();//Needs safe checking
|
||||||
|
statement.setString(1, listName);
|
||||||
statement.setString(2, cognitoID);
|
statement.setString(2, cognitoID);
|
||||||
statement.setTimestamp(3, Timestamp.from(Instant.now()));
|
statement.setTimestamp(3, Timestamp.from(Instant.now()));
|
||||||
System.out.println(statement);
|
System.out.println(statement);
|
||||||
@ -25,7 +28,13 @@ public class ListAdder implements CallHandler {
|
|||||||
ResultSet newIDRS = statement.getGeneratedKeys();
|
ResultSet newIDRS = statement.getGeneratedKeys();
|
||||||
newIDRS.first();
|
newIDRS.first();
|
||||||
Integer newID = newIDRS.getInt(1);
|
Integer newID = newIDRS.getInt(1);
|
||||||
|
PreparedStatement accessGrant = connection.prepareStatement(LIST_ACCESS_GRANT);
|
||||||
|
accessGrant.setInt(1, newID);
|
||||||
|
accessGrant.setString(2, cognitoID);
|
||||||
|
System.out.println(accessGrant);
|
||||||
|
accessGrant.executeUpdate();
|
||||||
connection.commit();
|
connection.commit();
|
||||||
|
System.out.println(newID);
|
||||||
return newID;
|
return newID;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
12
Lambdas/Lists/List/src/ListDELETE.java
Normal file
12
Lambdas/Lists/List/src/ListDELETE.java
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import com.amazonaws.services.lambda.runtime.Context;
|
||||||
|
import com.amazonaws.services.lambda.runtime.RequestHandler;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class ListDELETE implements RequestHandler<Map<String,Object>, Object> {
|
||||||
|
|
||||||
|
public Object handleRequest(Map<String, Object> inputMap, Context unfilled) {
|
||||||
|
return BasicHandler.handleRequest(inputMap, unfilled, ListDeleter.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
50
Lambdas/Lists/List/src/ListDeleter.java
Normal file
50
Lambdas/Lists/List/src/ListDeleter.java
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
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.Map;
|
||||||
|
|
||||||
|
public class ListDeleter implements CallHandler {
|
||||||
|
private final Connection connection;
|
||||||
|
private final String cognitoID;
|
||||||
|
|
||||||
|
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_ACCESS = "DELETE FROM ListSharee where listID = ?;";
|
||||||
|
private final String DELETE_LIST_ENTRIES = "DELETE FROM ListProduct where listID = ?;";
|
||||||
|
|
||||||
|
public ListDeleter(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(queryMap.get("id"));
|
||||||
|
PreparedStatement accessCheck = connection.prepareStatement(GET_LISTS);
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
PreparedStatement cleanAccess = connection.prepareStatement(DELETE_LIST_ACCESS);
|
||||||
|
cleanAccess.setInt(1, listID);
|
||||||
|
System.out.println(cleanAccess);
|
||||||
|
cleanAccess.executeUpdate();
|
||||||
|
PreparedStatement deleteEntries = connection.prepareStatement(DELETE_LIST_ENTRIES);
|
||||||
|
deleteEntries.setInt(1, listID);
|
||||||
|
System.out.println(deleteEntries);
|
||||||
|
deleteEntries.executeUpdate();
|
||||||
|
PreparedStatement cleanList = connection.prepareStatement(DELETE_LIST);
|
||||||
|
cleanList.setInt(1, listID);
|
||||||
|
System.out.println(cleanList);
|
||||||
|
cleanList.executeUpdate();
|
||||||
|
connection.commit();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -11,7 +11,8 @@ public class ListGetter implements CallHandler{
|
|||||||
private final String cognitoID;
|
private final String cognitoID;
|
||||||
|
|
||||||
private final String GET_LIST = "SELECT * FROM List WHERE listID = ?;";
|
private final String GET_LIST = "SELECT * FROM List WHERE listID = ?;";
|
||||||
private final String GET_LISTS = "SELECT listID FROM List WHERE owner = ?;";
|
private final String GET_LISTS = "SELECT listID FROM ListSharee WHERE userID = ?;";
|
||||||
|
private final String SHARE_CHECK = "SELECT * FROM ListSharee WHERE listID = ?;";
|
||||||
private final String GET_ENTRIES = "SELECT * FROM ListProduct WHERE listID = ?;";
|
private final String GET_ENTRIES = "SELECT * FROM ListProduct WHERE listID = ?;";
|
||||||
|
|
||||||
public ListGetter(Connection connection, String cognitoID) {
|
public ListGetter(Connection connection, String cognitoID) {
|
||||||
@ -34,13 +35,25 @@ public class ListGetter implements CallHandler{
|
|||||||
}
|
}
|
||||||
return listIds;
|
return listIds;
|
||||||
}
|
}
|
||||||
|
PreparedStatement checkAccess = connection.prepareStatement(SHARE_CHECK);
|
||||||
|
checkAccess.setInt(1, id);
|
||||||
|
System.out.println(checkAccess);
|
||||||
|
ResultSet accessResults = checkAccess.executeQuery();
|
||||||
|
int sharees = 0;
|
||||||
|
while (sharees < 2 && accessResults.next()) {
|
||||||
|
sharees++;
|
||||||
|
}
|
||||||
|
boolean shared = false;
|
||||||
|
if (sharees > 1) {
|
||||||
|
shared = true;
|
||||||
|
}
|
||||||
PreparedStatement getList = connection.prepareStatement(GET_LIST);
|
PreparedStatement getList = connection.prepareStatement(GET_LIST);
|
||||||
getList.setInt(1, id);
|
getList.setInt(1, id);
|
||||||
System.out.println(getList);
|
System.out.println(getList);
|
||||||
ResultSet getListResults = getList.executeQuery();
|
ResultSet getListResults = getList.executeQuery();
|
||||||
getListResults.first();
|
getListResults.first();
|
||||||
System.out.println(getListResults);
|
System.out.println(getListResults);
|
||||||
List retrievedList = new List(getListResults);
|
List retrievedList = new List(getListResults, shared);
|
||||||
System.out.println(retrievedList);
|
System.out.println(retrievedList);
|
||||||
PreparedStatement getListEntries = connection.prepareStatement(GET_ENTRIES);
|
PreparedStatement getListEntries = connection.prepareStatement(GET_ENTRIES);
|
||||||
getListEntries.setInt(1, id);
|
getListEntries.setInt(1, id);
|
||||||
|
|||||||
@ -40,7 +40,7 @@ public class TestListAdder {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
assert (((Integer) rawIDReturn) == 1);
|
assert (((Integer) rawIDReturn) == 1);
|
||||||
assert (injector.getStatementString().contains("INSERT INTO List (name, owner, lastUpdated) VALUES (?, ?, ?)[aname, cognitoID,"));
|
assert (injector.getStatementString().contains("INSERT INTO List (name, owner, lastUpdated) VALUES (?, ?, ?);INSERT INTO ListSharee(listID, userID) VALUES(?, ?);[1, cognitoID]"));
|
||||||
} catch (SQLException throwables) {
|
} catch (SQLException throwables) {
|
||||||
assert shouldThrow;
|
assert shouldThrow;
|
||||||
throwables.printStackTrace();
|
throwables.printStackTrace();
|
||||||
|
|||||||
57
Lambdas/Lists/List/test/TestListDelete.java
Normal file
57
Lambdas/Lists/List/test/TestListDelete.java
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.security.AccessControlException;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class TestListDelete {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testListDeleterValid() {
|
||||||
|
testListDeleterCore(false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testListDeleterWOAccess() {
|
||||||
|
testListDeleterCore(false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testListDeleterError() {
|
||||||
|
testListDeleterCore(true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testListDeleterCore(boolean shouldThrow, boolean hasAccess) {
|
||||||
|
StatementInjector injector;
|
||||||
|
ArrayList<Object> rsReturns = new ArrayList<>();
|
||||||
|
rsReturns.add("cognitoID");
|
||||||
|
try {
|
||||||
|
if (!hasAccess) {
|
||||||
|
rsReturns = null;
|
||||||
|
}
|
||||||
|
injector = new StatementInjector(null, rsReturns, shouldThrow);
|
||||||
|
} catch (SQLException throwables) {
|
||||||
|
throwables.printStackTrace();
|
||||||
|
assert false; //Error in test infrastructure
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ListDeleter listDeleter = new ListDeleter(injector, "cognitoID");
|
||||||
|
Map<String, Object> body = (Map<String, Object>) TestBasicHandler.buildFullSampleMap().get("body");
|
||||||
|
HashMap<String, String> queryParams = (HashMap<String, String>) TestBasicHandler.buildFullSampleMap().get("body");
|
||||||
|
queryParams.put("id", "30");
|
||||||
|
|
||||||
|
try {
|
||||||
|
Object rawIDReturn = listDeleter.conductAction(body, queryParams, "cognitoID");
|
||||||
|
assert !shouldThrow;
|
||||||
|
assert (rawIDReturn == null);
|
||||||
|
System.out.println(injector.getStatementString());
|
||||||
|
assert (injector.getStatementString().equals("SELECT * FROM List WHERE (owner = ? AND listID = ?);DELETE FROM ListSharee where listID = ?;DELETE FROM ListProduct where listID = ?;DELETE FROM List WHERE listID = ?;[30]"));
|
||||||
|
} catch (SQLException throwables) {
|
||||||
|
assert shouldThrow ;
|
||||||
|
} catch (AccessControlException accessControlException) {
|
||||||
|
assert !hasAccess;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Lambdas/Lists/ListShare/src/ListSharePOST.java
Normal file
11
Lambdas/Lists/ListShare/src/ListSharePOST.java
Normal 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 ListSharePOST implements RequestHandler<Map<String,Object>, Object> {
|
||||||
|
|
||||||
|
public Object handleRequest(Map<String, Object> inputMap, Context unfilled) {
|
||||||
|
return BasicHandler.handleRequest(inputMap, unfilled, ListSharer.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
68
Lambdas/Lists/ListShare/src/ListSharer.java
Normal file
68
Lambdas/Lists/ListShare/src/ListSharer.java
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
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 ListSharer implements CallHandler {
|
||||||
|
|
||||||
|
private Connection connection;
|
||||||
|
private String cognitoID;
|
||||||
|
|
||||||
|
public ListSharer(Connection connection, String cognitoID) {
|
||||||
|
this.connection = connection;
|
||||||
|
this.cognitoID = cognitoID;
|
||||||
|
}
|
||||||
|
|
||||||
|
final private String CHECK_ACCESS = "SELECT * from ListSharee WHERE listID = ? AND userID = ?;";
|
||||||
|
final private String SHARE_LIST = "INSERT INTO ListSharee(listID, userID) VALUES(?, ?);";
|
||||||
|
|
||||||
|
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());
|
||||||
|
checkAccess.setInt(1, listID);
|
||||||
|
checkAccess.setString(2, cognitoID);
|
||||||
|
ResultSet checkAccessRS = checkAccess.executeQuery();
|
||||||
|
if (!checkAccessRS.next()) {
|
||||||
|
throw new AccessControlException("The requesting user does not have access to the requested list");
|
||||||
|
}
|
||||||
|
InvokeRequest invokeRequest = new InvokeRequest();
|
||||||
|
invokeRequest.setFunctionName("UserGET");
|
||||||
|
invokeRequest.setPayload("{" +
|
||||||
|
" \"body\": {" +
|
||||||
|
" \"emailToCheck\": \"" + bodyMap.get("shareWithEmail").toString() + "\"" +
|
||||||
|
" }," +
|
||||||
|
" \"params\": {" +
|
||||||
|
" \"querystring\": {" +
|
||||||
|
" }" +
|
||||||
|
" }," +
|
||||||
|
" \"context\": {" +
|
||||||
|
" \"sub\": \"not used\"" +
|
||||||
|
" }" +
|
||||||
|
"}");
|
||||||
|
InvokeResult invokeResult = AWSLambdaClientBuilder.defaultClient().invoke(invokeRequest);
|
||||||
|
if (invokeResult.getStatusCode() != 200) {
|
||||||
|
throw new InputMismatchException("Could not find specified user to share with");
|
||||||
|
}
|
||||||
|
String shareWithSub = new String(invokeResult.getPayload().array()).replace("\"", "");
|
||||||
|
checkAccess.setString(2, shareWithSub);
|
||||||
|
checkAccessRS = checkAccess.executeQuery();
|
||||||
|
if (checkAccessRS.next()) {
|
||||||
|
throw new InputMismatchException("The specified user already has access");
|
||||||
|
}
|
||||||
|
|
||||||
|
PreparedStatement shareList = connection.prepareStatement(SHARE_LIST);
|
||||||
|
shareList.setInt(1, listID);
|
||||||
|
shareList.setString(2, shareWithSub);
|
||||||
|
shareList.executeUpdate();
|
||||||
|
connection.commit();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
45
Lambdas/Lists/ListShare/test/TestListSharer.java
Normal file
45
Lambdas/Lists/ListShare/test/TestListSharer.java
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.security.AccessControlException;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class TestListSharer {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testListSharerWOAccess() {
|
||||||
|
testListEntryAdderCore(false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testListSharerError() {
|
||||||
|
testListEntryAdderCore(true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testListEntryAdderCore(boolean shouldThrow, boolean hasAccess) {
|
||||||
|
StatementInjector injector;
|
||||||
|
try {
|
||||||
|
injector = new StatementInjector(null, null, shouldThrow);
|
||||||
|
} catch (SQLException throwables) {
|
||||||
|
throwables.printStackTrace();
|
||||||
|
assert false; //Error in test infrastructure
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ListSharer listSharer = new ListSharer(injector, "cognitoID");
|
||||||
|
Map<String, Object> ignore = new HashMap<>();
|
||||||
|
Map<String, Object> body = TestInputUtils.addBody(ignore);
|
||||||
|
body.put("listID", 49);
|
||||||
|
|
||||||
|
try {
|
||||||
|
listSharer.conductAction(body, TestInputUtils.addQueryParams(ignore), "cognitoID");
|
||||||
|
assert !shouldThrow;
|
||||||
|
assert (injector.getStatementString().contains("INSERT INTO ListProduct (productID, listID, quantity, addedDate, purchased) VALUES (?, ?, ?, ?, ?)[16, 15, 14, "));
|
||||||
|
assert (injector.getStatementString().contains(", false]"));
|
||||||
|
} catch (SQLException throwables) {
|
||||||
|
assert shouldThrow;
|
||||||
|
} catch (AccessControlException accessControlException) {
|
||||||
|
assert !hasAccess;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -19,6 +19,8 @@ public class UserDeleter implements CallHandler {
|
|||||||
private final String GET_LISTS = "SELECT * FROM List WHERE (owner = ?);";
|
private final String GET_LISTS = "SELECT * FROM List WHERE (owner = ?);";
|
||||||
private final String DELETE_LIST_PRODUCT = "DELETE FROM ListProduct WHERE (listID = ?);";
|
private final String DELETE_LIST_PRODUCT = "DELETE FROM ListProduct WHERE (listID = ?);";
|
||||||
private final String DELETE_LISTS = "DELETE FROM List WHERE (owner = ?);";
|
private final String DELETE_LISTS = "DELETE FROM List WHERE (owner = ?);";
|
||||||
|
private final String DELETE_LIST_SHARES = "DELETE FROM ListSharee WHERE (listID = ?);";
|
||||||
|
private final String DELETE_LIST_ACCESS = "DELETE FROM ListSharee WHERE (userID = ?);";
|
||||||
|
|
||||||
public UserDeleter(Connection connection, String cognitoID) {
|
public UserDeleter(Connection connection, String cognitoID) {
|
||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
@ -57,13 +59,23 @@ public class UserDeleter implements CallHandler {
|
|||||||
statement = connection.prepareStatement(DELETE_LIST_PRODUCT);
|
statement = connection.prepareStatement(DELETE_LIST_PRODUCT);
|
||||||
statement.setInt(1, listID);
|
statement.setInt(1, listID);
|
||||||
System.out.println(statement);
|
System.out.println(statement);
|
||||||
statement.executeQuery();
|
statement.executeUpdate();
|
||||||
|
|
||||||
|
statement = connection.prepareStatement(DELETE_LIST_SHARES);
|
||||||
|
statement.setInt(1, listID);
|
||||||
|
System.out.println(statement);
|
||||||
|
statement.executeUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
statement = connection.prepareStatement(DELETE_LISTS);
|
statement = connection.prepareStatement(DELETE_LISTS);
|
||||||
statement.setString(1, cognitoID);
|
statement.setString(1, cognitoID);
|
||||||
System.out.println(statement);
|
System.out.println(statement);
|
||||||
statement.executeQuery();
|
statement.executeUpdate();
|
||||||
|
statement = connection.prepareStatement(DELETE_LIST_ACCESS);
|
||||||
|
statement.setString(1, cognitoID);
|
||||||
|
System.out.println(statement);
|
||||||
|
statement.executeUpdate();
|
||||||
|
|
||||||
connection.commit();
|
connection.commit();
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
11
Lambdas/Lists/User/src/UserGET.java
Normal file
11
Lambdas/Lists/User/src/UserGET.java
Normal 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 UserGET implements RequestHandler<Map<String,Object>, Object> {
|
||||||
|
|
||||||
|
public Object handleRequest(Map<String, Object> inputMap, Context unfilled) {
|
||||||
|
return BasicHandler.handleRequest(inputMap, unfilled, UserGetter.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
60
Lambdas/Lists/User/src/UserGetter.java
Normal file
60
Lambdas/Lists/User/src/UserGetter.java
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import com.amazonaws.services.cognitoidp.AWSCognitoIdentityProvider;
|
||||||
|
import com.amazonaws.services.cognitoidp.AWSCognitoIdentityProviderClientBuilder;
|
||||||
|
import com.amazonaws.services.cognitoidp.model.AttributeType;
|
||||||
|
import com.amazonaws.services.cognitoidp.model.ListUsersRequest;
|
||||||
|
import com.amazonaws.services.cognitoidp.model.ListUsersResult;
|
||||||
|
import com.amazonaws.services.cognitoidp.model.UserType;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class UserGetter implements CallHandler {
|
||||||
|
private String cognitoID;
|
||||||
|
|
||||||
|
public UserGetter(Connection connection, String cognitoID) {
|
||||||
|
this.cognitoID = cognitoID;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public Object conductAction(Map<String, Object> bodyMap, HashMap<String, String> queryMap, String cognitoID) {
|
||||||
|
Properties cognitoProperties;
|
||||||
|
try {
|
||||||
|
cognitoProperties = DBConnector.loadProperties("cognitoProperties.json");
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String userPoolId = cognitoProperties.get("userPoolId").toString();
|
||||||
|
System.out.println(userPoolId);
|
||||||
|
ListUsersRequest checkRequest = new ListUsersRequest().withUserPoolId(userPoolId);
|
||||||
|
Object emailObject = bodyMap.get("emailToCheck");
|
||||||
|
if (emailObject != null) {
|
||||||
|
checkRequest.setFilter("email=\"" + emailObject.toString() +"\"");
|
||||||
|
} else {
|
||||||
|
// checkRequest.setFilter("sub=\"" + cognitoID + "\"");
|
||||||
|
return cognitoID;
|
||||||
|
}
|
||||||
|
System.out.println(checkRequest);
|
||||||
|
AWSCognitoIdentityProvider awsCognitoIdentityProvider = AWSCognitoIdentityProviderClientBuilder.defaultClient();
|
||||||
|
ListUsersResult foundUsersResult = awsCognitoIdentityProvider.listUsers(checkRequest);
|
||||||
|
List<UserType> foundUsers = foundUsersResult.getUsers();
|
||||||
|
if (foundUsers.size() != 1) {
|
||||||
|
System.out.println(foundUsers);
|
||||||
|
if (foundUsers.size() == 0) {
|
||||||
|
throw new InputMismatchException("Not user with given email");
|
||||||
|
}
|
||||||
|
throw new InputMismatchException("Found more than one user with supposedly unique email");
|
||||||
|
}
|
||||||
|
UserType foundUser = foundUsers.get(0);
|
||||||
|
System.out.println(foundUser.getAttributes());
|
||||||
|
String sub = "";
|
||||||
|
for (AttributeType attribute : foundUser.getAttributes()) {
|
||||||
|
if (attribute.getName().equals("sub")) {
|
||||||
|
sub = attribute.getValue();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
System.out.println(attribute.getName() + ": " + attribute.getValue());
|
||||||
|
}
|
||||||
|
return sub;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,14 +1,17 @@
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.nio.file.NoSuchFileException;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
public class TestUserDeleter {
|
public class TestUserDeleter {
|
||||||
@Test
|
@Test
|
||||||
public void TestUserDelete(){
|
public void testUserDeleteFileUsage(){
|
||||||
try {
|
testUserDeleter(false);
|
||||||
testUserDeleter(false);
|
}
|
||||||
assert(false);
|
|
||||||
} catch (Exception e) {}
|
@Test
|
||||||
|
public void testUserDeleteInvalid(){
|
||||||
|
testUserDeleter(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testUserDeleter(boolean shouldThrow) {
|
public void testUserDeleter(boolean shouldThrow) {
|
||||||
@ -24,7 +27,9 @@ public class TestUserDeleter {
|
|||||||
try {
|
try {
|
||||||
userDeleter.conductAction(null, null, cognitoID);
|
userDeleter.conductAction(null, null, cognitoID);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
e.printStackTrace();
|
assert shouldThrow;
|
||||||
|
} catch (Exception e) {
|
||||||
|
assert e.getClass().equals(NoSuchFileException.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,10 +14,15 @@
|
|||||||
<artifactId>aws-lambda-java-core</artifactId>
|
<artifactId>aws-lambda-java-core</artifactId>
|
||||||
<version>1.2.1</version>
|
<version>1.2.1</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.amazonaws</groupId>
|
||||||
|
<artifactId>aws-java-sdk-lambda</artifactId>
|
||||||
|
<version>1.11.875</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.amazonaws</groupId>
|
<groupId>com.amazonaws</groupId>
|
||||||
<artifactId>aws-lambda-java-events</artifactId>
|
<artifactId>aws-lambda-java-events</artifactId>
|
||||||
<version>3.1.0</version>
|
<version>3.4.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.amazonaws</groupId>
|
<groupId>com.amazonaws</groupId>
|
||||||
@ -42,7 +47,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.httpcomponents</groupId>
|
<groupId>org.apache.httpcomponents</groupId>
|
||||||
<artifactId>httpclient</artifactId>
|
<artifactId>httpclient</artifactId>
|
||||||
<version>4.5.12</version>
|
<version>4.5.13</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.google.code.gson</groupId>
|
<groupId>com.google.code.gson</groupId>
|
||||||
|
|||||||
94
Lambdas/Scraping/KohlsScraper.py
Normal file
94
Lambdas/Scraping/KohlsScraper.py
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
import requests
|
||||||
|
import json
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
|
||||||
|
import pymysql.cursors
|
||||||
|
import time
|
||||||
|
from random import randint
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
def lambda_handler(event, context):
|
||||||
|
print(event["toScrape"])
|
||||||
|
scraper_configs = None
|
||||||
|
with open("scraperConfigs.json", "r") as scraper_configs_file:
|
||||||
|
scraper_configs = json.load(scraper_configs_file)
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
"apikey": scraper_configs["apikey"]
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
params = (
|
||||||
|
("url","https://www.kohls.com/search.jsp?submit-search=web-regular&search=" + event["toScrape"]),
|
||||||
|
("location","na"),
|
||||||
|
);
|
||||||
|
|
||||||
|
response = requests.get("https://app.zenscrape.com/api/v1/get", headers=headers, params=params)
|
||||||
|
|
||||||
|
retry_counter = 1
|
||||||
|
while response.status_code == 500:
|
||||||
|
print("Retry #" + str(retry_counter))
|
||||||
|
retry_counter += 1
|
||||||
|
time.sleep(randint(5,20))
|
||||||
|
response = requests.get("https://app.zenscrape.com/api/v1/get", headers=headers, params=params)
|
||||||
|
|
||||||
|
if response.status_code != 200:
|
||||||
|
print("Scraping status code: " + str(response.status_code ))
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
soup = BeautifulSoup(response.text, "html.parser")
|
||||||
|
|
||||||
|
|
||||||
|
insert_params = []
|
||||||
|
|
||||||
|
for match in soup.find_all(id=re.compile(".*_prod_price")):
|
||||||
|
price = None
|
||||||
|
description = ""
|
||||||
|
match_split = match.text.split()
|
||||||
|
for section in match_split:
|
||||||
|
if '$' in section:
|
||||||
|
description = ""
|
||||||
|
if price == None:
|
||||||
|
price = section
|
||||||
|
continue
|
||||||
|
if ('(' in section) or (')' in section):
|
||||||
|
continue
|
||||||
|
description += section + " "
|
||||||
|
description = description.strip()
|
||||||
|
imgUrl = ""
|
||||||
|
imgUrlBase = "https://media.kohlsimg.com/is/image/kohls/"
|
||||||
|
for prior in match.previous_siblings:
|
||||||
|
if imgUrlBase in str(prior):
|
||||||
|
imgUrl = imgUrlBase + str(prior).split(imgUrlBase)[1].split('?')[0].split('"')[0]
|
||||||
|
print(price + " for: " + description + " @: " + imgUrl)
|
||||||
|
insert_params.append((3, description, float(price.split('$')[1]), imgUrl))
|
||||||
|
|
||||||
|
db_configs = None
|
||||||
|
with open("dbConfigs.json", "r") as db_configs_file:
|
||||||
|
db_configs = json.load(db_configs_file)
|
||||||
|
|
||||||
|
|
||||||
|
connection = pymysql.connect(host=db_configs["host"],
|
||||||
|
user=db_configs["user"],
|
||||||
|
password=db_configs["password"],
|
||||||
|
db=db_configs["db_name"],
|
||||||
|
charset='utf8mb4',
|
||||||
|
cursorclass=pymysql.cursors.DictCursor)
|
||||||
|
|
||||||
|
try:
|
||||||
|
with connection.cursor() as cursor:
|
||||||
|
PRODUCT_INSERT_SYNTAX = "INSERT INTO Product (chainID, description, price, imageURL) VALUES (%s, %s, %s, %s);"
|
||||||
|
cursor.executemany(PRODUCT_INSERT_SYNTAX, insert_params)
|
||||||
|
connection.commit()
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
traceback.print_exc()
|
||||||
|
finally:
|
||||||
|
connection.close()
|
||||||
|
|
||||||
|
return {
|
||||||
|
'statusCode': 200,
|
||||||
|
'body': 'Scraped: ' + event["toScrape"]
|
||||||
|
}
|
||||||
9
Lambdas/Scraping/buildKohlsZip.sh
Normal file
9
Lambdas/Scraping/buildKohlsZip.sh
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#Currently to be run only from the Scraping directory
|
||||||
|
rm artifacts/kohlsScraper.zip
|
||||||
|
OLDPWD=$(pwd)
|
||||||
|
cd build/
|
||||||
|
zip -r9 ${OLDPWD}/artifacts/kohlsScraper.zip .
|
||||||
|
cd ${OLDPWD}
|
||||||
|
zip -r9 artifacts/kohlsScraper.zip *.json
|
||||||
|
zip -r9 artifacts/kohlsScraper.zip KohlsScraper.py
|
||||||
5
Lambdas/Scraping/buildRunOrchestratorZip.sh
Normal file
5
Lambdas/Scraping/buildRunOrchestratorZip.sh
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#Currently to be run only from the Scraping directory
|
||||||
|
rm artifacts/runOrchestrator.zip
|
||||||
|
zip -r9 artifacts/runOrchestrator.zip prefix_list_part*.txt
|
||||||
|
zip -r9 artifacts/runOrchestrator.zip runOrchestrator.py
|
||||||
6801
Lambdas/Scraping/nounlist.txt
Normal file
6801
Lambdas/Scraping/nounlist.txt
Normal file
File diff suppressed because it is too large
Load Diff
33
Lambdas/Scraping/prefix_list_builder.py
Normal file
33
Lambdas/Scraping/prefix_list_builder.py
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import json
|
||||||
|
|
||||||
|
LIST_SIZE = 100
|
||||||
|
|
||||||
|
wordlist = []
|
||||||
|
with open("nounlist.txt") as nounlist:
|
||||||
|
for noun in nounlist:
|
||||||
|
wordlist.append(noun)
|
||||||
|
|
||||||
|
prefix_list = []
|
||||||
|
for word in wordlist:
|
||||||
|
prefix_list.append(word[:min(len(word), 3)])
|
||||||
|
|
||||||
|
word_lists = []
|
||||||
|
for i in range(int(len(prefix_list) / LIST_SIZE)):
|
||||||
|
word_lists.append([])
|
||||||
|
|
||||||
|
current_list_len = 0
|
||||||
|
current_list = 0
|
||||||
|
for prefix in prefix_list:
|
||||||
|
prefix = prefix.strip()
|
||||||
|
if current_list_len >= LIST_SIZE:
|
||||||
|
if (word_lists[current_list][-1] != prefix):
|
||||||
|
current_list_len = 0
|
||||||
|
current_list += 1
|
||||||
|
if (current_list_len == 0 or word_lists[current_list][-1] != prefix):
|
||||||
|
word_lists[current_list].append(prefix)
|
||||||
|
current_list_len += 1
|
||||||
|
|
||||||
|
for i in range(current_list + 1):
|
||||||
|
with open("prefix_list_part" + str(i + 1) + ".txt", "w") as prefix_list_part:
|
||||||
|
json.dump(word_lists[i], prefix_list_part)
|
||||||
|
|
||||||
1
Lambdas/Scraping/prefix_list_part1.txt
Normal file
1
Lambdas/Scraping/prefix_list_part1.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
["ATM", "CD", "SUV", "TV", "aar", "aba", "abb", "abd", "abi", "abn", "abo", "abr", "abs", "abu", "aca", "acc", "ace", "ach", "aci", "ack", "aco", "acq", "acr", "act", "acu", "ad", "ada", "add", "adj", "adm", "ado", "adr", "adu", "adv", "aff", "afo", "aft", "age", "agg", "agl", "ago", "agr", "aid", "aim", "air", "ala", "alb", "alc", "ald", "ale", "alf", "alg", "ali", "all", "alm", "alp", "alt", "alu", "ama", "amb", "ame", "amm", "amn", "amo", "amu", "ana", "anc", "and", "ane", "ang", "ani", "ank", "ann", "ano", "ans", "ant", "anx", "any", "apa", "ape", "apo", "app", "apr", "aps", "aqu", "arc", "are", "arg", "ari", "ark", "arm", "arr", "art", "asc", "ash", "asi", "asp", "ass", "ast", "asy"]
|
||||||
1
Lambdas/Scraping/prefix_list_part10.txt
Normal file
1
Lambdas/Scraping/prefix_list_part10.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
["mow", "moz", "mud", "muf", "mug", "muk", "mul", "mur", "mus", "mut", "myc", "mys", "myt", "nai", "nam", "nan", "nap", "nar", "nas", "nat", "nav", "nec", "nee", "neg", "nei", "neo", "nep", "ner", "nes", "net", "neu", "new", "nex", "nib", "nic", "nie", "nig", "nin", "nit", "nob", "nod", "noi", "non", "noo", "nor", "nos", "not", "nou", "nov", "nuc", "nud", "nuk", "num", "nun", "nur", "nut", "nyl", "nym", "oak", "oar", "oas", "oat", "obe", "obi", "obj", "obl", "obo", "obs", "occ", "oce", "oct", "odo", "ody", "oeu", "off", "oil", "okr", "old", "ole", "oli", "ome", "omi", "omn", "onc", "oni", "onl", "ons", "ope", "oph", "opi", "opo", "opp", "opt", "ora", "orc", "ord", "ore", "org", "ori", "orn"]
|
||||||
1
Lambdas/Scraping/prefix_list_part11.txt
Normal file
1
Lambdas/Scraping/prefix_list_part11.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
["osm", "osp", "ost", "oth", "ott", "oun", "out", "ova", "ove", "owl", "own", "ox", "oxf", "oxy", "oys", "ozo", "pac", "pad", "pag", "pai", "paj", "pal", "pam", "pan", "pap", "par", "pas", "pat", "pau", "pav", "paw", "pay", "pea", "pec", "ped", "pee", "peg", "pel", "pen", "peo", "pep", "per", "pes", "pet", "pew", "pha", "phe", "phi", "pho", "phr", "phy", "pia", "pic", "pie", "pig", "pik", "pil", "pim", "pin", "pio", "pip", "pir", "pis", "pit", "piz", "pla", "ple", "pli", "plo", "plu", "ply", "pne", "poc", "pod", "poe", "poi", "pok", "pol", "pom", "pon", "poo", "pop", "por", "pos", "pot", "pou", "pov", "pow", "pra", "pre", "pri", "pro", "pru", "pse", "psy", "pta", "pub", "pud", "puf", "pug"]
|
||||||
1
Lambdas/Scraping/prefix_list_part12.txt
Normal file
1
Lambdas/Scraping/prefix_list_part12.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
["pul", "pum", "pun", "pup", "pur", "pus", "put", "puz", "pyr", "qua", "que", "qui", "quo", "rab", "rac", "rad", "raf", "rag", "rai", "rak", "ral", "ram", "ran", "rap", "ras", "rat", "rav", "raw", "ray", "raz", "rea", "reb", "rec", "red", "ree", "ref", "reg", "reh", "rei", "rej", "rel", "rem", "ren", "reo", "rep", "req", "res", "ret", "reu", "rev", "rew", "rhe", "rhi", "rhu", "rhy", "rib", "ric", "rid", "rif", "rig", "rim", "rin", "rio", "rip", "ris", "rit", "riv", "roa", "rob", "roc", "rod", "rol", "rom", "roo", "rop", "ros", "rot", "rou", "row", "rub", "ruc", "rud", "ruf", "rug", "rui", "rul", "rum", "run", "rus", "rut", "rye", "sab", "sac", "sad", "saf", "sag", "sai", "sak", "sal", "sam"]
|
||||||
1
Lambdas/Scraping/prefix_list_part13.txt
Normal file
1
Lambdas/Scraping/prefix_list_part13.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
["san", "sar", "sas", "sat", "sau", "sav", "saw", "sax", "sca", "sce", "sch", "sci", "sco", "scr", "scu", "sea", "sec", "sed", "see", "seg", "sei", "sel", "sem", "sen", "sep", "seq", "ser", "ses", "set", "sev", "sew", "sex", "sha", "she", "shi", "sho", "shr", "shu", "sib", "sic", "sid", "sie", "sig", "sil", "sim", "sin", "sip", "sir", "sis", "sit", "siz", "ska", "ske", "ski", "sku", "sky", "sla", "sle", "sli", "slo", "slu", "sme", "smi", "smo", "smu", "sna", "sne", "sni", "sno", "snu", "soa", "soc", "sod", "sof", "soi", "sol", "som", "son", "soo", "sop", "sor", "sou", "sov", "sow", "soy", "spa", "spe", "sph", "spi", "spl", "spo", "spr", "spu", "spy", "squ", "sta", "ste", "sti", "sto", "str"]
|
||||||
1
Lambdas/Scraping/prefix_list_part14.txt
Normal file
1
Lambdas/Scraping/prefix_list_part14.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
["stu", "sty", "sub", "suc", "sue", "suf", "sug", "sui", "sul", "sum", "sun", "sup", "sur", "sus", "swa", "swe", "swi", "swo", "syc", "sym", "syn", "syr", "sys", "t-s", "tab", "tac", "tad", "tag", "tai", "tak", "tal", "tam", "tan", "tap", "tar", "tas", "tat", "tav", "tax", "tea", "tec", "tee", "tel", "tem", "ten", "tep", "ter", "tes", "tex", "tha", "the", "thi", "tho", "thr", "thu", "thy", "tia", "tic", "tid", "tie", "tig", "til", "tim", "tin", "tip", "tir", "tis", "tit", "toa", "tob", "tod", "toe", "tof", "tog", "toi", "tol", "tom", "ton", "too", "top", "toq", "tor", "tos", "tot", "tou", "tow", "toy", "tra", "tre", "tri", "tro", "tru", "try", "tsu", "tub", "tug", "tui", "tul", "tum", "tun"]
|
||||||
1
Lambdas/Scraping/prefix_list_part15.txt
Normal file
1
Lambdas/Scraping/prefix_list_part15.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
["tur", "tus", "tut", "tux", "twe", "twi", "typ", "uku", "ult", "umb", "unb", "unc", "und", "une", "uni", "upd", "upg", "upl", "upp", "ups", "upw", "urg", "urn", "usa", "use", "ush", "usu", "ute", "uti", "vac", "vag", "val", "vam", "van", "var", "vas", "vau", "vea", "vec", "veg", "veh", "vei", "vel", "ven", "ver", "ves", "vet", "via", "vib", "vic", "vid", "vie", "vig", "vil", "vin", "vio", "vir", "vis", "vit", "viv", "vix", "vod", "vog", "voi", "vol", "vom", "vot", "voy", "vul", "wad", "waf", "wag", "wai", "wak", "wal", "wam", "wan", "war", "was", "wat", "wav", "wax", "way", "wea", "web", "wed", "wee", "wei", "wel", "wes", "wet", "wha", "whe", "whi", "who", "wic", "wid", "wif", "wil", "win"]
|
||||||
1
Lambdas/Scraping/prefix_list_part16.txt
Normal file
1
Lambdas/Scraping/prefix_list_part16.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
["wir", "wis", "wit", "wok", "wol", "wom", "won", "woo", "wor", "wou", "wra", "wre", "wri", "wro", "xyl", "yac", "yah", "yak", "yam", "yan", "yar", "yaw", "yea", "yel", "yes", "yew", "yin", "yog", "yok", "yol", "you", "yoy", "yur", "zam", "zeb", "zen", "zep", "zer", "zig", "zin", "zip", "zit", "zom", "zon", "zoo", "zuc"]
|
||||||
1
Lambdas/Scraping/prefix_list_part2.txt
Normal file
1
Lambdas/Scraping/prefix_list_part2.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
["ate", "ath", "atm", "ato", "atr", "att", "auc", "aud", "aun", "aut", "ava", "ave", "avo", "awa", "awe", "axi", "azi", "bab", "bac", "bad", "baf", "bag", "bai", "bak", "bal", "bam", "ban", "bao", "bar", "bas", "bat", "bay", "bea", "bec", "bed", "bee", "beg", "beh", "bei", "bel", "ben", "ber", "bes", "bet", "bev", "bey", "bia", "bib", "bic", "bid", "bif", "bij", "bik", "bil", "bin", "bio", "bip", "bir", "bis", "bit", "bla", "ble", "bli", "blo", "blu", "boa", "bob", "bod", "bog", "bol", "bom", "bon", "boo", "bor", "bos", "bot", "bou", "bow", "box", "boy", "bra", "bre", "bri", "bro", "bru", "bub", "buc", "bud", "buf", "bug", "bui", "bul", "bum", "bun", "bur", "bus", "but", "buy", "buz", "c-c"]
|
||||||
1
Lambdas/Scraping/prefix_list_part3.txt
Normal file
1
Lambdas/Scraping/prefix_list_part3.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
["cab", "cac", "cad", "caf", "cag", "cak", "cal", "cam", "can", "cap", "car", "cas", "cat", "cau", "cav", "cay", "cei", "cel", "cem", "cen", "cep", "cer", "ces", "cha", "che", "chi", "cho", "chr", "chu", "cic", "cig", "cil", "cin", "cir", "cit", "civ", "cla", "cle", "cli", "clo", "clu", "co-", "coa", "cob", "coc", "cod", "coe", "cof", "coh", "coi", "cok", "col", "com", "con", "coo", "cop", "cor", "cos", "cot", "cou", "cov", "cow", "coy", "cra", "cre", "cri", "cro", "cru", "cry", "cub", "cuc", "cue", "cuf", "cui", "cul", "cum", "cup", "cur", "cus", "cut", "cyc", "cyg", "cyl", "cym", "cyn", "cys", "cyt", "dad", "daf", "dag", "dah", "dai", "dam", "dan", "dar", "das", "dat", "dau", "daw", "day"]
|
||||||
1
Lambdas/Scraping/prefix_list_part4.txt
Normal file
1
Lambdas/Scraping/prefix_list_part4.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
["dea", "deb", "dec", "ded", "dee", "def", "deg", "del", "dem", "den", "deo", "dep", "der", "des", "det", "dev", "dew", "dho", "dia", "dib", "dic", "die", "dif", "dig", "dil", "dim", "din", "dio", "dip", "dir", "dis", "div", "doc", "doe", "dog", "doi", "dol", "dom", "don", "doo", "dor", "dos", "dot", "dou", "dow", "doz", "dra", "dre", "dri", "dro", "dru", "dry", "duc", "dud", "due", "duf", "dug", "dul", "dum", "dun", "dup", "dur", "dus", "dut", "dwa", "dwe", "dyn", "dys", "e-b", "e-m", "e-r", "eag", "ear", "eas", "eat", "eav", "ecc", "ech", "ecl", "eco", "ect", "ecu", "edd", "edg", "edi", "edu", "eel", "eff", "egg", "ego", "eic", "eje", "elb", "eld", "ele", "elf", "eli", "elk", "ell", "elm"]
|
||||||
1
Lambdas/Scraping/prefix_list_part5.txt
Normal file
1
Lambdas/Scraping/prefix_list_part5.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
["elo", "elv", "ema", "emb", "eme", "emi", "emo", "emp", "emu", "ena", "enc", "end", "ene", "enf", "eng", "eni", "enj", "enq", "enr", "ent", "env", "enz", "epa", "epe", "eph", "epi", "epo", "equ", "era", "ere", "ero", "err", "esc", "esp", "ess", "est", "ete", "eth", "eup", "eur", "eva", "eve", "evi", "evo", "ex-", "exa", "exc", "exe", "exh", "exi", "exo", "exp", "ext", "eye", "eyr", "fab", "fac", "fah", "fai", "fal", "fam", "fan", "far", "fas", "fat", "fau", "fav", "faw", "fax", "fea", "fed", "fee", "fel", "fem", "fen", "fer", "fes", "fet", "few", "fib", "fic", "fid", "fie", "fif", "fig", "fil", "fin", "fir", "fis", "fit", "fix", "fla", "fle", "fli", "flo", "flu", "fly", "foa", "fob", "foc"]
|
||||||
1
Lambdas/Scraping/prefix_list_part6.txt
Normal file
1
Lambdas/Scraping/prefix_list_part6.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
["fog", "fol", "fon", "foo", "for", "fou", "fow", "fox", "fra", "fre", "fri", "fro", "fru", "fry", "fuc", "fue", "fug", "ful", "fun", "fur", "fus", "fut", "gad", "gaf", "gai", "gal", "gam", "gan", "gap", "gar", "gas", "gat", "gau", "gav", "gaz", "gea", "gee", "gel", "gem", "gen", "geo", "ger", "ges", "gey", "ghe", "gho", "gia", "gif", "gig", "gin", "gir", "git", "gla", "gle", "gli", "glo", "glu", "gna", "gnu", "go-", "goa", "gob", "god", "gog", "goi", "gol", "gon", "goo", "gop", "gor", "gos", "gov", "gow", "gra", "gre", "gri", "gro", "gru", "gua", "gue", "gui", "gum", "gun", "gut", "guy", "gym", "gyn", "gyr", "hab", "hac", "hai", "hak", "hal", "ham", "han", "hap", "har", "has", "hat", "hau"]
|
||||||
1
Lambdas/Scraping/prefix_list_part7.txt
Normal file
1
Lambdas/Scraping/prefix_list_part7.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
["hav", "haw", "hay", "haz", "hea", "hec", "hed", "hee", "hei", "hel", "hem", "hen", "hep", "her", "hes", "het", "hex", "hey", "hic", "hid", "hie", "hig", "hik", "hil", "hin", "hip", "hir", "his", "hit", "hiv", "hob", "hoc", "hoe", "hog", "hol", "hom", "hon", "hoo", "hop", "hor", "hos", "hot", "hou", "hov", "how", "hub", "hug", "hul", "hum", "hun", "hur", "hus", "hut", "hya", "hyb", "hyd", "hye", "hyg", "hyp", "ice", "ici", "ico", "icy", "id", "ide", "idi", "igl", "ign", "ike", "ill", "ima", "imb", "imi", "imm", "imp", "in-", "ina", "inb", "inc", "ind", "ine", "inf", "ing", "inh", "ini", "inj", "ink", "inl", "inn", "inp", "inq", "ins", "int", "inv", "iri", "iro", "irr", "isc", "isl", "iso"]
|
||||||
1
Lambdas/Scraping/prefix_list_part8.txt
Normal file
1
Lambdas/Scraping/prefix_list_part8.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
["iss", "ite", "iti", "ivo", "jac", "jad", "jag", "jai", "jal", "jam", "jar", "jas", "jaw", "jaz", "jea", "jee", "jel", "jer", "jet", "jew", "jic", "jif", "job", "joc", "jod", "joe", "jog", "joi", "jok", "jot", "jou", "joy", "jud", "jug", "jui", "jul", "jum", "jun", "jur", "jus", "jut", "kal", "kam", "kan", "kar", "kay", "kaz", "keb", "kee", "ken", "ket", "key", "kic", "kid", "kie", "kil", "kim", "kin", "kio", "kis", "kit", "kiw", "kne", "kni", "kno", "knu", "koa", "koh", "kum", "lab", "lac", "lad", "lag", "lak", "lam", "lan", "lap", "lar", "las", "lat", "lau", "lav", "law", "lay", "lea", "lec", "lee", "lef", "leg", "lei", "lem", "len", "leo", "lep", "les", "let", "lev", "lia", "lib", "lic"]
|
||||||
1
Lambdas/Scraping/prefix_list_part9.txt
Normal file
1
Lambdas/Scraping/prefix_list_part9.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
["lid", "lie", "lif", "lig", "lik", "lil", "lim", "lin", "lio", "lip", "liq", "lis", "lit", "liv", "liz", "lla", "loa", "lob", "loc", "lod", "lof", "log", "loi", "lol", "lon", "loo", "loq", "lor", "los", "lot", "lou", "lov", "lox", "loy", "luc", "lug", "lum", "lun", "lus", "lut", "lux", "lyc", "lye", "lym", "lyn", "lyo", "lyr", "lys", "mRN", "mac", "mad", "mae", "mag", "mai", "maj", "mak", "mal", "mam", "man", "map", "mar", "mas", "mat", "max", "may", "mea", "mec", "med", "mee", "mel", "mem", "men", "mer", "mes", "met", "mez", "mic", "mid", "mig", "mil", "mim", "min", "mir", "mis", "mit", "mix", "moa", "mob", "moc", "mod", "mol", "mom", "mon", "moo", "mop", "mor", "mos", "mot", "mou", "mov"]
|
||||||
32
Lambdas/Scraping/runOrchestrator.py
Normal file
32
Lambdas/Scraping/runOrchestrator.py
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import json
|
||||||
|
import boto3
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
def lambda_handler(event, context):
|
||||||
|
list_num = event["list_num"]
|
||||||
|
with open("prefix_list_part" + str(list_num) + ".txt") as words_file:
|
||||||
|
words = json.load(words_file)
|
||||||
|
print(words)
|
||||||
|
for word in words:
|
||||||
|
time.sleep(6)
|
||||||
|
client = boto3.client('lambda')
|
||||||
|
response = client.invoke(
|
||||||
|
FunctionName='KohlsScraper',
|
||||||
|
InvocationType="Event",
|
||||||
|
LogType="None",
|
||||||
|
Payload= """{"toScrape": \"""" + word + "\"}"
|
||||||
|
)
|
||||||
|
if (event["linked"]):
|
||||||
|
if list_num < 16:
|
||||||
|
time.sleep(200)
|
||||||
|
client.invoke(
|
||||||
|
FunctionName='RunOrchestrator',
|
||||||
|
InvocationType="Event",
|
||||||
|
LogType="None",
|
||||||
|
Payload= "{\"list_num\": "+ str(list_num + 1) + ",\"linked\": true}"
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
'statusCode': 200,
|
||||||
|
'body': json.dumps('Hello from Lambda!')
|
||||||
|
}
|
||||||
7
Lambdas/Scraping/setupBuildFolder.sh
Normal file
7
Lambdas/Scraping/setupBuildFolder.sh
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#Currently to be run only from the Scraping directory
|
||||||
|
mkdir build
|
||||||
|
pip3 install --target build requests
|
||||||
|
pip3 install --target build PyMySQL
|
||||||
|
pip3 install --target build beautifulsoup4
|
||||||
|
mkdir artifacts
|
||||||
@ -52,4 +52,5 @@ dependencies {
|
|||||||
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
|
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
|
||||||
implementation 'com.squareup.okhttp3:okhttp:4.8.1'
|
implementation 'com.squareup.okhttp3:okhttp:4.8.1'
|
||||||
implementation 'com.crystal:crystalrangeseekbar:1.1.3'
|
implementation 'com.crystal:crystalrangeseekbar:1.1.3'
|
||||||
|
implementation 'com.chauthai.swipereveallayout:swipe-reveal-layout:1.4.1'
|
||||||
}
|
}
|
||||||
@ -1,5 +1,8 @@
|
|||||||
package com.example.listify;
|
package com.example.listify;
|
||||||
|
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.drawable.ColorDrawable;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
||||||
import com.amplifyframework.auth.AuthException;
|
import com.amplifyframework.auth.AuthException;
|
||||||
@ -11,6 +14,8 @@ import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
|||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.appcompat.widget.Toolbar;
|
import androidx.appcompat.widget.Toolbar;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.view.Window;
|
||||||
|
import android.view.WindowManager;
|
||||||
import android.widget.ImageButton;
|
import android.widget.ImageButton;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
@ -71,6 +76,8 @@ public class ItemDetails extends AppCompatActivity implements ListPickerDialogFr
|
|||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
closeFABMenu();
|
closeFABMenu();
|
||||||
|
LoadingCircleDialog loadingDialog = new LoadingCircleDialog(ItemDetails.this);
|
||||||
|
loadingDialog.show();
|
||||||
|
|
||||||
Properties configs = new Properties();
|
Properties configs = new Properties();
|
||||||
try {
|
try {
|
||||||
@ -81,21 +88,57 @@ public class ItemDetails extends AppCompatActivity implements ListPickerDialogFr
|
|||||||
|
|
||||||
Requestor requestor = new Requestor(am, configs.getProperty("apiKey"));
|
Requestor requestor = new Requestor(am, configs.getProperty("apiKey"));
|
||||||
SynchronousReceiver<Integer[]> listIdsReceiver = new SynchronousReceiver<>();
|
SynchronousReceiver<Integer[]> listIdsReceiver = new SynchronousReceiver<>();
|
||||||
SynchronousReceiver<List> listReceiver = new SynchronousReceiver<>();
|
|
||||||
|
|
||||||
requestor.getListOfIds(List.class, listIdsReceiver, listIdsReceiver);
|
requestor.getListOfIds(List.class, listIdsReceiver, listIdsReceiver);
|
||||||
try {
|
|
||||||
Integer[] listIds = listIdsReceiver.await();
|
|
||||||
for (int i = 0; i < listIds.length; i++) {
|
|
||||||
requestor.getObject(Integer.toString(listIds[i]), List.class, listReceiver, listReceiver);
|
|
||||||
shoppingLists.add(listReceiver.await());
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
ListPickerDialogFragment listPickerDialog = new ListPickerDialogFragment(shoppingLists);
|
Thread t = new Thread(new Runnable() {
|
||||||
listPickerDialog.show(getSupportFragmentManager(), "User Lists");
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Integer[] listIds = null;
|
||||||
|
try {
|
||||||
|
listIds = listIdsReceiver.await();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create threads and add them to a list
|
||||||
|
Thread[] threads = new Thread[listIds.length];
|
||||||
|
List[] results = new List[listIds.length];
|
||||||
|
for (int i = 0; i < listIds.length; i++) {
|
||||||
|
SynchronousReceiver<List> listReceiver = new SynchronousReceiver<>();
|
||||||
|
requestor.getObject(Integer.toString(listIds[i]), List.class, listReceiver, listReceiver);
|
||||||
|
final int finalI = i;
|
||||||
|
Thread l = new Thread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
results[finalI] = listReceiver.await();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
threads[i] = l;
|
||||||
|
l.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
shoppingLists.clear();
|
||||||
|
// Wait for each thread to finish and add results to shoppingLists
|
||||||
|
for (int i = 0; i < threads.length; i++) {
|
||||||
|
try {
|
||||||
|
threads[i].join();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
shoppingLists.add(results[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
loadingDialog.cancel();
|
||||||
|
ListPickerDialogFragment listPickerDialog = new ListPickerDialogFragment(shoppingLists);
|
||||||
|
listPickerDialog.show(getSupportFragmentManager(), "User Lists");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
t.start();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -157,7 +200,7 @@ public class ItemDetails extends AppCompatActivity implements ListPickerDialogFr
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Add the viewed item to the selected list
|
// Add the selected item to the selected list
|
||||||
@Override
|
@Override
|
||||||
public void sendListSelection(int selectedListIndex, int quantity) {
|
public void sendListSelection(int selectedListIndex, int quantity) {
|
||||||
|
|
||||||
@ -184,6 +227,8 @@ public class ItemDetails extends AppCompatActivity implements ListPickerDialogFr
|
|||||||
// Create a new list and add the item to it
|
// Create a new list and add the item to it
|
||||||
@Override
|
@Override
|
||||||
public void sendNewListName(String name, int quantity) {
|
public void sendNewListName(String name, int quantity) {
|
||||||
|
LoadingCircleDialog loadingDialog = new LoadingCircleDialog(this);
|
||||||
|
loadingDialog.show();
|
||||||
|
|
||||||
Properties configs = new Properties();
|
Properties configs = new Properties();
|
||||||
try {
|
try {
|
||||||
@ -196,16 +241,34 @@ public class ItemDetails extends AppCompatActivity implements ListPickerDialogFr
|
|||||||
|
|
||||||
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());
|
||||||
|
|
||||||
try {
|
Thread t = new Thread(new Runnable() {
|
||||||
requestor.postObject(newList, idReceiver, idReceiver);
|
@Override
|
||||||
int newListId = idReceiver.await();
|
public void run() {
|
||||||
ListEntry entry = new ListEntry(newListId, curProduct.getItemId(), quantity, Instant.now().toEpochMilli(),false);
|
try {
|
||||||
requestor.postObject(entry);
|
requestor.postObject(newList, idReceiver, idReceiver);
|
||||||
|
int newListId = idReceiver.await();
|
||||||
|
ListEntry entry = new ListEntry(newListId, curProduct.getItemId(), quantity, Instant.now().toEpochMilli(),false);
|
||||||
|
requestor.postObject(entry);
|
||||||
|
|
||||||
Toast.makeText(this, String.format("%s created and item added", name), Toast.LENGTH_LONG).show();
|
runOnUiThread(new Runnable() {
|
||||||
} catch (Exception e) {
|
@Override
|
||||||
Toast.makeText(this, "An error occurred", Toast.LENGTH_LONG).show();
|
public void run() {
|
||||||
e.printStackTrace();
|
Toast.makeText(ItemDetails.this, String.format("%s created and item added", name), Toast.LENGTH_LONG).show();
|
||||||
}
|
loadingDialog.cancel();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Toast.makeText(ItemDetails.this, "An error occurred", Toast.LENGTH_LONG).show();
|
||||||
|
loadingDialog.cancel();
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
t.start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,6 +1,9 @@
|
|||||||
package com.example.listify;
|
package com.example.listify;
|
||||||
|
|
||||||
|
import android.app.AlertDialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
@ -10,38 +13,61 @@ import android.widget.*;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
|
||||||
|
import com.bumptech.glide.Glide;
|
||||||
|
|
||||||
import com.example.listify.data.Item;
|
import com.example.listify.data.Item;
|
||||||
import com.example.listify.data.List;
|
import com.example.listify.data.List;
|
||||||
import com.example.listify.data.ListEntry;
|
import com.example.listify.data.ListEntry;
|
||||||
import org.json.JSONException;
|
import com.example.listify.data.ListShare;
|
||||||
|
import com.example.listify.ui.SignupPage;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.text.DecimalFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
|
||||||
import static com.example.listify.MainActivity.am;
|
import static com.example.listify.MainActivity.am;
|
||||||
|
|
||||||
public class ListPage extends AppCompatActivity {
|
public class ListPage extends AppCompatActivity implements Requestor.Receiver {
|
||||||
ListView listView;
|
ListView listView;
|
||||||
MyAdapter myAdapter;
|
MyAdapter myAdapter;
|
||||||
|
Requestor requestor;
|
||||||
|
|
||||||
Button incrQuan;
|
Button incrQuan;
|
||||||
Button decrQuan;
|
Button decrQuan;
|
||||||
Button removeItem;
|
Button removeItem;
|
||||||
|
Button clearAll;
|
||||||
|
//Button shareList;
|
||||||
|
|
||||||
|
TextView tvTotalPrice;
|
||||||
|
ProgressBar loadingListItems;
|
||||||
|
|
||||||
ArrayList<String> pNames = new ArrayList<>();
|
ArrayList<String> pNames = new ArrayList<>();
|
||||||
ArrayList<String> pStores = new ArrayList<>();
|
ArrayList<String> pStores = new ArrayList<>();
|
||||||
ArrayList<String> pPrices = new ArrayList<>();
|
ArrayList<String> pPrices = new ArrayList<>();
|
||||||
ArrayList<String> pQuantity = new ArrayList<>();
|
ArrayList<String> pQuantity = new ArrayList<>();
|
||||||
ArrayList<Integer> pImages = new ArrayList<>();
|
ArrayList<String> pImages = new ArrayList<>();
|
||||||
|
|
||||||
ArrayList<ListEntry> pListItemPair = new ArrayList<>();
|
ArrayList<ListEntry> pListItemPair = new ArrayList<>();
|
||||||
|
|
||||||
Requestor requestor;
|
double totalPrice = 0;
|
||||||
|
|
||||||
|
Map<String, Double> totalPriceByStore = new HashMap<>();
|
||||||
|
Map<String, Integer> storeHeaderIndex = new HashMap<>();
|
||||||
|
|
||||||
|
DecimalFormat df = new DecimalFormat("0.00");
|
||||||
|
|
||||||
|
// TODO: Display a message if their list is empty
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
// Read list ID from caller
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_list);
|
||||||
|
|
||||||
final int listID = (int) getIntent().getSerializableExtra("listID");
|
final int listID = (int) getIntent().getSerializableExtra("listID");
|
||||||
|
|
||||||
Properties configs = new Properties();
|
Properties configs = new Properties();
|
||||||
@ -51,18 +77,74 @@ public class ListPage extends AppCompatActivity {
|
|||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
requestor = new Requestor(am, configs.getProperty("apiKey"));
|
requestor = new Requestor(am, configs.getProperty("apiKey"));
|
||||||
SynchronousReceiver<List> lr = new SynchronousReceiver<>();
|
requestor.getObject(Integer.toString(listID), List.class, this);
|
||||||
//ListReceiver<List> lr = new ListReceiver<>();
|
|
||||||
requestor.getObject(Integer.toString(listID), List.class, lr);
|
|
||||||
|
|
||||||
List list;
|
listView = findViewById(R.id.listView);
|
||||||
|
myAdapter = new MyAdapter(this, pNames, pStores, pPrices, pQuantity, pImages);
|
||||||
|
listView.setAdapter(myAdapter);
|
||||||
|
|
||||||
try {
|
loadingListItems = findViewById(R.id.progress_loading_list_items);
|
||||||
list = lr.await();
|
loadingListItems.setVisibility(View.VISIBLE);
|
||||||
}
|
|
||||||
catch (Exception e) {
|
clearAll = (Button) findViewById(R.id.buttonClear);
|
||||||
list = null;
|
clearAll.setOnClickListener(new View.OnClickListener() {
|
||||||
}
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
pNames.clear();
|
||||||
|
pStores.clear();
|
||||||
|
pPrices.clear();
|
||||||
|
pQuantity.clear();
|
||||||
|
pImages.clear();
|
||||||
|
|
||||||
|
while(!pListItemPair.isEmpty()) {
|
||||||
|
try {
|
||||||
|
requestor.deleteObject(pListItemPair.remove(0));
|
||||||
|
}
|
||||||
|
catch(Exception e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
totalPrice = 0;
|
||||||
|
tvTotalPrice.setText(String.format("$%.2f", totalPrice));
|
||||||
|
myAdapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/*shareList = (Button) findViewById(R.id.buttonShare);
|
||||||
|
shareList.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
View codeView = getLayoutInflater().inflate(R.layout.activity_sharedemail, null);
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(ListPage.this);
|
||||||
|
builder.setView(codeView);
|
||||||
|
builder.setTitle("Share list");
|
||||||
|
builder.setMessage("Please enter the email of the user who you want to share the list with.");
|
||||||
|
builder.setPositiveButton("Submit", new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
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);
|
||||||
|
try {
|
||||||
|
requestor.postObject(listShare);
|
||||||
|
}
|
||||||
|
catch(Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int which) {}
|
||||||
|
});
|
||||||
|
AlertDialog dialog = builder.create();
|
||||||
|
dialog.show();
|
||||||
|
}
|
||||||
|
});*/
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void acceptDelivery(Object delivered) {
|
||||||
|
List list = (List) delivered;
|
||||||
|
|
||||||
if(list != null) {
|
if(list != null) {
|
||||||
for (ListEntry entry : list.getEntries()) {
|
for (ListEntry entry : list.getEntries()) {
|
||||||
@ -77,41 +159,62 @@ public class ListPage extends AppCompatActivity {
|
|||||||
item = null;
|
item = null;
|
||||||
}
|
}
|
||||||
if(item != null) {
|
if(item != null) {
|
||||||
pNames.add(item.getDescription());
|
if(!totalPriceByStore.containsKey("Kroger")) {
|
||||||
pStores.add("Kroger");
|
totalPriceByStore.put("Kroger", item.getPrice().doubleValue() * entry.getQuantity());
|
||||||
pPrices.add(item.getPrice().toString());
|
storeHeaderIndex.put("Kroger", pNames.size());
|
||||||
pQuantity.add(entry.getQuantity().toString());
|
|
||||||
pImages.add(R.drawable.placeholder);
|
pNames.add("Kroger");
|
||||||
pListItemPair.add(entry);
|
pStores.add("");
|
||||||
|
pPrices.add(df.format(totalPriceByStore.get("Kroger")));
|
||||||
|
pQuantity.add("-1");
|
||||||
|
pImages.add("-1");
|
||||||
|
pListItemPair.add(null);
|
||||||
|
|
||||||
|
pNames.add(item.getDescription());
|
||||||
|
pStores.add("Kroger");
|
||||||
|
pPrices.add(df.format(item.getPrice()));
|
||||||
|
pQuantity.add(entry.getQuantity().toString());
|
||||||
|
pImages.add(item.getImageURL());
|
||||||
|
pListItemPair.add(entry);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
int index = storeHeaderIndex.get("Kroger");
|
||||||
|
|
||||||
|
totalPriceByStore.put("Kroger", totalPriceByStore.get("Kroger") + (item.getPrice().doubleValue() * entry.getQuantity()));
|
||||||
|
pPrices.set(index, df.format(totalPriceByStore.get("Kroger")));
|
||||||
|
|
||||||
|
index++;
|
||||||
|
|
||||||
|
pNames.add(index, item.getDescription());
|
||||||
|
pStores.add(index, "Kroger");
|
||||||
|
pPrices.add(index, df.format(item.getPrice()));
|
||||||
|
pQuantity.add(index, entry.getQuantity().toString());
|
||||||
|
pImages.add(index, item.getImageURL());
|
||||||
|
pListItemPair.add(index, entry);
|
||||||
|
|
||||||
|
for(String store : storeHeaderIndex.keySet()) {
|
||||||
|
if(storeHeaderIndex.get(store) > index) {
|
||||||
|
storeHeaderIndex.put(store, storeHeaderIndex.get(store) + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment total price
|
||||||
|
totalPrice += (item.getPrice().doubleValue() * entry.getQuantity());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
tvTotalPrice = (TextView) findViewById(R.id.total_price);
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
tvTotalPrice.setText(String.format("$%.2f", totalPrice));
|
||||||
|
loadingListItems.setVisibility(View.GONE);
|
||||||
|
myAdapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/*pNames.add("Half-gallon organic whole milk");
|
|
||||||
pStores.add("Kroger");
|
|
||||||
pPrices.add("$5.00");
|
|
||||||
pQuantity.add("1");
|
|
||||||
pImages.add(R.drawable.milk);
|
|
||||||
|
|
||||||
pNames.add("5-bunch medium bananas");
|
|
||||||
pStores.add("Kroger");
|
|
||||||
pPrices.add("$3.00");
|
|
||||||
pQuantity.add("1");
|
|
||||||
pImages.add(R.drawable.bananas);
|
|
||||||
|
|
||||||
pNames.add("JIF 40-oz creamy peanut butter");
|
|
||||||
pStores.add("Kroger");
|
|
||||||
pPrices.add("$7.00");
|
|
||||||
pQuantity.add("1");
|
|
||||||
pImages.add(R.drawable.peanutbutter);*/
|
|
||||||
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
setContentView(R.layout.activity_list);
|
|
||||||
|
|
||||||
listView = findViewById(R.id.listView);
|
|
||||||
myAdapter = new MyAdapter(this, pNames, pStores, pPrices, pQuantity, pImages);
|
|
||||||
|
|
||||||
listView.setAdapter(myAdapter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyAdapter extends ArrayAdapter<String> {
|
class MyAdapter extends ArrayAdapter<String> {
|
||||||
@ -120,9 +223,10 @@ public class ListPage extends AppCompatActivity {
|
|||||||
ArrayList<String> pStores;
|
ArrayList<String> pStores;
|
||||||
ArrayList<String> pPrices;
|
ArrayList<String> pPrices;
|
||||||
ArrayList<String> pQuantity;
|
ArrayList<String> pQuantity;
|
||||||
ArrayList<Integer> pImages;
|
ArrayList<String> pImages;
|
||||||
|
|
||||||
MyAdapter (Context c, ArrayList<String> names, ArrayList<String> stores, ArrayList<String> prices, ArrayList<String> quantity, ArrayList<Integer> 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;
|
||||||
pNames = names;
|
pNames = names;
|
||||||
@ -147,9 +251,15 @@ public class ListPage extends AppCompatActivity {
|
|||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
int q = Integer.parseInt(pQuantity.get(position)) - 1;
|
int q = Integer.parseInt(pQuantity.get(position)) - 1;
|
||||||
pQuantity.set(position, Integer.toString(q));
|
pQuantity.set(position, Integer.toString(q));
|
||||||
|
totalPriceByStore.put(pStores.get(position), totalPriceByStore.get(pStores.get(position)) - Double.parseDouble(pPrices.get(position)));
|
||||||
|
pPrices.set(storeHeaderIndex.get(pStores.get(position)), df.format(totalPriceByStore.get(pStores.get(position))));
|
||||||
ListEntry le = pListItemPair.remove(position);
|
ListEntry le = pListItemPair.remove(position);
|
||||||
le.setQuantity(le.getQuantity() - 1);
|
le.setQuantity(le.getQuantity() - 1);
|
||||||
pListItemPair.add(position, le);
|
pListItemPair.add(position, le);
|
||||||
|
|
||||||
|
totalPrice -= Double.parseDouble(pPrices.get(position));
|
||||||
|
tvTotalPrice.setText(String.format("$%.2f", totalPrice));
|
||||||
|
|
||||||
SynchronousReceiver<Integer> synchronousenforcer = new SynchronousReceiver<>();
|
SynchronousReceiver<Integer> synchronousenforcer = new SynchronousReceiver<>();
|
||||||
requestor.deleteObject(le, synchronousenforcer, synchronousenforcer);
|
requestor.deleteObject(le, synchronousenforcer, synchronousenforcer);
|
||||||
try {
|
try {
|
||||||
@ -176,9 +286,15 @@ public class ListPage extends AppCompatActivity {
|
|||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
int q = Integer.parseInt(pQuantity.get(position)) + 1;
|
int q = Integer.parseInt(pQuantity.get(position)) + 1;
|
||||||
pQuantity.set(position, Integer.toString(q));
|
pQuantity.set(position, Integer.toString(q));
|
||||||
|
totalPriceByStore.put(pStores.get(position), totalPriceByStore.get(pStores.get(position)) + Double.parseDouble(pPrices.get(position)));
|
||||||
|
pPrices.set(storeHeaderIndex.get(pStores.get(position)), df.format(totalPriceByStore.get(pStores.get(position))));
|
||||||
ListEntry le = pListItemPair.remove(position);
|
ListEntry le = pListItemPair.remove(position);
|
||||||
le.setQuantity(le.getQuantity() + 1);
|
le.setQuantity(le.getQuantity() + 1);
|
||||||
pListItemPair.add(position, le);
|
pListItemPair.add(position, le);
|
||||||
|
|
||||||
|
totalPrice += Double.parseDouble(pPrices.get(position));
|
||||||
|
tvTotalPrice.setText(String.format("$%.2f", totalPrice));
|
||||||
|
|
||||||
SynchronousReceiver<Integer> synchronousenforcer = new SynchronousReceiver<>();
|
SynchronousReceiver<Integer> synchronousenforcer = new SynchronousReceiver<>();
|
||||||
requestor.deleteObject(le, synchronousenforcer, synchronousenforcer);
|
requestor.deleteObject(le, synchronousenforcer, synchronousenforcer);
|
||||||
try {
|
try {
|
||||||
@ -203,6 +319,13 @@ public class ListPage extends AppCompatActivity {
|
|||||||
removeItem.setOnClickListener(new View.OnClickListener() {
|
removeItem.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
|
totalPriceByStore.put("Kroger", totalPriceByStore.get("Kroger") - (Double.parseDouble(pPrices.get(position)) * Integer.parseInt(pQuantity.get(position))));
|
||||||
|
pPrices.set(storeHeaderIndex.get(pStores.get(position)), df.format(totalPriceByStore.get(pStores.get(position))));
|
||||||
|
|
||||||
|
totalPrice -= (Double.parseDouble(pPrices.get(position)) *
|
||||||
|
Double.parseDouble(pQuantity.get(position)));
|
||||||
|
tvTotalPrice.setText(String.format("$%.2f", totalPrice));
|
||||||
|
|
||||||
pNames.remove(position);
|
pNames.remove(position);
|
||||||
pStores.remove(position);
|
pStores.remove(position);
|
||||||
pPrices.remove(position);
|
pPrices.remove(position);
|
||||||
@ -224,36 +347,27 @@ public class ListPage extends AppCompatActivity {
|
|||||||
if(!pNames.isEmpty()) {
|
if(!pNames.isEmpty()) {
|
||||||
name.setText(pNames.get(position));
|
name.setText(pNames.get(position));
|
||||||
store.setText(pStores.get(position));
|
store.setText(pStores.get(position));
|
||||||
price.setText(pPrices.get(position));
|
price.setText("$" + pPrices.get(position));
|
||||||
quantity.setText(pQuantity.get(position));
|
|
||||||
image.setImageResource(pImages.get(position));
|
if(pQuantity.get(position).equals("-1")) {
|
||||||
|
quantity.setVisibility(View.GONE);
|
||||||
|
decrQuan.setVisibility(View.GONE);
|
||||||
|
incrQuan.setVisibility(View.GONE);
|
||||||
|
removeItem.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
quantity.setText(pQuantity.get(position));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(pImages.get(position).equals("-1")) {
|
||||||
|
image.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Glide.with(getContext()).load(pImages.get(position)).into(image);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return listproduct;
|
return listproduct;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ListReceiver<T> implements Requestor.Receiver<T> {
|
|
||||||
@Override
|
|
||||||
public void acceptDelivery(T delivered) {
|
|
||||||
for(ListEntry entry : ((List) delivered).getEntries()) {
|
|
||||||
int product = entry.getProductID();
|
|
||||||
ProductReceiver<Item> pr = new ProductReceiver<>();
|
|
||||||
requestor.getObject(Integer.toString(product), Item.class, pr);
|
|
||||||
pQuantity.add(entry.getQuantity().toString());
|
|
||||||
pListItemPair.add(entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ProductReceiver<T> implements Requestor.Receiver<T> {
|
|
||||||
@Override
|
|
||||||
public void acceptDelivery(T delivered) {
|
|
||||||
Item i = (Item) delivered;
|
|
||||||
pNames.add(i.getDescription());
|
|
||||||
pStores.add("Kroger");
|
|
||||||
pPrices.add(i.getPrice().toString());
|
|
||||||
pImages.add(R.drawable.placeholder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,6 @@ 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.content.DialogInterface;
|
||||||
import android.graphics.Color;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
@ -14,9 +13,9 @@ import android.widget.ListView;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.fragment.app.DialogFragment;
|
import androidx.fragment.app.DialogFragment;
|
||||||
import com.example.listify.adapter.DisplayShoppingListsAdapter;
|
import com.example.listify.adapter.ShoppingListsAdapter;
|
||||||
import com.example.listify.data.List;
|
import com.example.listify.data.List;
|
||||||
import com.example.listify.model.ShoppingList;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
|
||||||
@ -29,7 +28,7 @@ public class ListPickerDialogFragment extends DialogFragment {
|
|||||||
public OnListPickListener onListPickListener;
|
public OnListPickListener onListPickListener;
|
||||||
|
|
||||||
ListView userListsView;
|
ListView userListsView;
|
||||||
DisplayShoppingListsAdapter displayShoppingListsAdapter;
|
ShoppingListsAdapter shoppingListsAdapter;
|
||||||
Button btnMinus;
|
Button btnMinus;
|
||||||
Button btnPlus;
|
Button btnPlus;
|
||||||
EditText etQuantity;
|
EditText etQuantity;
|
||||||
@ -67,8 +66,8 @@ public class ListPickerDialogFragment extends DialogFragment {
|
|||||||
|
|
||||||
// Display user's shopping lists
|
// Display user's shopping lists
|
||||||
userListsView = (ListView) root.findViewById(R.id.user_lists);
|
userListsView = (ListView) root.findViewById(R.id.user_lists);
|
||||||
displayShoppingListsAdapter = new DisplayShoppingListsAdapter(getActivity(), userLists);
|
shoppingListsAdapter = new ShoppingListsAdapter(getActivity(), userLists);
|
||||||
userListsView.setAdapter(displayShoppingListsAdapter);
|
userListsView.setAdapter(shoppingListsAdapter);
|
||||||
|
|
||||||
// TODO: fix highlighting error
|
// TODO: fix highlighting error
|
||||||
userListsView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
userListsView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||||
|
|||||||
@ -0,0 +1,36 @@
|
|||||||
|
package com.example.listify;
|
||||||
|
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.drawable.ColorDrawable;
|
||||||
|
import android.view.Window;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
|
||||||
|
public class LoadingCircleDialog {
|
||||||
|
Dialog loadingDialog;
|
||||||
|
|
||||||
|
public LoadingCircleDialog(Context context) {
|
||||||
|
loadingDialog = new Dialog(context);
|
||||||
|
|
||||||
|
// Create and show a loading dialog
|
||||||
|
loadingDialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
|
||||||
|
loadingDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||||
|
|
||||||
|
// layout to display
|
||||||
|
loadingDialog.setContentView(R.layout.dialog_loading);
|
||||||
|
|
||||||
|
// set color transpartent
|
||||||
|
loadingDialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
|
||||||
|
loadingDialog.setCancelable(false);
|
||||||
|
loadingDialog.setCanceledOnTouchOutside(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void show() {
|
||||||
|
loadingDialog.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cancel() {
|
||||||
|
loadingDialog.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -208,6 +208,24 @@ 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);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
Log.i("Authentication", e.toString());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendNewListName(String name) {
|
public void sendNewListName(String name) {
|
||||||
Properties configs = new Properties();
|
Properties configs = new Properties();
|
||||||
|
|||||||
@ -3,7 +3,6 @@ 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.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@ -12,6 +11,7 @@ import android.widget.EditText;
|
|||||||
import android.widget.ImageButton;
|
import android.widget.ImageButton;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.ListView;
|
import android.widget.ListView;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
import android.widget.SearchView;
|
import android.widget.SearchView;
|
||||||
import com.example.listify.adapter.SearchResultsListAdapter;
|
import com.example.listify.adapter.SearchResultsListAdapter;
|
||||||
import com.example.listify.data.ItemSearch;
|
import com.example.listify.data.ItemSearch;
|
||||||
@ -25,9 +25,10 @@ import java.util.Properties;
|
|||||||
|
|
||||||
import static com.example.listify.MainActivity.am;
|
import static com.example.listify.MainActivity.am;
|
||||||
|
|
||||||
public class SearchResults extends AppCompatActivity implements FilterDialogFragment.OnFilterListener, SortDialogFragment.OnSortListener {
|
public class SearchResults extends AppCompatActivity implements FilterDialogFragment.OnFilterListener, SortDialogFragment.OnSortListener, Requestor.Receiver {
|
||||||
private ListView listView;
|
private ListView listView;
|
||||||
private MenuItem filterItem;
|
private MenuItem filterItem;
|
||||||
|
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<>();
|
||||||
@ -60,6 +61,8 @@ public class SearchResults extends AppCompatActivity implements FilterDialogFrag
|
|||||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||||
setSupportActionBar(toolbar);
|
setSupportActionBar(toolbar);
|
||||||
|
|
||||||
|
loadingSearch = (ProgressBar) findViewById(R.id.progress_loading_search);
|
||||||
|
|
||||||
// Back button closes this activity and returns to previous activity (MainActivity)
|
// Back button closes this activity and returns to previous activity (MainActivity)
|
||||||
ImageButton backButton = (ImageButton) findViewById(R.id.backToHomeButton);
|
ImageButton backButton = (ImageButton) findViewById(R.id.backToHomeButton);
|
||||||
backButton.setOnClickListener(new View.OnClickListener() {
|
backButton.setOnClickListener(new View.OnClickListener() {
|
||||||
@ -90,7 +93,7 @@ public class SearchResults extends AppCompatActivity implements FilterDialogFrag
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
listView = (ListView) findViewById(R.id.search_results_list);
|
ListView listView = (ListView) findViewById(R.id.search_results_list);
|
||||||
searchResultsListAdapter = new SearchResultsListAdapter(this, resultsProductListSorted);
|
searchResultsListAdapter = new SearchResultsListAdapter(this, resultsProductListSorted);
|
||||||
listView.setAdapter(searchResultsListAdapter);
|
listView.setAdapter(searchResultsListAdapter);
|
||||||
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||||
@ -108,6 +111,15 @@ public class SearchResults extends AppCompatActivity implements FilterDialogFrag
|
|||||||
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
|
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
|
||||||
@Override
|
@Override
|
||||||
public boolean onQueryTextSubmit(String query) {
|
public boolean onQueryTextSubmit(String query) {
|
||||||
|
// Show progress bar
|
||||||
|
loadingSearch.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
// Clear the old search results
|
||||||
|
resultsProductList.clear();
|
||||||
|
|
||||||
|
// Clear old search results from the view
|
||||||
|
resultsProductListSorted.clear();
|
||||||
|
searchResultsListAdapter.notifyDataSetChanged();
|
||||||
doSearch(query);
|
doSearch(query);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -175,9 +187,6 @@ public class SearchResults extends AppCompatActivity implements FilterDialogFrag
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Override default phone back button to add animation
|
// Override default phone back button to add animation
|
||||||
@Override
|
@Override
|
||||||
public void onBackPressed() {
|
public void onBackPressed() {
|
||||||
@ -186,10 +195,6 @@ public class SearchResults extends AppCompatActivity implements FilterDialogFrag
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void doSearch(String query) {
|
private void doSearch(String query) {
|
||||||
|
|
||||||
// Clear the old search results
|
|
||||||
resultsProductList.clear();
|
|
||||||
|
|
||||||
Properties configs = new Properties();
|
Properties configs = new Properties();
|
||||||
try {
|
try {
|
||||||
configs = AuthManager.loadProperties(this, "android.resource://" + getPackageName() + "/raw/auths.json");
|
configs = AuthManager.loadProperties(this, "android.resource://" + getPackageName() + "/raw/auths.json");
|
||||||
@ -198,53 +203,10 @@ public class SearchResults extends AppCompatActivity implements FilterDialogFrag
|
|||||||
}
|
}
|
||||||
|
|
||||||
Requestor requestor = new Requestor(am, configs.getProperty("apiKey"));
|
Requestor requestor = new Requestor(am, configs.getProperty("apiKey"));
|
||||||
|
requestor.getObject(query, ItemSearch.class, this);
|
||||||
SynchronousReceiver<ItemSearch> itemReceiver = new SynchronousReceiver<>();
|
|
||||||
requestor.getObject(query, ItemSearch.class, itemReceiver, itemReceiver);
|
|
||||||
ItemSearch results;
|
|
||||||
try {
|
|
||||||
results = itemReceiver.await();
|
|
||||||
for (int i = 0; i < results.getResults().size(); i++) {
|
|
||||||
// TODO: Change to dynamically grab chain name by id
|
|
||||||
resultsProductList.add(new Product(results.getResults().get(i).getDescription(), results.getResults().get(i).getProductID(), "Kroger", results.getResults().get(i).getChainID(), results.getResults().get(i).getUpc(), results.getResults().get(i).getDescription(), results.getResults().get(i).getPrice(), results.getResults().get(i).getImageURL(), results.getResults().get(i).getDepartment()));
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a list of all stores in the results so the user can filter by store name
|
|
||||||
for (int i = 0; i < resultsProductList.size(); i++) {
|
|
||||||
if (!stores.contains(resultsProductList.get(i).getChainName())) {
|
|
||||||
stores.add(resultsProductList.get(i).getChainName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort the store list
|
|
||||||
stores.sort(new Comparator<String>() {
|
|
||||||
@Override
|
|
||||||
public int compare(String o1, String o2) {
|
|
||||||
return o1.compareTo(o2);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 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
|
|
||||||
resultsProductListSorted.addAll(resultsProductList);
|
|
||||||
|
|
||||||
// Filtering should only be allowed if there are items in the results
|
|
||||||
if (resultsProductList.isEmpty()) {
|
|
||||||
filterItem.setEnabled(false);
|
|
||||||
} else {
|
|
||||||
filterItem.setEnabled(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply selected sorting to the list
|
|
||||||
sortResults();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Scroll the list back to the top when a search, sort, or filter is performed
|
||||||
// Sorts the search results
|
// Sorts the search results
|
||||||
private void sortResults() {
|
private void sortResults() {
|
||||||
// Reset the filtered list
|
// Reset the filtered list
|
||||||
@ -263,6 +225,7 @@ public class SearchResults extends AppCompatActivity implements FilterDialogFrag
|
|||||||
case NONE:
|
case NONE:
|
||||||
// Do nothing
|
// Do nothing
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NAME:
|
case NAME:
|
||||||
resultsProductListSorted.sort(new Comparator<Product>() {
|
resultsProductListSorted.sort(new Comparator<Product>() {
|
||||||
@Override
|
@Override
|
||||||
@ -271,6 +234,7 @@ public class SearchResults extends AppCompatActivity implements FilterDialogFrag
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PRICE:
|
case PRICE:
|
||||||
resultsProductListSorted.sort(new Comparator<Product>() {
|
resultsProductListSorted.sort(new Comparator<Product>() {
|
||||||
@Override
|
@Override
|
||||||
@ -326,7 +290,7 @@ public class SearchResults extends AppCompatActivity implements FilterDialogFrag
|
|||||||
resultsProductListSorted.addAll(temp);
|
resultsProductListSorted.addAll(temp);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter out products that don't fit price restraints
|
// Filter out products that don't fit price restraints
|
||||||
ArrayList<Product> temp = new ArrayList<>();
|
ArrayList<Product> temp = new ArrayList<>();
|
||||||
resultsProductListSorted.forEach(product -> {
|
resultsProductListSorted.forEach(product -> {
|
||||||
if (product.getPrice().doubleValue() >= this.minPrice &&
|
if (product.getPrice().doubleValue() >= this.minPrice &&
|
||||||
@ -337,6 +301,67 @@ public class SearchResults extends AppCompatActivity implements FilterDialogFrag
|
|||||||
resultsProductListSorted.clear();
|
resultsProductListSorted.clear();
|
||||||
resultsProductListSorted.addAll(temp);
|
resultsProductListSorted.addAll(temp);
|
||||||
|
|
||||||
searchResultsListAdapter.notifyDataSetChanged();
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
searchResultsListAdapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
try {
|
||||||
|
for (int i = 0; i < results.getResults().size(); i++) {
|
||||||
|
// TODO: Change to dynamically grab chain name by id
|
||||||
|
resultsProductList.add(new Product(
|
||||||
|
results.getResults().get(i).getDescription(),
|
||||||
|
results.getResults().get(i).getProductID(),
|
||||||
|
"Kroger",
|
||||||
|
results.getResults().get(i).getChainID(),
|
||||||
|
results.getResults().get(i).getUpc(),
|
||||||
|
results.getResults().get(i).getDescription(),
|
||||||
|
results.getResults().get(i).getPrice(),
|
||||||
|
results.getResults().get(i).getImageURL(),
|
||||||
|
results.getResults().get(i).getDepartment()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a list of all stores in the results so the user can filter by store name
|
||||||
|
for (int i = 0; i < resultsProductList.size(); i++) {
|
||||||
|
if (!stores.contains(resultsProductList.get(i).getChainName())) {
|
||||||
|
stores.add(resultsProductList.get(i).getChainName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add all results to the sorted list
|
||||||
|
resultsProductListSorted.addAll(resultsProductList);
|
||||||
|
|
||||||
|
// Filtering should only be allowed if there are items in the results
|
||||||
|
if (resultsProductList.isEmpty()) {
|
||||||
|
filterItem.setEnabled(false);
|
||||||
|
} else {
|
||||||
|
filterItem.setEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply selected sorting to the list
|
||||||
|
sortResults();
|
||||||
|
|
||||||
|
// Updates the list of search results. Runs on the main UI thread since other threads are
|
||||||
|
// not allowed to change UI elements
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
// Hide progress bar
|
||||||
|
loadingSearch.setVisibility(View.GONE);
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -11,12 +11,12 @@ import com.example.listify.R;
|
|||||||
import com.example.listify.data.List;
|
import com.example.listify.data.List;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
public class DisplayShoppingListsAdapter extends BaseAdapter {
|
public class ShoppingListsAdapter extends BaseAdapter {
|
||||||
private Activity activity;
|
private Activity activity;
|
||||||
private ArrayList<List> lists;
|
private ArrayList<List> lists;
|
||||||
private LayoutInflater inflater;
|
private LayoutInflater inflater;
|
||||||
|
|
||||||
public DisplayShoppingListsAdapter(Activity activity, ArrayList<List> lists){
|
public ShoppingListsAdapter(Activity activity, ArrayList<List> lists){
|
||||||
this.activity = activity;
|
this.activity = activity;
|
||||||
this.lists = lists;
|
this.lists = lists;
|
||||||
}
|
}
|
||||||
@ -42,7 +42,7 @@ public class DisplayShoppingListsAdapter extends BaseAdapter {
|
|||||||
inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||||
}
|
}
|
||||||
if (convertView == null) {
|
if (convertView == null) {
|
||||||
convertView = inflater.inflate(R.layout.display_shopping_lists_item, null);
|
convertView = inflater.inflate(R.layout.shopping_lists_name_item, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
List curList = lists.get(position);
|
List curList = lists.get(position);
|
||||||
@ -0,0 +1,175 @@
|
|||||||
|
package com.example.listify.adapter;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.BaseAdapter;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.chauthai.swipereveallayout.SwipeRevealLayout;
|
||||||
|
import com.chauthai.swipereveallayout.ViewBinderHelper;
|
||||||
|
import com.example.listify.AuthManager;
|
||||||
|
import com.example.listify.ListPage;
|
||||||
|
import com.example.listify.R;
|
||||||
|
import com.example.listify.Requestor;
|
||||||
|
import com.example.listify.data.List;
|
||||||
|
import com.example.listify.data.ListShare;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import static com.example.listify.MainActivity.am;
|
||||||
|
|
||||||
|
public class ShoppingListsSwipeableAdapter extends BaseAdapter {
|
||||||
|
private Activity activity;
|
||||||
|
private ArrayList<List> lists;
|
||||||
|
private LayoutInflater inflater;
|
||||||
|
private final ViewBinderHelper binderHelper;
|
||||||
|
|
||||||
|
public ShoppingListsSwipeableAdapter(Activity activity, ArrayList<List> lists){
|
||||||
|
binderHelper = new ViewBinderHelper();
|
||||||
|
this.activity = activity;
|
||||||
|
this.lists = lists;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCount() {
|
||||||
|
return lists.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getItem(int position) {
|
||||||
|
return lists.get(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getItemId(int position) {
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View getView(int position, View convertView, ViewGroup parent) {
|
||||||
|
ViewHolder holder;
|
||||||
|
|
||||||
|
Properties configs = new Properties();
|
||||||
|
try {
|
||||||
|
configs = AuthManager.loadProperties(activity, "android.resource://" + activity.getPackageName() + "/raw/auths.json");
|
||||||
|
} catch (IOException | JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
Requestor requestor = new Requestor(am, configs.getProperty("apiKey"));
|
||||||
|
|
||||||
|
if (inflater == null) {
|
||||||
|
inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||||
|
}
|
||||||
|
if (convertView == null) {
|
||||||
|
convertView = inflater.inflate(R.layout.shopping_lists_swipeable_name_item, null);
|
||||||
|
|
||||||
|
holder = new ViewHolder();
|
||||||
|
holder.swipeLayout = (SwipeRevealLayout)convertView.findViewById(R.id.swipe_layout);
|
||||||
|
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);
|
||||||
|
|
||||||
|
convertView.setTag(holder);
|
||||||
|
} else {
|
||||||
|
holder = (ViewHolder) convertView.getTag();
|
||||||
|
}
|
||||||
|
|
||||||
|
final List curList = lists.get(position);
|
||||||
|
|
||||||
|
// Bind the view to the unique list ID
|
||||||
|
binderHelper.bind(holder.swipeLayout, Integer.toString(curList.getItemID()));
|
||||||
|
|
||||||
|
holder.textView.setText(curList.getName());
|
||||||
|
holder.deleteList.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
// TODO: Add database call to delete the list on the server
|
||||||
|
|
||||||
|
try {
|
||||||
|
requestor.deleteObject(Integer.toString(curList.getItemID()), List.class);
|
||||||
|
}
|
||||||
|
catch(Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
Toast.makeText(activity, String.format("%s deleted", curList.getName()), Toast.LENGTH_SHORT).show();
|
||||||
|
lists.remove(position);
|
||||||
|
|
||||||
|
// Update listView
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
holder.shareList.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
// TODO: Add database call to share list
|
||||||
|
|
||||||
|
View codeView = inflater.inflate(R.layout.activity_sharedemail, null);
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
||||||
|
builder.setView(codeView);
|
||||||
|
builder.setTitle("Share list");
|
||||||
|
builder.setMessage("Please enter the email of the user who you want to share the list with.");
|
||||||
|
builder.setPositiveButton("Submit", new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
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);
|
||||||
|
try {
|
||||||
|
requestor.postObject(listShare);
|
||||||
|
}
|
||||||
|
catch(Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int which) {}
|
||||||
|
});
|
||||||
|
AlertDialog dialog = builder.create();
|
||||||
|
dialog.show();
|
||||||
|
|
||||||
|
Toast.makeText(activity, String.format("Share %s", curList.getName()), Toast.LENGTH_SHORT).show();
|
||||||
|
|
||||||
|
// Close the layout
|
||||||
|
binderHelper.closeLayout(Integer.toString(curList.getItemID()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
holder.frontView.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
Intent listPage = new Intent(activity, ListPage.class);
|
||||||
|
|
||||||
|
// Send the list ID
|
||||||
|
listPage.putExtra("listID", curList.getItemID());
|
||||||
|
activity.startActivity(listPage);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return convertView;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ViewHolder {
|
||||||
|
SwipeRevealLayout swipeLayout;
|
||||||
|
View frontView;
|
||||||
|
View deleteList;
|
||||||
|
View shareList;
|
||||||
|
TextView textView;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -8,17 +8,19 @@ public class List {
|
|||||||
String owner;
|
String owner;
|
||||||
long lastUpdated;
|
long lastUpdated;
|
||||||
final ListEntry[] entries;
|
final ListEntry[] entries;
|
||||||
|
boolean shared;
|
||||||
|
|
||||||
public List(Integer itemID, String name, String owner, long lastUpdated, ListEntry[] entries) {
|
public List(Integer itemID, String name, String owner, long lastUpdated, ListEntry[] entries, boolean shared) {
|
||||||
this.itemID = itemID;
|
this.itemID = itemID;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
this.lastUpdated = lastUpdated;
|
this.lastUpdated = lastUpdated;
|
||||||
this.entries = entries;
|
this.entries = entries;
|
||||||
|
this.shared = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List(Integer itemID, String name, String owner, long lastUpdated) {
|
public List(Integer itemID, String name, String owner, long lastUpdated) {
|
||||||
this(itemID, name, owner, lastUpdated, null);
|
this(itemID, name, owner, lastUpdated, null, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -29,6 +31,7 @@ public class List {
|
|||||||
", owner='" + owner + '\'' +
|
", owner='" + owner + '\'' +
|
||||||
", lastUpdated=" + lastUpdated +
|
", lastUpdated=" + lastUpdated +
|
||||||
", entries=" + Arrays.toString(entries) +
|
", entries=" + Arrays.toString(entries) +
|
||||||
|
", shared=" + shared +
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,4 +70,12 @@ public class List {
|
|||||||
public ListEntry[] getEntries() {
|
public ListEntry[] getEntries() {
|
||||||
return entries;
|
return entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isShared() {
|
||||||
|
return shared;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setShared(boolean shared) {
|
||||||
|
this.shared = shared;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,27 @@
|
|||||||
|
package com.example.listify.data;
|
||||||
|
|
||||||
|
public class ListShare {
|
||||||
|
Integer listID;
|
||||||
|
String shareWithEmail;
|
||||||
|
|
||||||
|
public ListShare(Integer listID, String shareWithEmail) {
|
||||||
|
this.listID = listID;
|
||||||
|
this.shareWithEmail = shareWithEmail;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getListID() {
|
||||||
|
return listID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setListID(Integer listID) {
|
||||||
|
this.listID = listID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getShareWithEmail() {
|
||||||
|
return shareWithEmail;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setShareWithEmail(String shareWithEmail) {
|
||||||
|
this.shareWithEmail = shareWithEmail;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -84,9 +84,7 @@ public class ForgotPasswordPage extends AppCompatActivity {
|
|||||||
});
|
});
|
||||||
builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
|
builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
public void onClick(DialogInterface dialog, int which) {}
|
||||||
return;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
AlertDialog dialog = builder.create();
|
AlertDialog dialog = builder.create();
|
||||||
dialog.show();
|
dialog.show();
|
||||||
|
|||||||
@ -103,9 +103,7 @@ public class SignupPage extends AppCompatActivity {
|
|||||||
});
|
});
|
||||||
builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
|
builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
public void onClick(DialogInterface dialog, int which) {}
|
||||||
return;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
AlertDialog dialog = builder.create();
|
AlertDialog dialog = builder.create();
|
||||||
dialog.show();
|
dialog.show();
|
||||||
|
|||||||
@ -70,9 +70,7 @@ public class HomeFragment extends Fragment {
|
|||||||
});
|
});
|
||||||
builder.setNegativeButton("No", new DialogInterface.OnClickListener() {
|
builder.setNegativeButton("No", new DialogInterface.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
public void onClick(DialogInterface dialog, int which) {}
|
||||||
return;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
AlertDialog dialog = builder.create();
|
AlertDialog dialog = builder.create();
|
||||||
dialog.show();
|
dialog.show();
|
||||||
|
|||||||
@ -1,27 +1,22 @@
|
|||||||
package com.example.listify.ui.lists;
|
package com.example.listify.ui.lists;
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.widget.AdapterView;
|
|
||||||
import android.widget.ListView;
|
import android.widget.ListView;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
import com.amplifyframework.auth.AuthException;
|
|
||||||
import com.example.listify.AuthManager;
|
import com.example.listify.AuthManager;
|
||||||
import com.example.listify.CreateListAddDialogFragment;
|
|
||||||
import com.example.listify.CreateListDialogFragment;
|
import com.example.listify.CreateListDialogFragment;
|
||||||
import com.example.listify.ItemDetails;
|
import com.example.listify.LoadingCircleDialog;
|
||||||
import com.example.listify.ListPage;
|
|
||||||
import com.example.listify.R;
|
import com.example.listify.R;
|
||||||
import com.example.listify.Requestor;
|
import com.example.listify.Requestor;
|
||||||
import com.example.listify.SearchResults;
|
|
||||||
import com.example.listify.SynchronousReceiver;
|
import com.example.listify.SynchronousReceiver;
|
||||||
import com.example.listify.adapter.DisplayShoppingListsAdapter;
|
import com.example.listify.adapter.ShoppingListsSwipeableAdapter;
|
||||||
import com.example.listify.data.List;
|
import com.example.listify.data.List;
|
||||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||||
|
|
||||||
@ -34,16 +29,20 @@ import java.util.Properties;
|
|||||||
|
|
||||||
import static com.example.listify.MainActivity.am;
|
import static com.example.listify.MainActivity.am;
|
||||||
|
|
||||||
public class ListsFragment extends Fragment implements CreateListDialogFragment.OnNewListListener {
|
public class ListsFragment extends Fragment implements CreateListDialogFragment.OnNewListListener, Requestor.Receiver {
|
||||||
ArrayList<List> shoppingLists = new ArrayList<>();
|
ArrayList<List> shoppingLists = new ArrayList<>();
|
||||||
DisplayShoppingListsAdapter displayShoppingListsAdapter;
|
ShoppingListsSwipeableAdapter shoppingListsSwipeableAdapter;
|
||||||
|
Requestor requestor;
|
||||||
ListView shoppingListsView;
|
ListView shoppingListsView;
|
||||||
|
ProgressBar loadingLists;
|
||||||
|
int resultsIndex;
|
||||||
|
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
View root = inflater.inflate(R.layout.fragment_lists, container, false);
|
View root = inflater.inflate(R.layout.fragment_lists, container, false);
|
||||||
shoppingListsView = root.findViewById(R.id.shopping_lists);
|
shoppingListsView = root.findViewById(R.id.shopping_lists);
|
||||||
|
loadingLists = (ProgressBar) root.findViewById(R.id.progress_loading_lists);
|
||||||
|
loadingLists.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
// TODO: Switch this to async
|
|
||||||
Properties configs = new Properties();
|
Properties configs = new Properties();
|
||||||
try {
|
try {
|
||||||
configs = AuthManager.loadProperties(getContext(), "android.resource://" + getActivity().getPackageName() + "/raw/auths.json");
|
configs = AuthManager.loadProperties(getContext(), "android.resource://" + getActivity().getPackageName() + "/raw/auths.json");
|
||||||
@ -51,36 +50,13 @@ public class ListsFragment extends Fragment implements CreateListDialogFragment.
|
|||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
Requestor requestor = new Requestor(am, configs.getProperty("apiKey"));
|
requestor = new Requestor(am, configs.getProperty("apiKey"));
|
||||||
SynchronousReceiver<Integer[]> listIdsReceiver = new SynchronousReceiver<>();
|
SynchronousReceiver<Integer[]> listIdsReceiver = new SynchronousReceiver<>();
|
||||||
SynchronousReceiver<List> listReceiver = new SynchronousReceiver<>();
|
|
||||||
|
|
||||||
requestor.getListOfIds(List.class, listIdsReceiver, listIdsReceiver);
|
final Requestor.Receiver<Integer[]> recv = this;
|
||||||
try {
|
requestor.getListOfIds(List.class, recv, null);
|
||||||
Integer[] listIds = listIdsReceiver.await();
|
|
||||||
for (int i = 0; i < listIds.length; i++) {
|
|
||||||
requestor.getObject(Integer.toString(listIds[i]), List.class, listReceiver, listReceiver);
|
|
||||||
shoppingLists.add(listReceiver.await());
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Set adapter and display this users lists
|
|
||||||
displayShoppingListsAdapter = new DisplayShoppingListsAdapter(getActivity(), shoppingLists);
|
|
||||||
shoppingListsView.setAdapter(displayShoppingListsAdapter);
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
FloatingActionButton fab = (FloatingActionButton) root.findViewById(R.id.new_list_fab);
|
FloatingActionButton fab = (FloatingActionButton) root.findViewById(R.id.new_list_fab);
|
||||||
Fragment thisFragment = this;
|
Fragment thisFragment = this;
|
||||||
fab.setOnClickListener(new View.OnClickListener() {
|
fab.setOnClickListener(new View.OnClickListener() {
|
||||||
@ -97,6 +73,8 @@ public class ListsFragment extends Fragment implements CreateListDialogFragment.
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendNewListName(String name) {
|
public void sendNewListName(String name) {
|
||||||
|
LoadingCircleDialog loadingDialog = new LoadingCircleDialog(getActivity());
|
||||||
|
loadingDialog.show();
|
||||||
|
|
||||||
Properties configs = new Properties();
|
Properties configs = new Properties();
|
||||||
try {
|
try {
|
||||||
@ -111,13 +89,95 @@ public class ListsFragment extends Fragment implements CreateListDialogFragment.
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
requestor.postObject(newList, idReceiver, idReceiver);
|
requestor.postObject(newList, idReceiver, idReceiver);
|
||||||
newList.setItemID(idReceiver.await());
|
|
||||||
shoppingLists.add(newList);
|
|
||||||
displayShoppingListsAdapter.notifyDataSetChanged();
|
|
||||||
Toast.makeText(getContext(), String.format("%s created", name), Toast.LENGTH_LONG).show();
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Toast.makeText(getContext(), "An error occurred", Toast.LENGTH_LONG).show();
|
Toast.makeText(getContext(), "An error occurred", Toast.LENGTH_LONG).show();
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
Thread t = new Thread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
newList.setItemID(idReceiver.await());
|
||||||
|
} catch (Exception e) {
|
||||||
|
getActivity().runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Toast.makeText(getContext(), "An error occurred", Toast.LENGTH_LONG).show();
|
||||||
|
loadingDialog.cancel();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
e.printStackTrace();
|
||||||
|
|
||||||
|
}
|
||||||
|
shoppingLists.add(newList);
|
||||||
|
|
||||||
|
getActivity().runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
shoppingListsSwipeableAdapter.notifyDataSetChanged();
|
||||||
|
loadingDialog.cancel();
|
||||||
|
Toast.makeText(getContext(), String.format("%s created", name), Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
t.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void acceptDelivery(Object delivered) {
|
||||||
|
Integer[] listIds = (Integer[]) delivered;
|
||||||
|
// Create threads and add them to a list
|
||||||
|
Thread[] threads = new Thread[listIds.length];
|
||||||
|
List[] results = new List[listIds.length];
|
||||||
|
for (int i = 0; i < listIds.length; i++) {
|
||||||
|
SynchronousReceiver<List> listReceiver = new SynchronousReceiver<>();
|
||||||
|
requestor.getObject(Integer.toString(listIds[i]), List.class, listReceiver, listReceiver);
|
||||||
|
final int finalI = i;
|
||||||
|
Thread t = new Thread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
results[finalI] = listReceiver.await();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
threads[i] = t;
|
||||||
|
t.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for each thread to finish and add results to shoppingLists
|
||||||
|
for (int i = 0; i < threads.length; i++) {
|
||||||
|
try {
|
||||||
|
threads[i].join();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
shoppingLists.add(results[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set adapter and display this users lists
|
||||||
|
shoppingListsSwipeableAdapter = new ShoppingListsSwipeableAdapter(getActivity(), shoppingLists);
|
||||||
|
|
||||||
|
getActivity().runOnUiThread(new Runnable() {
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2L18,7L6,7v12zM8.46,11.88l1.41,-1.41L12,12.59l2.12,-2.12 1.41,1.41L13.41,14l2.12,2.12 -1.41,1.41L12,15.41l-2.12,2.12 -1.41,-1.41L10.59,14l-2.13,-2.12zM15.5,4l-1,-1h-5l-1,1L5,4v2h14L19,4z"/>
|
||||||
|
</vector>
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z"/>
|
||||||
|
</vector>
|
||||||
@ -1,14 +1,71 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<RelativeLayout
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<ListView
|
<ProgressBar
|
||||||
|
android:id="@+id/progress_loading_list_items"
|
||||||
|
style="?android:attr/progressBarStyleLarge"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerHorizontal="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:indeterminate="true"
|
||||||
|
android:visibility="gone"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="wrap_content">
|
||||||
android:id="@+id/listView">
|
|
||||||
|
|
||||||
</ListView>
|
<Button
|
||||||
|
android:id="@+id/buttonClear"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="1dp"
|
||||||
|
android:layout_marginTop="1dp"
|
||||||
|
android:text="Clear All"
|
||||||
|
android:textSize="10sp"/>
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
<!--<Button
|
||||||
|
android:id="@+id/buttonShare"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="26dp"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:text="Share"
|
||||||
|
android:textSize="10sp"/>-->
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<ListView
|
||||||
|
android:id="@+id/listView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="600dp"
|
||||||
|
android:layout_marginTop="50dp"
|
||||||
|
android:paddingBottom="20dp"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentBottom="true"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:background="@color/colorPrimaryDark">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@android:color/white"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:text="Total: "/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/total_price"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@android:color/white"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:text="@string/default__00_00"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
21
Listify/app/src/main/res/layout/activity_sharedemail.xml
Normal file
21
Listify/app/src/main/res/layout/activity_sharedemail.xml
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/editTextTextSharedEmail"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
android:layout_marginEnd="15dp"
|
||||||
|
android:layout_marginStart="15dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:ems="10"
|
||||||
|
android:inputType="textPersonName"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
@ -12,6 +12,16 @@
|
|||||||
tools:context=".SearchResults"
|
tools:context=".SearchResults"
|
||||||
tools:showIn="@layout/activity_search_results">
|
tools:showIn="@layout/activity_search_results">
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/progress_loading_search"
|
||||||
|
style="?android:attr/progressBarStyleLarge"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerHorizontal="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:indeterminate="true"
|
||||||
|
android:visibility="gone"/>
|
||||||
|
|
||||||
<ListView
|
<ListView
|
||||||
android:id="@+id/search_results_list"
|
android:id="@+id/search_results_list"
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
|
|||||||
16
Listify/app/src/main/res/layout/dialog_loading.xml
Normal file
16
Listify/app/src/main/res/layout/dialog_loading.xml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?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"
|
||||||
|
android:background="@null">
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/progress_loading_lists"
|
||||||
|
style="?android:attr/progressBarStyleLarge"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerHorizontal="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:indeterminate="true"
|
||||||
|
android:visibility="visible"/>
|
||||||
|
</RelativeLayout>
|
||||||
@ -12,6 +12,16 @@
|
|||||||
tools:context=".ui.lists.ListsFragment"
|
tools:context=".ui.lists.ListsFragment"
|
||||||
tools:showIn="@layout/fragment_lists">
|
tools:showIn="@layout/fragment_lists">
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/progress_loading_lists"
|
||||||
|
style="?android:attr/progressBarStyleLarge"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerHorizontal="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:indeterminate="true"
|
||||||
|
android:visibility="gone"/>
|
||||||
|
|
||||||
<ListView
|
<ListView
|
||||||
android:id="@+id/shopping_lists"
|
android:id="@+id/shopping_lists"
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
|
|||||||
@ -0,0 +1,50 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<com.chauthai.swipereveallayout.SwipeRevealLayout
|
||||||
|
android:id="@+id/swipe_layout"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:dragEdge="right"
|
||||||
|
app:mode="same_level">
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/back_layout"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="50dp" >
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/share_list"
|
||||||
|
android:src="@drawable/ic_baseline_share_24"
|
||||||
|
android:layout_width="50dp"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:background="@color/colorAccent"/>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/delete_list"
|
||||||
|
android:src="@drawable/ic_baseline_delete_forever_24"
|
||||||
|
android:layout_width="50dp"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:background="@android:color/holo_red_dark"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/front_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="50dp">
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/shopping_list_name"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:layout_gravity="center"/>
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
</com.chauthai.swipereveallayout.SwipeRevealLayout>
|
||||||
Loading…
Reference in New Issue
Block a user