ca.ualberta.trinkettrader.User.User.java Source code

Java tutorial

Introduction

Here is the source code for ca.ualberta.trinkettrader.User.User.java

Source

// Copyright 2015 Andrea McIntosh, Dylan Ashley, Anju Eappen, Jenna Hatchard, Kirsten Svidal, Raghav Vamaraju
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package ca.ualberta.trinkettrader.User;

import android.content.pm.PackageManager;
import android.location.Location;
import android.util.Log;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Observer;

import ca.ualberta.trinkettrader.ApplicationState;
import ca.ualberta.trinkettrader.Elastic.ElasticStorable;
import ca.ualberta.trinkettrader.Elastic.SearchHit;
import ca.ualberta.trinkettrader.Friends.FriendsList;
import ca.ualberta.trinkettrader.Friends.TrackedFriends.TrackedFriendsList;
import ca.ualberta.trinkettrader.Inventory.Inventory;
import ca.ualberta.trinkettrader.Inventory.Trinket.Pictures.Picture;
import ca.ualberta.trinkettrader.Inventory.Trinket.Pictures.PictureDirectoryManager;
import ca.ualberta.trinkettrader.Inventory.Trinket.Trinket;
import ca.ualberta.trinkettrader.NotificationManager;
import ca.ualberta.trinkettrader.Trades.TradeManager;
import ca.ualberta.trinkettrader.User.Profile.UserProfile;

/**
 * Abstract class representing a user of the app. This class is implemented in the app by
 * {@link LoggedInUser LoggedInUser}, who represents the current user of the app on a particular device.
 * This class mainly acts as a container for all of the various classes that make up a user.
 * <p/>
 * In the system, a user has a list of {@link ca.ualberta.trinkettrader.Friends.Friend Friends} contained
 * in a {@link FriendsList FriendsList}, a {@link TrackedFriendsList TrackedFriendsList} for their
 * tracked friends, an {@link Inventory Inventory} which contains the
 * {@link ca.ualberta.trinkettrader.Inventory.Trinket.Trinket Trinkets} they own and want to be available
 * for trading, a {@link NotificationManager NotificationManager} to notify them if another user wishes to
 * trade with them, a {@link TradeManager TradeManager} to handle their trades with their friends, and
 * a user profile which stores and displays their information.
 */
public class User extends ElasticStorable implements ca.ualberta.trinkettrader.Observable {

    private static final String RESOURCE_URL = "http://cmput301.softwareprocess.es:8080/cmput301f15t01/user/";
    private static final String SEARCH_URL = "http://cmput301.softwareprocess.es:8080/cmput301f15t01/user/_search";
    private static final String TAG = "User";
    protected Boolean needToSave;
    protected FriendsList friendsList;
    protected Inventory inventory;
    protected NotificationManager notificationManager;
    protected TrackedFriendsList trackedFriendsList;
    protected TradeManager tradeManager;
    protected UserProfile profile;
    private ArrayList<Observer> observers;

    /**
     * Public constructor for user: initializes all attribute classes as empty classes with no
     * active data.  This constructor is mainly used for testing purposes as it implies that no information is
     * available about the user, not even an email address.  By default a new user needs to be saved to
     * the network, so the needToSave attribute is set to true.
     */
    public User() {
        this.friendsList = new FriendsList();
        this.inventory = new Inventory();
        this.needToSave = Boolean.TRUE;
        this.notificationManager = new NotificationManager();
        this.profile = new UserProfile();
        this.trackedFriendsList = new TrackedFriendsList();
        this.tradeManager = new TradeManager();
    }

    /**
     * A public constructor for User in the case where the user's details are known for all its
     * attribute classes.  This constructor would be called for a user already on the system, whose
     * data can be pulled from Elastic Search or the phone's local data.
     *
     * @param friendsList         - a FriendsList of the user's friends
     * @param inventory           - the user's Inventory with their trinkets
     * @param notificationManager - a notification manager to notify the user if a new trade has been offered to them
     * @param profile             - a profile storing their information
     * @param trackedFriends      - a FriendsList which stores the Friends the user wishes to track
     * @param tradeManager        - a TradeManager for handling the user's trades
     */
    public User(FriendsList friendsList, Inventory inventory, NotificationManager notificationManager,
            UserProfile profile, TrackedFriendsList trackedFriends, TradeManager tradeManager) {
        this.friendsList = friendsList;
        this.inventory = inventory;
        this.needToSave = Boolean.TRUE;
        this.notificationManager = notificationManager;
        this.profile = profile;
        this.trackedFriendsList = trackedFriends;
        this.tradeManager = tradeManager;
        this.tradeManager.setUsername(this.profile.getUsername());
    }

    /**
     * Constructor that uses the email a user enters to register in the {@link ca.ualberta.trinkettrader.LoginActivity LoginActivity}.
     * This email is then used to initialize their TradeManager.  All other user attributes are initialized
     * as empty.  This constructor is used when a new user logs in for the first time, as there is no information
     * in the system about the user except the email they entered to register.  By default a new user needs to be saved to
     * the network, so the needToSave attribute is set to true.
     *
     * @param email - the user's email address.  Taken from the email they enter into the LoginActivity
     */
    public User(String email) {
        super();
        this.tradeManager = new TradeManager();
        this.tradeManager.setUsername(email);
    }

    /**
     * Adds the specified observer to the list of observers. If it is already
     * registered, it is not added a second time.
     *
     * @param observer the Observer to add.
     */
    @Override
    public void addObserver(Observer observer) {
        observers.add(observer);
    }

    /**
     * Removes the specified observer from the list of observers. Passing null
     * won't do anything.
     *
     * @param observer the observer to remove.
     */
    @Override
    public synchronized void deleteObserver(Observer observer) {
        observers.remove(observer);
    }

    /**
     * If {@code hasChanged()} returns {@code true}, calls the {@code update()}
     * method for every observer in the list of observers using null as the
     * argument. Afterwards, calls {@code clearChanged()}.
     * <p/>
     * Equivalent to calling {@code notifyObservers(null)}.
     */
    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.notify();
        }
    }

    /**
     * Returns a boolean indicating if the {@link ca.ualberta.trinkettrader.User.User User} with this
     * contact information needs to be saved to the network.  If the contact information of the user has
     * been changed then needToSave is set to true and the user needs to be saved.  Otherwise, this should
     * return false indicating that the user does not need to be saved.
     *
     * @return Boolean - if true then the User with this contact information needs to be re-saved to
     * the network, if false then there is nothing new that needs to be sav
     */

    public Boolean getNeedToSave() {
        //TODO: need to implement needToSave for friendslist as well...
        return this.needToSave | this.profile.getNeedToSave() | this.inventory.getNeedToSave();
    }

    /**
     * Sets a boolean indicating if the {@link ca.ualberta.trinkettrader.User.User User} with this
     * contact information needs to be saved to the network.  If the contact information of the user has
     * been changed then needToSave should be set to true meaning the user needs to be saved.  Otherwise, this should
     * be set to false indicating that the user does not need to be saved.
     *
     * @return Boolean - if true then the User with this contact information needs to be re-saved to
     * the network, if false then there is nothing new that needs to be sav
     */
    protected void setNeedToSave(Boolean needToSave) {
        this.needToSave = needToSave;
    }

    /**
     * Returns the tag section of the elastic search entry's URL.  Trades, photos, and users are all
     * saved separately in elastic search within their own tags.  This method will return "User" as the
     * tag for {@link ca.ualberta.trinkettrader.User.User User}.  Implementation of the {@link ElasticStorable ElasticStorable}
     * method.
     *
     * @return String - the tag used to generate the url for each type of item stored in elastic search
     */
    @Override
    public String getTag() {
        return TAG;
    }

    /**
     * Returns the base URL each class of ElasticStorable object will be saved to.  Trades, photos, and users are all
     * saved separately in elastic search within their own tags.  Each individual object is then stored with
     * their own unique ids.  The resource URL specifies the base URL which is composed of the server
     * URL ("http://cmput301.softwareprocess.es:8080/cmput301f15t01/") followed by the tag of the particular class,
     * "User" as the tag for {@link ca.ualberta.trinkettrader.User.User User} objects.  Implementation of the
     * {@link ElasticStorable ElasticStorable} method.
     *
     * @return String - base URL (no unique ID) for an object being stored to elastic search
     */
    @Override
    public String getResourceUrl() {
        return RESOURCE_URL;
    }

    /**
     * * Returns a unique ID for each user's objects being stored to Elastic Search.  This is appended to the
     * resource url to save each object to the elastic search server.  For a particular user of the
     * system associated with a unique email address, this method will always return the same UID.  Implementation of the
     * {@link ElasticStorable ElasticStorable} method.
     *
     * @return - an id unique to each user of the system for saving objects to elastic search
     */
    @Override
    public String getUid() {
        // Vasyl Keretsman; http://stackoverflow.com/questions/15429257/how-to-convert-byte-array-to-hexstring-in-java; 2015-11-28
        final StringBuilder builder = new StringBuilder();
        for (byte b : this.profile.getEmail().getBytes()) {
            builder.append(String.format("%02x", b));
        }
        String uid = builder.toString();
        return uid;
    }

    /**
     * Searches for ElasticStorable objects on the network matching the attribute and attribute
     * value pairs. Calls onSearchResult with the results when the search completes.
     *
     * @param postParameters pairs of attributes to use when searching
     * @param type
     * @throws IOException
     */
    @Override
    public <T extends ElasticStorable> void searchOnNetwork(ArrayList<NameValuePair> postParameters, Class<T> type)
            throws IOException {
    }

    /**
     * Attempts to find this object on the elasticsearch server. If the object
     * cannot be found then pushes the current version to the server.  Each object is searched for using
     * its know URL, which is composed of its resource url ({@link User#getResourceUrl()}) and an ID
     * unique to the user associated with the object ({@link User#getUid()}).  Implementation of the
     * {@link ElasticStorable ElasticStorable} method.
     *
     * @param type - class of the object being saved
     * @throws IOException
     */
    @Override
    public <T extends ElasticStorable> void getFromNetwork(Class<T> type) throws IOException {
        // Alexis C.; http://stackoverflow.com/questions/27253555/com-google-gson-internal-linkedtreemap-cannot-be-cast-to-my-class; 2015-11-28
        // Android-Droid; http://stackoverflow.com/questions/8120220/how-to-use-parameters-with-httppost; 2015-11-18
        final HttpGet getRequest = new HttpGet(this.getResourceUrl() + this.getUid());
        final HttpClient httpClient = new DefaultHttpClient();
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    HttpResponse response = httpClient.execute(getRequest);
                    Log.i("HttpResponse", response.getStatusLine().toString());
                    Type searchHitType = new TypeToken<SearchHit<LoggedInUser>>() {
                    }.getType();
                    SearchHit<LoggedInUser> returned = new Gson()
                            .fromJson(new InputStreamReader(response.getEntity().getContent()), searchHitType);
                    onGetResult(returned.getSource());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();
    }

    /**
     * Method called after getFromNetwork gets a response. Implementation of the
     * {@link ElasticStorable ElasticStorable} method.
     *
     * When a User is retrieved from the network, a new User object is created and its attributes
     * are set to the data returned by elastic search.  This is used when a user with data existing on
     * the server logs in again, so that their data and setting from previous uses of the system is
     * restored.
     *
     * @param result result of getFromNetwork, a User object
     */
    @Override
    public <T extends ElasticStorable> void onGetResult(T result) {
        if (result != null) {
            User user = (User) result;
            this.setFriendsList(user.getFriendsList());
            this.setInventory(user.getInventory());
            for (Trinket trinket : user.getInventory()) {
                ArrayList<Picture> pictures = new ArrayList<>();
                for (String filename : trinket.getPictureFileNames()) {
                    try {
                        Picture picture = new Picture(filename,
                                new PictureDirectoryManager(ApplicationState.getInstance().getActivity()),
                                ApplicationState.getInstance().getActivity());
                        if (this.getProfile().getArePhotosDownloadable()) {
                            picture.loadPicture();
                        }
                        pictures.add(picture);
                    } catch (IOException | PackageManager.NameNotFoundException e) {
                        e.printStackTrace();
                    }
                }
                trinket.setPictures(pictures);
            }
            this.setNotificationManager(user.getNotificationManager());
            this.setProfile(user.getProfile());
            this.setTrackedFriends(user.getTrackedFriendsList());
            this.setTradeManager(user.getTradeManager());
        } else {
            try {
                this.saveToNetwork();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * Method called after searchOnNetwork gets a response. Implementation of the
     * {@link ElasticStorable ElasticStorable} method.  Current app functionality does not require
     * searching for users.
     *
     * @param result result of searchOnNetwork
     */
    @Override
    public <T extends ElasticStorable> void onSearchResult(Collection<T> result) {
    }

    /**
     * Returns user's UserProfile, which is an object containing the information about a user and settings
     * for their account.
     *
     * @return UserProfile - the user's UserProfile object
     */

    public UserProfile getProfile() {
        return profile;
    }

    /**
     * Sets user's {@link UserProfile UserProfile}, which is an object containing the information about a user and settings
     * for their account.  This method is called after an existing user's data is pulled off the network, so
     * that the user's existing details are repopulated.  After setting this field then needToSave is set to
     * true so that the change will be saved to the network.
     *
     * @param profile
     */
    public void setProfile(UserProfile profile) {
        this.profile = profile;
        this.needToSave = Boolean.TRUE;
    }

    /**
     * Sets user's {@link TrackedFriendsList TrackedFriendsList}. This method is called after an existing user's data
     * is pulled off the network, so that the user's existing details are repopulated.  After setting this field then
     * needToSave is set to true so that the change will be saved to the network.  The list may be empty.
    \    *
     * @param trackedFriendsList - an user's TrackedFriendsList
     */
    public void setTrackedFriends(TrackedFriendsList trackedFriendsList) {
        this.trackedFriendsList = trackedFriendsList;
    }

    /**
     * Returns user's {@link TrackedFriendsList TrackedFriendsList}.  This method is used to populate
     * which friends are displayed in the
     * {@link ca.ualberta.trinkettrader.Friends.TrackedFriends.TrackedFriendsListActivity TrackedFriendsListActivity}.
     * The list may be empty.
     *
     * @return TrackedFriendsList - an user's TrackedFriendsList
     */
    public TrackedFriendsList getTrackedFriendsList() {
        return trackedFriendsList;
    }

    /**
     * Returns User's {@link FriendsList FriendsList}.  This method is used to populate
     * the friends displayed in the {@link ca.ualberta.trinkettrader.Friends.FriendsListActivity FriendsListActivity}.
     * The list may be empty.
     *
     * @return FriendList - a user's FriendsList
     */

    public FriendsList getFriendsList() {
        return friendsList;
    }

    /**
     * Sets User's {@link FriendsList FriendsList}.  This method is called after an existing user's data
     * is pulled off the network, so that the user's existing details are repopulated.  After setting this field then
     * needToSave is set to true so that the change will be saved to the network.  The list may be empty.
     *
     * @param friendsList - a user's FriendsList
     */
    public void setFriendsList(FriendsList friendsList) {
        this.friendsList = friendsList;
        this.needToSave = Boolean.TRUE;
    }

    /**
     * Returns user's {@link Inventory Inventory}.  This method is used to populate
     * the trinkets displayed in the {@link ca.ualberta.trinkettrader.Inventory.InventoryActivity InventoryActivity}.
     * The Inventory may be empty.
     *
     * @return Inventory - a list of the Trinkets a user has added to their account
     */
    public Inventory getInventory() {
        return inventory;
    }

    /**
     * Sets user's {@link Inventory Inventory}.  This method is called after an existing user's data
     * is pulled off the network, so that the user's existing details are repopulated.  After setting this field then
     * needToSave is set to true so that the change will be saved to the network.  The Inventory may be empty.
     *
     * @param inventory - a list of the Trinkets a user has added to their account
     */
    public void setInventory(Inventory inventory) {
        this.inventory = inventory;
        this.needToSave = Boolean.TRUE;
    }

    /**
     * Returns user's {@link NotificationManager NotificationManager}.  This method is used to get the NotificationManager
     * object that handles notifications of new trades for the user, displayed in
     * {@link ca.ualberta.trinkettrader.Trades.TradesActivity TradesActivity}.
     *
     * @return NotificationManager - object holding and managing notifications for the user about new trades
     */
    public NotificationManager getNotificationManager() {
        return notificationManager;
    }

    /**
     * Sets user's {@link NotificationManager NotificationManager}.  This method is called after an existing user's data
     * is pulled off the network, so that the user's existing details are repopulated.
     *
     * @param notificationManager -  object holding and managing notifications for the user about new trades
     */
    public void setNotificationManager(NotificationManager notificationManager) {
        this.notificationManager = notificationManager;
    }

    /**
     * Returns user's {@link TradeManager TradeManager}.  This method is used to get the TradeManager
     * object that manages the trades a user is engaged in with other users, and the records of the user's
     * previous trades.
     *
     * @return TradeManager
     */
    public TradeManager getTradeManager() {
        return tradeManager;
    }

    /**
     * Sets user's {@link TradeManager TradeManager}.  This method is called after an existing user's data
     * is pulled off the network, so that the user's existing details are repopulated.
     *
     * @param tradeManager
     */
    public void setTradeManager(TradeManager tradeManager) {
        this.tradeManager = tradeManager;
    }

    /**
     * Returns the base URL each class of ElasticStorable object can be searched on the server with.  Trades, photos,
     * and users are all saved separately in elastic search within their own tags.  Each individual object is then stored with
     * their own unique ids.  The search URL specifies the base URL which is composed of the server
     * URL ("http://cmput301.softwareprocess.es:8080/cmput301f15t01/") followed by the tag of the particular class,
     * "User" as the tag for {@link ca.ualberta.trinkettrader.User.User User} objects, and then followed
     * by "/_search/".
     *
     * @return - url for searching Users on elastic search
     */
    @Override
    public String getSearchUrl() {
        return SEARCH_URL;
    }

    /**
     * Returns the default location specifying where a trinket is located.  The location is expressed as
     * a latitude/longitude pair.  This attribute will allow other users to see if it will be convenient for
     * them to collect a trinket if they choose to trade for it.  By default the default location is
     * the phone's location as returned by the phone's GPS, but the user may set it to another value.
     *
     * @return Location - the location that a {@link ca.ualberta.trinkettrader.Inventory.Trinket.Trinket Trinket's}
     * location will be set to by default.
     */
    public Location getDefaultLocation() {
        return this.profile.getDefaultLocation();
    }

    /**
     * Sets the default location specifying where a trinket is located.  The location is expressed as
     * a latitude/longitude pair.  This attribute will allow other users to see if it will be convenient for
     * them to collect a trinket if they choose to trade for it.  By default the default location is
     * the phone's location as returned by the phone's GPS, but the user may set it to another value.
     *
     * @param defaultLocation - the location that a {@link ca.ualberta.trinkettrader.Inventory.Trinket.Trinket Trinket's}
     * location will be set to by default.
     */
    public void setDefaultLocation(Location defaultLocation) {
        this.profile.setDefaultLocation(defaultLocation);
    }

    /**
     * Sets the user's email address.  The user's email address is how they are identified in the
     * app and how other users will be able to search for and interact with them.  It is also used to email
     * confirmation of completed trades to the users involved in the trade.  At minimum, all users must
     * have an email address attached to them.  After setting this field then needToSave is
     * set to true so that the change will be saved to the network.
     *
     * This method will be used when the user enters their email in the {@link ca.ualberta.trinkettrader.LoginActivity LoginActivity}
     * and logs in for the first time.
     *
     * @param email - the user's email address they enter to log in
     */
    public void setEmail(String email) {
        this.getProfile().setEmail(email);
    }

    public void getFromNetwork() throws IOException {
        // Alexis C.; http://stackoverflow.com/questions/27253555/com-google-gson-internal-linkedtreemap-cannot-be-cast-to-my-class; 2015-11-28
        // Android-Droid; http://stackoverflow.com/questions/8120220/how-to-use-parameters-with-httppost; 2015-11-18
        final HttpGet getRequest = new HttpGet(this.getResourceUrl() + this.getUid());
        final HttpClient httpClient = new DefaultHttpClient();
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    HttpResponse response = httpClient.execute(getRequest);
                    Log.i("HttpResponse", response.getStatusLine().toString());
                    Type searchHitType = new TypeToken<SearchHit<User>>() {
                    }.getType();
                    SearchHit<User> returned = new Gson()
                            .fromJson(new InputStreamReader(response.getEntity().getContent()), searchHitType);
                    onGetResult(returned.getSource());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();
    }
}