package mil.nga.giat.mage.observation;
import android.Manifest;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.Dialog;
import android.content.ClipData;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.location.Location;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.SystemClock;
import android.provider.MediaStore;
import android.provider.Settings;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.FileProvider;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.MapFragment;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.model.Circle;
import com.google.android.gms.maps.model.CircleOptions;
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.gson.JsonArray;
import com.google.gson.JsonObject;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.Point;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import mil.nga.giat.mage.BuildConfig;
import mil.nga.giat.mage.R;
import mil.nga.giat.mage.form.LayoutBaker;
import mil.nga.giat.mage.form.LayoutBaker.ControlGenerationType;
import mil.nga.giat.mage.form.MageSelectView;
import mil.nga.giat.mage.form.MageTextView;
import mil.nga.giat.mage.map.marker.ObservationBitmapFactory;
import mil.nga.giat.mage.sdk.datastore.observation.Attachment;
import mil.nga.giat.mage.sdk.datastore.observation.Observation;
import mil.nga.giat.mage.sdk.datastore.observation.ObservationHelper;
import mil.nga.giat.mage.sdk.datastore.observation.ObservationProperty;
import mil.nga.giat.mage.sdk.datastore.observation.State;
import mil.nga.giat.mage.sdk.datastore.user.Event;
import mil.nga.giat.mage.sdk.datastore.user.EventHelper;
import mil.nga.giat.mage.sdk.datastore.user.User;
import mil.nga.giat.mage.sdk.datastore.user.UserHelper;
import mil.nga.giat.mage.sdk.exceptions.ObservationException;
import mil.nga.giat.mage.sdk.exceptions.UserException;
import mil.nga.giat.mage.sdk.utils.ISO8601DateFormatFactory;
import mil.nga.giat.mage.sdk.utils.MediaUtility;
public class ObservationEditActivity extends AppCompatActivity implements OnMapReadyCallback {
private static final String LOG_NAME = ObservationEditActivity.class.getName();
private static final int PERMISSIONS_REQUEST_CAMERA = 100;
private static final int PERMISSIONS_REQUEST_VIDEO = 200;
private static final int PERMISSIONS_REQUEST_AUDIO = 300;
private static final int PERMISSIONS_REQUEST_STORAGE = 400;
private final DateFormat iso8601Format = ISO8601DateFormatFactory.ISO8601();
public static final String OBSERVATION_ID = "OBSERVATION_ID";
public static final String LOCATION = "LOCATION";
public static final String INITIAL_LOCATION = "INITIAL_LOCATION";
public static final String INITIAL_ZOOM = "INITIAL_ZOOM";
private static final String CURRENT_MEDIA_PATH = "CURRENT_MEDIA_PATH";
private static final int CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE = 100;
private static final int CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE = 200;
private static final int CAPTURE_VOICE_ACTIVITY_REQUEST_CODE = 300;
private static final int GALLERY_ACTIVITY_REQUEST_CODE = 400;
private static final int LOCATION_EDIT_ACTIVITY_REQUEST_CODE = 600;
private static final int SELECT_ACTIVITY_REQUEST_CODE = 700;
private static final long NEW_OBSERVATION = -1L;
private static Integer FIELD_ID_SELECT = 7;
private Map<String, View> fieldIdMap = new HashMap<>(); //FieldId + " " + UnqiueId / View
private final DecimalFormat latLngFormat = new DecimalFormat("###.#####");
private ArrayList<Attachment> attachmentsToCreate = new ArrayList<>();
private Location l;
private Observation observation;
private GoogleMap map;
private Marker observationMarker;
private Circle accuracyCircle;
private long locationElapsedTimeMilliseconds = 0;
private LinearLayout attachmentLayout;
private AttachmentGallery attachmentGallery;
private String currentMediaPath;
// control key to default position
private static Map<String, Integer> spinnersLastPositions = new HashMap<>();
private List<View> controls = new ArrayList<>();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.observation_editor);
Event event = EventHelper.getInstance(getApplicationContext()).getCurrentEvent();
if (event != null) {
getSupportActionBar().setSubtitle(event.getName());
}
getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_close_white_24dp);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
attachmentLayout = (LinearLayout) findViewById(R.id.image_gallery);
attachmentGallery = new AttachmentGallery(getApplicationContext(), 100, 100);
attachmentGallery.addOnAttachmentClickListener(new AttachmentGallery.OnAttachmentClickListener() {
@Override
public void onAttachmentClick(Attachment attachment) {
Intent intent = new Intent(getApplicationContext(), AttachmentViewerActivity.class);
if (attachment.getId() != null) {
intent.putExtra(AttachmentViewerActivity.ATTACHMENT_ID, attachment.getId());
} else {
intent.putExtra(AttachmentViewerActivity.ATTACHMENT_PATH, attachment.getLocalPath());
}
intent.putExtra(AttachmentViewerActivity.EDITABLE, false);
startActivity(intent);
}
});
final long observationId = getIntent().getLongExtra(OBSERVATION_ID, NEW_OBSERVATION);
JsonObject dynamicFormJson;
if (observationId == NEW_OBSERVATION) {
observation = new Observation();
dynamicFormJson = EventHelper.getInstance(getApplicationContext()).getCurrentEvent().getForm();
} else {
try {
observation = ObservationHelper.getInstance(getApplicationContext()).read(getIntent().getLongExtra(OBSERVATION_ID, 0L));
dynamicFormJson = observation.getEvent().getForm();
} catch (ObservationException oe) {
Log.e(LOG_NAME, "Problem reading observation.", oe);
return;
}
}
controls = LayoutBaker.createControlsFromJson(this, ControlGenerationType.EDIT, dynamicFormJson);
for (View view : controls) {
if (view instanceof MageSelectView) {
final MageSelectView selectView = (MageSelectView) view;
fieldIdMap.put(getSelectId(selectView.getId()), selectView);
selectView.getEditText().setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
selectClick(selectView);
}
});
} else if( view instanceof LinearLayout) {
LinearLayout currentView = (LinearLayout) view;
for (int index = 0; index < currentView.getChildCount(); index++) {
View childView = currentView.getChildAt(index);
if (childView instanceof MageSelectView) {
final MageSelectView childSelectView = (MageSelectView) childView;
fieldIdMap.put(getSelectId(childSelectView.getId()), childSelectView);
childSelectView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
selectClick(childSelectView);
}
});
}
}
}
}
// add dynamic controls to view
LayoutBaker.populateLayoutWithControls((LinearLayout) findViewById(R.id.location_dynamic_form), controls);
((MapFragment) getFragmentManager().findFragmentById(R.id.background_map)).getMapAsync(this);
hideKeyboardOnClick(findViewById(R.id.observation_edit));
if (observationId == NEW_OBSERVATION) {
getSupportActionBar().setTitle("New Observation");
l = getIntent().getParcelableExtra(LOCATION);
observation.setEvent(EventHelper.getInstance(getApplicationContext()).getCurrentEvent());
observation.setTimestamp(new Date());
Serializable timestamp = iso8601Format.format(observation.getTimestamp());
ObservationProperty timestampProperty = new ObservationProperty("timestamp", timestamp);
Map<String, ObservationProperty> propertyMap = LayoutBaker.populateMapFromLayout((LinearLayout) findViewById(R.id.form));
propertyMap.put("timestamp", timestampProperty);
observation.addProperties(propertyMap.values());
try {
User u = UserHelper.getInstance(getApplicationContext()).readCurrentUser();
if (u != null) {
observation.setUserId(u.getRemoteId());
}
} catch (UserException ue) {
ue.printStackTrace();
}
LayoutBaker.populateLayoutFromMap((LinearLayout) findViewById(R.id.form), ControlGenerationType.EDIT, observation.getPropertiesMap());
} else {
getSupportActionBar().setTitle("Edit Observation");
// this is an edit of an existing observation
attachmentGallery.addAttachments(attachmentLayout, observation.getAttachments());
Map<String, ObservationProperty> propertiesMap = observation.getPropertiesMap();
Geometry geo = observation.getGeometry();
if (geo instanceof Point) {
Point point = (Point) geo;
String provider = "manual";
if (propertiesMap.get("provider") != null) {
provider = propertiesMap.get("provider").getValue().toString();
}
l = new Location(provider);
if (propertiesMap.containsKey("accuracy")) {
l.setAccuracy(Float.parseFloat(propertiesMap.get("accuracy").getValue().toString()));
}
l.setLatitude(point.getY());
l.setLongitude(point.getX());
}
LayoutBaker.populateLayoutFromMap((LinearLayout) findViewById(R.id.form), ControlGenerationType.EDIT, propertiesMap);
}
findViewById(R.id.date_edit).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Serializable value = ((MageTextView) findViewById(R.id.date)).getPropertyValue();
Date date = null;
try {
date = ISO8601DateFormatFactory.ISO8601().parse(value.toString());
} catch (ParseException pe) {
Log.e(LOG_NAME, "Problem parsing date.", pe);
}
DateTimePickerDialog dialog = DateTimePickerDialog.newInstance(date);
dialog.setOnDateTimeChangedListener(new DateTimePickerDialog.OnDateTimeChangedListener() {
@Override
public void onDateTimeChanged(Date date) {
((MageTextView) findViewById(R.id.date)).setPropertyValue(date);
}
});
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
dialog.show(ft, "DATE_TIME_PICKER_DIALOG");
}
});
findViewById(R.id.location_edit).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(ObservationEditActivity.this, LocationEditActivity.class);
intent.putExtra(LocationEditActivity.LOCATION, l);
intent.putExtra(LocationEditActivity.MARKER_BITMAP, ObservationBitmapFactory.bitmap(ObservationEditActivity.this, observation));
startActivityForResult(intent, LOCATION_EDIT_ACTIVITY_REQUEST_CODE);
}
});
}
/**
* Hides keyboard when clicking elsewhere
*
* @param view view
*/
private void hideKeyboardOnClick(View view) {
// Set up touch listener for non-text box views to hide keyboard.
if (!(view instanceof EditText) && !(view instanceof Button)) {
view.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Activity.INPUT_METHOD_SERVICE);
if (getCurrentFocus() != null) {
inputMethodManager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
}
return false;
}
});
}
// If a layout container, iterate over children and seed recursion.
if (view instanceof ViewGroup) {
for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
View innerView = ((ViewGroup) view).getChildAt(i);
hideKeyboardOnClick(innerView);
}
}
}
@Override
public void onMapReady(GoogleMap map) {
this.map = map;
map.getUiSettings().setZoomControlsEnabled(false);
setupMap();
}
private void setupMap() {
if (map == null) return;
LatLng location = new LatLng(l.getLatitude(), l.getLongitude());
((TextView) findViewById(R.id.location)).setText(latLngFormat.format(l.getLatitude()) + ", " + latLngFormat.format(l.getLongitude()));
if (l.getProvider() != null) {
((TextView)findViewById(R.id.location_provider)).setText("("+l.getProvider()+")");
} else {
findViewById(R.id.location_provider).setVisibility(View.GONE);
}
if (l.getAccuracy() != 0) {
((TextView)findViewById(R.id.location_accuracy)).setText("\u00B1" + l.getAccuracy() + "m");
} else {
findViewById(R.id.location_accuracy).setVisibility(View.GONE);
}
locationElapsedTimeMilliseconds = getElapsedTimeInMilliseconds();
if (locationElapsedTimeMilliseconds != 0 && !("manual".equalsIgnoreCase(l.getProvider()))) {
//String dateText = DateUtils.getRelativeTimeSpanString(System.currentTimeMillis() - locationElapsedTimeMilliseconds, System.currentTimeMillis(), 0).toString();
String dateText = elapsedTime(locationElapsedTimeMilliseconds);
((TextView)findViewById(R.id.location_elapsed_time)).setText(dateText);
} else {
findViewById(R.id.location_elapsed_time).setVisibility(View.GONE);
}
LatLng latLng = getIntent().getParcelableExtra(INITIAL_LOCATION);
if (latLng == null) {
latLng = new LatLng(0,0);
}
float zoom = getIntent().getFloatExtra(INITIAL_ZOOM, 0);
map.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, zoom));
map.animateCamera(CameraUpdateFactory.newLatLngZoom(location, 18));
if (accuracyCircle != null) {
accuracyCircle.remove();
}
CircleOptions circleOptions = new CircleOptions()
.fillColor(getResources().getColor(R.color.accuracy_circle_fill))
.strokeColor(getResources().getColor(R.color.accuracy_circle_stroke))
.strokeWidth(5)
.center(location)
.radius(l.getAccuracy());
accuracyCircle = map.addCircle(circleOptions);
if (observationMarker != null) {
observationMarker.setPosition(location);
// make sure to set the Anchor after this call as well, because the size of the icon might have changed
observationMarker.setIcon(ObservationBitmapFactory.bitmapDescriptor(this, observation));
observationMarker.setAnchor(0.5f, 1.0f);
} else {
observationMarker = map.addMarker(new MarkerOptions().position(location).icon(ObservationBitmapFactory.bitmapDescriptor(this, observation)));
}
}
@SuppressLint("NewApi")
private long getElapsedTimeInMilliseconds() {
long elapsedTimeInMilliseconds = 0;
if (observation.getPropertiesMap().containsKey("delta")) {
elapsedTimeInMilliseconds = Long.parseLong(observation.getPropertiesMap().get("delta").getValue().toString());
}
if (Build.VERSION.SDK_INT >= 17 && l.getElapsedRealtimeNanos() != 0) {
elapsedTimeInMilliseconds = ((SystemClock.elapsedRealtimeNanos() - l.getElapsedRealtimeNanos()) / (1000000l));
} else {
elapsedTimeInMilliseconds = System.currentTimeMillis() - l.getTime();
}
return Math.max(0l, elapsedTimeInMilliseconds);
}
private String elapsedTime(long ms) {
String s = "";
long sec = ms / 1000;
long min = sec / 60;
if (observation.getRemoteId() == null) {
if (ms < 1000) {
return "now";
}
if (min == 0) {
s = sec + ((sec == 1) ? " sec ago" : " secs ago");
} else if (min < 60) {
s = min + ((min == 1) ? " min ago" : " mins ago");
} else {
long hour = Math.round(Math.floor(min / 60));
s = hour + ((hour == 1) ? " hour ago" : " hours ago");
}
} else {
return "";
}
return s;
}
@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
// Always call the superclass so it can restore the view hierarchy
super.onRestoreInstanceState(savedInstanceState);
l = savedInstanceState.getParcelable("location");
attachmentsToCreate = savedInstanceState.getParcelableArrayList("attachmentsToCreate");
for (Attachment a : attachmentsToCreate) {
attachmentGallery.addAttachment(attachmentLayout, a);
}
LinearLayout form = (LinearLayout) findViewById(R.id.form);
LayoutBaker.populateLayoutFromBundle(form, ControlGenerationType.EDIT, savedInstanceState);
currentMediaPath = savedInstanceState.getString(CURRENT_MEDIA_PATH);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
LayoutBaker.populateBundleFromLayout((LinearLayout) findViewById(R.id.form), outState);
outState.putParcelable("location", l);
outState.putParcelableArrayList("attachmentsToCreate", attachmentsToCreate);
outState.putString(CURRENT_MEDIA_PATH, currentMediaPath);
super.onSaveInstanceState(outState);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.observation_edit_menu, menu);
return true;
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK)) {
new AlertDialog.Builder(this)
.setTitle("Discard Changes")
.setMessage(R.string.cancel_edit)
.setPositiveButton(R.string.discard_changes, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
finish();
}
}).setNegativeButton(R.string.no, null)
.show();
}
return false;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
new AlertDialog.Builder(this)
.setTitle("Discard Changes")
.setMessage(R.string.cancel_edit)
.setPositiveButton(R.string.discard_changes, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
finish();
}
}).setNegativeButton(R.string.no, null)
.show();
break;
case R.id.observation_save:
List<View> invalid = LayoutBaker.validateControls(controls);
if (!invalid.isEmpty()) {
// scroll to first invalid control
View firstInvalid = invalid.get(0);
findViewById(R.id.properties).scrollTo(0, firstInvalid.getBottom());
firstInvalid.clearFocus();
firstInvalid.requestFocus();
firstInvalid.requestFocusFromTouch();
break;
}
observation.setState(State.ACTIVE);
observation.setDirty(true);
observation.setGeometry(new GeometryFactory().createPoint(new Coordinate(l.getLongitude(), l.getLatitude())));
Map<String, ObservationProperty> propertyMap = LayoutBaker.populateMapFromLayout((LinearLayout) findViewById(R.id.form));
try {
observation.setTimestamp(iso8601Format.parse(propertyMap.get("timestamp").getValue().toString()));
} catch (ParseException pe) {
Log.e(LOG_NAME, "Could not parse timestamp", pe);
}
// Add properties that weren't part of the layout
propertyMap.put("accuracy", new ObservationProperty("accuracy", l.getAccuracy()));
String provider = l.getProvider();
if (provider == null || provider.trim().isEmpty()) {
provider = "manual";
}
propertyMap.put("provider", new ObservationProperty("provider", provider));
if (!"manual".equalsIgnoreCase(provider)) {
propertyMap.put("delta", new ObservationProperty("delta", Long.toString(locationElapsedTimeMilliseconds)));
}
observation.addProperties(propertyMap.values());
observation.getAttachments().addAll(attachmentsToCreate);
ObservationHelper oh = ObservationHelper.getInstance(getApplicationContext());
try {
if (observation.getId() == null) {
Observation newObs = oh.create(observation);
Log.i(LOG_NAME, "Created new observation with id: " + newObs.getId());
} else {
oh.update(observation);
Log.i(LOG_NAME, "Updated observation with remote id: " + observation.getRemoteId());
}
finish();
} catch (Exception e) {
Log.e(LOG_NAME, e.getMessage(), e);
}
break;
}
return super.onOptionsItemSelected(item);
}
public void onCameraClick(View v) {
if (ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED ||
ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(ObservationEditActivity.this, new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSIONS_REQUEST_CAMERA);
} else {
launchCameraIntent();
}
}
public void onVideoClick(View v) {
if (ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED ||
ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(ObservationEditActivity.this, new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSIONS_REQUEST_VIDEO);
} else {
launchVideoIntent();
}
}
public void onAudioClick(View v) {
if (ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED ||
ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(ObservationEditActivity.this, new String[]{Manifest.permission.RECORD_AUDIO, Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSIONS_REQUEST_AUDIO);
} else {
launchAudioIntent();
}
}
public void onGalleryClick(View v) {
if (ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(ObservationEditActivity.this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, PERMISSIONS_REQUEST_STORAGE);
} else {
launchGalleryIntent();
}
}
private void launchCameraIntent() {
try {
File file = MediaUtility.createImageFile();
currentMediaPath = file.getAbsolutePath();
Uri uri = FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID + ".fileprovider", file);
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivityForResult(intent, CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE);
} catch (IOException e) {
Log.e(LOG_NAME, "Error creating video media file", e);
}
}
private void launchVideoIntent() {
try {
File file = MediaUtility.createVideoFile();
currentMediaPath = file.getAbsolutePath();
Uri uri = FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID + ".fileprovider", file);
Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivityForResult(intent, CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE);
} catch (IOException e) {
Log.e(LOG_NAME, "Error creating video media file", e);
}
}
private void launchGalleryIntent() {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*, video/*");
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[] {"image/*", "video/*"});
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
}
startActivityForResult(intent, GALLERY_ACTIVITY_REQUEST_CODE);
}
private void launchAudioIntent() {
Intent intent = new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION);
List<ResolveInfo> resolveInfo = getApplicationContext().getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
if (resolveInfo.size() > 0) {
startActivityForResult(intent, CAPTURE_VOICE_ACTIVITY_REQUEST_CODE);
} else {
Toast.makeText(getApplicationContext(), "Device has no voice recorder application.", Toast.LENGTH_SHORT).show();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case PERMISSIONS_REQUEST_CAMERA:
case PERMISSIONS_REQUEST_VIDEO: {
Map<String, Integer> grants = new HashMap<>();
grants.put(Manifest.permission.CAMERA, PackageManager.PERMISSION_GRANTED);
grants.put(Manifest.permission.WRITE_EXTERNAL_STORAGE, PackageManager.PERMISSION_GRANTED);
for (int i = 0; i < grantResults.length; i++) {
grants.put(permissions[i], grantResults[i]);
}
if (grants.get(Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED &&
grants.get(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
if (requestCode == PERMISSIONS_REQUEST_CAMERA) {
launchCameraIntent();
} else {
launchVideoIntent();
}
} else if ((!ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA) && grants.get(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) ||
(!ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) && grants.get(Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) ||
!ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA) && !ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
// User denied camera or storage with never ask again. Since they will get here
// by clicking the camera button give them a dialog that will
// guide them to settings if they want to enable the permission
showDisabledPermissionsDialog(
getResources().getString(R.string.camera_access_title),
getResources().getString(R.string.camera_access_message));
}
break;
}
case PERMISSIONS_REQUEST_AUDIO: {
Map<String, Integer> grants = new HashMap<String, Integer>();
grants.put(Manifest.permission.RECORD_AUDIO, PackageManager.PERMISSION_GRANTED);
grants.put(Manifest.permission.WRITE_EXTERNAL_STORAGE, PackageManager.PERMISSION_GRANTED);
if (grants.get(Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED &&
grants.get(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
launchAudioIntent();
} else if ((!ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.RECORD_AUDIO) && grants.get(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) ||
(!ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) && grants.get(Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED) ||
!ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.RECORD_AUDIO) && !ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
// User denied camera or storage with never ask again. Since they will get here
// by clicking the camera button give them a dialog that will
// guide them to settings if they want to enable the permission
showDisabledPermissionsDialog(
getResources().getString(R.string.camera_access_title),
getResources().getString(R.string.camera_access_message));
}
break;
}
case PERMISSIONS_REQUEST_STORAGE: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
launchGalleryIntent();
} else {
if (!ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_EXTERNAL_STORAGE)) {
// User denied storage with never ask again. Since they will get here
// by clicking the gallery button give them a dialog that will
// guide them to settings if they want to enable the permission
showDisabledPermissionsDialog(
getResources().getString(R.string.gallery_access_title),
getResources().getString(R.string.gallery_access_message));
}
}
break;
}
}
}
private void showDisabledPermissionsDialog(String title, String message) {
new AlertDialog.Builder(this, R.style.AppCompatAlertDialogStyle)
.setTitle(title)
.setMessage(message)
.setPositiveButton(R.string.settings, new Dialog.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.fromParts("package", getApplicationContext().getPackageName(), null));
startActivity(intent);
}
})
.setNegativeButton(android.R.string.cancel, null)
.show();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != RESULT_OK) {
return;
}
switch (requestCode) {
case CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE:
case CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE:
Attachment capture = new Attachment();
capture.setLocalPath(currentMediaPath);
attachmentsToCreate.add(capture);
attachmentGallery.addAttachment(attachmentLayout, capture);
MediaUtility.addImageToGallery(getApplicationContext(), Uri.fromFile(new File(currentMediaPath)));
break;
case GALLERY_ACTIVITY_REQUEST_CODE:
case CAPTURE_VOICE_ACTIVITY_REQUEST_CODE:
Collection<Uri> uris = getUris(data);
for (Uri uri : uris) {
try {
File file = MediaUtility.copyMediaFromUri(getApplicationContext(), uri);
Attachment a = new Attachment();
a.setLocalPath(file.getAbsolutePath());
attachmentsToCreate.add(a);
attachmentGallery.addAttachment(attachmentLayout, a);
} catch (IOException e) {
Log.e(LOG_NAME, "Error copying gallery file to local storage", e);
}
}
break;
case LOCATION_EDIT_ACTIVITY_REQUEST_CODE:
l = data.getParcelableExtra(LocationEditActivity.LOCATION);
setupMap();
break;
case SELECT_ACTIVITY_REQUEST_CODE:
ArrayList<String> selectedChoices = data.getStringArrayListExtra(SelectEditActivity.SELECT_SELECTED);
Integer fieldId = data.getIntExtra(SelectEditActivity.FIELD_ID, 0);
MageSelectView mageSelectView = (MageSelectView) fieldIdMap.get(getSelectId(fieldId));
Serializable selectedChoicesSerialized = null;
if (selectedChoices != null) {
if (mageSelectView.isMultiSelect()) {
selectedChoicesSerialized = selectedChoices;
} else {
if (!selectedChoices.isEmpty()) {
selectedChoicesSerialized = selectedChoices.get(0);
}
}
}
mageSelectView.setPropertyValue(selectedChoicesSerialized);
break;
}
}
private Collection<Uri> getUris(Intent intent) {
Set<Uri> uris = new HashSet<>();
if (intent.getData() != null) {
uris.add(intent.getData());
}
uris.addAll(getClipDataUris(intent));
return uris;
}
@TargetApi(16)
private Collection<Uri> getClipDataUris(Intent intent) {
Collection<Uri> uris = new ArrayList<>();
ClipData cd = intent.getClipData();
if (cd != null) {
for (int i = 0; i < cd.getItemCount(); i++) {
uris.add(cd.getItemAt(i).getUri());
}
}
return uris;
}
private void updateMapIcon() {
if (map == null) return;
if (observationMarker != null) {
observationMarker.remove();
}
observationMarker = map.addMarker(new MarkerOptions().position(new LatLng(l.getLatitude(), l.getLongitude())).icon(ObservationBitmapFactory.bitmapDescriptor(this, observation)));
}
private String getSelectId(Integer fieldId) {
return FIELD_ID_SELECT + " " + fieldId;
}
public void selectClick(MageSelectView mageSelectView) {
JsonObject field = mageSelectView.getJsonObject();
Boolean isMultiSelect = mageSelectView.isMultiSelect();
Integer fieldId = mageSelectView.getId();
String fieldTitle = field.get("title").getAsString();
Intent intent = new Intent(ObservationEditActivity.this, SelectEditActivity.class);
JsonArray jsonArray = field.getAsJsonArray(SelectEditActivity.MULTISELECT_JSON_CHOICE_KEY);
intent.putExtra(SelectEditActivity.SELECT_CHOICES, jsonArray.toString());
Serializable serializableValue = mageSelectView.getPropertyValue();
ArrayList<String> selectedValues = null;
if (serializableValue != null) {
if (isMultiSelect) {
selectedValues = (ArrayList<String>) serializableValue;
} else {
String selectedValue = (String) serializableValue;
selectedValues = new ArrayList<String>();
selectedValues.add(selectedValue);
}
}
intent.putStringArrayListExtra(SelectEditActivity.SELECT_SELECTED, selectedValues);
intent.putExtra(SelectEditActivity.IS_MULTISELECT, isMultiSelect);
intent.putExtra(SelectEditActivity.FIELD_ID, fieldId);
intent.putExtra(SelectEditActivity.FIELD_TITLE, fieldTitle);
startActivityForResult(intent, SELECT_ACTIVITY_REQUEST_CODE);
}
}