2018-SWENG/2018-SWENG-Radius

View on GitHub
app/src/main/java/ch/epfl/sweng/radius/home/HomeFragment.java

Summary

Maintainability
A
2 hrs
Test Coverage
package ch.epfl.sweng.radius.home;

import android.Manifest;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.os.Bundle;
import android.support.design.widget.TabLayout;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.content.ContextCompat;
import android.support.v4.view.ViewPager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.Toast;

import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.MapView;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.CircleOptions;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;

import ch.epfl.sweng.radius.R;
import ch.epfl.sweng.radius.database.DBLocationObserver;
import ch.epfl.sweng.radius.database.Database;
import ch.epfl.sweng.radius.database.MLocation;
import ch.epfl.sweng.radius.database.OthersInfo;
import ch.epfl.sweng.radius.database.UserInfo;
import ch.epfl.sweng.radius.utils.MapUtility;
import ch.epfl.sweng.radius.utils.NotificationUtility;
import ch.epfl.sweng.radius.utils.TabAdapter;

/**
 * This Class represent the HomeFragment and handle all actions  for the map and people/topics nearby
 */
public class HomeFragment extends Fragment implements OnMapReadyCallback, DBLocationObserver {

    //constants
    private static final String TAG = "HomeFragment";
    private static float ZOOM = 13f/2;

    //properties
    private static GoogleMap mobileMap; //make sure the fragment doesn't crash if the map is null
    private static MapView mapView;
    private static CircleOptions radiusOptions;
    private static double radius;
    private static LatLng coord;
    private TabAdapter adapter;
    private TabLayout tabLayout;
    private ViewPager viewPager;
    private ImageView zoomInButton;

    //testing
    public static MapUtility mapListener = MapUtility.getMapInstance();
    private static Map<String, String> friendsID;
    private static ArrayList<MLocation> usersLoc;
    private static List<MarkerOptions> mapMarkers = new ArrayList<>();

    // For debug purpose only
    public static HomeFragment newInstance(MapUtility mapUtility, GoogleMap googleMap, int radiusValue){
        HomeFragment fragment = new HomeFragment();
        radius = radiusValue;
        mobileMap = googleMap;
        usersLoc = new ArrayList<>();
        coord = new LatLng(UserInfo.getInstance().getCurrentPosition().getLatitude(),
                UserInfo.getInstance().getCurrentPosition().getLongitude());
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        UserInfo.getInstance().addLocationObserver(this);
        OthersInfo.getInstance().addLocationObserver(this);
        super.onCreate(savedInstanceState);
        radius = UserInfo.getInstance().getCurrentPosition().getRadius();
        friendsID = new HashMap<>();
        usersLoc = new ArrayList<>();
        coord = new LatLng(UserInfo.getInstance().getCurrentPosition().getLatitude(),
                UserInfo.getInstance().getCurrentPosition().getLongitude());
    }

    @Override
    public View onCreateView(LayoutInflater infltr, ViewGroup container, Bundle savedInstanceState) {
        View view = infltr.inflate(R.layout.fragment_home, container, false);

        // Create the tab layout under the map
        viewPager = view.findViewById(R.id.viewPager);
        tabLayout = view.findViewById(R.id.tabLayout);

        adapter = new TabAdapter(this.getChildFragmentManager());
        adapter.addFragment(new PeopleTab(), "People");
        adapter.addFragment(new GroupTab(), "Groups");
        adapter.addFragment(new TopicsTab(), "Topics");

        viewPager.setAdapter(adapter); tabLayout.setupWithViewPager(viewPager);
        getReadWritePermission(getContext(), getActivity());

        return view;
    }

    /**
     * Get the permission from the user to write data in their external storage
     * @param context the context of the app
     * @param activity the current activity
     */
    public void getReadWritePermission(Context context, FragmentActivity activity){
        String[] permissions = {Manifest.permission.READ_EXTERNAL_STORAGE,
                Manifest.permission.WRITE_EXTERNAL_STORAGE};
        if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED ||
                ContextCompat.checkSelfPermission(context,
                        Manifest.permission.WRITE_EXTERNAL_STORAGE)
                        != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(activity, permissions, 123);
        }
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        mapListener = MapUtility.getMapInstance();

        mapView = view.findViewById(R.id.map);
        mapView.onCreate(savedInstanceState);
        mapView.onResume();mapView.getMapAsync(this);

        zoomInButton = view.findViewById(R.id.zoomButton);
        zoomInButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                if (coord != null) {
                    moveCamera(coord, ZOOM);

                }
            }
        });
    }

    @Override
    public void onMapReady(GoogleMap googleMap) {
        Toast.makeText(getContext(), "Map is ready", Toast.LENGTH_SHORT).show();
        if(googleMap == null)
            return;

        mobileMap = googleMap; //use map utility here
        mapListener.getLocationPermission(getContext(), getActivity());

        if (mapListener.getPermissionResult()) {
            updateLocation();
            if (ActivityCompat.checkSelfPermission(getContext(),
                    Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
                    && ActivityCompat.checkSelfPermission(getContext(),
                    Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                return;
            }

            //mobileMap.setMyLocationEnabled(true);

            try {
                getActivity().runOnUiThread(new Runnable() {
                    public void run() {
                        initMap();
                    }
                });
            } catch (NullPointerException e) {/* Only happens in Unit Test*/}

        }
    }

    /**
     * Fetch the location of the device and update it in the Database
     */
    private void updateLocation(){
        mapListener.getDeviceLocation(getActivity()); // use map utility here
        UserInfo.getInstance().getCurrentPosition().setLongitude(mapListener.getCurrCoordinates().longitude);
        UserInfo.getInstance().getCurrentPosition().setLatitude(mapListener.getCurrCoordinates().latitude);
        UserInfo.getInstance().updateLocationInDB();
    }

    /**
     * Initialize the map once we got the permissions and the position
     */
    public void initMap() {
        if (mapListener.getCurrCoordinates() != null) {
            updateLocation();
            MLocation curPos = UserInfo.getInstance().getCurrentPosition();
            coord = new LatLng(curPos.getLatitude(), curPos.getLongitude());
            initCircle(coord);moveCamera(coord, ZOOM);

            // Do locations here
            markNearbyUsers();
        }
    }

    /**
     * Draw the circle representing the radius of the user on the map
     * @param currentCoordinates the coordinate of the center
     */
    private void initCircle(LatLng currentCoordinates) {
        radiusOptions = new CircleOptions().center(currentCoordinates)
                .strokeColor(Color.RED)
                .fillColor(Color.parseColor("#22FF0000"))
                .radius(radius);
        try
        {
            getActivity().runOnUiThread(new Runnable(){
                public void run(){
                    if(mobileMap != null)
                        mobileMap.addCircle(radiusOptions);
                }
        });
        }catch(NullPointerException e){/* Only happens in Unit Test*/}

        if (radiusOptions != null){
            double radius = radiusOptions.getRadius();
            double scale = radius / 300;
            ZOOM =(int) (16 - Math.log(scale) / Math.log(2));
        }
    }

    /**
     * Move the Map camera to the corresponding coordinates
     * @param latLng the coordinate
     * @param zoom the zoom of the camera
     */
    private void moveCamera(final LatLng latLng, final float zoom) {
        try
        {
            getActivity().runOnUiThread(new Runnable(){
                public void run(){
                    if(mobileMap != null)
                        mobileMap.moveCamera(CameraUpdateFactory.newLatLngZoom( latLng, zoom));
                }
            });
        }catch(NullPointerException e){/* Only happens in Unit Test*/}

    }
    /**
     * Marks the other users that are within the distance specified by the users.
     * */
    public void markNearbyUsers() {

        // Clear Markers
        try
        {
            getActivity().runOnUiThread(new Runnable(){
                public void run(){
                    if(mobileMap != null){
                        mobileMap.clear(); mobileMap.addCircle(radiusOptions);
                    }

                }
            });
        }catch(NullPointerException e){/* Only happens in Unit Test*/}

        usersLoc = new ArrayList<>(OthersInfo.getInstance().getUsersInRadius().values());

        getFriendsID();
        if(usersLoc != null) {
            for (int i = 0; i < usersLoc.size(); i++) {
                markNearbyUser(i, usersLoc.get(i).getMessage(), usersLoc.get(i).getTitle(), usersLoc.get(i).getID());
            }
        }
    }

    /**
     * Getter
     */
    public void getFriendsID() {
        friendsID = UserInfo.getInstance().getCurrentUser().getFriends();
    }

    /**
     * Marks a user that are within the distance specified by the users.
     * @param indexOfUser index of user to mark
     * @param status status of this user
     * @param userName username of the user
     * @param locID id of his location
     */
    public void markNearbyUser(int indexOfUser, String status, String userName, String locID) {
        if(!usersLoc.get(indexOfUser).getVisible()) return;
        LatLng newPos = new LatLng(usersLoc.get(indexOfUser).getLatitude(),
                                    usersLoc.get(indexOfUser).getLongitude()    );
        float color = friendsID.containsKey(locID) ? BitmapDescriptorFactory.HUE_BLUE :
                                                        BitmapDescriptorFactory.HUE_RED;

        //Change the marker color to green if users speak the same language but they are not friends
        if(color != BitmapDescriptorFactory.HUE_BLUE){
            for(String language : UserInfo.getInstance().getCurrentPosition().getLanguageList()){
                if(usersLoc.get(indexOfUser).getLanguageList().contains(language)){
                    color = BitmapDescriptorFactory.HUE_GREEN;
                    break;
                }
            }
        }

        if(friendsID.containsKey(locID) && OthersInfo.getInstance().getNewUsersPos().containsKey(locID)){
            showNearFriendNotification(locID, userName);
        }

        final MarkerOptions marker = new MarkerOptions().position(newPos)
                .title(userName + ": " + status)
                .icon(BitmapDescriptorFactory.defaultMarker(color));
        mapMarkers.add(marker);
        try
        {
            getActivity().runOnUiThread(new Runnable(){
            public void run(){
                if(mobileMap != null && mobileMap.getProjection() != null)
                    mobileMap.addMarker(marker);
            }
            });
        }catch(NullPointerException e){/* Only happens in Unit Test*/}

    }

    @Override
    public void onLocationChange(String id) {
        radius = UserInfo.getInstance().getCurrentPosition().getRadius();
        coord = new LatLng(UserInfo.getInstance().getCurrentPosition().getLatitude(), UserInfo.getInstance().getCurrentPosition().getLongitude());
        if (getActivity() != null && !Database.DEBUG_MODE) {
            initCircle(coord);
            markNearbyUsers();
        }
    }

    /**
     * Show notification if a friend is nearby
     * @param userID the userId we want to show
     * @param userNickname his nickname
     */
    public void showNearFriendNotification(String userID, String userNickname) {
        // Setup Intent to end here in case of click
        Intent notifIntent = new Intent(this.getActivity(), HomeFragment.class);
        PendingIntent pi = PendingIntent.getActivity(this.getActivity(), 0, notifIntent, 0);
        // Build and show notification
        NotificationUtility.getInstance(null, null, null, null).notifyFriendIsNear(userID, userNickname, pi);
    }
}