Java tutorial
/* * Copyright (C) 2010 Adam Nybck * * 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 se.anyro.tagtider; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.message.BasicNameValuePair; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import se.anyro.tagtider.utils.Http; import se.anyro.tagtider.utils.StringUtils; import android.app.Activity; import android.app.AlertDialog; import android.app.ListActivity; import android.app.PendingIntent; import android.app.ProgressDialog; import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.telephony.gsm.SmsManager; import android.text.SpannableString; import android.text.style.StrikethroughSpan; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.SimpleAdapter; import android.widget.TextView; import android.widget.SimpleAdapter.ViewBinder; @SuppressWarnings("deprecation") public class TransferActivity extends ListActivity { private static final int FIVE_MINUTES = 300000; private static long sLastUpdate = 0; private static String sLastTransferId; private String mTrain, mStationId, mStationName, mTransferId; private String mRegistrationId; private Bundle mNewExtras; private static SimpleAdapter sChangesAdapter; private static List<Map<String, Object>> sChanges = new ArrayList<Map<String, Object>>(); private static final String SMS_SENT = "se.anyro.tagtider.SMS_SENT"; private SmsStatusReceiver mSmsStatusReceiver = new SmsStatusReceiver(); private IntentFilter mSmsSentFilter = new IntentFilter(SMS_SENT); private ProgressDialog mProgressDialog; private View mProgressBar; private AlertDialog mDialog; private String mPhoneNumber; public static final String C2DM_REGISTERED = "se.anyro.tagtider.C2DM_REGISTERED"; private C2dmStatusReceiver mC2dmStatusReceiver = new C2dmStatusReceiver(); private IntentFilter mC2dmRegisteredFilter = new IntentFilter(C2DM_REGISTERED); // Display mapping from keys to view id:s private static final String[] FROM = { "detected", "comment", "other" }; private static final int[] TO = { R.id.time, R.id.comment, R.id.other }; private TextView mEmptyView; private static final String TYPE_AC2DM = "ac2dm"; private static final String TYPE_SMS = "sms"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.transfer); // Use c2dm if we have Android 2.2 int sdkVersion = Integer.parseInt(Build.VERSION.SDK); // Cupcake style if (sdkVersion >= Build.VERSION_CODES.FROYO + 10) { // TODO: Change when we want to use C2DM findViewById(R.id.sms).setVisibility(View.GONE); } else { //TelephonyManager tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE); mPhoneNumber = null; // tm.getLine1Number(); TODO: Change when server supports this if (mPhoneNumber != null && mPhoneNumber.length() == 0) mPhoneNumber = null; findViewById(R.id.c2dm).setVisibility(View.GONE); } mProgressBar = findViewById(R.id.progress); setupTransferData(getIntent().getExtras()); setupDialogs(); if (!mTransferId.equals(sLastTransferId)) { sLastUpdate = 0; sLastTransferId = mTransferId; } } @Override protected void onResume() { super.onResume(); if (getListAdapter() == null) { addChangesAdapter(); } long timeSinceLastUpdate = System.currentTimeMillis() - sLastUpdate; if (timeSinceLastUpdate > FIVE_MINUTES) { // Fetch additional data about changes made to this transfer new FetchChangesTask().execute(mTransferId); } } @Override protected void onNewIntent(Intent intent) { Bundle extras = intent.getExtras(); if (extras.getString("id") != null) { mTransferId = extras.getString("id"); } new FetchChangesTask().execute(mTransferId); } private void setupTransferData(final Bundle extras) { TextView trainView = (TextView) findViewById(R.id.train); ViewGroup originGroup = (ViewGroup) findViewById(R.id.origin_group); TextView originView = (TextView) findViewById(R.id.origin); TextView arrivalView = (TextView) findViewById(R.id.arrival); TextView stationTrackView = (TextView) findViewById(R.id.station_track); ViewGroup destinationGroup = (ViewGroup) findViewById(R.id.destination_group); TextView destinationView = (TextView) findViewById(R.id.destination); TextView departureView = (TextView) findViewById(R.id.departure); TextView commentView = (TextView) findViewById(R.id.comment); mEmptyView = (TextView) findViewById(android.R.id.empty); trainView.setText("Tg " + extras.getString("train") + " (" + extras.getString("type") + ")"); String origin = extras.getString("origin"); if (origin != null && origin.length() > 0) { originView.setText("Frn " + origin); originGroup.setVisibility(View.VISIBLE); } else { originGroup.setVisibility(View.GONE); } String track = extras.getString("track"); if (track == null || track.equalsIgnoreCase("x") || track.equalsIgnoreCase("null")) track = ""; String arrival = extras.getString("arrival"); if (arrival != null && !arrival.startsWith("0000")) { arrivalView.setText("Ankommer " + StringUtils.extractTime(arrival)); String newArrival = extras.getString("newArrival"); if (newArrival != null) { newArrival = StringUtils.extractTime(newArrival); SpannableString strike = new SpannableString(arrivalView.getText() + " " + newArrival); strike.setSpan(new StrikethroughSpan(), strike.length() - 11, strike.length() - 6, 0); arrivalView.setText(strike, TextView.BufferType.SPANNABLE); } if (track.length() == 0) { SpannableString strike = new SpannableString(arrivalView.getText()); strike.setSpan(new StrikethroughSpan(), strike.length() - 5, strike.length(), 0); arrivalView.setText(strike, TextView.BufferType.SPANNABLE); } } if (extras.getString("stationName") != null) { mStationName = extras.getString("stationName"); } if (track.length() > 0 && mStationName != null) stationTrackView.setText(mStationName + ", spr " + track); else if (mStationName != null) stationTrackView.setText(mStationName); else if (track.length() > 0) stationTrackView.setText("Spr " + track); else stationTrackView.setText(""); String destination = extras.getString("destination"); if (destination != null && destination.length() > 0) { destinationView.setText("Till " + destination); destinationGroup.setVisibility(View.VISIBLE); } else { destinationGroup.setVisibility(View.GONE); } String departure = extras.getString("departure"); if (departure != null && !departure.startsWith("0000")) { departureView.setText("Avgr " + StringUtils.extractTime(departure)); String newDeparture = extras.getString("newDeparture"); if (newDeparture != null) { newDeparture = StringUtils.extractTime(newDeparture); SpannableString strike = new SpannableString(departureView.getText() + " " + newDeparture); strike.setSpan(new StrikethroughSpan(), strike.length() - 11, strike.length() - 6, 0); departureView.setText(strike, TextView.BufferType.SPANNABLE); } if (track.length() == 0) { SpannableString strike = new SpannableString(departureView.getText()); strike.setSpan(new StrikethroughSpan(), strike.length() - 5, strike.length(), 0); departureView.setText(strike, TextView.BufferType.SPANNABLE); } } String comment = extras.getString("comment"); if ((comment == null || comment.length() == 0) && track.length() == 0) comment = "Instllt"; if (comment != null && comment.length() > 0) { commentView.setText(comment); commentView.setVisibility(View.VISIBLE); } else { commentView.setVisibility(View.GONE); } mTrain = extras.getString("train"); mStationId = extras.getString("stationId"); mTransferId = extras.getString("id"); } private void setupDialogs() { final AlertDialog smsDialog = createSmsDialog(); Button sendSmsButton = (Button) findViewById(R.id.sms); sendSmsButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { smsDialog.show(); } }); final AlertDialog c2dmDialog = createC2dmDialog(); Button sendC2dmButton = (Button) findViewById(R.id.c2dm); sendC2dmButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { c2dmDialog.show(); } }); mProgressDialog = new ProgressDialog(this); mDialog = new AlertDialog.Builder(this).setNeutralButton("Ok", null).create(); } private AlertDialog createSmsDialog() { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(R.string.sms_dialog_title); if (mPhoneNumber == null) { builder.setMessage(R.string.sms_dialog_message); } else { builder.setMessage(String.format(getString(R.string.sms_dialog_message_free), mPhoneNumber)); } builder.setPositiveButton("Ok", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { if (mPhoneNumber == null) { sendSms("0730121096", mTrain + " " + mStationId); } else { new StartSubscriptionTask().execute(mPhoneNumber, TYPE_SMS); } } }); builder.setNegativeButton("Avbryt", null); final AlertDialog smsDialog = builder.create(); return smsDialog; } private AlertDialog createC2dmDialog() { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(R.string.c2dm_dialog_title); builder.setMessage(R.string.c2dm_dialog_message); builder.setPositiveButton("Ok", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { if (mRegistrationId == null) { registerC2dm(); } else { new StartSubscriptionTask().execute(mRegistrationId, TYPE_AC2DM); } } }); builder.setNegativeButton("Avbryt", null); final AlertDialog smsDialog = builder.create(); return smsDialog; } private void addChangesAdapter() { sChangesAdapter = new SimpleAdapter(this, sChanges, R.layout.change_row, FROM, TO); sChangesAdapter.setViewBinder(new ViewBinder() { public boolean setViewValue(View view, Object data, String textRepresentation) { // Hide views with empty text if (textRepresentation.length() == 0) { view.setVisibility(View.GONE); return true; } view.setVisibility(View.VISIBLE); return false; } }); setListAdapter(sChangesAdapter); } private void deleteChangesAdapter() { sChanges.clear(); setListAdapter(null); } /** * Class for fetching transfer changes asynchronously */ private class FetchChangesTask extends AsyncTask<String, String, String> { @Override protected void onPreExecute() { mEmptyView.setText("Hmtar ndringar..."); mProgressBar.setVisibility(View.VISIBLE); } @Override protected String doInBackground(String... params) { String transferId = params[0]; HttpGet httpGet = new HttpGet("http://api.tagtider.net/v1/transfers/" + transferId + ".json"); httpGet.setHeader("User-Agent", Http.getUserAgent()); try { HttpResponse response = Http.getClient().execute(httpGet); int statusCode = response.getStatusLine().getStatusCode(); if (statusCode != 200) return "Tillflligt fel " + statusCode + " :-("; HttpEntity entity = response.getEntity(); InputStream content = entity.getContent(); String json = StringUtils.readTextFile(content); try { // Parse json response into objects JSONObject root = new JSONObject(json); JSONObject transfer = root.getJSONObject("transfer"); @SuppressWarnings("unchecked") Iterator transferKeys = transfer.keys(); mNewExtras = new Bundle(); while (transferKeys.hasNext()) { Object key = transferKeys.next(); Object value = transfer.get((String) key); if (value == JSONObject.NULL) value = null; if (value instanceof String) mNewExtras.putString((String) key, (String) value); } if (!transfer.has("changes")) return "Inga ndringar nnu"; JSONArray changes = transfer.getJSONObject("changes").getJSONArray("change"); int maxChanges = changes.length(); if (maxChanges > 3) { maxChanges = 3; } sChanges.clear(); for (int i = 0; i < maxChanges; ++i) { JSONObject change = changes.getJSONObject(i); @SuppressWarnings("unchecked") Iterator keys = change.keys(); Map<String, Object> changeMap = new HashMap<String, Object>(); while (keys.hasNext()) { Object key = keys.next(); Object value = change.get((String) key); if (value == JSONObject.NULL) value = null; changeMap.put((String) key, value); } String track = change.getString("track"); if (track.length() > 0 && !track.equalsIgnoreCase("null")) track = "Spr " + track; else track = ""; String arrival = change.getString("arrival"); if (arrival.length() > 0 && arrival.charAt(0) == '2') arrival = "Ankommer " + StringUtils.extractTime(arrival); else arrival = ""; String departure = change.getString("departure"); if (departure.length() > 0 && departure.charAt(0) == '2') departure = "Avgr " + StringUtils.extractTime(departure); else departure = ""; String[] other = new String[] { track, arrival, departure }; changeMap.put("other", StringUtils.join(other, ", ")); sChanges.add(changeMap); } return null; } catch (JSONException e) { return "Tillflligt fel"; } } catch (ClientProtocolException e) { return "Kommunikationsfel"; } catch (IOException e) { return "Ingen kontakt"; } } @Override protected void onPostExecute(String result) { mProgressBar.setVisibility(View.GONE); if (result == null) { sLastUpdate = System.currentTimeMillis(); addChangesAdapter(); if (mNewExtras != null) { setupTransferData(mNewExtras); } } else { mEmptyView.setText(result); deleteChangesAdapter(); sLastUpdate = 0; } } } /** * Class for sending message to server to start subscription */ private class StartSubscriptionTask extends AsyncTask<String, String, String> { @Override protected void onPreExecute() { mProgressDialog.setMessage("Registrerar bevakning..."); mProgressDialog.show(); } @Override protected String doInBackground(String... params) { String id = params[0]; String type = params[1]; HttpPost httpPost = new HttpPost("http://api.tagtider.net/v1/subscriptions.json?device_token=" + id); final List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(); nameValuePairs.add(new BasicNameValuePair("train", mTrain)); nameValuePairs.add(new BasicNameValuePair("station", mStationId)); // nameValuePairs.add(new BasicNameValuePair("date", value)); nameValuePairs.add(new BasicNameValuePair("type", type)); HttpEntity postEntity = null; try { postEntity = new UrlEncodedFormEntity(nameValuePairs); } catch (final UnsupportedEncodingException e) { // this should never happen. throw new AssertionError(e); } httpPost.setEntity(postEntity); httpPost.setHeader(postEntity.getContentType()); httpPost.setHeader("User-Agent", Http.getUserAgent()); try { HttpResponse response = Http.getClient().execute(httpPost); int statusCode = response.getStatusLine().getStatusCode(); if (statusCode != 200) return "Tillflligt fel " + statusCode + " :-("; HttpEntity entity = response.getEntity(); InputStream content = entity.getContent(); String json = StringUtils.readTextFile(content); try { // Parse json response into objects JSONObject root = new JSONObject(json); /* JSONObject transfer = root.getJSONObject("transfer"); if (!transfer.has("changes")) return "Inga ndringar nnu"; JSONArray changes = transfer.getJSONObject("changes").getJSONArray("change"); int maxChanges = changes.length(); if (maxChanges > 3) { maxChanges = 3; }*/ return null; } catch (JSONException e) { return "Tillflligt fel"; } } catch (ClientProtocolException e) { return "Kommunikationsfel"; } catch (IOException e) { return "Ingen kontakt"; } } @Override protected void onPostExecute(String result) { mProgressDialog.hide(); if (result != null) { showMessage("Problem", result); } } } private void registerC2dm() { mProgressDialog.setMessage("Initierar bevakning..."); mProgressDialog.show(); registerReceiver(mC2dmStatusReceiver, mC2dmRegisteredFilter); Intent registrationIntent = new Intent("com.google.android.c2dm.intent.REGISTER"); registrationIntent.putExtra("app", PendingIntent.getBroadcast(this, 0, new Intent(), 0)); registrationIntent.putExtra("sender", "tagtider@gmail.com"); startService(registrationIntent); } private class C2dmStatusReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(C2DM_REGISTERED)) { String error = intent.getStringExtra("error"); if (error != null) { mProgressDialog.hide(); showMessage("Problem", error); } else { mRegistrationId = intent.getStringExtra("registrationId"); new StartSubscriptionTask().execute(mRegistrationId, TYPE_AC2DM); } unregisterReceiver(mC2dmStatusReceiver); } } } private void sendSms(String phoneNumber, String message) { registerReceiver(mSmsStatusReceiver, mSmsSentFilter); PendingIntent sentIntent = PendingIntent.getBroadcast(this, 0, new Intent(SMS_SENT), 0); ArrayList<PendingIntent> sentIntents = new ArrayList<PendingIntent>(); sentIntents.add(sentIntent); SmsManager sms = SmsManager.getDefault(); ArrayList<String> smstext = sms.divideMessage(message); mProgressDialog.setMessage("Skickar SMS..."); mProgressDialog.show(); // Using multipart as a work-around for a bug in HTC Tattoo sms.sendMultipartTextMessage(phoneNumber, null, smstext, sentIntents, null); } private class SmsStatusReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(SMS_SENT)) { onSmsSent(); unregisterReceiver(mSmsStatusReceiver); } } private void onSmsSent() { mProgressDialog.hide(); String error = null; switch (getResultCode()) { case Activity.RESULT_OK: break; case SmsManager.RESULT_ERROR_GENERIC_FAILURE: error = "Tekniskt fel"; break; case SmsManager.RESULT_ERROR_NO_SERVICE: error = "Ingen service"; break; case SmsManager.RESULT_ERROR_NULL_PDU: error = "Ingen PDU"; break; case SmsManager.RESULT_ERROR_RADIO_OFF: error = "SMS r avstngt p telefonen"; break; default: error = "Oknt fel: " + getResultCode(); } if (error != null) { showMessage("Problem", error); } } } private void showMessage(String title, String message) { mDialog.setTitle(title); mDialog.setMessage(message); mDialog.show(); } }