Merge branch 'master' into aaron-branch-2

This commit is contained in:
Aaron Sun 2020-11-29 13:00:37 -08:00 committed by GitHub
commit 2d453612cf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 656 additions and 148 deletions

View File

@ -85,9 +85,6 @@ public class List {
public void setUiPosition(Integer uiPosition) { public void setUiPosition(Integer uiPosition) {
this.uiPosition = uiPosition; this.uiPosition = uiPosition;
public ItemEntry[] getEntries() {
return entries.toArray(new ItemEntry[entries.size()]);
} }
public void addItemEntry(ItemEntry entry) { public void addItemEntry(ItemEntry entry) {

View File

@ -1,16 +1,57 @@
package com.example.listify.data; import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
public class ListShare { public class ListShare {
Integer listID; Integer listID;
String shareWithEmail; String shareWithEmail;
Integer permissionLevel;
Integer uiPosition;
ArrayList<ListShare> other; ArrayList<ListShare> other;
public ListShare(ResultSet listRow) throws SQLException { public ListShare(ResultSet listRow) throws SQLException {
this.listID = listRow.getInt("listID"); this.listID = listRow.getInt("listID");
this.shareWithEmail = listRow.getString("userID"); this.shareWithEmail = listRow.getString("userID");
this.permissionLevel = listRow.getInt("permissionLevel");
this.uiPosition = listRow.getInt("uiPosition");
other = new ArrayList<>(); other = new ArrayList<>();
} }
@Override
public String toString() {
return "ListShare{" +
"listID=" + listID +
", shareWithEmail='" + shareWithEmail + '\'' +
", permissionLevel=" + permissionLevel +
", uiPosition=" + uiPosition +
", other=" + other +
'}';
}
public Integer getPermissionLevel() {
return permissionLevel;
}
public void setPermissionLevel(Integer permissionLevel) {
this.permissionLevel = permissionLevel;
}
public Integer getUiPosition() {
return uiPosition;
}
public void setUiPosition(Integer uiPosition) {
this.uiPosition = uiPosition;
}
public ArrayList<ListShare> getOther() {
return other;
}
public void setOther(ArrayList<ListShare> other) {
this.other = other;
}
public Integer getListID() { public Integer getListID() {
return listID; return listID;
} }

View File

@ -1,65 +0,0 @@
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 ListShareDeleter implements CallHandler {
private final Connection connection;
private final String cognitoID;
private final String GET_LIST_ACCESS = "SELECT * FROM List WHERE (owner = ? AND listID = ?);";
private final String REMOVE_SHAREE = "DELETE FROM ListSharee WHERE listID = ? AND user = ?;";
public ListShareDeleter(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"));
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);
String shareeID = new String(invokeResult.getPayload().array()).replace("\"", "");
//Ensure that the user who is unsharing a list is the owner of that list
PreparedStatement accessCheck = connection.prepareStatement(GET_LIST_ACCESS);
accessCheck.setString(1, cognitoID);
accessCheck.setInt(2, listID);
ResultSet userLists = accessCheck.executeQuery();
//User does not own the list; unshare attempt fails
if (!userLists.next()) {
throw new AccessControlException("User does not have access to list");
}
//Unshare the list with the specified sharee
PreparedStatement unshareList = connection.prepareStatement(REMOVE_SHAREE);
unshareList.setInt(1, listID);
unshareList.setInt(2, shareeID);
cleanAccess.executeUpdate();
connection.commit();
return null;
}
}

View File

@ -2,7 +2,6 @@ import java.sql.Connection;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -21,7 +20,7 @@ public class ListShareGetter implements CallHandler{
public Object conductAction(Map<String, Object> bodyMap, HashMap<String, String> queryMap, String cognitoID) throws SQLException { public Object conductAction(Map<String, Object> bodyMap, HashMap<String, String> queryMap, String cognitoID) throws SQLException {
Integer listID = Integer.parseInt(queryMap.get("id")); Integer listID = Integer.parseInt(queryMap.get("id"));
PreparedStatement getList = connection.prepareStatement(GET_LIST); PreparedStatement getList = connection.prepareStatement(GET_LISTS);
getList.setInt(1, listID); getList.setInt(1, listID);
ResultSet getListResults = getList.executeQuery(); ResultSet getListResults = getList.executeQuery();

View File

@ -0,0 +1,34 @@
import java.sql.ResultSet;
import java.sql.SQLException;
public class Picture {
String base64EncodedImage;
public Picture(ResultSet rs) {
try {
this.base64EncodedImage = rs.getString("base64image");
} catch (SQLException throwables) {
throwables.printStackTrace();
this.base64EncodedImage = null;
}
}
public Picture(String base64EncodedImage) {
this.base64EncodedImage = base64EncodedImage;
}
@Override
public String toString() {
return "Picture{" +
"base64EncodedImage='" + base64EncodedImage + '\'' +
'}';
}
public String getBase64EncodedImage() {
return base64EncodedImage;
}
public void setBase64EncodedImage(String base64EncodedImage) {
this.base64EncodedImage = base64EncodedImage;
}
}

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

View File

@ -0,0 +1,34 @@
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 PictureGetter implements CallHandler {
private final Connection connection;
private final String cognitoID;
private final String GET_ITEM = "SELECT * FROM Pictures WHERE cognitoID = ?;";
public PictureGetter(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 {
PreparedStatement statement = connection.prepareStatement(GET_ITEM);
if (!queryMap.get("id").toString().equals("profile")) {
throw new IllegalArgumentException("Only profile pictures are currently supported.");
}
statement.setString(1, cognitoID);
System.out.println(statement);
ResultSet queryResults = statement.executeQuery();
queryResults.first();
System.out.println(queryResults);
Picture retrievedPicture = new Picture(queryResults);
// System.out.println(retrievedPicture);
return retrievedPicture;
}
}

View File

@ -3,10 +3,9 @@ import com.amazonaws.services.lambda.runtime.RequestHandler;
import java.util.Map; import java.util.Map;
public class ListShareDELETE implements RequestHandler<Map<String,Object>, Object> { public class PicturePUT implements RequestHandler<Map<String,Object>, Object> {
public Object handleRequest(Map<String, Object> inputMap, Context unfilled) { public Object handleRequest(Map<String, Object> inputMap, Context unfilled) {
return BasicHandler.handleRequest(inputMap, unfilled, ListShareDeleter.class); return BasicHandler.handleRequest(inputMap, unfilled, PicturePutter.class);
} }
} }

View File

@ -0,0 +1,28 @@
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
public class PicturePutter implements CallHandler {
private Connection connection;
private String cognitoID;
public PicturePutter(Connection connection, String cognitoID) {
this.connection = connection;
this.cognitoID = cognitoID;
}
final private String STORE_PICTURE_SQL = "REPLACE INTO Pictures(cognitoID, base64image) VALUES(?, ?);";
public Object conductAction(Map<String, Object> bodyMap, HashMap<String, String> queryString, String cognitoID) throws SQLException {
PreparedStatement storePicture = connection.prepareStatement(STORE_PICTURE_SQL);
storePicture.setString(1, cognitoID);
storePicture.setString(2, bodyMap.get("base64EncodedImage").toString());
System.out.println(storePicture);
storePicture.executeUpdate();
connection.commit();
return null;
}
}

View File

@ -53,4 +53,6 @@ dependencies {
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' implementation 'com.chauthai.swipereveallayout:swipe-reveal-layout:1.4.1'
implementation "androidx.cardview:cardview:1.0.0"
} }

View File

@ -3,6 +3,9 @@
package="com.example.listify"> package="com.example.listify">
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<application <application
android:name=".Listify" android:name=".Listify"
@ -13,6 +16,18 @@
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/AppTheme" android:theme="@style/AppTheme"
android:usesCleartextTraffic="true"> android:usesCleartextTraffic="true">
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.example.listify.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths">
</meta-data>
</provider>
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:label="@string/app_name" android:label="@string/app_name"

View File

@ -1,15 +1,17 @@
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.DialogInterface;
import android.content.Intent;
import android.graphics.Color; import android.graphics.Color;
import android.os.Bundle; import android.os.Bundle;
import android.text.Editable;
import android.util.Log; import android.util.Log;
import android.view.*; import android.view.*;
import android.widget.*; import android.widget.*;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.constraintlayout.widget.ConstraintLayout; import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
@ -27,7 +29,7 @@ import java.util.Properties;
import static com.example.listify.MainActivity.am; import static com.example.listify.MainActivity.am;
public class ListPage extends AppCompatActivity implements Requestor.Receiver { public class ListPage extends AppCompatActivity implements Requestor.Receiver, RenameListDialogFragment.OnRenameListListener {
ListView listView; ListView listView;
MyAdapter myAdapter; MyAdapter myAdapter;
Requestor requestor; Requestor requestor;
@ -59,15 +61,15 @@ public class ListPage extends AppCompatActivity implements Requestor.Receiver {
Map<String, Integer> storeHeaderIndex = new HashMap<>(); Map<String, Integer> storeHeaderIndex = new HashMap<>();
DecimalFormat df = new DecimalFormat("0.00"); DecimalFormat df = new DecimalFormat("0.00");
List selectedList;
@Override @Override
protected void onCreate(@Nullable Bundle savedInstanceState) { protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list); setContentView(R.layout.activity_list);
final int LIST_ID = (int) getIntent().getSerializableExtra("listID"); selectedList = (List) getIntent().getSerializableExtra("selectedList");
final String LIST_NAME = (String) getIntent().getSerializableExtra("listName"); setTitle(selectedList.getName());
setTitle(LIST_NAME);
Properties configs = new Properties(); Properties configs = new Properties();
try { try {
@ -76,7 +78,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(LIST_ID), List.class, this); requestor.getObject(Integer.toString(selectedList.getListID()), 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);
@ -158,7 +160,7 @@ public class ListPage extends AppCompatActivity implements Requestor.Receiver {
} }
requestor = new Requestor(am, configs.getProperty("apiKey")); requestor = new Requestor(am, configs.getProperty("apiKey"));
requestor.getObject(Integer.toString(LIST_ID), List.class, ListPage.this); requestor.getObject(Integer.toString(selectedList.getListID()), List.class, ListPage.this);
} }
}); });
} }
@ -166,16 +168,16 @@ public class ListPage extends AppCompatActivity implements Requestor.Receiver {
@Override @Override
public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(Menu menu) {
//Inflate the menu; this adds items to the action bar if it is present. //Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.list, menu); getMenuInflater().inflate(R.menu.list, menu);
//return super.onCreateOptionsMenu(menu);
MenuItem renameItem = menu.findItem(R.id.action_rename_list); MenuItem renameItem = menu.findItem(R.id.action_rename_list);
renameItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { renameItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override @Override
public boolean onMenuItemClick(MenuItem item) { public boolean onMenuItemClick(MenuItem item) {
Toast.makeText(ListPage.this, "Rename List", Toast.LENGTH_SHORT).show(); RenameListDialogFragment renameListDialog = new RenameListDialogFragment();
renameListDialog.show(getSupportFragmentManager(), "Rename List");
return false; return false;
} }
}); });
@ -193,7 +195,24 @@ public class ListPage extends AppCompatActivity implements Requestor.Receiver {
duplicateItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { duplicateItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override @Override
public boolean onMenuItemClick(MenuItem item) { public boolean onMenuItemClick(MenuItem item) {
Toast.makeText(ListPage.this, "Duplicate List", Toast.LENGTH_SHORT).show();
ListDuplicate duplicate = new ListDuplicate(selectedList.getListID(), String.format("%s copy", selectedList.getName()));
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"));
try {
requestor.postObject(duplicate);
} catch (JSONException e) {
e.printStackTrace();
}
Toast.makeText(ListPage.this, "List duplicated", Toast.LENGTH_SHORT).show();
return false; return false;
} }
}); });
@ -202,6 +221,24 @@ public class ListPage extends AppCompatActivity implements Requestor.Receiver {
exportItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { exportItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override @Override
public boolean onMenuItemClick(MenuItem item) { public boolean onMenuItemClick(MenuItem item) {
StringBuilder listContents = new StringBuilder();
for (int i = 0; i < pNames.size(); i++) {
if (Integer.parseInt(pQuantity.get(i)) == -1) {
listContents.append(String.format("\n%s\n", pNames.get(i)));
} else {
listContents.append(String.format(" %s of %s\n", pQuantity.get(i), pNames.get(i)));
}
}
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, listContents.toString());
sendIntent.setType("text/plain");
Intent shareIntent = Intent.createChooser(sendIntent, null);
startActivity(shareIntent);
Toast.makeText(ListPage.this, "Export List", Toast.LENGTH_SHORT).show(); Toast.makeText(ListPage.this, "Export List", Toast.LENGTH_SHORT).show();
return false; return false;
} }
@ -213,22 +250,16 @@ public class ListPage extends AppCompatActivity implements Requestor.Receiver {
@Override @Override
public void acceptDelivery(Object delivered) { public void acceptDelivery(Object delivered) {
// Clear out old values // Clear out old values
runOnUiThread(new Runnable() { pNames.clear();
@Override pStores.clear();
public void run() { pPrices.clear();
pNames.clear(); pQuantity.clear();
pStores.clear(); pImages.clear();
pPrices.clear(); totalPriceByStore.clear();
pQuantity.clear(); storeID2Name.clear();
pImages.clear(); storeHeaderIndex.clear();
totalPriceByStore.clear(); pListItemPair.clear();
storeID2Name.clear(); totalPrice = 0;
storeHeaderIndex.clear();
pListItemPair.clear();
totalPrice = 0;
tvTotalPrice.setText(String.format("$%.2f", totalPrice));
}
});
List list = (List) delivered; List list = (List) delivered;
@ -326,9 +357,44 @@ public class ListPage extends AppCompatActivity implements Requestor.Receiver {
}); });
} }
runOnUiThread(new Runnable() {
@Override
public void run() {
tvTotalPrice.setText(String.format("$%.2f", totalPrice));
}
});
refreshList.setRefreshing(false); refreshList.setRefreshing(false);
} }
@Override
public void sendRenameListName(String name) {
selectedList.setName(name);
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"));
try {
requestor.putObject(selectedList);
} catch (Exception e) {
e.printStackTrace();
}
runOnUiThread(new Runnable() {
@Override
public void run() {
setTitle(name);
Toast.makeText(ListPage.this, "List Renamed", Toast.LENGTH_SHORT).show();
}
});
}
class MyAdapter extends ArrayAdapter<String> { class MyAdapter extends ArrayAdapter<String> {
Context context; Context context;
ArrayList<String> pNames; ArrayList<String> pNames;

View File

@ -1,15 +1,26 @@
package com.example.listify; package com.example.listify;
import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Environment;
import android.os.Handler; import android.os.Handler;
import android.provider.MediaStore;
import android.util.Base64;
import android.util.Base64OutputStream;
import android.util.Log; import android.util.Log;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.widget.ImageButton; import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar; import androidx.appcompat.widget.Toolbar;
import androidx.core.content.FileProvider;
import androidx.drawerlayout.widget.DrawerLayout; import androidx.drawerlayout.widget.DrawerLayout;
import androidx.navigation.NavController; import androidx.navigation.NavController;
import androidx.navigation.Navigation; import androidx.navigation.Navigation;
@ -17,15 +28,15 @@ 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.Picture;
import com.example.listify.data.ListReposition;
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;
import org.json.JSONException; import org.json.JSONException;
import java.io.IOException; import java.io.*;
import java.text.SimpleDateFormat;
import java.time.Instant; import java.time.Instant;
import java.util.Date;
import java.util.Properties; import java.util.Properties;
import static com.example.listify.SplashActivity.showSplash; import static com.example.listify.SplashActivity.showSplash;
@ -33,6 +44,9 @@ import static com.example.listify.SplashActivity.showSplash;
public class MainActivity extends AppCompatActivity implements CreateListDialogFragment.OnNewListListener { public class MainActivity extends AppCompatActivity implements CreateListDialogFragment.OnNewListListener {
private AppBarConfiguration mAppBarConfiguration; private AppBarConfiguration mAppBarConfiguration;
public static AuthManager am = new AuthManager(); public static AuthManager am = new AuthManager();
private File newImageFileLocation = null;
private final int CAMERA_CAPTURE = 1;
private final int IMAGE_SELECT = 2;
@Override @Override
public void onBackPressed() { public void onBackPressed() {
@ -151,12 +165,62 @@ public class MainActivity extends AppCompatActivity implements CreateListDialogF
} }
//------------------------------------------------------------------------------------------// //------------------------------------------------------------------------------------------//
setContentView(R.layout.activity_main); setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar); Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar); setSupportActionBar(toolbar);
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);
TextView emailView = navigationView.getHeaderView(0).findViewById(R.id.textViewEmailSidebar);
emailView.setText(am.getEmail());
Properties configs = new Properties();
try {
configs = AuthManager.loadProperties(this, "android.resource://" + getPackageName() + "/raw/auths.json");
} catch (IOException | JSONException e) {
e.printStackTrace();
}
Requestor requestor = new Requestor(am, configs.getProperty("apiKey"));
SynchronousReceiver<Picture> profilePictureReceiver = new SynchronousReceiver<>();
ImageView profilePictureView = navigationView.getHeaderView(0).findViewById(R.id.imageViewProfilePicture);
try {
requestor.getObject("profile", Picture.class, profilePictureReceiver);
profilePictureView.setImageURI(Uri.fromFile(saveImage(profilePictureReceiver.await().getBase64EncodedImage())));
} catch (Exception e) {
e.printStackTrace();
}
profilePictureView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
// builder.setView(navigationView.getHeaderView(0));
builder.setTitle("Change picture");
builder.setMessage("Please select a method to add a new profile picture.");
builder.setCancelable(true);
builder.setPositiveButton("Take picture", (dialog, which) -> {
Intent takePicture = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File imageFileLocation = getOutputImageFile();
Log.i("Profile Picture", "New image file at " + imageFileLocation.getAbsolutePath());
newImageFileLocation = imageFileLocation;
Uri imageUri = FileProvider.getUriForFile(
MainActivity.this,
BuildConfig.APPLICATION_ID + ".provider",
imageFileLocation);
takePicture.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(takePicture, CAMERA_CAPTURE);
});
builder.setNegativeButton("Select picture", (dialog, which) -> {
Intent pickPhoto = new Intent(Intent.ACTION_PICK,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(pickPhoto, IMAGE_SELECT);
});
builder.setNeutralButton("Cancel", (dialog, which) -> {
});
AlertDialog dialog = builder.create();
dialog.show();
}
});
mAppBarConfiguration = new AppBarConfiguration.Builder( mAppBarConfiguration = new AppBarConfiguration.Builder(
R.id.nav_home, R.id.nav_profile, R.id.nav_logout) R.id.nav_home, R.id.nav_profile, R.id.nav_logout)
.setDrawerLayout(drawer) .setDrawerLayout(drawer)
@ -176,6 +240,144 @@ public class MainActivity extends AppCompatActivity implements CreateListDialogF
}); });
} }
//From: https://stackoverflow.com/questions/30005815/convert-encoded-base64-image-to-file-object-in-android
private File saveImage(final String imageData) throws IOException {
final byte[] imgBytesData = android.util.Base64.decode(imageData,
android.util.Base64.DEFAULT);
final File file = File.createTempFile("profilePicture", null, this.getCacheDir());
final FileOutputStream fileOutputStream;
try {
fileOutputStream = new FileOutputStream(file);
} catch (FileNotFoundException e) {
e.printStackTrace();
return null;
}
final BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(
fileOutputStream);
try {
bufferedOutputStream.write(imgBytesData);
} catch (IOException e) {
e.printStackTrace();
return null;
} finally {
try {
bufferedOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return file;
}
protected void onActivityResult (int requestCode,
int resultCode,
Intent data) {
Uri selectedImage = null;
Properties configs = new Properties();
try {
configs = AuthManager.loadProperties(this, "android.resource://" + getPackageName() + "/raw/auths.json");
} catch (IOException | JSONException e) {
e.printStackTrace();
}
Requestor requestor = new Requestor(am, configs.getProperty("apiKey"));
switch (requestCode){
case CAMERA_CAPTURE:
Log.i("Profile Picture", "Pulling image file at " + this.newImageFileLocation.getAbsolutePath());
selectedImage = Uri.fromFile(this.newImageFileLocation);
try {
requestor.putObject(new Picture(fileToString(this.newImageFileLocation)));
} catch (JSONException | IOException jsonException) {
jsonException.printStackTrace();
}
break;
case IMAGE_SELECT:
if ((data == null) || (data.getData() == null)) {
return;
}
selectedImage = data.getData();
try {
requestor.putObject(new Picture(fileToString(new File(getRealPathFromUri(this, selectedImage)))));
} catch (JSONException | IOException exception) {
exception.printStackTrace();
}
break;
}
MainActivity.super.onActivityResult(requestCode, resultCode, data);
if (selectedImage == null) {
return;
}
NavigationView navigationView = findViewById(R.id.nav_view);
ImageView profilePicture = navigationView.getHeaderView(0).findViewById(R.id.imageViewProfilePicture);
profilePicture.setImageURI(selectedImage);
}
//From: https://stackoverflow.com/questions/20028319/how-to-convert-content-media-external-images-media-y-to-file-storage-sdc
private static String getRealPathFromUri(Context context, Uri contentUri) {
Cursor cursor = null;
try {
String[] proj = { MediaStore.Images.Media.DATA };
cursor = context.getContentResolver().query(contentUri, proj, null, null, null);
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
return cursor.getString(column_index);
} finally {
if (cursor != null) {
cursor.close();
}
}
}
//From: https://stackoverflow.com/questions/27784230/convert-a-file-100mo-in-base64-on-android
private String fileToString(File toStringify) throws IOException {
InputStream inputStream = new FileInputStream(toStringify.getAbsolutePath());
byte[] buffer = new byte[8192];
int bytesRead;
ByteArrayOutputStream output = new ByteArrayOutputStream();
Base64OutputStream output64 = new Base64OutputStream(output, Base64.DEFAULT);
try {
while ((bytesRead = inputStream.read(buffer)) != -1) {
output64.write(buffer, 0, bytesRead);
}
} catch (IOException e) {
e.printStackTrace();
}
output64.close();
return output.toString();
}
//getOutputImageFile from https://developer.android.com/guide/topics/media/camera
private static File getOutputImageFile(){
// To be safe, you should check that the SDCard is mounted
// using Environment.getExternalStorageState() before doing this.
File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES), "MyCameraApp");
// This location works best if you want the created images to be shared
// between applications and persist after your app has been uninstalled.
// Create the storage directory if it does not exist
if (! mediaStorageDir.exists()){
if (! mediaStorageDir.mkdirs()){
Log.d("File creation", "failed to create directory");
return null;
}
}
// Create a media file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
File mediaFile = new File(mediaStorageDir.getPath() + File.separator +
"IMG_"+ timeStamp + ".jpg");
Log.i("File creation", mediaFile.toString());
return mediaFile;
}
@Override @Override
public boolean onSupportNavigateUp() { public boolean onSupportNavigateUp() {
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment); NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
@ -214,5 +416,8 @@ public class MainActivity extends AppCompatActivity implements CreateListDialogF
Toast.makeText(this, "An error occurred", Toast.LENGTH_LONG).show(); Toast.makeText(this, "An error occurred", Toast.LENGTH_LONG).show();
e.printStackTrace(); e.printStackTrace();
} }
} }
} }

View File

@ -0,0 +1,71 @@
package com.example.listify;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.DialogFragment;
public class RenameListDialogFragment extends DialogFragment {
public interface OnRenameListListener {
void sendRenameListName(String name);
}
public OnRenameListListener onRenameListListener;
EditText etRenameListName;
public RenameListDialogFragment() {}
@Override
public Dialog onCreateDialog(final Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
// Get the layout inflater
LayoutInflater inflater = requireActivity().getLayoutInflater();
// Inflate and set the layout for the dialog
// Pass null as the parent view because its going in the dialog layout
View root = inflater.inflate(R.layout.dialog_rename_list, null);
builder.setView(root)
// Add action buttons
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
onRenameListListener.sendRenameListName(etRenameListName.getText().toString());
}
})
.setNegativeButton("cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
RenameListDialogFragment.this.getDialog().cancel();
}
});
etRenameListName = (EditText) root.findViewById(R.id.et_renamed_list_name);
return builder.create();
}
// Required to extend DialogFragment
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
try {
onRenameListListener = (OnRenameListListener) getTargetFragment();
if (onRenameListListener == null) {
onRenameListListener = (OnRenameListListener) getActivity();
}
} catch (ClassCastException e) {
Log.e("CreateListDialogFragment", "onAttach: ClassCastException: " + e.getMessage());
}
}
}

View File

@ -79,7 +79,7 @@ public class ShareeSwipeableAdapter extends BaseAdapter {
holder.swipeLayout = (SwipeRevealLayout)convertView.findViewById(R.id.swipe_layout); holder.swipeLayout = (SwipeRevealLayout)convertView.findViewById(R.id.swipe_layout);
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.textView = (TextView) convertView.findViewById(R.id.shopping_list_name);
convertView.setTag(holder); convertView.setTag(holder);
@ -101,12 +101,12 @@ public class ShareeSwipeableAdapter extends BaseAdapter {
} }
}); });
holder.shareList.setOnClickListener(new View.OnClickListener() { // holder.shareList.setOnClickListener(new View.OnClickListener() {
@Override // @Override
public void onClick(View v) { // public void onClick(View v) {
//
} // }
}); // });
holder.frontView.setOnClickListener(new View.OnClickListener() { holder.frontView.setOnClickListener(new View.OnClickListener() {
@Override @Override

View File

@ -80,7 +80,7 @@ public class ShoppingListsSwipeableAdapter extends BaseAdapter {
holder.swipeLayout = (SwipeRevealLayout)convertView.findViewById(R.id.swipe_layout); holder.swipeLayout = (SwipeRevealLayout)convertView.findViewById(R.id.swipe_layout);
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.listName = (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); holder.itemCount = (TextView) convertView.findViewById(R.id.shopping_list_item_count);
@ -101,7 +101,11 @@ public class ShoppingListsSwipeableAdapter extends BaseAdapter {
holder.listName.setText(curList.getName()); holder.listName.setText(curList.getName());
} }
holder.itemCount.setText(String.format("%d items", curList.getEntries().length)); if (curList.getEntries() != null) {
holder.itemCount.setText(String.format("%d items", curList.getEntries().length));
} else {
holder.itemCount.setText("0 items");
}
holder.deleteList.setOnClickListener(new View.OnClickListener() { holder.deleteList.setOnClickListener(new View.OnClickListener() {
@Override @Override
@ -139,9 +143,8 @@ 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 and list name // Send the selected list
listPage.putExtra("listID", curList.getListID()); listPage.putExtra("selectedList", curList);
listPage.putExtra("listName", curList.getName());
activity.startActivity(listPage); activity.startActivity(listPage);
} }
@ -154,7 +157,7 @@ public class ShoppingListsSwipeableAdapter extends BaseAdapter {
SwipeRevealLayout swipeLayout; SwipeRevealLayout swipeLayout;
View frontView; View frontView;
View deleteList; View deleteList;
View shareList; // View shareList;
TextView listName; TextView listName;
TextView itemCount; TextView itemCount;
} }

View File

@ -1,8 +1,9 @@
package com.example.listify.data; package com.example.listify.data;
import java.io.Serializable;
import java.util.Arrays; import java.util.Arrays;
public class List { public class List implements Serializable {
Integer listID; Integer listID;
String name; String name;
String owner; String owner;

View File

@ -1,6 +1,8 @@
package com.example.listify.data; package com.example.listify.data;
public class ListEntry { import java.io.Serializable;
public class ListEntry implements Serializable {
Integer listID; Integer listID;
Integer productID; Integer productID;
Integer quantity; Integer quantity;

View File

@ -11,6 +11,7 @@ public class ListShare {
String shareWithEmail; String shareWithEmail;
final ListShare[] other; final ListShare[] other;
Integer permissionLevel; Integer permissionLevel;
Integer uiPosition;
private static final Map<Integer, String> keysToPerms; private static final Map<Integer, String> keysToPerms;
static { static {
@ -24,11 +25,17 @@ public class ListShare {
keysToPerms = Collections.unmodifiableMap(keysToPermsTemp); keysToPerms = Collections.unmodifiableMap(keysToPermsTemp);
} }
public ListShare(Integer listID, String shareWithEmail, Integer permissionLevel, ListShare[] other) { public ListShare(Integer listID, String shareWithEmail, Integer permissionLevel, Integer uiPosition, ListShare[] other) {
this.listID = listID; this.listID = listID;
this.shareWithEmail = shareWithEmail; this.shareWithEmail = shareWithEmail;
this.permissionLevel = permissionLevel; this.permissionLevel = permissionLevel;
this.other = other; this.other = other;
this.uiPosition = uiPosition;
}
public ListShare(Integer listID, String shareWithEmail, String permissionsRaw, Integer uiPosition, ListShare[] other) {
this(listID, shareWithEmail, permissionsRaw, other);
this.uiPosition = uiPosition;
} }
public ListShare(Integer listID, String shareWithEmail, String permissionsRaw, ListShare[] other) { public ListShare(Integer listID, String shareWithEmail, String permissionsRaw, ListShare[] other) {
@ -42,6 +49,7 @@ public class ListShare {
permissionLevel *= keytoPermEntry.getKey(); permissionLevel *= keytoPermEntry.getKey();
} }
} }
this.uiPosition = -1;
} }
@Override @Override

View File

@ -0,0 +1,24 @@
package com.example.listify.data;
public class Picture {
String base64EncodedImage;
public Picture(String base64EncodedImage) {
this.base64EncodedImage = base64EncodedImage;
}
@Override
public String toString() {
return "Picture{" +
"base64EncodedImage='" + base64EncodedImage + '\'' +
'}';
}
public String getBase64EncodedImage() {
return base64EncodedImage;
}
public void setBase64EncodedImage(String base64EncodedImage) {
this.base64EncodedImage = base64EncodedImage;
}
}

View File

@ -0,0 +1,22 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Rename List"
android:layout_gravity="center"
android:textSize="20sp"
android:layout_marginTop="5dp"/>
<EditText
android:id="@+id/et_renamed_list_name"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginHorizontal="15dp"
android:singleLine="true"
android:hint="@string/new_list_name"/>
</LinearLayout>

View File

@ -1,8 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/Profile_Section"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="10dp" android:layout_height="@dimen/nav_header_height"
android:background="@drawable/side_nav_bar" android:background="@drawable/side_nav_bar"
android:gravity="bottom" android:gravity="bottom"
android:orientation="vertical" android:orientation="vertical"
@ -12,24 +13,29 @@
android:paddingBottom="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin"
android:theme="@style/ThemeOverlay.AppCompat.Dark"> android:theme="@style/ThemeOverlay.AppCompat.Dark">
<!-- <ImageView-->
<!-- android:id="@+id/imageView"-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:contentDescription="@string/nav_header_desc"-->
<!-- android:paddingTop="@dimen/nav_header_vertical_spacing"-->
<!-- app:srcCompat="@mipmap/ic_launcher_round" />-->
<!-- <TextView--> <androidx.cardview.widget.CardView
<!-- android:layout_width="match_parent"--> android:layout_width="96dp"
<!-- android:layout_height="wrap_content"--> android:layout_height="96dp"
<!-- android:paddingTop="@dimen/nav_header_vertical_spacing"--> android:elevation="12dp"
<!-- android:text="@string/nav_header_title"--> android:id="@+id/view2"
<!-- android:textAppearance="@style/TextAppearance.AppCompat.Body1" />--> app:cardCornerRadius="48dp"
android:layout_gravity="center_horizontal"
android:background="#80000000">
<ImageView
android:layout_height="96dp"
android:layout_width="match_parent"
android:id="@+id/imageViewProfilePicture"
android:src="@raw/ic_launcher_round"
android:scaleType="centerCrop"
android:background="#80000000"/>
</androidx.cardview.widget.CardView>
<!-- <TextView-->
<!-- android:id="@+id/textView"--> <TextView
<!-- android:layout_width="wrap_content"--> android:id="@+id/textViewEmailSidebar"
<!-- android:layout_height="wrap_content"--> android:layout_width="wrap_content"
<!-- android:text="@string/nav_header_subtitle" />--> android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="@string/nav_header_subtitle" />
</LinearLayout> </LinearLayout>

View File

@ -17,12 +17,12 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<ImageView <!-- <ImageView-->
android:id="@+id/share_list" <!-- android:id="@+id/share_list"-->
android:src="@drawable/ic_baseline_share_24" <!-- android:src="@drawable/ic_baseline_share_24"-->
android:layout_width="50dp" <!-- android:layout_width="50dp"-->
android:layout_height="50dp" <!-- android:layout_height="50dp"-->
android:background="@color/colorAccent"/> <!-- android:background="@color/colorAccent"/>-->
<ImageView <ImageView
android:id="@+id/delete_list" android:id="@+id/delete_list"

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="." />
</paths>