package com.papagiannis.tuberun;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.Settings.Secure;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.ToggleButton;
import com.google.android.vending.licensing.AESObfuscator;
import com.google.android.vending.licensing.LicenseChecker;
import com.google.android.vending.licensing.LicenseCheckerCallback;
import com.google.android.vending.licensing.Policy;
import com.google.android.vending.licensing.ServerManagedPolicy;
import com.papagiannis.tuberun.favorites.Favorite;
import com.papagiannis.tuberun.fetchers.Observer;
import com.papagiannis.tuberun.fetchers.OysterFetcher;
import com.papagiannis.tuberun.stores.CredentialsStore;
public class TubeRun extends Activity implements OnClickListener, Observer {
public static final String APPNAME = "TubeRun";
public static final String VERSION = "1.3.2";
// Don't forget to update the app version in the manifest
// Don't forget to update the changelog later in this file
public static final Boolean USE_LICENSING = false;
private static final String TUBE_MAP_URL1 = "https://www.tfl.gov.uk/assets/downloads/standard-tube-map.gif";
private static final String TUBE_MAP_URL2 = "https://dl.dropbox.com/s/u206bwzgdy3smn3/london-rail-and-tube-services-map.png?token_hash=AAEb4dT2kKWRIFkQ1H4TL0X4I2-V7g_nmb9aNGqYBSpy9Q";//"http://yia.nnis.gr/tuberun/london-rail-and-tube-services-map.png";
private static final String LICENCING_PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnskzkZ7GjJBChKebZfXVqdDnuqWDLNHuhhpIwL6a+g8OiNE52+LxolCAZJmOnHr3zvgdPw0vuRKrFfGjuPgVJV13nx1DKFi7LuXuK4rpmMucZ1qZf4kwbNw+iOmp6YqWT8OQ1RN94biWluZhwcee5sb16xmtJEeH2iHEKVtjheJUGebSm6mxiQO3S3LE4p9pWadPDfPmFEvw2vjVLtwyxUqBIhiMiEtOF3e6JDBE6kLndI97jZY4LXsfL7IDhiBe1pLCZrO90TQTKMzqwz8nowXqoQLvDJ78bUaCuJm7WwPPTgpZmAyL5P2bi+c5NDoJrZsntq82EL2hRnDPiP2+nwIDAQAB";
private static final byte[] LICENCING_SALT = new byte[] { 100, 78, 89, 45,
21, 45, 21, 90, 23, 45, 67, 12, 11, 54 };
private static final int DOWNLOAD_IMAGE_DIALOG = -1;
private static final int DOWNLOAD_IMAGE_PROGRESS_DIALOG = -2;
private static final int DOWNLOAD_IMAGE_FAILED_DIALOG = -3;
private static final int LICENCING_ERROR = -4;
private static final int SHOW_WELCOME = -5;
private static final int ERASE_IMAGE_DIALOG = -6;
public static final String PREFERENCES = "Preferences";
public static final String AUTOSTART = "autostart";
public static final Integer AUTOSTART_NONE = -1;
public static final String TUBEMAP_EXISTS = "tubeMapDownloaded";
TextView oysterBalance;
ProgressBar oysterProgress;
LinearLayout oysterLayout;
Button oysterButton;
Button oysterButtonActive;
Button logoButton;
ToggleButton favoritesButton;
Button mapsButton;
private SharedPreferences preferences;
private boolean tubeMapDownloaded = false;
private ImageDownloadTask task;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
preferences = getSharedPreferences(PREFERENCES, MODE_PRIVATE);
tubeMapDownloaded = preferences.getBoolean(TUBEMAP_EXISTS, false);
View statusButton = findViewById(R.id.button_status);
statusButton.setOnClickListener(this);
View departuresButton = findViewById(R.id.button_departures);
departuresButton.setOnClickListener(this);
mapsButton = (Button) findViewById(R.id.button_maps);
mapsButton.setOnClickListener(this);
View nearbyButton = findViewById(R.id.button_nearby);
nearbyButton.setOnClickListener(this);
logoButton = (Button) findViewById(R.id.button_logo);
logoButton.setOnClickListener(this);
favoritesButton = (ToggleButton) findViewById(R.id.button_favorites);
favoritesButton.setOnClickListener(this);
View claimsButton = findViewById(R.id.button_claims);
claimsButton.setOnClickListener(this);
View planButton = findViewById(R.id.button_planner);
planButton.setOnClickListener(this);
oysterButton = (Button) findViewById(R.id.button_oyster);
oysterButton.setOnClickListener(this);
oysterButtonActive = (Button) findViewById(R.id.button_oyster_active);
oysterButtonActive.setOnClickListener(this);
oysterBalance = (TextView) findViewById(R.id.view_balance);
oysterProgress = (ProgressBar) findViewById(R.id.progressbar_balance);
oysterLayout = (LinearLayout) findViewById(R.id.layout_balance);
mapsButton.setOnLongClickListener(new View.OnLongClickListener() {
@SuppressWarnings("deprecation")
@Override
public boolean onLongClick(View v) {
showDialog(ERASE_IMAGE_DIALOG);
return true;
}
});
if (USE_LICENSING)
initializeLicencing();
Intent i = getIntent();
Boolean showMap = i.getBooleanExtra(MainMenu.SHOWMAP, false);
if (showMap)
onNewIntent(i);
else {
setIntent(new Intent());
showWelcome();
}
}
@Override
protected void onNewIntent(Intent intent) {
setIntent(intent);
Boolean showMap = intent.getBooleanExtra(MainMenu.SHOWMAP, false);
if (showMap)
onClick(mapsButton);
};
@SuppressWarnings("deprecation")
private void showWelcome() {
String l = preferences.getString("lastNotice", "");
if (!l.equals(VERSION)) {
Editor editor = preferences.edit();
editor.putString("lastNotice", VERSION);
editor.commit();
copyDatabase();
showDialog(SHOW_WELCOME);
} else
jump();
}
private void jump() {
SharedPreferences shPrefs = getSharedPreferences(PREFERENCES,
MODE_PRIVATE);
int viewId = shPrefs.getInt(AUTOSTART, AUTOSTART_NONE);
if (viewId != AUTOSTART_NONE) {
onClick(findViewById(viewId));
finish();
}
}
@SuppressWarnings("deprecation")
public void onClick(View v) {
Intent i = null;
switch (v.getId()) {
case R.id.button_status:
i = new Intent(this, StatusActivity.class);
break;
case R.id.button_departures:
i = new Intent(this, SelectLineActivity.class);
i.putExtra("type", "departures");
break;
case R.id.button_maps:
if (tubeMapDownloaded)
i = getMapIntent();
else {
showDialog(DOWNLOAD_IMAGE_DIALOG);
return;
}
break;
case R.id.button_nearby:
i = new Intent(this, NearbyStationsActivity.class);
break;
case R.id.button_favorites:
favoritesButton.setChecked(!favoritesButton.isChecked()); // no
// toggling
i = new Intent(this, FavoritesActivity.class);
break;
case R.id.button_claims:
i = new Intent(this, ClaimsActivity.class);
break;
case R.id.button_planner:
i = new Intent(this, PlanActivity.class);
break;
case R.id.button_oyster:
case R.id.button_oyster_active:
i = new Intent(this, OysterActivity.class);
break;
case R.id.button_logo:
i = new Intent(this, AboutActivity.class);
break;
}
if (i != null)
startActivity(i);
}
private Intent getMapIntent() {
Intent i;
i = new Intent(this, StatusMapActivity.class);
i.putExtra("line",
LinePresentation.getStringRespresentation(LineType.ALL));
i.putExtra("type", "maps");
return i;
}
private CredentialsStore store = CredentialsStore.getInstance();
private OysterFetcher fetcher;
private String username = "";
private void fetchBalance() {
oysterButtonActive.setVisibility(View.GONE);
oysterButton.setVisibility(View.VISIBLE);
oysterBalance.setVisibility(View.GONE);
oysterProgress.setVisibility(View.GONE);
oysterLayout.setVisibility(View.GONE);
ArrayList<String> credentials = store.getAll(this);
if (credentials.size() == 0)
return;
Date now = new Date();
// skip fetching oyster balance if it has been fetched before (in the
// last 5 min).
if (!username.equals("")
&& username.equals(credentials.get(0))
&& !fetcher.isErrorResult()
&& !fetcher.getResult().equals("")
&& (now.getTime() - fetcher.getUpdateTime().getTime()) / 1000 < 5 * 60) {
// there is a result i can reuse
update();
} else if (credentials.size() == 2) {
OysterFetcher newFetcher = OysterFetcher.getInstance(
credentials.get(0), credentials.get(1));
if (fetcher != newFetcher) {
fetcher = newFetcher;
fetcher.registerCallback(this);
}
oysterButtonActive.setVisibility(View.VISIBLE);
oysterButton.setVisibility(View.GONE);
oysterLayout.setVisibility(View.VISIBLE);
oysterProgress.setVisibility(View.VISIBLE);
username = credentials.get(0);
fetcher.update();
}
}
@Override
public void update() {
SharedPreferences preferences = getSharedPreferences(
TubeRun.PREFERENCES, MODE_PRIVATE);
String defaultCard = preferences.getString(OysterActivity.DEFAULT_CARD,
"");
CharSequence balance = "";
HashMap<String, String> cards = fetcher.getCards();
if (!defaultCard.equals("") && cards.containsKey(defaultCard)) {
balance = cards.get(defaultCard);
} else
balance = fetcher.getResult();
oysterBalance.setText(balance);
oysterButtonActive.setVisibility(View.VISIBLE);
oysterButton.setVisibility(View.GONE);
oysterLayout.setVisibility(View.VISIBLE);
oysterProgress.setVisibility(View.GONE);
oysterBalance.setVisibility(View.VISIBLE);
}
private Dialog wait_dialog;
ProgressDialog progressDialog;
@Override
protected Dialog onCreateDialog(int id) {
Dialog result = null;
AlertDialog.Builder builder = new AlertDialog.Builder(this);
switch (id) {
case ERASE_IMAGE_DIALOG:
if (tubeMapDownloaded) {
wait_dialog = getEraseMapDialog();
result = wait_dialog;
}
break;
case DOWNLOAD_IMAGE_DIALOG:
builder.setTitle("Tube Map Required")
.setMessage(
"The official Tube Map is property of TfL and is not included in this app. "
+ "However, TubeRun can fetch it from TfL and cache it for future use.\n\n"
+ "File sizes: ~2300KB / ~550KB\n\n"
+ "PS: Long-press the map button to delete the map.")
.setCancelable(true)
.setPositiveButton("Get Tube Map",
new DialogInterface.OnClickListener() {
@SuppressWarnings("deprecation")
public void onClick(DialogInterface dialog,
int id) {
dismissDialog(DOWNLOAD_IMAGE_DIALOG);
showDialog(DOWNLOAD_IMAGE_PROGRESS_DIALOG);
fetchTubeMap(TUBE_MAP_URL1);
}
})
.setNeutralButton("Get Tube+Rail Map",
new DialogInterface.OnClickListener() {
@Override @SuppressWarnings("deprecation")
public void onClick(DialogInterface dialog,
int which) {
dismissDialog(DOWNLOAD_IMAGE_DIALOG);
showDialog(DOWNLOAD_IMAGE_PROGRESS_DIALOG);
fetchTubeMap(TUBE_MAP_URL2);
}
});
wait_dialog = builder.create();
result = wait_dialog;
break;
case DOWNLOAD_IMAGE_PROGRESS_DIALOG:
progressDialog = new ProgressDialog(this);
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setMessage("Downloading Tube Map");
progressDialog.setCancelable(true);
progressDialog
.setOnCancelListener(new DialogInterface.OnCancelListener() {
@SuppressWarnings("deprecation")
@Override
public void onCancel(DialogInterface dialog) {
task.cancel(true);
dismissDialog(DOWNLOAD_IMAGE_PROGRESS_DIALOG);
}
});
wait_dialog = progressDialog;
result = progressDialog;
break;
case DOWNLOAD_IMAGE_FAILED_DIALOG:
builder.setTitle("Download failed")
.setMessage(
"Could not download the Tube Map. Please try again later. "
+ "Make sure that you have Internet access.")
.setCancelable(true)
.setPositiveButton("OK",
new DialogInterface.OnClickListener() {
@SuppressWarnings("deprecation")
public void onClick(DialogInterface dialog,
int id) {
dismissDialog(DOWNLOAD_IMAGE_FAILED_DIALOG);
}
});
wait_dialog = builder.create();
result = wait_dialog;
break;
case LICENCING_ERROR:
wait_dialog = getLicensingErrorDialog();
result = wait_dialog;
break;
case SHOW_WELCOME:
wait_dialog = getWelcomeDialog();
result = wait_dialog;
break;
}
return result;
}
private Dialog getEraseMapDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Delete Tube Map")
.setMessage(
"Do you want to delete the stored tube map? This will allow you to download another version.")
.setCancelable(true)
.setNegativeButton("Cancel", null)
.setPositiveButton("Delete",
new DialogInterface.OnClickListener() {
@SuppressWarnings("deprecation")
public void onClick(DialogInterface dialog, int id) {
Editor editor = preferences.edit();
editor.putBoolean(TUBEMAP_EXISTS, false);
editor.commit();
tubeMapDownloaded = false;
removeDialog(ERASE_IMAGE_DIALOG);
}
});
return builder.create();
}
@Override
protected void onPrepareDialog(int id, Dialog dialog) {
switch (id) {
case DOWNLOAD_IMAGE_PROGRESS_DIALOG:
progressDialog.setProgress(0);
progressDialog.setMax(100);
break;
}
};
private void fetchTubeMap(String url) {
task = new ImageDownloadTask();
task.execute(url);
}
@SuppressWarnings("deprecation")
@Override
protected void onPause() {
super.onPause();
if (fetcher != null) {
fetcher.abort();
}
if (task != null) {
if (progressDialog != null)
dismissDialog(DOWNLOAD_IMAGE_PROGRESS_DIALOG);
task.cancel(true);
}
}
@Override
protected void onResume() {
super.onResume();
ArrayList<Favorite> favs = new ArrayList<Favorite>();
fetchBalance();
try {
favs = Favorite.getFavorites(this);
} catch (Exception e) {
Log.w("Main", e);
}
favoritesButton.setChecked(favs.size() > 0);
}
class ImageDownloadTask extends AsyncTask<String, Integer, Boolean> {
@Override
protected Boolean doInBackground(String... params) {
try {
URL url = new URL(params[0]);
HttpURLConnection urlConnection = (HttpURLConnection) url
.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.connect();
// File appdir = Environment.getExternalStorageDirectory();
// File dir = new File(appdir.getAbsoluteFile() + "/tuberun/");
// Boolean created = dir.mkdirs();
// File file = new File(dir, params[1]);
// FileOutputStream fileOutput = new FileOutputStream(file);
// Let's read everything to RAM first
InputStream inputStream = urlConnection.getInputStream();
int totalSize = urlConnection.getContentLength();
if (totalSize <= 0) {
return false;
}
int downloadedSize = 0;
byte[] buffer = new byte[1024];
byte[] fullFile = new byte[totalSize];
int bufferLength = 0; // used to store a temporary size of the
// // buffer
int i = 0;
while ((bufferLength = inputStream.read(buffer)) > 0) {
if (isCancelled())
return false;
// fileOutput.write(buffer, 0, bufferLength);
int k = 0;
for (int j = i; j < i + bufferLength; j++) {
fullFile[j] = buffer[k++];
}
i += bufferLength;
//
//
downloadedSize += bufferLength;
publishProgress(downloadedSize, totalSize);
}
// fileOutput.close();
inputStream.close();
ContentValues v = new ContentValues();
v.put("map", fullFile);
ContentResolver r = getContentResolver();
r.insert(
Uri.parse("content://"
+ TubeMapContentProvider.AUTHORITY + "/map"), v);
} catch (MalformedURLException e) {
e.printStackTrace();
return false;
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
@SuppressWarnings("deprecation")
@Override
protected void onPostExecute(Boolean result) {
dismissDialog(DOWNLOAD_IMAGE_PROGRESS_DIALOG);
if (result) {
tubeMapDownloaded = true;
Editor editor = preferences.edit();
editor.putBoolean(TUBEMAP_EXISTS, tubeMapDownloaded);
editor.commit();
Intent i = getMapIntent();
startActivity(i);
} else
showDialog(DOWNLOAD_IMAGE_FAILED_DIALOG);
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
int current = values[0];
int total = values[1];
// int percent=(100*current)/total;
progressDialog.setProgress(current);
progressDialog.setMax(total);
}
}
// *****************Licensing methods go here***********************
private LicenseCheckerCallback mLicenseCheckerCallback;
private LicenseChecker mChecker;
private void initializeLicencing() {
mLicenseCheckerCallback = new MyLicenseCheckerCallback();
String deviceId = Secure.getString(getContentResolver(),
Secure.ANDROID_ID);
mChecker = new LicenseChecker(this, new ServerManagedPolicy(this,
new AESObfuscator(LICENCING_SALT, getPackageName(), deviceId)),
LICENCING_PUBLIC_KEY);
mChecker.checkAccess(mLicenseCheckerCallback);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (USE_LICENSING)
mChecker.onDestroy();
}
private class MyLicenseCheckerCallback implements LicenseCheckerCallback {
public void allow(int reason) {
if (isFinishing()) {
return;
}
}
@SuppressWarnings("deprecation")
public void dontAllow(int reason) {
if (isFinishing()) {
return;
}
if (reason == Policy.RETRY) {
} else {
showDialog(LICENCING_ERROR);
}
}
@Override
public void applicationError(int errorCode) {
// this is called for ERROR_NOT_MARKET_MANAGED,
// ERROR_INVALID_PACKAGE_NAME, ERROR_NON_MATCHING_UID
// see LicenseValidator.java
// showDialog(LICENCING_ERROR);
}
}
private Dialog getLicensingErrorDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Application Not Licensed")
.setMessage(
"Your copy of this application is unauthorised. You may obtain a licensed copy from the Google Play "
+ "Store.\n\n"
+ "Please support the developer (I have spent many nights working on this).\n\n"
+ "If you think that you are seeing this in error, please contact the developer.")
.setCancelable(false)
.setPositiveButton("Open Play Store",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri
.parse("market://details?id=com.papagiannis.tuberun"));
startActivity(intent);
finish();
}
})
.setNeutralButton("Contact Developer",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,
int which) {
final Intent emailIntent = new Intent(
android.content.Intent.ACTION_SEND);
emailIntent.setType("plain/text");
emailIntent.putExtra(
android.content.Intent.EXTRA_EMAIL,
new String[] { "jpapayan@gmail.com" });
emailIntent.putExtra(
android.content.Intent.EXTRA_SUBJECT,
TubeRun.APPNAME + " Activation Error");
emailIntent.putExtra(
android.content.Intent.EXTRA_TEXT, "");
startActivity(Intent.createChooser(emailIntent,
"Send mail via"));
finish();
}
})
.setNegativeButton("Exit Application",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,
int which) {
finish();
}
});
return builder.create();
}
// *****************Welcome to TubeRun operations**************************
private Dialog getWelcomeDialog() {
ProgressDialog.Builder builder = new ProgressDialog.Builder(this);
builder.setTitle(APPNAME + " " + VERSION)
.setMessage(
"What's new:\n\n"
+ "*Journey planner suggestions improvements.\n\n"
+ "*Minor sliding menu UI updates.\n\n"
+ "*Better support for the Galaxy S4.\n")
.setCancelable(false).setPositiveButton("OK", null);
return builder.create();
}
private void copyDatabase() {
AsyncTask<Context, Integer, Boolean> task = new AsyncTask<Context, Integer, Boolean>() {
@Override
protected Boolean doInBackground(Context... params) {
try {
DatabaseHelper myDbHelper = new DatabaseHelper(params[0]);
myDbHelper.createDatabase();
myDbHelper.openDataBase();
myDbHelper.close();
wait_dialog.setCancelable(true);
} catch (Exception e) {
Log.w("CopyDatabase", e);
return false;
}
return true;
}
};
task.execute(this);
}
}