package org.azavea.otm.ui;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.Handler.Callback;
import android.os.Message;
import android.text.SpannableString;
import android.text.style.UnderlineSpan;
import android.view.View;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import org.azavea.helpers.Logger;
import org.azavea.otm.App;
import org.azavea.otm.LoginManager;
import org.azavea.otm.R;
import org.azavea.otm.adapters.InstanceInfoArrayAdapter;
import org.azavea.otm.data.InstanceInfo;
import org.azavea.otm.data.User;
import org.azavea.otm.rest.RequestGenerator;
import org.azavea.otm.rest.handlers.LoggingJsonHttpResponseHandler;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import cz.msebera.android.httpclient.Header;
public class InstanceSwitcherActivity extends Activity {
private static final int MINIMUM_DISTANCE_IN_METERS = 100;
private static final int INSTANCE_SELECT_REQUEST_CODE = 1;
private User user = null;
private Location userLocation;
LocationManager locationManager;
private ProgressDialog loadingInstances;
private ProgressDialog loadingInstance;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.instance_switcher_activity);
findViewById(R.id.login_button).setOnClickListener(v -> startActivity(new Intent(InstanceSwitcherActivity.this, LoginActivity.class)));
findViewById(R.id.logout_button).setOnClickListener(v -> {
App.getLoginManager().logOut(InstanceSwitcherActivity.this);
startActivity(new Intent(InstanceSwitcherActivity.this, LoginActivity.class));
});
findViewById(R.id.public_instances_button).setOnClickListener(v ->
startActivityForResult(
new Intent(InstanceSwitcherActivity.this, PublicInstanceListDisplay.class),
INSTANCE_SELECT_REQUEST_CODE));
locationManager = (LocationManager) App.getAppInstance().getSystemService(Context.LOCATION_SERVICE);
}
@Override
public void onDestroy() {
super.onDestroy();
if (loadingInstances != null) {
loadingInstances.dismiss();
}
if (loadingInstance != null) {
loadingInstance.dismiss();
}
}
private void redirectToTabLayout(InstanceInfo instance) {
loadingInstance = ProgressDialog.show(this,
getString(R.string.instance_switcher_dialog_heading),
getString(R.string.instance_switcher_loading_instance));
String instanceCode = instance.getUrlName();
App.reloadInstanceInfo(instanceCode, new RedirectCallback());
}
private class RedirectCallback implements Callback {
@Override
public boolean handleMessage(Message msg) {
Intent intent = new Intent(InstanceSwitcherActivity.this, TabLayout.class);
InstanceSwitcherActivity.this.startActivity(intent);
if (loadingInstance != null) {
loadingInstance.dismiss();
}
InstanceSwitcherActivity.this.finish();
return true;
}
}
// TODO: backport this algorithm, it's more fault tolerant
private Location getBestLocation(Criteria accuracyCrit) {
Location location = null;
if (locationManager != null) {
final String bestProvider = locationManager.getBestProvider(accuracyCrit, true);
List<String> providers = locationManager.getProviders(accuracyCrit, true);
if (bestProvider != null) {
Collections.sort(providers, (s1, s2) -> s1.equals(bestProvider) ? 1 :
s2.equals(bestProvider) ? -1 : 0);
}
for (String provider : providers) {
location = locationManager.getLastKnownLocation(provider);
if (location != null) {
break;
}
}
}
return location;
}
@Override
public void onStart() {
super.onStart();
// Only setup instance lists if the logged in user has changed or moved from their location
final Criteria criteria = new Criteria();
criteria.setAccuracy(Criteria.ACCURACY_COARSE);
final Location newLocation = getBestLocation(criteria);
final User newUser = App.getLoginManager().loggedInUser;
if (user != newUser || areLocationsDistant(userLocation, newLocation)) {
reloadNearbyInstanceList(newLocation);
}
userLocation = newLocation;
user = newUser;
updateAccountElements();
}
private void reloadNearbyInstanceList(final Location newLocation) {
RequestGenerator rg = new RequestGenerator();
// TODO: Rather than using 0,0 when the GPS is off, we should hit a different endpoint to retrieve just "my tree maps"
double latitude = 0;
double longitude = 0;
if (newLocation != null) {
latitude = newLocation.getLatitude();
longitude = newLocation.getLongitude();
}
loadingInstances = ProgressDialog.show(this, getString(R.string.instance_switcher_dialog_heading),
getString(R.string.instance_switcher_loading_instances));
final ListView instancesView = (ListView) findViewById(R.id.instance_list);
instancesView.setEmptyView(findViewById(R.id.instance_list_empty));
rg.getInstancesNearLocation(latitude, longitude,
new LoggingJsonHttpResponseHandler() {
@Override
public void onSuccess(int statusCode, Header[] headers, JSONObject data) {
final LinkedHashMap<CharSequence, List<InstanceInfo>> instances = new LinkedHashMap<>();
instances.put(getString(R.string.my_tree_maps), inflateForKey(data, "personal"));
instances.put(getString(R.string.neary_tree_maps), inflateForKey(data, "nearby"));
InstanceInfoArrayAdapter adapter =
new InstanceInfoArrayAdapter(instances, InstanceSwitcherActivity.this, newLocation);
instancesView.setAdapter(adapter);
resetInstanceListViews(newLocation);
instancesView.setOnItemClickListener((parent, v, position, id) -> {
InstanceInfo instance = adapter.getItem(position).value;
redirectToTabLayout(instance);
});
}
@Override
public void failure(Throwable e, String message) {
resetInstanceListViews(newLocation);
new AlertDialog.Builder(InstanceSwitcherActivity.this)
.setTitle(R.string.request_failed)
.setMessage(R.string.instance_switcher_request_failed)
.setNeutralButton(R.string.reload_instance_list,
(dialog, which) -> reloadNearbyInstanceList(newLocation))
.setCancelable(true)
.show();
}
}
);
}
private void resetInstanceListViews(Location newLocation) {
findViewById(R.id.instance_list_retry).setVisibility(View.VISIBLE);
if (newLocation == null) {
findViewById(R.id.instance_list_location_off).setVisibility(View.VISIBLE);
findViewById(R.id.instance_list_none_found).setVisibility(View.GONE);
} else {
findViewById(R.id.instance_list_location_off).setVisibility(View.GONE);
findViewById(R.id.instance_list_none_found).setVisibility(View.VISIBLE);
}
if (loadingInstances != null) {
loadingInstances.dismiss();
}
}
/**
* Returns true if the two locations are far away enough to requery for nearby instances,
* or if one is an invalid location
*/
private static boolean areLocationsDistant(Location a, Location b) {
return (a == null || b == null) || a.distanceTo(b) > MINIMUM_DISTANCE_IN_METERS;
}
private SpannableString makeUserNameString(String userName) {
String prefix = "Not ";
String suffix = "?";
SpannableString content = new SpannableString(prefix + userName + suffix);
content.setSpan(new UnderlineSpan(), prefix.length(), prefix.length() + userName.length(), 0);
return content;
}
private void updateAccountElements() {
TextView userNameView = (TextView) findViewById(R.id.username_text);
LoginManager loginManager = App.getLoginManager();
if (loginManager.isLoggedIn()) {
findViewById(R.id.have_account_text).setVisibility(View.GONE);
findViewById(R.id.login_button).setVisibility(View.GONE);
try {
userNameView.setText(makeUserNameString(loginManager.loggedInUser.getUserName()));
userNameView.setVisibility(View.VISIBLE);
} catch (JSONException e) {
Logger.error("Could not get username.", e);
}
findViewById(R.id.logout_button).setVisibility(View.VISIBLE);
} else {
findViewById(R.id.have_account_text).setVisibility(View.VISIBLE);
findViewById(R.id.login_button).setVisibility(View.VISIBLE);
userNameView.setVisibility(View.GONE);
findViewById(R.id.logout_button).setVisibility(View.GONE);
}
}
public ArrayList<InstanceInfo> inflateForKey(JSONObject data, String key) {
JSONArray instances = data.optJSONArray(key);
return InstanceInfo.getInstanceInfosFromJSON(instances);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case (INSTANCE_SELECT_REQUEST_CODE): {
if (resultCode == Activity.RESULT_OK) {
CharSequence instanceJSON = data.getCharSequenceExtra(PublicInstanceListDisplay.MODEL_DATA);
if (instanceJSON != null) {
try {
InstanceInfo instance = new InstanceInfo();
instance.setData(new JSONObject(instanceJSON.toString()));
redirectToTabLayout(instance);
} catch (JSONException e) {
String msg = "Unable to retrieve selected Tree Map";
Logger.error(msg, e);
Toast.makeText(this, msg, Toast.LENGTH_LONG).show();
}
}
}
break;
}
}
}
}