Java tutorial
/******************************************************************************* * Copyright (c) 2011 ETH Zurich. * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Public License v2.0 * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * * Contributors: * Paolo Carta - Implementation * Theus Hossmann - Implementation * Dominik Schatzmann - Message specification ******************************************************************************/ package ch.ethz.twimight.net.opportunistic; import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.lang.Thread.UncaughtExceptionHandler; import java.util.Date; import java.util.Random; import org.apache.http.util.ByteArrayBuffer; import org.json.JSONException; import org.json.JSONObject; import com.nostra13.universalimageloader.core.ImageLoader; import android.app.AlarmManager; import android.app.Service; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.database.Cursor; import android.graphics.Bitmap; import android.net.Uri; import android.os.AsyncTask; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.preference.PreferenceManager; import android.text.Html; import android.util.Base64; import android.util.Log; import ch.ethz.twimight.R; import ch.ethz.twimight.activities.LoginActivity; import ch.ethz.twimight.activities.TwimightBaseActivity; import ch.ethz.twimight.data.HtmlPagesDbHelper; import ch.ethz.twimight.data.MacsDBHelper; import ch.ethz.twimight.net.Html.HtmlPage; import ch.ethz.twimight.net.twitter.DirectMessages; import ch.ethz.twimight.net.twitter.Tweets; import ch.ethz.twimight.net.twitter.TweetsContentProvider; import ch.ethz.twimight.net.twitter.TwitterUsers; import ch.ethz.twimight.util.Constants; import ch.ethz.twimight.util.InternalStorageHelper; import ch.ethz.twimight.util.SDCardHelper; /** * This is the thread for scanning for Bluetooth peers. * * @author theus * @author pcarta */ public class ScanningService extends Service implements DevicesReceiver.ScanningFinished, StateChangedReceiver.BtSwitchingFinished { private static final String T = "btdebug"; private static final String TAG = ScanningService.class.getSimpleName(); /** For Debugging */ private static final String WAKE_LOCK = "ScanningServiceWakeLock"; private static final int MAX_IMAGE_DIMENSIONS_PX = 500; public Handler handler; /** Handler for delayed execution of the thread */ // manage bluetooth communication public BluetoothComms bluetoothHelper = null; // private Date lastScan; private MacsDBHelper dbHelper; StateChangedReceiver stateReceiver; private Cursor cursor; ConnectionAttemptTimeout connTimeout; EstablishedConnectionTimeout connectionTimeout; WakeLock wakeLock; public boolean closing_request_sent = false; public static final int STATE_SCANNING = 1; public static final int STATE_IDLE = 0; private static final long CONNECTING_TIMEOUT = 8000L; private static final long CONNECTION_TIMEOUT = 10000L; private static final String TYPE = "message_type"; public static final int MESSAGE_TYPE_TWEET = 0; public static final int MESSAGE_TYPE_DM = 1; public static final int MESSAGE_TYPE_PHOTO = 2; public static final int MESSAGE_TYPE_HTML = 3; public static final String FORCED_BLUE_SCAN = "forced_bluetooth_scan"; // photo private String photoPath; private static final String PHOTO_PATH = "twimight_photos"; // html private HtmlPagesDbHelper htmlDbHelper; // SDcard helper private SDCardHelper sdCardHelper; // SDcard checking var boolean isSDAvail = false; boolean isSDWritable = false; File SDcardPath = null; DevicesReceiver receiver; BluetoothAdapter mBtAdapter; volatile boolean restartingBlue = false; // has a scan been skipped because the adapter was restarting? private boolean mScanPending = false; @Override public void onCreate() { // TODO Auto-generated method stub super.onCreate(); handler = new Handler(); // set up Bluetooth bluetoothHelper = new BluetoothComms(this, mHandler); bluetoothHelper.start(); dbHelper = new MacsDBHelper(getApplicationContext()); dbHelper.open(); // sdCard helper sdCardHelper = new SDCardHelper(); // htmldb helper htmlDbHelper = new HtmlPagesDbHelper(getApplicationContext()); htmlDbHelper.open(); mBtAdapter = BluetoothAdapter.getDefaultAdapter(); } private void registerDevicesReceiver() { unregisterDevReceiver(); receiver = new DevicesReceiver(getApplicationContext()); receiver.setListener(this); IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); registerReceiver(receiver, filter); filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); registerReceiver(receiver, filter); } @Override public int onStartCommand(Intent intent, int flags, int startId) { super.onStartCommand(intent, flags, startId); Log.d(T, "onStartCommand()"); // Thread.setDefaultUncaughtExceptionHandler(new // CustomExceptionHandler()); ScanningAlarm.releaseWakeLock(); getWakeLock(this); // Register for broadcasts when discovery has finished registerDevicesReceiver(); float probability; if (intent != null && intent.getBooleanExtra(FORCED_BLUE_SCAN, true)) probability = 0; else { // get a random number Random r = new Random(System.currentTimeMillis()); probability = r.nextFloat(); } initiateScanningRound(probability); return START_STICKY; } private void initiateScanningRound(float probability) { if (mBtAdapter != null && !restartingBlue) { if (probability <= 1) { // If we're already discovering, stop it if (mBtAdapter.isDiscovering()) { mBtAdapter.cancelDiscovery(); } // Request discover from BluetoothAdapter dbHelper.updateMacsDeActive(); bluetoothHelper.stop(); boolean ret = mBtAdapter.startDiscovery(); BluetoothStatus.getInstance().setStatusDescription(getString(R.string.btstatus_searching)); Log.d(T, "started discovery (ret=" + ret + ")"); Log.d(T, "discovery running: " + mBtAdapter.isDiscovering()); } mScanPending = false; } else { Log.d(T, "skipping scan (mBtAdapter=" + mBtAdapter + ", restartingBlue=" + restartingBlue + ")"); mScanPending = true; stopSelf(); } } public class CustomExceptionHandler implements UncaughtExceptionHandler { @Override public void uncaughtException(Thread t, Throwable e) { Log.e(TAG, "error ", e); ScanningService.this.stopSelf(); AlarmManager mgr = (AlarmManager) LoginActivity.getInstance().getSystemService(Context.ALARM_SERVICE); mgr.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), LoginActivity.getRestartIntent()); System.exit(2); } } /** * Acquire the Wake Lock * * @param context */ void getWakeLock(Context context) { releaseWakeLock(); PowerManager mgr = (PowerManager) context.getSystemService(Context.POWER_SERVICE); wakeLock = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKE_LOCK); wakeLock.acquire(); } /** * We have to make sure to release the wake lock after the TDSThread is * done! * * @param context */ void releaseWakeLock() { if (wakeLock != null) if (wakeLock.isHeld()) wakeLock.release(); } @Override public void onDestroy() { Log.i(TAG, "inside onDestroy"); mHandler.removeMessages(Constants.MESSAGE_CONNECTION_FAILED); mHandler.removeMessages(Constants.MESSAGE_CONNECTION_LOST); mHandler.removeMessages(Constants.MESSAGE_CONNECTION_SUCCEEDED); mHandler.removeMessages(Constants.BLUETOOTH_RESTART); releaseWakeLock(); bluetoothHelper.stop(); // Make sure we're not doing discovery anymore if (mBtAdapter != null) { mBtAdapter.cancelDiscovery(); } if (receiver != null) Log.i(TAG, "receiver not null"); unregisterDevReceiver(); unregisterStateReceiver(); super.onDestroy(); } /** * Start the scanning. * * @return true if the connection with the TDS was successful, false * otherwise. */ private boolean startScanning() { // Get a cursor over all "active" MACs in the DB cursor = dbHelper.fetchActiveMacs(); Log.i(T, "active macs: " + cursor.getCount()); if (cursor.moveToFirst()) { // Get the field values String mac = cursor.getString(cursor.getColumnIndex(MacsDBHelper.KEY_MAC)); Log.i(T, "Connection Attempt to: " + mac + " (" + dbHelper.fetchMacSuccessful(mac) + "/" + dbHelper.fetchMacAttempts(mac) + ")"); // if (bluetoothHelper.getState() == bluetoothHelper.STATE_LISTEN) { // if ( (System.currentTimeMillis() - // dbHelper.getLastSuccessful(mac) ) > // Constants.MEETINGS_INTERVAL) { // If we're already discovering, stop it if (mBtAdapter.isDiscovering()) { mBtAdapter.cancelDiscovery(); } bluetoothHelper.connect(mac); connTimeout = new ConnectionAttemptTimeout(); handler.postDelayed(connTimeout, CONNECTING_TIMEOUT); // timeout // for // the // conn // attempt // } else { // Log.i(TAG,"skipping connection, last meeting was too recent"); // nextScanning(); // } // } else if (bluetoothHelper.getState() != // bluetoothHelper.STATE_CONNECTED) { // bluetoothHelper.start(); // // } } else stopScanning(); return false; } private class ConnectionAttemptTimeout implements Runnable { @Override public void run() { if (bluetoothHelper != null) { if (bluetoothHelper.getState() == BluetoothComms.STATE_CONNECTING) { bluetoothHelper.start(); } connTimeout = null; } } } private class EstablishedConnectionTimeout implements Runnable { @Override public void run() { if (bluetoothHelper != null) { if (bluetoothHelper.getState() == BluetoothComms.STATE_CONNECTED) { bluetoothHelper.start(); } connectionTimeout = null; } } } /** * Proceed to the next MAC address */ private void nextScanning() { if (cursor == null || bluetoothHelper.getState() == BluetoothComms.STATE_CONNECTED) stopScanning(); else { // do we have another MAC in the cursor? if (cursor.moveToNext()) { Log.i(TAG, "scanning for the next peer"); String mac = cursor.getString(cursor.getColumnIndex(MacsDBHelper.KEY_MAC)); Log.i(T, "Connection Attempt to: " + mac + " (" + dbHelper.fetchMacSuccessful(mac) + "/" + dbHelper.fetchMacAttempts(mac) + ")"); // if ( (System.currentTimeMillis() - // dbHelper.getLastSuccessful(mac) ) > // Constants.MEETINGS_INTERVAL) { Log.i(TAG, "Connection attempt to: " + mac + " (" + dbHelper.fetchMacSuccessful(mac) + "/" + dbHelper.fetchMacAttempts(mac) + ")"); // If we're already discovering, stop it if (mBtAdapter.isDiscovering()) { mBtAdapter.cancelDiscovery(); } bluetoothHelper.connect(mac); connTimeout = new ConnectionAttemptTimeout(); handler.postDelayed(connTimeout, CONNECTING_TIMEOUT); // timeout // for // the // conn // attempt // } else { // Log.i(TAG,"skipping connection, last meeting was too recent"); // nextScanning(); // } } else stopScanning(); } } /** * Terminates one round of scanning: cleans up and reschedules next scan */ private void stopScanning() { if (cursor != null) { cursor.close(); cursor = null; } removeConnectionAttemptTimeout(); // restart bluetooth because it MIGHT help to keep in it a good state Message msg = mHandler.obtainMessage(Constants.BLUETOOTH_RESTART, -1, -1, null); mHandler.sendMessage(msg); } private void removeConnectionAttemptTimeout() { if (connTimeout != null) { // I need to remove the timeout started at // the beginning handler.removeCallbacks(connTimeout); connTimeout = null; } } private void removeEstablishedConnectionTimeout() { if (connectionTimeout != null) { // I need to remove the timeout started // at the beginning handler.removeCallbacks(connectionTimeout); connectionTimeout = null; } } /** * The Handler that gets information back from the BluetoothService */ private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case Constants.MESSAGE_READ: if (msg.obj.toString().equals("<closing_request>")) { bluetoothHelper.write("<ack_closing_request>"); } else if (msg.obj.toString().equals("<ack_closing_request>")) { if (TwimightBaseActivity.D) Log.i(TAG, "ack closing request received, connection shutdown"); bluetoothHelper.start(); } else new ProcessDataReceived().execute(msg.obj.toString()); // not // String, // object // instead break; case Constants.MESSAGE_CONNECTION_SUCCEEDED: if (TwimightBaseActivity.D) Log.d(TAG, "connection succeeded"); removeConnectionAttemptTimeout(); connectionTimeout = new EstablishedConnectionTimeout(); handler.postDelayed(connectionTimeout, CONNECTION_TIMEOUT); // timeout // for // the // conn // attempt // Insert successful connection into DB dbHelper.updateMacSuccessful(msg.obj.toString(), 1); // Here starts the protocol for Tweet exchange. Long last = dbHelper.getLastSuccessful(msg.obj.toString()); // new SendDisasterData(msg.obj.toString()).execute(last); sendDisasterTweets(last); sendDisasterDM(last); if (bluetoothHelper != null) { bluetoothHelper.write("<closing_request>"); dbHelper.setLastSuccessful(msg.obj.toString(), new Date()); } break; case Constants.MESSAGE_CONNECTION_FAILED: if (TwimightBaseActivity.D) Log.i(TAG, "connection failed"); // Insert failed connection into DB dbHelper.updateMacAttempts(msg.obj.toString(), 1); removeConnectionAttemptTimeout(); // Next scan if (bluetoothHelper != null) nextScanning(); break; case Constants.MESSAGE_CONNECTION_LOST: if (TwimightBaseActivity.D) Log.i(TAG, "connection lost"); // Next scan removeEstablishedConnectionTimeout(); if (bluetoothHelper != null) nextScanning(); break; case Constants.BLUETOOTH_RESTART: if (TwimightBaseActivity.D) Log.i(T, "restarting Bluetooth"); unregisterStateReceiver(); stateReceiver = new StateChangedReceiver(); IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED); stateReceiver.setListener(ScanningService.this); registerReceiver(stateReceiver, filter); if (mBtAdapter != null) { if (mBtAdapter.isEnabled()) { Log.d(T, "disbling bt"); mBtAdapter.disable(); } else { Log.d(T, "bt disabled. enabling now..."); mBtAdapter.enable(); } } BluetoothStatus.getInstance().setStatusDescription(getString(R.string.btstatus_resetting)); restartingBlue = true; break; } } }; /** * process all the data received via bluetooth * * @author pcarta */ private class ProcessDataReceived extends AsyncTask<String, Void, Void> { @Override protected Void doInBackground(String... s) { JSONObject o; try { // if input parameter is String, then cast it to String o = new JSONObject(s[0]); if (o.getInt(TYPE) == MESSAGE_TYPE_TWEET) { Log.d("disaster", "receive a tweet"); processTweet(o); } else if (o.getInt(TYPE) == MESSAGE_TYPE_PHOTO) { Log.d("disaster", "receive a photo"); processPhoto(o); } else if (o.getInt(TYPE) == MESSAGE_TYPE_HTML) { Log.d("disaster", "receive xml"); processHtml(o); } else { Log.d("disaster", "receive a dm"); processDM(o); } getContentResolver().notifyChange(Tweets.TABLE_TIMELINE_URI, null); // if input parameter is a photo, then extract the photo and // save it locally } catch (JSONException e) { Log.e(TAG, "error", e); } return null; } } private void processDM(JSONObject o) { Log.i(TAG, "processing DM"); try { ContentValues dmValues = getDmContentValues(o); if (!dmValues.getAsLong(DirectMessages.COL_SENDER).toString() .equals(LoginActivity.getTwitterId(getApplicationContext()))) { ContentValues cvUser = getUserCV(o); // insert the tweet Uri insertUri = Uri.parse("content://" + DirectMessages.DM_AUTHORITY + "/" + DirectMessages.DMS + "/" + DirectMessages.DMS_LIST + "/" + DirectMessages.DMS_SOURCE_DISASTER); getContentResolver().insert(insertUri, dmValues); // insert the user Uri insertUserUri = Uri.parse( "content://" + TwitterUsers.TWITTERUSERS_AUTHORITY + "/" + TwitterUsers.TWITTERUSERS); getContentResolver().insert(insertUserUri, cvUser); } } catch (JSONException e1) { Log.e(TAG, "Exception while receiving disaster dm ", e1); } } private void processTweet(JSONObject o) { try { Log.i(TAG, "processTweet"); ContentValues cvTweet = getTweetCV(o); cvTweet.put(Tweets.COL_BUFFER, Tweets.BUFFER_DISASTER); // we don't enter our own tweets into the DB. if (!cvTweet.getAsLong(Tweets.COL_USER_TID).toString() .equals(LoginActivity.getTwitterId(getApplicationContext()))) { ContentValues cvUser = getUserCV(o); // insert the tweet Uri insertUri = Uri.parse("content://" + Tweets.TWEET_AUTHORITY + "/" + Tweets.TWEETS + "/" + Tweets.TWEETS_TABLE_TIMELINE + "/" + Tweets.TWEETS_SOURCE_DISASTER); getContentResolver().insert(insertUri, cvTweet); // insert the user Uri insertUserUri = Uri.parse( "content://" + TwitterUsers.TWITTERUSERS_AUTHORITY + "/" + TwitterUsers.TWITTERUSERS); getContentResolver().insert(insertUserUri, cvUser); } } catch (JSONException e1) { Log.e(TAG, "Exception while receiving disaster tweet ", e1); } } private void processPhoto(JSONObject o) { try { String jsonString = o.getString("image"); String userID = o.getString("userID"); String photoFileName = o.getString("photoName"); Log.d(TAG, "processPhoto " + jsonString); // locate the directory where the photos are stored photoPath = PHOTO_PATH + "/" + userID; String[] filePath = { photoPath }; if (sdCardHelper.checkSDState(filePath)) { File targetFile = sdCardHelper.getFileFromSDCard(photoPath, photoFileName);// photoFileParent, // photoFilename)); saveFile(targetFile, jsonString); } } catch (JSONException e1) { Log.e(TAG, "Exception while receiving disaster tweet photo", e1); } } private void processHtml(JSONObject o) { try { Log.i(TAG, "process HTML"); String xmlContent = o.getString(HtmlPage.COL_HTML); String filename = o.getString(HtmlPage.COL_FILENAME); Long tweetId = o.getLong(HtmlPage.COL_DISASTERID); String htmlUrl = o.getString(HtmlPage.COL_URL); String[] filePath = { HtmlPage.HTML_PATH + "/" + LoginActivity.getTwitterId(getApplicationContext()) }; if (sdCardHelper.checkSDState(filePath)) { File targetFile = sdCardHelper.getFileFromSDCard(filePath[0], filename);// photoFileParent, // photoFilename)); if (saveFile(targetFile, xmlContent)) { // downloaded = 1; } } htmlDbHelper.insertPage(htmlUrl, filename, tweetId, 0); } catch (JSONException e1) { Log.e(TAG, "Exception while receiving disaster tweet photo", e1); } } private boolean saveFile(File file, String fileContent) { try { FileOutputStream fOut = new FileOutputStream(file); byte[] decodedString = Base64.decode(fileContent, Base64.DEFAULT); fOut.write(decodedString); fOut.close(); return true; } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return false; } private void sendDisasterDM(Long last) { Uri uriQuery = Uri.parse("content://" + DirectMessages.DM_AUTHORITY + "/" + DirectMessages.DMS + "/" + DirectMessages.DMS_LIST + "/" + DirectMessages.DMS_SOURCE_DISASTER); Cursor c = getContentResolver().query(uriQuery, null, null, null, null); Log.i(TAG, "c.getCount: " + c.getCount()); if (c.getCount() > 0) { c.moveToFirst(); while (!c.isAfterLast()) { if (c.getLong(c.getColumnIndex(DirectMessages.COL_RECEIVED)) > (last - 1 * 30 * 1000L)) { JSONObject dmToSend; try { dmToSend = getDmJSON(c); if (dmToSend != null) { Log.i(TAG, "sending dm"); bluetoothHelper.write(dmToSend.toString()); } } catch (JSONException ex) { } } c.moveToNext(); } } c.close(); } private void sendDisasterTweets(Long last) { // get disaster tweets Uri queryUri = Uri.parse("content://" + Tweets.TWEET_AUTHORITY + "/" + Tweets.TWEETS + "/" + Tweets.TWEETS_TABLE_TIMELINE + "/" + Tweets.TWEETS_SOURCE_DISASTER); Cursor c = getContentResolver().query(queryUri, null, null, null, null); Log.d(TAG, "count:" + String.valueOf(c.getCount())); boolean prefWebShare = PreferenceManager.getDefaultSharedPreferences(this).getBoolean("prefWebShare", false); Log.d(TAG, "web share:" + String.valueOf(prefWebShare)); if (c.getCount() > 0) { c.moveToFirst(); while (!c.isAfterLast()) { try { if (prefWebShare) { if (c.getInt(c.getColumnIndex(Tweets.COL_HTML_PAGES)) == 1) { if (c.getLong(c.getColumnIndex(Tweets.COL_RECEIVED)) > (last - 10 * 60 * 1000L)) { JSONObject toSend; toSend = getJSON(c); if (toSend != null) { // if there is a photo related to this // tweet, send it first! if (c.getString(c.getColumnIndex(Tweets.COL_MEDIA_URIS)) != null) { sendDisasterPhoto(c); } Log.i(TAG, "sending tweet"); Log.d(TAG, toSend.toString(5)); bluetoothHelper.write(toSend.toString()); } sendDisasterHtmls(c); } } } else { if (c.getString(c.getColumnIndex(Tweets.COL_MEDIA_URIS)) != null) { if (c.getLong(c.getColumnIndex(Tweets.COL_RECEIVED)) > (last - 5 * 60 * 1000L)) { JSONObject toSend; toSend = getJSON(c); if (toSend != null) { // if there is a photo related to this // tweet, send it first! if (c.getString(c.getColumnIndex(Tweets.COL_MEDIA_URIS)) != null) { sendDisasterPhoto(c); } Log.i(TAG, "sending tweet"); Log.d(TAG, toSend.toString(5)); bluetoothHelper.write(toSend.toString()); } } } else if (c.getLong(c.getColumnIndex(Tweets.COL_RECEIVED)) > (last - 1 * 30 * 1000L)) { JSONObject toSend; toSend = getJSON(c); if (toSend != null) { Log.i(TAG, "sending tweet"); Log.d(TAG, toSend.toString(5)); bluetoothHelper.write(toSend.toString()); } } } } catch (JSONException e) { Log.e(TAG, "exception ", e); } c.moveToNext(); } } // else // bluetoothHelper.write("####CLOSING_REQUEST####"); c.close(); } private boolean sendDisasterPhoto(Cursor c) throws JSONException { String photoFileUri = c.getString(c.getColumnIndex(Tweets.COL_MEDIA_URIS)); SDCardHelper sdCardHelper = new SDCardHelper(); String encodedPhoto = sdCardHelper.getImageAsBas64Jpeg(photoFileUri, MAX_IMAGE_DIMENSIONS_PX); JSONObject toSendPhoto = new JSONObject("{\"image\":\"" + encodedPhoto + "\"}"); toSendPhoto.put(TYPE, MESSAGE_TYPE_PHOTO); String userID = String.valueOf(c.getLong(c.getColumnIndex(TwitterUsers.COL_TWITTER_USER_ID))); toSendPhoto.put("userID", userID); Uri uri = Uri.parse(photoFileUri); String photoFileName = uri.getLastPathSegment(); Log.d(TAG, "photoFileName:+ " + photoFileName); toSendPhoto.put("photoName", photoFileName); bluetoothHelper.write(toSendPhoto.toString()); return true; } private String getProcessedImage(String photoFileUri) { Bitmap bitmap = ImageLoader.getInstance().loadImageSync(photoFileUri); String encodedImage = null; if (bitmap != null) { // scale down large images if (bitmap.getHeight() > MAX_IMAGE_DIMENSIONS_PX || bitmap.getWidth() > MAX_IMAGE_DIMENSIONS_PX) { double shrinkFactor = ((double) MAX_IMAGE_DIMENSIONS_PX) / Math.max(bitmap.getWidth(), bitmap.getHeight()); bitmap = Bitmap.createScaledBitmap(bitmap, (int) (bitmap.getWidth() * shrinkFactor), (int) (bitmap.getHeight() * shrinkFactor), false); } ByteArrayOutputStream byteArrayBitmapStream = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.JPEG, 100, byteArrayBitmapStream); byte[] bytes = byteArrayBitmapStream.toByteArray(); encodedImage = Base64.encodeToString(bytes, Base64.DEFAULT); } return encodedImage; } private void sendDisasterHtmls(Cursor c) throws JSONException { JSONObject toSendXml; // String userId = String.valueOf(c.getLong(c // .getColumnIndex(Tweets.COL_TWITTERUSER))); String substr = Html.fromHtml(c.getString(c.getColumnIndex(Tweets.COL_TEXT))).toString(); String[] strarr = substr.split(" "); // check the urls of the tweet for (String subStrarr : strarr) { if (subStrarr.indexOf("http://") >= 0 || subStrarr.indexOf("https://") >= 0) { String subUrl = null; if (subStrarr.indexOf("http://") >= 0) { subUrl = subStrarr.substring(subStrarr.indexOf("http://")); } else if (subStrarr.indexOf("https://") >= 0) { subUrl = subStrarr.substring(subStrarr.indexOf("https://")); } Cursor cursorHtml = htmlDbHelper.getPageInfo(subUrl); if (cursorHtml != null) { if (!cursorHtml.isNull(cursorHtml.getColumnIndex(HtmlPage.COL_FILENAME))) { String[] filePath = { HtmlPage.HTML_PATH + "/" + LoginActivity.getTwitterId(this) }; String filename = cursorHtml.getString(cursorHtml.getColumnIndex(HtmlPage.COL_FILENAME)); Long tweetId = cursorHtml.getLong(cursorHtml.getColumnIndex(HtmlPage.COL_DISASTERID)); if (sdCardHelper.checkSDState(filePath)) { File xmlFile = sdCardHelper.getFileFromSDCard(filePath[0], filename); if (xmlFile.exists()) { toSendXml = getJSONFromXml(xmlFile); toSendXml.put(HtmlPage.COL_URL, subUrl); toSendXml.put(HtmlPage.COL_FILENAME, filename); toSendXml.put(HtmlPage.COL_DISASTERID, tweetId); Log.d(TAG, "sending htmls"); Log.d(TAG, toSendXml.toString(5)); bluetoothHelper.write(toSendXml.toString()); } } } } } } } private JSONObject getJSONFromXml(File xml) { try { JSONObject jsonObj = new JSONObject(); FileInputStream xmlStream = null; try { xmlStream = new FileInputStream(xml); ByteArrayOutputStream bos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int length; while ((length = xmlStream.read(buffer)) != -1) { bos.write(buffer, 0, length); } byte[] b = bos.toByteArray(); String xmlString = Base64.encodeToString(b, Base64.DEFAULT); jsonObj.put(HtmlPage.COL_HTML, xmlString); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { if (xmlStream != null) { try { xmlStream.close(); } catch (IOException e) { e.printStackTrace(); } } } jsonObj.put(TYPE, MESSAGE_TYPE_HTML); return jsonObj; } catch (JSONException e) { // TODO Auto-generated catch block Log.d(TAG, "exception:" + e.getMessage()); return null; } } /** * Creates a JSON Object from a direct message * * @param c * @return * @throws JSONException */ private JSONObject getDmJSON(Cursor c) throws JSONException { JSONObject o = new JSONObject(); if (c.getColumnIndex(DirectMessages.COL_RECEIVER) < 0 || c.getColumnIndex(DirectMessages.COL_SENDER) < 0 || c.isNull(c.getColumnIndex(DirectMessages.COL_CRYPTEXT))) { Log.i(TAG, "missing users data"); return null; } else { o.put(TYPE, MESSAGE_TYPE_DM); o.put(DirectMessages.COL_DISASTERID, c.getLong(c.getColumnIndex(DirectMessages.COL_DISASTERID))); o.put(DirectMessages.COL_CRYPTEXT, c.getString(c.getColumnIndex(DirectMessages.COL_CRYPTEXT))); o.put(DirectMessages.COL_SENDER, c.getString(c.getColumnIndex(DirectMessages.COL_SENDER))); if (c.getColumnIndex(DirectMessages.COL_CREATED) >= 0) o.put(DirectMessages.COL_CREATED, c.getLong(c.getColumnIndex(DirectMessages.COL_CREATED))); o.put(DirectMessages.COL_RECEIVER, c.getLong(c.getColumnIndex(DirectMessages.COL_RECEIVER))); o.put(DirectMessages.COL_RECEIVER_SCREENNAME, c.getString(c.getColumnIndex(DirectMessages.COL_RECEIVER_SCREENNAME))); o.put(DirectMessages.COL_DISASTERID, c.getLong(c.getColumnIndex(DirectMessages.COL_DISASTERID))); o.put(DirectMessages.COL_SIGNATURE, c.getString(c.getColumnIndex(DirectMessages.COL_SIGNATURE))); o.put(DirectMessages.COL_CERTIFICATE, c.getString(c.getColumnIndex(DirectMessages.COL_CERTIFICATE))); return o; } } /** * Creates a JSON Object from a Tweet TODO: Move this where it belongs! * * @param c * @return * @throws JSONException */ protected JSONObject getJSON(Cursor c) throws JSONException { JSONObject o = new JSONObject(); if (c.getColumnIndex(Tweets.COL_USER_TID) < 0 || c.getColumnIndex(TwitterUsers.COL_SCREEN_NAME) < 0) { Log.i(TAG, "missing user data"); return null; } else { o.put(Tweets.COL_USER_TID, c.getLong(c.getColumnIndex(Tweets.COL_USER_TID))); o.put(TYPE, MESSAGE_TYPE_TWEET); o.put(TwitterUsers.COL_SCREEN_NAME, c.getString(c.getColumnIndex(TwitterUsers.COL_SCREEN_NAME))); if (c.getColumnIndex(Tweets.COL_CREATED_AT) >= 0) o.put(Tweets.COL_CREATED_AT, c.getLong(c.getColumnIndex(Tweets.COL_CREATED_AT))); if (c.getColumnIndex(Tweets.COL_CERTIFICATE) >= 0) o.put(Tweets.COL_CERTIFICATE, c.getString(c.getColumnIndex(Tweets.COL_CERTIFICATE))); if (c.getColumnIndex(Tweets.COL_SIGNATURE) >= 0) o.put(Tweets.COL_SIGNATURE, c.getString(c.getColumnIndex(Tweets.COL_SIGNATURE))); if (c.getColumnIndex(Tweets.COL_TEXT) >= 0) o.put(Tweets.COL_TEXT, c.getString(c.getColumnIndex(Tweets.COL_TEXT))); if (c.getColumnIndex(Tweets.COL_REPLY_TO_TWEET_TID) >= 0) o.put(Tweets.COL_REPLY_TO_TWEET_TID, c.getLong(c.getColumnIndex(Tweets.COL_REPLY_TO_TWEET_TID))); if (c.getColumnIndex(Tweets.COL_LAT) >= 0) o.put(Tweets.COL_LAT, c.getDouble(c.getColumnIndex(Tweets.COL_LAT))); if (c.getColumnIndex(Tweets.COL_LNG) >= 0) o.put(Tweets.COL_LNG, c.getDouble(c.getColumnIndex(Tweets.COL_LNG))); if (!c.isNull(c.getColumnIndex(Tweets.COL_MEDIA_URIS))) { String photoUri = c.getString(c.getColumnIndex(Tweets.COL_MEDIA_URIS)); Uri uri = Uri.parse(photoUri); o.put(Tweets.COL_MEDIA_URIS, uri.getLastPathSegment()); } if (c.getColumnIndex(Tweets.COL_HTML_PAGES) >= 0) o.put(Tweets.COL_HTML_PAGES, c.getString(c.getColumnIndex(Tweets.COL_HTML_PAGES))); if (c.getColumnIndex(Tweets.COL_SOURCE) >= 0) o.put(Tweets.COL_SOURCE, c.getString(c.getColumnIndex(Tweets.COL_SOURCE))); if (c.getColumnIndex(Tweets.COL_TID) >= 0 && !c.isNull(c.getColumnIndex(Tweets.COL_TID))) o.put(Tweets.COL_TID, c.getLong(c.getColumnIndex(Tweets.COL_TID))); if (c.getColumnIndex(TwitterUsers.COL_PROFILE_IMAGE_URI) >= 0 && c.getColumnIndex(TweetsContentProvider.COL_USER_ROW_ID) >= 0) { String imageUri = c.getString(c.getColumnIndex(TwitterUsers.COL_PROFILE_IMAGE_URI)); Bitmap profileImage = ImageLoader.getInstance().loadImageSync(imageUri); if (profileImage != null) { ByteArrayOutputStream stream = new ByteArrayOutputStream(); profileImage.compress(Bitmap.CompressFormat.PNG, 100, stream); byte[] byteArray = stream.toByteArray(); String profileImageBase64 = Base64.encodeToString(byteArray, Base64.DEFAULT); o.put(TwitterUsers.JSON_FIELD_PROFILE_IMAGE, profileImageBase64); } } return o; } } public static byte[] toByteArray(InputStream in) throws IOException { BufferedInputStream bis = new BufferedInputStream(in); ByteArrayBuffer baf = new ByteArrayBuffer(2048); // get the bytes one by one int current = 0; while ((current = bis.read()) != -1) { baf.append((byte) current); } return baf.toByteArray(); } /** * Creates content values for a Tweet from a JSON object TODO: Move this to * where it belongs * * @param o * @return * @throws JSONException */ protected ContentValues getTweetCV(JSONObject o) throws JSONException { ContentValues cv = new ContentValues(); if (o.has(Tweets.COL_CERTIFICATE)) cv.put(Tweets.COL_CERTIFICATE, o.getString(Tweets.COL_CERTIFICATE)); if (o.has(Tweets.COL_SIGNATURE)) cv.put(Tweets.COL_SIGNATURE, o.getString(Tweets.COL_SIGNATURE)); if (o.has(Tweets.COL_CREATED_AT)) cv.put(Tweets.COL_CREATED_AT, o.getLong(Tweets.COL_CREATED_AT)); if (o.has(Tweets.COL_TEXT)) { cv.put(Tweets.COL_TEXT, o.getString(Tweets.COL_TEXT)); cv.put(Tweets.COL_TEXT_PLAIN, Html.fromHtml(o.getString(Tweets.COL_TEXT)).toString()); } if (o.has(Tweets.COL_USER_TID)) { cv.put(Tweets.COL_USER_TID, o.getLong(Tweets.COL_USER_TID)); } if (o.has(Tweets.COL_TID)) { cv.put(Tweets.COL_TID, o.getLong(Tweets.COL_TID)); } if (o.has(Tweets.COL_REPLY_TO_TWEET_TID)) cv.put(Tweets.COL_REPLY_TO_TWEET_TID, o.getLong(Tweets.COL_REPLY_TO_TWEET_TID)); if (o.has(Tweets.COL_LAT)) cv.put(Tweets.COL_LAT, o.getDouble(Tweets.COL_LAT)); if (o.has(Tweets.COL_LNG)) cv.put(Tweets.COL_LNG, o.getDouble(Tweets.COL_LNG)); if (o.has(Tweets.COL_SOURCE)) cv.put(Tweets.COL_SOURCE, o.getString(Tweets.COL_SOURCE)); if (o.has(Tweets.COL_MEDIA_URIS)) { String userID = cv.getAsString(Tweets.COL_USER_TID); photoPath = PHOTO_PATH + "/" + userID; String photoFileName = o.getString(Tweets.COL_MEDIA_URIS); File targetFile = sdCardHelper.getFileFromSDCard(photoPath, photoFileName); cv.put(Tweets.COL_MEDIA_URIS, Uri.fromFile(targetFile).toString()); } if (o.has(Tweets.COL_HTML_PAGES)) cv.put(Tweets.COL_HTML_PAGES, o.getString(Tweets.COL_HTML_PAGES)); if (o.has(TwitterUsers.COL_SCREEN_NAME)) { cv.put(Tweets.COL_SCREEN_NAME, o.getString(TwitterUsers.COL_SCREEN_NAME)); } return cv; } /** * Creates content values for a DM from a JSON object * * @param o * @return * @throws JSONException */ private ContentValues getDmContentValues(JSONObject o) throws JSONException { ContentValues cv = new ContentValues(); if (o.has(DirectMessages.COL_CERTIFICATE)) cv.put(DirectMessages.COL_CERTIFICATE, o.getString(DirectMessages.COL_CERTIFICATE)); if (o.has(DirectMessages.COL_SIGNATURE)) cv.put(DirectMessages.COL_SIGNATURE, o.getString(DirectMessages.COL_SIGNATURE)); if (o.has(DirectMessages.COL_CREATED)) cv.put(DirectMessages.COL_CREATED, o.getLong(DirectMessages.COL_CREATED)); if (o.has(DirectMessages.COL_CRYPTEXT)) cv.put(DirectMessages.COL_CRYPTEXT, o.getString(DirectMessages.COL_CRYPTEXT)); if (o.has(DirectMessages.COL_DISASTERID)) cv.put(DirectMessages.COL_DISASTERID, o.getLong(DirectMessages.COL_DISASTERID)); if (o.has(DirectMessages.COL_SENDER)) cv.put(DirectMessages.COL_SENDER, o.getLong(DirectMessages.COL_SENDER)); if (o.has(DirectMessages.COL_RECEIVER)) cv.put(DirectMessages.COL_RECEIVER, o.getLong(DirectMessages.COL_RECEIVER)); return cv; } /** * Creates content values for a User from a JSON object TODO: Move this to * where it belongs * * @param o * @return * @throws JSONException */ protected ContentValues getUserCV(JSONObject o) throws JSONException { // create the content values for the user ContentValues cv = new ContentValues(); String screenName = null; if (o.has(TwitterUsers.COL_SCREEN_NAME)) { screenName = o.getString(TwitterUsers.COL_SCREEN_NAME); cv.put(TwitterUsers.COL_SCREEN_NAME, o.getString(TwitterUsers.COL_SCREEN_NAME)); } if (o.has(TwitterUsers.JSON_FIELD_PROFILE_IMAGE) && screenName != null) { InternalStorageHelper helper = new InternalStorageHelper(getBaseContext()); byte[] image = Base64.decode(o.getString(TwitterUsers.JSON_FIELD_PROFILE_IMAGE), Base64.DEFAULT); helper.writeImage(image, screenName); String profileImageUri = Uri.fromFile(new File(getFilesDir(), screenName)).toString(); Log.d(TAG, "storing profile image at: " + profileImageUri); cv.put(TwitterUsers.COL_PROFILE_IMAGE_URI, profileImageUri); } if (o.has(Tweets.COL_USER_TID)) { cv.put(TwitterUsers.COL_TWITTER_USER_ID, o.getLong(Tweets.COL_USER_TID)); } cv.put(TwitterUsers.COL_IS_DISASTER_PEER, 1); return cv; } @Override public IBinder onBind(Intent intent) { // TODO Auto-generated method stub return null; } @Override public void onScanningFinished() { Log.i(TAG, "onScanningFinished"); unregisterDevReceiver(); receiver = null; startScanning(); } private void unregisterDevReceiver() { if (receiver != null) { receiver.setListener(null); try { unregisterReceiver(receiver); receiver = null; } catch (IllegalArgumentException ex) { } } } private void unregisterStateReceiver() { if (stateReceiver != null) { stateReceiver.setListener(null); try { unregisterReceiver(stateReceiver); stateReceiver = null; } catch (IllegalArgumentException ex) { } } } @Override public void onSwitchingFinished() { if (bluetoothHelper != null) { unregisterStateReceiver(); restartingBlue = false; Log.i(T, "switching finished"); // if a scan was postponed due to the adapter being restarted, do it // now, otherwise start listening if (mScanPending) { Log.d(T, "executing pending scan"); initiateScanningRound(1); } else { Log.d(T, "no pending scan -> listen"); bluetoothHelper.start(); } } } };