package bg.mentormate.academy.radarapp.activities; import android.app.AlarmManager; import android.app.AlertDialog; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.graphics.Bitmap; import android.location.Location; import android.os.AsyncTask; import android.os.Bundle; import android.os.SystemClock; import android.support.v7.app.ActionBarActivity; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.AdapterView; import android.widget.EditText; import android.widget.GridView; import android.widget.ImageView; import com.google.android.gms.maps.CameraUpdateFactory; import com.google.android.gms.maps.GoogleMap; import com.google.android.gms.maps.OnMapReadyCallback; import com.google.android.gms.maps.SupportMapFragment; import com.google.android.gms.maps.model.BitmapDescriptorFactory; import com.google.android.gms.maps.model.LatLng; import com.google.android.gms.maps.model.Marker; import com.google.android.gms.maps.model.MarkerOptions; import com.google.maps.android.ui.IconGenerator; import com.parse.GetCallback; import com.parse.ParseException; import com.parse.ParseGeoPoint; import com.parse.ParseObject; import com.parse.SaveCallback; import java.util.ArrayList; import java.util.ConcurrentModificationException; import java.util.HashMap; import java.util.List; import java.util.Map; import bg.mentormate.academy.radarapp.Constants; import bg.mentormate.academy.radarapp.R; import bg.mentormate.academy.radarapp.adapters.RoomUserAdapter; import bg.mentormate.academy.radarapp.data.LocalDb; import bg.mentormate.academy.radarapp.models.UserDetail; import bg.mentormate.academy.radarapp.models.Room; import bg.mentormate.academy.radarapp.models.User; import bg.mentormate.academy.radarapp.services.LocationTrackingService; import bg.mentormate.academy.radarapp.services.RetrieveRoomDataService; import bg.mentormate.academy.radarapp.tools.BitmapHelper; import bg.mentormate.academy.radarapp.tools.LocationHelper; import bg.mentormate.academy.radarapp.tools.NotificationHelper; import bg.mentormate.academy.radarapp.tools.QueryHelper; public class RoomActivity extends ActionBarActivity implements AdapterView.OnItemClickListener { private final static long DATA_UPDATE_INTERVAL = 4000; private GoogleMap mMap; // Might be null if Google Play services APK is not available. private LocalDb mLocalDb; private User mCurrentUser; private Room mRoom; private List<User> mUsers; private Map<String, Marker> mMarkers; private Map<String, String> mStates; private RoomUserAdapter mRoomUserAdapter; private AlarmManager mAlarmManager; private PendingIntent mPendingIntent; private Intent mDataServiceIntent; private GridView mGvUsers; private BroadcastReceiver positionsUpdateReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { mRoom = mLocalDb.getSelectedRoom(); updateMarkers(); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_room); getSupportActionBar().setDisplayHomeAsUpEnabled(true); initData(); initViews(); setUpMapIfNeeded(); } private void initData() { mUsers = new ArrayList<>(); mMarkers = new HashMap<>(); mStates = new HashMap<>(); mLocalDb = LocalDb.getInstance(); mCurrentUser = (User) User.getCurrentUser(); } private void initViews() { mGvUsers = (GridView) findViewById(R.id.gvUsers); mRoomUserAdapter = new RoomUserAdapter(this, mUsers); mGvUsers.setAdapter(mRoomUserAdapter); mGvUsers.setOnItemClickListener(this); } private void setUpMapIfNeeded() { // Do a null check to confirm that we have not already instantiated the map. if (mMap == null) { // Try to obtain the map from the SupportMapFragment. ((SupportMapFragment) getSupportFragmentManager() .findFragmentById(R.id.map)) .getMapAsync(new OnMapReadyCallback() { @Override public void onMapReady(GoogleMap googleMap) { mMap = googleMap; // Check if we were successful in obtaining the map. if (mMap != null) { setUpMap(); } } }); } } @Override protected void onResume() { super.onResume(); startServiceForLocationTracking(); startServiceForUpdatingPositions(); registerReceiver(positionsUpdateReceiver, new IntentFilter(RetrieveRoomDataService.BROADCAST_RESULT)); } private void startServiceForLocationTracking() { Intent trackingIntent = new Intent(this, LocationTrackingService.class); trackingIntent.setAction(LocationTrackingService.ACTION_START_MONITORING); startService(trackingIntent); } private void startServiceForUpdatingPositions() { final long ALARM_TRIGGER_AT_TIME = SystemClock.elapsedRealtime() + 20000; mAlarmManager = (AlarmManager)getSystemService(ALARM_SERVICE); mDataServiceIntent = new Intent(this, RetrieveRoomDataService.class); mPendingIntent = PendingIntent.getService(this, 0, mDataServiceIntent, 0); mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP, ALARM_TRIGGER_AT_TIME, DATA_UPDATE_INTERVAL, mPendingIntent); } private void setUpMap() { mMap.setMyLocationEnabled(true); navigateToCurrentLocationOnMap(); String roomId = getIntent().getStringExtra(Constants.ROOM_ID); if (roomId != null) { QueryHelper.getRoomById(roomId, new GetCallback<Room>() { @Override public void done(Room room, ParseException e) { if (e == null) { mRoom = room; mLocalDb.setSelectedRoom(mRoom); // Set activity title getSupportActionBar().setTitle(mRoom.getName()); // Do an async update updateMarkers(); } else { NotificationHelper.alert(RoomActivity.this, getString(R.string.dialog_error_title), e.getMessage()); } } }); } } private void navigateToCurrentLocationOnMap() { final UserDetail locationOnDb = mCurrentUser.getUserDetail(); final Location lastKnownLocation = LocationHelper.getLastKnownLocation(this); if (lastKnownLocation != null) { // Show the position of the last know location on the map mMap.animateCamera(CameraUpdateFactory.newLatLngZoom( new LatLng(lastKnownLocation.getLatitude(), lastKnownLocation.getLongitude()), 15)); // Save the last know location to Db locationOnDb.fetchIfNeededInBackground(new GetCallback<ParseObject>() { @Override public void done(ParseObject parseObject, ParseException e) { locationOnDb.setLocation(new ParseGeoPoint(lastKnownLocation.getLatitude(), lastKnownLocation.getLongitude())); locationOnDb.saveInBackground(); } }); } else { // Show the position from Db on the map locationOnDb.fetchIfNeededInBackground(new GetCallback<ParseObject>() { @Override public void done(ParseObject parseObject, ParseException e) { mMap.animateCamera(CameraUpdateFactory.newLatLngZoom( new LatLng(locationOnDb.getLocation().getLatitude(), locationOnDb.getLocation().getLongitude()), 15)); } }); } } private void updateMarkers() { MarkersUpdateTask markersUpdateTask = new MarkersUpdateTask(); markersUpdateTask.execute(); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_room_activity, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); switch (id) { case android.R.id.home: finish(); break; case R.id.action_change_status: changeStatus(); break; } return super.onOptionsItemSelected(item); } private void changeStatus() { AlertDialog.Builder builder = new AlertDialog.Builder(this); LayoutInflater inflater = LayoutInflater.from(this); final View dvCreateRoom = inflater.inflate(R.layout.dialog_change_status, null); final EditText etStatus = (EditText) dvCreateRoom.findViewById(R.id.etEditStatus); builder.setView(dvCreateRoom) .setTitle(getString(R.string.dialog_change_status_title)) .setPositiveButton(getString(R.string.update_button), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { String status = etStatus.getText().toString().trim(); if (!status.isEmpty()) { mCurrentUser.getUserDetail().setStatus(status); mCurrentUser.getUserDetail().saveInBackground(new SaveCallback() { @Override public void done(ParseException e) { if (e != null) { NotificationHelper.alert(RoomActivity.this, getString(R.string.dialog_error_title), e.getMessage()); } } }); } } }) .setNegativeButton(getString(R.string.cancel_btn), null); AlertDialog dialog = builder.create(); dialog.show(); } @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { ParseGeoPoint userLocation = mUsers.get(position) .getUserDetail() .getLocation(); LatLng latLng = new LatLng(userLocation.getLatitude(), userLocation.getLongitude()); mMap.animateCamera(CameraUpdateFactory.newLatLng(latLng)); Marker userMarker = mMarkers.get(mUsers.get(position).getObjectId()); if (userMarker != null) { userMarker.showInfoWindow(); } } @Override protected void onStop() { if (mDataServiceIntent != null) { stopService(mDataServiceIntent); if (mAlarmManager != null && mPendingIntent != null) { mAlarmManager.cancel(mPendingIntent); } } if (positionsUpdateReceiver != null) { unregisterReceiver(positionsUpdateReceiver); } super.onStop(); } /** * Using this class to do updating the markers in background */ private class MarkersUpdateTask extends AsyncTask<Void, MarkerOptions, Void> { // Those variables are needed for building the avatar icon private IconGenerator mIconGenerator; private ImageView mImageView; private MarkersUpdateTask() { this.mIconGenerator = new IconGenerator(RoomActivity.this); this.mImageView = new ImageView(RoomActivity.this); mIconGenerator.setContentView(mImageView); } @Override protected Void doInBackground(Void... params) { List<User> fetchedUsers = mRoom.getUsers(); boolean isInTheRoom = false; boolean newUserAdded = false; boolean userHasUnregistered = false; // Naive way to check if someone has left and remove his marker from the Map for (User localUser : mUsers) { if (!fetchedUsers.contains(localUser)) { userHasUnregistered = true; mUsers.remove(localUser); if (mMarkers.containsKey(localUser.getObjectId())) { Marker marker = mMarkers.get(localUser.getObjectId()); removeMarkerFromMap(marker); mMarkers.remove(localUser.getObjectId()); } if (mStates.containsKey(localUser.getObjectId())) { mStates.remove(localUser.getObjectId()); } } } try { for (final User user : fetchedUsers) { ParseGeoPoint userLocation = null; final Bitmap[] avatarIcon = {null}; try { user.fetchIfNeeded(); if (!mUsers.contains(user)) { mUsers.add(user); newUserAdded = true; } // If the user is me, don't do anything if (user.equals(mCurrentUser)) { isInTheRoom = true; continue; } user.getUserDetail().fetchIfNeeded(); userLocation = user.getUserDetail().getLocation(); } catch (ParseException e) { e.printStackTrace(); } // Visualize the user's marker if (userLocation != null) { updateMarkerOnMap(user, userLocation, avatarIcon); } } } catch (ConcurrentModificationException e) { kickFromTheRoom(); Log.d(RoomActivity.class.getSimpleName(), "Concurrency problem"); } // If the user is not from the room list then... if (!isInTheRoom) { kickFromTheRoom(); } // Update the User Grid final boolean toRefreshTheUserGrid = newUserAdded || userHasUnregistered; runOnUiThread(new Runnable() { @Override public void run() { if (toRefreshTheUserGrid) { mRoomUserAdapter.notifyDataSetChanged(); } } }); return null; } private void removeMarkerFromMap(final Marker marker) { runOnUiThread(new Runnable() { @Override public void run() { marker.remove(); } }); } private void updateMarkerOnMap(final User user, ParseGeoPoint userLocation, final Bitmap[] avatarIcon) { final LatLng latLngPosition = new LatLng( userLocation.getLatitude(), userLocation.getLongitude()); // Add new marker to the markers Map Collection if it's not in it if (!mMarkers.containsKey(user.getObjectId())) { final MarkerOptions markerOptions = getMarkerOptions(user, avatarIcon, latLngPosition); runOnUiThread(new Runnable() { @Override public void run() { Marker marker = mMap.addMarker(markerOptions); mMarkers.put(user.getObjectId(), marker); mStates.put(user.getObjectId(), user.getUserDetail().getProvider()); } }); } else { final Marker marker = mMarkers.get(user.getObjectId()); runOnUiThread(new Runnable() { @Override public void run() { updateMarker(marker, latLngPosition, avatarIcon, user); } }); } } private MarkerOptions getMarkerOptions(User user, Bitmap[] avatarIcon, LatLng position) { MarkerOptions markerOptions = new MarkerOptions(); // Build user avatar avatarIcon[0] = BitmapHelper.buildAvatarIcon( RoomActivity.this, user, mImageView, mIconGenerator); markerOptions.icon(BitmapDescriptorFactory.fromBitmap(avatarIcon[0])) .title(getMarkerTitle(user)) .position(position); return markerOptions; } private void updateMarker(Marker marker, LatLng latLngPosition, Bitmap[] avatarIcon, User user) { if (marker.getPosition().latitude != latLngPosition.latitude || marker.getPosition().longitude != latLngPosition.longitude) { marker.setPosition(latLngPosition); } UserDetail userLocation = user.getUserDetail(); if ((!userLocation.getProvider().equals(mStates.get(user.getObjectId())) && userLocation.getActive())) { // Build user avatar setMarkerIcon(marker, avatarIcon, user); } else if (!userLocation.getActive()) { // Build user avatar setMarkerIcon(marker, avatarIcon, user); mStates.put(user.getObjectId(), Constants.INACTIVE_STATE); } marker.setTitle(getMarkerTitle(user)); } private void setMarkerIcon(Marker marker, Bitmap[] avatarIcon, User user) { avatarIcon[0] = BitmapHelper.buildAvatarIcon( RoomActivity.this, user, mImageView, mIconGenerator); marker.setIcon(BitmapDescriptorFactory.fromBitmap(avatarIcon[0])); mStates.put(user.getObjectId(), user.getUserDetail().getProvider()); } private void kickFromTheRoom() { // Intent to send a email to the room owner if needed Intent intent = new Intent(Intent.ACTION_SEND); intent.setType("text/plain"); String[] emailList = new String[1]; emailList[0] = mRoom.getCreatedBy().getEmail(); intent.putExtra(Intent.EXTRA_EMAIL, emailList); NotificationHelper.notifyTheUser(RoomActivity.this, R.string.kick_out_text_title, R.string.kickout_text_description, intent); finish(); } } private String getMarkerTitle(User user) { String mMarkerTitle; String mCurrentStatus; mCurrentStatus = user.getUserDetail().getStatus(); if(mCurrentStatus == null || mCurrentStatus.isEmpty()){ mMarkerTitle = user.getUsername(); } else { mMarkerTitle = user.getUsername() + ": " + mCurrentStatus; } return mMarkerTitle; } }