/*
* Copyright 2011 Adi Sayoga.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.adisayoga.earthquake.ui;
import java.util.ArrayList;
import java.util.List;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.Drawable;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Toast;
import com.adisayoga.earthquake.R;
import com.adisayoga.earthquake.dto.EarthquakeDTO;
import com.adisayoga.earthquake.models.EarthquakeModel;
import com.adisayoga.earthquake.providers.EarthquakeColumns;
import com.adisayoga.earthquake.receivers.EarthquakeReceiver;
import com.adisayoga.earthquake.receivers.RefreshReceiver;
import com.adisayoga.earthquake.ui.preferences.PrefsActivity;
import com.adisayoga.earthquake.utils.BaseLocationListener;
import com.adisayoga.earthquake.utils.LocationFinder;
import com.adisayoga.earthquake.wrapper.Prefs;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
/**
* Activity peta gempa.
*
* @author Adi Sayoga
*/
public class EarthquakeMapActivity extends MapActivity {
private static final String TAG = "EarthquakeMapActivity";
private static final int SHOW_PREFS_REQUEST = 1;
private static final String LAYER_SATTELITE_KEY = "map_layer_sattelite";
private static final String LAYER_STREET_VIEW_KEY = "map_layer_street_view";
private static final String LAYER_TRAFFIC_KEY = "map_layer_traffic";
private static final int LAYER_SATTELITE_INDEX = 0;
private static final int LAYER_STREET_VIEW_INDEX = 1;
private static final int LAYER_TRAFFIC_INDEX = 2;
private static Prefs prefs;
private boolean firstStart = true;
private boolean centerLocationWhenAvailable = false;
private EarthquakeReceiver quakeReceiver;
private List<EarthquakeDTO> quakes = new ArrayList<EarthquakeDTO>();
private LocationManager locationManager;
private LocationFinder locationFinder;
private Location location;
private ImageButton refreshButton;
private ImageButton myLocationButton;
private MapView mapView;
private MapController mapController;
private final boolean[] layers = new boolean[3];
private List<Overlay> overlays;
private List<EarthquakeOverlay> quakeOverlays;
private LinearLayout locationLayout = null;
@Override
public void onWindowFocusChanged(boolean hasFocus) {
Log.d(TAG, "onWindowFocusChanged");
super.onWindowFocusChanged(hasFocus);
if (firstStart) {
// Animasi tidak dapat dimulai pada onCreate, pilihan terbaik adalah pada
// onWindowFocusChaged ini, dan ini haya dijalankan sekali saja.
firstStart = false;
Toast.makeText(this, R.string.refreshing, Toast.LENGTH_SHORT).show();
animateRefreshButton(true);
showLocationMarker(location);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
prefs = Prefs.getInstance(this);
setTheme(prefs.getTheme().resId);
setContentView(R.layout.map);
quakeReceiver = new EarthquakeReceiver(handler);
// Mendapatkan lokasi saat ini
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
locationFinder = new LocationFinder(this);
location = getCurrentLocation();
// Action Bar
setupActionBarListener();
// Inisialisasi mapView
initializeMapView();
// Load data gempa dari provider, kemudian tampilkan pada peta
quakeOverlays = new ArrayList<EarthquakeOverlay>();
updateQuakes(false);
refreshData();
// Mendapatkan item data gempa dari intent untuk ditampilkan di tengah-tengah.
centerQuake();
}
/**
* Setup listener ActionBar.
*/
private void setupActionBarListener() {
refreshButton = (ImageButton) findViewById(R.id.refresh);
refreshButton.setOnClickListener(actionBarListener);
myLocationButton = (ImageButton) findViewById(R.id.location);
myLocationButton.setOnClickListener(actionBarListener);
}
/**
* Mendapatkan lokasi saat ini sesuai dengan preference. Jika dapat mendeteksi
* lokasi akan digunakan gps, atau jika tidak lokasi manual yang digunakan.
*
* @return Lokasi
*/
private Location getCurrentLocation() {
locationFinder.setChangedLocationListener(locationListener);
Location location;
if (prefs.isDetectLocation()) {
location = locationFinder.getLastLocation(LocationFinder.MAX_DISTANCE,
System.currentTimeMillis() + prefs.getInterval());
} else {
location = prefs.getManualLocation();
}
return location;
}
/**
* Inisiasi MapView.
*/
private void initializeMapView() {
mapView = (MapView) findViewById(R.id.quake_mapview);
mapView.setBuiltInZoomControls(true);
boolean satelliteShown = prefs.isLayerUsed(LAYER_SATTELITE_KEY);
layers[LAYER_SATTELITE_INDEX] = satelliteShown;
mapView.setSatellite(satelliteShown);
boolean streetViewShown = prefs.isLayerUsed(LAYER_STREET_VIEW_KEY);
layers[LAYER_STREET_VIEW_INDEX] = streetViewShown;
mapView.setStreetView(streetViewShown);
boolean trafficShown = prefs.isLayerUsed(LAYER_TRAFFIC_KEY);
layers[LAYER_TRAFFIC_INDEX] = trafficShown;
mapView.setTraffic(trafficShown);
mapController = mapView.getController();
overlays = mapView.getOverlays();
}
/**
* Buat tombol refresh berputar, sebagai tanda sedang ada proses pada background.
*
* @param animate True jika animasi aktif, false jika tidak
*/
private void animateRefreshButton(final boolean animate) {
handler.post(new Runnable() {
@Override
public void run() {
AnimationDrawable anim = (AnimationDrawable) refreshButton.getDrawable();
if (animate) {
anim.start();
} else {
anim.stop();
}
}
});
}
/**
* Menampilkan lokasi user saat ini (animasi).
*
* @param Location Lokasi saat ini
*/
private void showLocationMarker(Location Location) {
if (location == null) return;
GeoPoint point = new GeoPoint((int) (location.getLatitude() * 1E6),
(int) (location.getLongitude() * 1E6));
if (locationLayout == null) {
locationLayout = (LinearLayout) View.inflate(this,
R.layout.location_marker, null);
ImageView marker = (ImageView) locationLayout.findViewById(R.id.marker);
AnimationDrawable markerImage = (AnimationDrawable) marker.getDrawable();
mapView.addView(locationLayout, 0);
markerImage.start();
}
MapView.LayoutParams params = new MapView.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams
.WRAP_CONTENT, point, MapView.LayoutParams.CENTER);
locationLayout.setLayoutParams(params);
}
/**
* Load data dari provider kemudian update overlay. (pada background thread)
*
* @param finishLoading Menandakan proses loading selesai, animasi tombol refresh
* akan dihentikan
*/
private void updateQuakes(final boolean finishLoading) {
new AsyncTask<Boolean, Void, Void>() {
private boolean finishLoading;
@Override
protected Void doInBackground(Boolean... params) {
this.finishLoading = params[0];
// Load data dari provider, urutkan berdasarkan tanggal ascending,
// karena pada ovelay list yang paling akhir yang akan ditampilkan
// paling depan pada MapView
EarthquakeModel table = new EarthquakeModel(EarthquakeMapActivity.this);
quakes = table.getMatchQuakes(prefs.getMinMagnitude(), prefs.getMaxAge(),
EarthquakeColumns.DATE + " ASC");
return null;
}
@Override
protected void onPostExecute(Void result) {
setupQuakeOverlay();
if (finishLoading) animateRefreshButton(false);
}
}.execute(finishLoading);
}
/**
* Setup overlay gempa.
*/
private void setupQuakeOverlay() {
// Kosongkan quakeOverlays dari overlays
for (Overlay overlay : quakeOverlays) {
overlays.remove(overlay);
}
quakeOverlays.clear();
// Tambahkan quakeOverlay baru yang sesuai dengan database
for (EarthquakeDTO quake : quakes) {
Drawable quakeDrawable = getResources().getDrawable(R.drawable.one);
EarthquakeOverlay quakeOverlay = new EarthquakeOverlay(quakeDrawable,
mapView, location);
quakeOverlay.addQuake(quake);
quakeOverlays.add(quakeOverlay);
overlays.add(quakeOverlay);
}
mapView.invalidate();
}
/**
* Tampilkan gempa di tengah-tengah. Data gempa didapat dari extra pada intent.
*/
private void centerQuake() {
EarthquakeDTO quake = null;
Intent intent = getIntent();
if (intent.hasExtra(EarthquakeColumns.TABLE_NAME)) {
quake = (EarthquakeDTO) intent.getExtras().get(EarthquakeColumns.TABLE_NAME);
}
if (quake != null) {
zoomToMagnitude(quake.magnitude);
mapController.animateTo(quake.getPoint());
}
}
/**
* Zoom peta sesuai dengan magnitudo gempa.
*
* @param magnitude Magnitudo gempa
*/
private void zoomToMagnitude(float magnitude) {
int zoom;
if (magnitude < 2) {
zoom = 11;
} else if (magnitude < 3) {
zoom = 10;
} else if (magnitude < 4) {
zoom = 9;
} else if (magnitude < 5) {
zoom = 8;
} else if (magnitude < 6) {
zoom = 7;
} else {
zoom = 6;
}
mapController.setZoom(zoom);
}
@Override
protected void onResume() {
Log.d(TAG, "onResume");
super.onResume();
registerReceiver(quakeReceiver, new IntentFilter(EarthquakeReceiver.NEW_QUAKE_FOUND));
registerReceiver(quakeReceiver, new IntentFilter(EarthquakeReceiver.NO_NEW_QUAKE));
registerReceiver(quakeReceiver, new IntentFilter(EarthquakeReceiver.NETWORK_ERROR));
if (prefs.isDetectLocation()) {
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0,
locationListener);
}
}
/**
* Kirim broadcast agar data direfresh.
*/
private void refreshData() {
sendBroadcast(new Intent(RefreshReceiver.REFRESH, null, this,
RefreshReceiver.class));
}
@Override
protected void onPause() {
Log.d(TAG, "onPause");
super.onPause();
unregisterReceiver(quakeReceiver);
locationManager.removeUpdates(locationListener);
animateRefreshButton(false);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
MenuInflater inflater = new MenuInflater(this);
inflater.inflate(R.menu.map_menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
super.onOptionsItemSelected(item);
Intent intent;
switch (item.getItemId()) {
case R.id.layers:
AlertDialog.Builder layersBuilder = new AlertDialog.Builder(this);
layersBuilder.setTitle(R.string.layers);
layersBuilder.setMultiChoiceItems(R.array.layers,
new boolean[] {
layers[LAYER_SATTELITE_INDEX],
layers[LAYER_STREET_VIEW_INDEX],
layers[LAYER_TRAFFIC_INDEX]
},
new DialogInterface.OnMultiChoiceClickListener() {
@Override
public void onClick(DialogInterface dialog, int which, boolean isChecked) {
layers[which] = isChecked;
switch (which) {
case LAYER_SATTELITE_INDEX:
mapView.setSatellite(isChecked);
prefs.setLayerUsed(LAYER_SATTELITE_KEY, isChecked);
break;
case LAYER_STREET_VIEW_INDEX:
mapView.setStreetView(isChecked);
prefs.setLayerUsed(LAYER_STREET_VIEW_KEY, isChecked);
break;
case LAYER_TRAFFIC_INDEX:
mapView.setTraffic(isChecked);
prefs.setLayerUsed(LAYER_TRAFFIC_KEY, isChecked);
break;
}
}
});
layersBuilder.show();
break;
case R.id.preferences:
intent = new Intent(this, PrefsActivity.class);
startActivityForResult(intent, SHOW_PREFS_REQUEST);
return true;
case R.id.help:
intent = new Intent(this, HelpActivity.class);
startActivity(intent);
return true;
}
return false;
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Log.d(TAG, "Activity result: request=" + requestCode + ", result=" + resultCode);
switch (requestCode) {
case SHOW_PREFS_REQUEST:
if ((resultCode & PrefsActivity.RESULT_RESTART) == PrefsActivity.RESULT_RESTART) {
finish();
startActivity(new Intent(this, getClass()));
return;
}
if ((resultCode & PrefsActivity.RESULT_REFRESH) == PrefsActivity.RESULT_REFRESH) {
animateRefreshButton(true);
refreshData();
}
break;
}
}
@Override
protected boolean isRouteDisplayed() {
return false;
}
/**
* Handler dari refresh data.
*/
private final Handler handler = new Handler() {
@Override
public void handleMessage(Message message) {
Log.d(TAG, "Handler: message.what=" + message.what);
switch (message.what) {
case EarthquakeReceiver.NEW_QUAKE_FOUND_WHAT:
// Terdapat gempa baru, load data dari provider
updateQuakes(false);
case EarthquakeReceiver.NO_NEW_QUAKE_WHAT:
// Tidak terdapat data, hanya menghentikan animasi refresh
animateRefreshButton(false);
break;
case EarthquakeReceiver.NETWORK_ERROR_WHAT:
// Perlihatkan dialog network error
animateRefreshButton(false);
Toast.makeText(EarthquakeMapActivity.this,
R.string.dialog_network_error_title, Toast.LENGTH_LONG).show();
break;
}
}
};
/**
* Listener lokasi.
*/
private final LocationListener locationListener = new BaseLocationListener() {
@Override
public void onLocationChanged(Location location) {
Log.d(TAG, "Lokasi berubah, Lat: " + location.getLatitude() + ", Lng: "
+ location.getLongitude());
EarthquakeMapActivity.this.location = location;
// Update location pada location overlay
//setupLocationOverlay();
showLocationMarker(location);
// Update location pada earthquake overlay
for (EarthquakeOverlay quakeOverlay : quakeOverlays) {
quakeOverlay.setLocation(location);
}
// Tampilkan lokasi saat ini di tengah-tengah
if (centerLocationWhenAvailable) {
centerLocationWhenAvailable = false;
int latitude = (int) (location.getLatitude() * 1E6);
int longitude = (int) (location.getLongitude() * 1E6);
GeoPoint point = new GeoPoint(latitude, longitude);
mapController.animateTo(point);
}
}
};
/**
* Listener untuk ActionBar
*/
private final View.OnClickListener actionBarListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.refresh:
animateRefreshButton(true);
refreshData();
break;
case R.id.location:
if (prefs.isDetectLocation()) {
location = locationFinder.getLastLocation(LocationFinder.MAX_DISTANCE,
System.currentTimeMillis() + prefs.getInterval());
if (location != null) {
int latitude = (int) (location.getLatitude() * 1E6);
int longitude = (int) (location.getLongitude() * 1E6);
GeoPoint point = new GeoPoint(latitude, longitude);
mapController.animateTo(point);
} else {
centerLocationWhenAvailable = true;
Toast.makeText(getApplicationContext(), R.string.waiting_location,
Toast.LENGTH_SHORT).show();
}
} else {
if (location != null) {
int latitude = (int) (location.getLatitude() * 1E6);
int longitude = (int) (location.getLongitude() * 1E6);
GeoPoint point = new GeoPoint(latitude, longitude);
mapController.animateTo(point);
} else {
Toast.makeText(getApplicationContext(), R.string.unknown_location,
Toast.LENGTH_SHORT).show();
}
}
break;
}
}
};
}