Merge pull request #126 from ClaytonWWilson/master

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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