package kr.kdev.dg1s.biowiki.ui.info.distribution;
import android.annotation.SuppressLint;
import android.graphics.Color;
import android.location.Location;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.SpannableString;
import android.text.style.ForegroundColorSpan;
import android.util.Log;
import android.view.View;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.RadioGroup;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
import android.widget.Toast;
import com.actionbarsherlock.view.MenuItem;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks;
import com.google.android.gms.common.GooglePlayServicesClient.OnConnectionFailedListener;
import com.google.android.gms.location.LocationClient;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.InfoWindowAdapter;
import com.google.android.gms.maps.GoogleMap.OnInfoWindowClickListener;
import com.google.android.gms.maps.GoogleMap.OnMarkerClickListener;
import com.google.android.gms.maps.GoogleMap.OnMarkerDragListener;
import com.google.android.gms.maps.GoogleMap.OnMyLocationButtonClickListener;
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.LatLngBounds;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import net.htmlparser.jericho.Element;
import net.htmlparser.jericho.Source;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import kr.kdev.dg1s.biowiki.BioWiki;
import kr.kdev.dg1s.biowiki.R;
import kr.kdev.dg1s.biowiki.ui.BIActionBarActivity;
public class DistributionViewer extends BIActionBarActivity
implements
OnMarkerClickListener,
OnInfoWindowClickListener,
OnMarkerDragListener,
OnSeekBarChangeListener,
OnMyLocationButtonClickListener,
ConnectionCallbacks,
LocationListener,
OnConnectionFailedListener {
private static final LatLng LOCATION_DEFAULT = new LatLng(35.886826, 128.721226);
private static final LatLng DG1S = new LatLng(35.886545, 128.722626);
// These settings are the same as the settings for the map. They will in fact give you updates
// at the maximal rates currently possible.
private static final LocationRequest REQUEST = LocationRequest.create()
.setInterval(5000) // 5 seconds
.setFastestInterval(16) // 16ms = 60fps
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
private final List<Marker> mDistributionStats = new ArrayList<Marker>();
private final List<MarkerOptions> mMarkerOptions = new ArrayList<MarkerOptions>();
private Handler handler = new Handler() {
@Override
public void handleMessage(Message message) {
if (message.what == 573) {
loadMarkers(mMarkerOptions);
if (getApplicationContext() != null) {
Toast.makeText(getApplicationContext(), getString(R.string.locationLoaded), Toast.LENGTH_SHORT).show();
}
} else if (message.what == 7) {
mMarkerOptions.clear();
}
if (message.getData().getStringArrayList("position") != null) {
List<String> mImport = message.getData().getStringArrayList("position");
Log.d("Handler", "Message received and initializing");
String hueIndex;
if (mImport.get(3) == null) {
hueIndex = "";
} else {
hueIndex = mImport.get(3);
}
mMarkerOptions.add(new MarkerOptions()
.position(new LatLng(
Double.parseDouble(mImport.get(1)),
Double.parseDouble(mImport.get(2))))
.title(mImport.get(0))
.icon(BitmapDescriptorFactory.defaultMarker(getHue(hueIndex)))
);
Log.d("Marker", "Added Marker : " + mImport.get(0) + " @" + mImport.get(1) + ", " + mImport.get(2));
}
}
};
boolean isOffline;
SeekBar mRotationBar;
private GoogleMap mMap;
private LocationClient mLocationClient;
private Marker mDG1S;
private Marker mUser;
private boolean loadedMarkers = false;
private TextView mTopText;
private CheckBox mFlatBox;
@Override
public boolean onCreateOptionsMenu(com.actionbarsherlock.view.Menu menu) {
getSupportMenuInflater().inflate(R.menu.maps, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
if (mMenuDrawer != null) {
mMenuDrawer.toggleMenu();
return true;
}
case R.id.map_normal:
mMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
return true;
case R.id.map_terrain:
mMap.setMapType(GoogleMap.MAP_TYPE_TERRAIN);
return true;
case R.id.map_hybrid:
mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);
return true;
//case R.id.map_satellite:
// mMap.setMapType(GoogleMap.MAP_TYPE_SATELLITE);
}
return false;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
createMenuDrawer(R.layout.maps_marker);
isOffline = getIntent().getBooleanExtra("offline", false);
if (isOffline)
Toast.makeText(this, R.string.offline_enabled, Toast.LENGTH_LONG).show();
mTopText = (TextView) findViewById(R.id.top_text);
mRotationBar = (SeekBar) findViewById(R.id.rotationSeekBar);
mRotationBar.setMax(360);
mRotationBar.setOnSeekBarChangeListener(this);
mFlatBox = (CheckBox) findViewById(R.id.flat);
setUpMapIfNeeded();
}
@Override
protected void onResume() {
super.onResume();
setUpMapIfNeeded();
setUpLocationClientIfNeeded();
mLocationClient.connect();
}
/**
* Callback called when connected to GCore. Implementation of {@link ConnectionCallbacks}.
*/
@Override
public void onConnected(Bundle connectionHint) {
mLocationClient.requestLocationUpdates(
REQUEST,
this); // LocationListener
}
/**
* Callback called when disconnected from GCore. Implementation of {@link ConnectionCallbacks}.
*/
@Override
public void onDisconnected() {
// Do nothing
}
/**
* Implementation of {@link OnConnectionFailedListener}.
*/
@Override
public void onConnectionFailed(ConnectionResult result) {
// Do nothing
}
/**
* Implementation of {@link LocationListener}.
*/
@Override
public void onLocationChanged(Location location) {
// Do nothing
}
private void setUpMapIfNeeded() {
Log.d("Map", "Checking if needed");
// 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.
mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map))
.getMap();
// Check if we were successful in obtaining the map.
if (mMap != null) {
mMap.setMyLocationEnabled(true);
mMap.setOnMyLocationButtonClickListener(this);
setUpMap();
}
} else {
setUpMap();
}
}
private void setUpLocationClientIfNeeded() {
if (mLocationClient == null) {
mLocationClient = new LocationClient(
getApplicationContext(),
this, // ConnectionCallbacks
this); // OnConnectionFailedListener
}
}
private Source bringSource(URL url) throws IOException {
Log.d("Source Input", "Sending source");
return new Source(new InputStreamReader(url.openStream(), "UTF-8"));
}
private List<Message> parseMarkers(Source source) {
Log.d("ParseMarkers", "XML Input : " + source);
List<Message> messages = new ArrayList<Message>();
List<Element> speciesArray = source.getAllElements("species");
for (Element species : speciesArray) {
for (Element location : species.getAllElements("location")) {
ArrayList<String> attributes = new ArrayList<String>();
attributes.add(0, species.getAttributeValue("name"));
attributes.add(1, location.getAttributeValue("latitude"));
attributes.add(2, location.getAttributeValue("longitude"));
attributes.add(3, species.getAttributeValue("color"));
Bundle bundle = new Bundle();
bundle.putStringArrayList("position", attributes);
Message message = new Message();
message.setData(bundle);
messages.add(message);
Log.d("Messages", "Added Message to array : " + species.getAttributeValue("name") + " @"
+ location.getAttributeValue("latitude") + ", " + location.getAttributeValue("longitude"));
Log.d("Message", "Detail : " + messages);
}
}
Message endTagMessage = new Message();
endTagMessage.what = 573;
messages.add(endTagMessage);
return messages;
}
private void loadMarkers(List<MarkerOptions> mMarkerOptions) {
for (MarkerOptions markerOptions : mMarkerOptions) {
Log.d("Markers", "Added markers to map : " + markerOptions.getTitle() + " @" +
markerOptions.getPosition().latitude + ", " + markerOptions.getPosition().longitude);
mDistributionStats.clear();
mDistributionStats.add(mMap.addMarker(markerOptions));
}
}
private void setUpMap() {
// Hide the zoom controls as the button panel will cover it.
mMap.getUiSettings().setZoomControlsEnabled(true);
mMap.setOnMyLocationButtonClickListener(this);
// Add lots of markers to the map.
addMarkersToMap();
// Setting an info window adapter allows us to change the both the contents and look of the
// info window.
mMap.setInfoWindowAdapter(new CustomInfoWindowAdapter());
// Set listeners for marker events. See the bottom of this class for their behavior.
mMap.setOnMarkerClickListener(this);
mMap.setOnInfoWindowClickListener(this);
mMap.setOnMarkerDragListener(this);
mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);
// Pan to see all markers in view.
// Cannot zoom to bounds until the map has a size.
final View mapView = getSupportFragmentManager().findFragmentById(R.id.map).getView();
if (mapView.getViewTreeObserver().isAlive()) {
mapView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@SuppressWarnings("deprecation") // We use the new method when supported
@SuppressLint("NewApi") // We check which build version we are using.
@Override
public void onGlobalLayout() {
LatLngBounds bounds = new LatLngBounds.Builder()
.include(LOCATION_DEFAULT)
.include(DG1S)
.build();
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
mapView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
} else {
mapView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
mMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 50));
}
});
}
}
private void addMarkersToMap() {
// Start thread for updating markers
if (loadedMarkers) {
updateMarkers updateMarkers = new updateMarkers();
updateMarkers.setContextClassLoader(getClass().getClassLoader());
updateMarkers.start();
}
loadedMarkers = true;
/*
// Uses a colored icon.
mDG1S = mMap.addMarker(new MarkerOptions()
.position(LOCATION_DEFAULT)
.title("Brisbane")
.snippet("Population: 2,074,200")
.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_AZURE)));
// Creates a draggable marker. Long press to drag.
mUser = mMap.addMarker(new MarkerOptions()
.position(DG1S)
.title("Daegu Il Science High School")
.snippet("!!!")
.draggable(true));
*/
}
@Override
public boolean onMyLocationButtonClick() {
Toast.makeText(this, getString(R.string.locationButtonClicked), Toast.LENGTH_SHORT).show();
// Return false so that we don't consume the event and the default behavior still occurs
// (the camera animates to the user's current position).
return false;
}
private boolean checkReady() {
if (mMap == null) {
Toast.makeText(this, "Map isn't ready", Toast.LENGTH_SHORT).show();
return false;
}
return true;
}
private float getHue(String config) {
Log.d("COLOR", config);
if (config.equals("azure")) {
return BitmapDescriptorFactory.HUE_AZURE;
} else if (config.equals("blue")) {
return BitmapDescriptorFactory.HUE_BLUE;
} else if (config.equals("cyan")) {
return BitmapDescriptorFactory.HUE_CYAN;
} else if (config.equals("green")) {
return BitmapDescriptorFactory.HUE_GREEN;
} else if (config.equals("magenta")) {
return BitmapDescriptorFactory.HUE_MAGENTA;
} else if (config.equals("orange")) {
return BitmapDescriptorFactory.HUE_ORANGE;
} else if (config.equals("red")) {
return BitmapDescriptorFactory.HUE_RED;
} else if (config.equals("rose")) {
return BitmapDescriptorFactory.HUE_ROSE;
} else if (config.equals("violet")) {
return BitmapDescriptorFactory.HUE_VIOLET;
} else if (config.equals("yellow")) {
return BitmapDescriptorFactory.HUE_YELLOW;
} else {
return 0;
}
}
/**
* Called when the Clear button is clicked.
*/
public void onClearMap(View view) {
if (!checkReady()) {
return;
}
mMap.clear();
}
/**
* Called when the Reset button is clicked.
*/
public void onResetMap(View view) {
if (!checkReady()) {
return;
}
// Clear the map because we don't want duplicates of the markers.
mMap.clear();
addMarkersToMap();
}
/**
* Called when the Reset button is clicked.
*/
public void onToggleFlat(View view) {
if (!checkReady()) {
return;
}
boolean flat = mFlatBox.isChecked();
for (Marker marker : mDistributionStats) {
marker.setFlat(flat);
}
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (!checkReady()) {
return;
}
float rotation = seekBar.getProgress();
for (Marker marker : mDistributionStats) {
marker.setRotation(rotation);
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
// Do nothing.
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
// Do nothing.
}
@Override
public boolean onMarkerClick(final Marker marker) {
/*
if (marker.equals(mPerth)) {
// This causes the marker at Perth to bounce into position when it is clicked.
final Handler handler = new Handler();
final long start = SystemClock.uptimeMillis();
final long duration = 1500;
final Interpolator interpolator = new BounceInterpolator();
handler.post(new Runnable() {
@Override
public void run() {
long elapsed = SystemClock.uptimeMillis() - start;
float t = Math.max(1 - interpolator
.getInterpolation((float) elapsed / duration), 0);
marker.setAnchor(0.5f, 1.0f + 2 * t);
if (t > 0.0) {
// Post again 16ms later.
handler.postDelayed(this, 16);
}
}
});
}
else if (marker.equals(mAdelaide)) {
// This causes the marker at Adelaide to change color and alpha.
marker.setIcon(BitmapDescriptorFactory.defaultMarker(mRandom.nextFloat() * 360));
marker.setAlpha(mRandom.nextFloat());
}
*/
// We return false to indicate that we have not consumed the event and that we wish
// for the default behavior to occur (which is for the camera to move such that the
// marker is centered and for the marker's info window to open, if it has one).
return false;
}
@Override
public void onInfoWindowClick(Marker marker) {
Toast.makeText(this, "Click Info Window", Toast.LENGTH_SHORT).show();
}
//
// Marker related listeners.
//
@Override
public void onMarkerDragStart(Marker marker) {
mTopText.setText("onMarkerDragStart");
}
@Override
public void onMarkerDragEnd(Marker marker) {
mTopText.setText("onMarkerDragEnd");
}
@Override
public void onMarkerDrag(Marker marker) {
mTopText.setText("onMarkerDrag. Current Position: " + marker.getPosition());
}
/**
* Demonstrates customizing the info window and/or its contents.
*/
class CustomInfoWindowAdapter implements InfoWindowAdapter {
private final RadioGroup mOptions;
// These a both viewgroups containing an ImageView with id "badge" and two TextViews with id
// "title" and "snippet".
private final View mWindow;
private final View mContents;
CustomInfoWindowAdapter() {
mWindow = getLayoutInflater().inflate(R.layout.custom_info_window, null);
mContents = getLayoutInflater().inflate(R.layout.custom_info_contents, null);
mOptions = (RadioGroup) findViewById(R.id.custom_info_window_options);
}
@Override
public View getInfoWindow(Marker marker) {
if (mOptions.getCheckedRadioButtonId() != R.id.custom_info_window) {
// This means that getInfoContents will be called.
return null;
}
render(marker, mWindow);
return mWindow;
}
@Override
public View getInfoContents(Marker marker) {
if (mOptions.getCheckedRadioButtonId() != R.id.custom_info_contents) {
// This means that the default info contents will be used.
return null;
}
render(marker, mContents);
return mContents;
}
private void render(Marker marker, View view) {
int badge;
// Use the equals() method on a Marker to check for equals. Do not use ==.
if (marker.equals(DG1S)) {
badge = R.drawable.badge_qld;
} else if (marker.equals(mUser)) {
badge = R.drawable.badge_victoria;
} else {
// Passing 0 to setImageResource will clear the image view.
badge = 0;
}
((ImageView) view.findViewById(R.id.badge)).setImageResource(badge);
String title = marker.getTitle();
TextView titleUi = ((TextView) view.findViewById(R.id.title));
if (title != null) {
// Spannable string allows us to edit the formatting of the text.
SpannableString titleText = new SpannableString(title);
titleText.setSpan(new ForegroundColorSpan(Color.RED), 0, titleText.length(), 0);
titleUi.setText(titleText);
} else {
titleUi.setText("");
}
String snippet = marker.getSnippet();
TextView snippetUi = ((TextView) view.findViewById(R.id.snippet));
if (snippet != null && snippet.length() > 12) {
SpannableString snippetText = new SpannableString(snippet);
snippetText.setSpan(new ForegroundColorSpan(Color.MAGENTA), 0, 10, 0);
snippetText.setSpan(new ForegroundColorSpan(Color.BLUE), 12, snippet.length(), 0);
snippetUi.setText(snippetText);
} else {
snippetUi.setText("");
}
}
}
class updateMarkers extends Thread {
public void run() {
try {
Log.d("Network", "Initiating..." + BioWiki.getCurrentBlog().getHomeURL());
List<Message> messages;
if (isOffline) {
messages = parseMarkers(new Source(new InputStreamReader(getAssets().open("xmls/location.xml"))));
} else {
messages = parseMarkers(bringSource(new URL(BioWiki.getCurrentBlog().getHomeURL() + getString(R.string.server_subdomain)
+ getString(R.string.distribution_sample))));
}
Log.d("Network", "Received source");
Message clearSign = new Message();
clearSign.what = 7;
handler.sendMessage(clearSign);
if (messages == null) {
Log.d("Network", "SOURCE IS NULL!");
} else for (Message message : messages) {
handler.sendMessage(message);
Log.d("Handler", "Message sent");
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}