package com.boombuler.piraten.map; import java.io.IOException; import java.util.ArrayList; import org.apache.http.HttpResponse; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.ByteArrayEntity; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.util.EntityUtils; import com.boombuler.piraten.map.data.DBAdapter; import com.boombuler.piraten.map.data.PlakatOverlayItem; import com.boombuler.piraten.map.proto.Api.AddRequest; import com.boombuler.piraten.map.proto.Api.BoundingBox; import com.boombuler.piraten.map.proto.Api.ChangeRequest; import com.boombuler.piraten.map.proto.Api.DeleteRequest; import com.boombuler.piraten.map.proto.Api.Plakat; import com.boombuler.piraten.map.proto.Api.Request; import com.boombuler.piraten.map.proto.Api.Response; import com.boombuler.piraten.map.proto.Api.ViewRequest; import com.boombuler.piraten.utils.MyHttpClient; import android.app.AlertDialog; import android.app.ProgressDialog; import android.content.Context; import android.content.DialogInterface; import android.content.SharedPreferences; import android.content.DialogInterface.OnCancelListener; import android.content.DialogInterface.OnDismissListener; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.os.Bundle; import android.preference.PreferenceManager; import android.util.Log; import org.osmdroid.util.GeoPoint; public class SyncController implements Runnable { private static final String TAG = "boombuler.synccontroller"; private final String mAPIUrl; private static final int BATCH_SIZE = 100; private final DefaultHttpClient mClient; private final String mUsername; private final String mPassword; private final PirateMap mContext; private ProgressDialog mProgress; private Runnable mOnCompleteListener; private Location mLocation = null; private double mSyncRange = 0; public SyncController(PirateMap context) { mContext = context; mClient = new MyHttpClient(context); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); mUsername = prefs.getString(SettingsActivity.KEY_USERNAME, ""); mPassword = prefs.getString(SettingsActivity.KEY_PASSWORD, ""); mSyncRange = Double.parseDouble(prefs.getString(SettingsActivity.KEY_SYNC_RANGE, "0")); mAPIUrl = prefs.getString(SettingsActivity.KEY_SERVER, "") + "api.php"; Log.d(TAG, "using server: "+mAPIUrl); } private double checkBoundsLatitude(double value) { value = Math.max(value, -90); return Math.min(value, +90); } private double checkBoundsLongitude(double value) { while (value < -180 || value > 180) { if (value < -180) value += 360; if (value > 180) value -= 360; } return value; } private BoundingBox.Builder getBoundingBox() { BoundingBox.Builder bbox = BoundingBox.newBuilder(); double yDiff = mSyncRange / 111120f; bbox.setNorth(checkBoundsLatitude(mLocation.getLatitude() + yDiff)); bbox.setSouth(checkBoundsLatitude(mLocation.getLatitude() - yDiff)); double xDiff = yDiff / Math.cos(Math.toRadians(mLocation.getLatitude())); bbox.setWest(checkBoundsLongitude(mLocation.getLongitude() - xDiff)); bbox.setEast(checkBoundsLongitude(mLocation.getLongitude() + xDiff)); return bbox; } private boolean RunRequest() { Request.Builder builder = Request.newBuilder() .setUsername(mUsername).setPassword(mPassword); if (mLocation != null && mSyncRange > 0) { // Check for ViewBox ViewRequest.Builder view = ViewRequest.newBuilder(); view.setViewBox(getBoundingBox()); builder.setViewRequest(view); } ArrayList<PlakatOverlayItem> inserted = new ArrayList<PlakatOverlayItem>(); ArrayList<PlakatOverlayItem> changed = new ArrayList<PlakatOverlayItem>(); ArrayList<Integer> deleted = new ArrayList<Integer>(); DBAdapter dba = new DBAdapter(mContext); try { dba.open(); dba.getChangedItems(inserted, changed, deleted); } finally { dba.close(); } for(PlakatOverlayItem itm : inserted) { builder.addAdd(SendNewItem(itm)); } for(PlakatOverlayItem itm : changed) { builder.addChange(SendChangedItem(itm)); } for(Integer id : deleted) { builder.addDelete(DeleteRequest.newBuilder().setId(id).build()); } Request request = builder.build(); HttpPost post = new HttpPost(mAPIUrl); post.setEntity(new ByteArrayEntity(request.toByteArray())); try { HttpResponse rp = mClient.execute(post); byte[] res = EntityUtils.toByteArray(rp.getEntity()); Response response = Response.parseFrom(res); int addedCnt = response.hasAddedCount() ? response.getAddedCount() : 0; int changedCnt = response.hasChangedCount() ? response.getChangedCount() : 0; int deletedCnt = response.hasDeletedCount() ? response.getDeletedCount() : 0; if (addedCnt == inserted.size() && changedCnt == changed.size() && deletedCnt == deleted.size()) { SetProgressText(R.string.sync_cleardata); ClearAllData(); LoadItems(response); return true; } } catch (ClientProtocolException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } return false; } private void LoadItems(Response resp) { SetProgressText(R.string.sync_loading); DBAdapter dba = new DBAdapter(mContext); try { dba.open(); dba.beginTransaction(); int insertCount = 0; for(Plakat plakat : resp.getPlakateList()) { int lon = (int)(plakat.getLon() * 1E6); int lat = (int)(plakat.getLat() * 1E6); int type = PlakatOverlayItem.TypeToTypeId(plakat.getType()); dba.Insert(plakat.getId(), lat, lon,type, plakat.getLastModifiedUser(), plakat.getComment()); insertCount++; if (insertCount >= BATCH_SIZE) { dba.setTransactionSuccessful(); dba.endTransaction(); dba.beginTransaction(); insertCount = 0; } } dba.setTransactionSuccessful(); } finally { dba.endTransaction(); dba.close(); } } private void ClearAllData() { DBAdapter dba = new DBAdapter(mContext); try { dba.open(); dba.ClearAllData(); } finally { dba.close(); } } private AddRequest SendNewItem(PlakatOverlayItem item) { AddRequest.Builder result = AddRequest.newBuilder(); result.setType(item.getTypeStr()); GeoPoint pt = item.getPoint(); double lat = ((double)pt.getLatitudeE6()) / 1E6; double lon = ((double)pt.getLongitudeE6()) / 1E6; result.setLat(lat); result.setLon(lon); result.setComment(item.getComment()); return result.build(); } private ChangeRequest SendChangedItem(PlakatOverlayItem item) { ChangeRequest.Builder result = ChangeRequest.newBuilder(); result.setId(item.getId()); result.setType(item.getTypeStr()); result.setComment(item.getComment()); return result.build(); } private void SetProgressText(int textid) { final String txt = mContext.getString(textid); if (mProgress != null) { mContext.runOnUiThread(new Runnable() { public void run() { mProgress.setMessage(txt); } }); } } private void ShowProgressDialog() { if (mProgress != null) { mContext.runOnUiThread(new Runnable() { public void run() { mProgress.show(); } }); } } private void CloseProgressDialog() { if (mProgress != null) { mContext.runOnUiThread(new Runnable() { public void run() { mProgress.dismiss(); } }); } } public void setOnCompleteListener(Runnable listener) { mOnCompleteListener = listener; } public void run() { SetProgressText(R.string.sync_sending); ShowProgressDialog(); boolean failed = false; try { if (RunRequest()) { SetProgressText(R.string.sync_processing); if (mOnCompleteListener != null) mContext.runOnUiThread(mOnCompleteListener); } else failed = true; } finally { CloseProgressDialog(); } if (failed) { final AlertDialog.Builder ab = new AlertDialog.Builder(mContext); ab.setIcon(android.R.drawable.ic_dialog_alert); ab.setTitle(R.string.alert_error); ab.setMessage(R.string.error_sync_failed); mContext.runOnUiThread(new Runnable() { public void run() { final AlertDialog ad = ab.create(); ad.setButton(mContext.getString(android.R.string.ok), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { ad.dismiss(); } }); ad.show(); } }); } } private void StartSync() { mProgress = new ProgressDialog(mContext); mProgress.setProgressStyle(ProgressDialog.STYLE_SPINNER); mProgress.setCancelable(false); mProgress.setTitle(R.string.menu_sync); mProgress.setOwnerActivity(mContext); new Thread(this).start(); } public void synchronize() { if (mSyncRange <= 0) { StartSync(); return; } final ProgressDialog progressDlg = new ProgressDialog(mContext); final LocationListener ll = new LocationListener() { public void onStatusChanged(String provider, int status, Bundle extras) { } public void onProviderEnabled(String provider) { } public void onProviderDisabled(String provider) { } public void onLocationChanged(Location location) { if (location.getAccuracy() <= 100) { progressDlg.dismiss(); mLocation = location; StartSync(); } } }; final LocationManager lm = (LocationManager)mContext.getSystemService(Context.LOCATION_SERVICE); progressDlg.setOwnerActivity(mContext); progressDlg.setCancelable(true); progressDlg.setCanceledOnTouchOutside(false); progressDlg.setIndeterminate(true); progressDlg.setProgressStyle(ProgressDialog.STYLE_SPINNER); progressDlg.setTitle(R.string.menu_sync); progressDlg.setMessage(mContext.getString(R.string.get_position)); progressDlg.setButton(mContext.getString(android.R.string.cancel), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { progressDlg.cancel(); } }); progressDlg.setOnCancelListener(new OnCancelListener() { public void onCancel(DialogInterface dialog) { lm.removeUpdates(ll); } }); progressDlg.setOnDismissListener(new OnDismissListener() { public void onDismiss(DialogInterface dialog) { lm.removeUpdates(ll); } }); progressDlg.show(); if (lm.isProviderEnabled(LocationManager.GPS_PROVIDER)) lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, ll); if (lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) lm.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, ll); } }