package ca.josephroque.bowlingcompanion.utilities;
import android.content.Context;
import android.content.res.Resources;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.util.Log;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import ca.josephroque.bowlingcompanion.R;
/**
* Created by Joseph Roque on 2016-03-13. Provides methods and constants for enabling the transferring of data to a new
* device.
*/
public final class TransferUtils {
/** Identifies output from this class in Logcat. */
@SuppressWarnings("unused")
private static final String TAG = "TransferUtils";
/** URL to upload or download data to/from. */
private static String sTransferServerUrl = null;
/** Target percentage of transfers. */
public static final int TARGET_PERCENTAGE = 100;
/** Starting location of the transfer key in the response. */
public static final int TRANSFER_KEY_START = 10;
/** Length of the transfer key in the response. */
public static final int TRANSFER_KEY_LENGTH = 5;
/** Time to wait before closing connection. */
public static final int CONNECTION_TIMEOUT = 1000 * 10;
/** Time to wait before closing connection, if previous connections failed. */
public static final int CONNECTION_EXTENDED_TIMEOUT = 1000 * 25;
/** Represents a lack of available connection to the internet. */
public static final String ERROR_NO_INTERNET = "NO_INTERNET";
/** Represents an invalid key provided by the user. */
public static final String ERROR_INVALID_KEY = "INVALID_KEY";
/** Represents an error in which the server is currently unavailable. User should try again later. */
public static final String ERROR_UNAVAILABLE = "UNAVAILABLE";
/** Represents a timeout error. */
public static final String ERROR_TIMEOUT = "TIMEOUT";
/** Represents an error in which a connection was cancelled. */
public static final String ERROR_CANCELLED = "CANCELLED";
/** Represents an IO error. */
public static final String ERROR_IO_EXCEPTION = "IO";
/** Represents an out of memory error during upload/download. */
public static final String ERROR_OUT_OF_MEMORY = "OOM";
/** Represents an error in which the database file could not be found for upload. */
public static final String ERROR_FILE_NOT_FOUND = "MIA";
/** Represents an incorrect URL error. */
public static final String ERROR_MALFORMED_URL = "URL";
/** Represents any other error which may occur during upload/download. */
public static final String ERROR_EXCEPTION = "ERROR";
/** Represents a successful download of bowler data. */
public static final String SUCCESSFUL_IMPORT = "IM_SUCCESS";
/** String modifier to represent location of downloaded data. */
public static final String DATA_DOWNLOADED = "_dl";
/** String modifier to represent location of backup data. */
public static final String DATA_BACKUP = "_backup";
/** Max buffer size during data transfer. */
public static final int MAX_BUFFER_SIZE = 32 * 1024;
/** Buffer size when backing up database. */
public static final int BACKUP_BUFFER_SIZE = 1024;
/**
* Returns the URL for GET requests to check the status of the server.
*
* @return URL for server status check
*/
public static String getStatusEndpoint() {
return sTransferServerUrl + "status";
}
/**
* Returns the URL for POST requests to upload bowler data.
*
* @return URL for data upload
*/
public static String getUploadEndpoint() {
return sTransferServerUrl + "upload";
}
/**
* Returns the URL for GET requests to download user data.
*
* @param key unique key which represents data.
* @return URL for data download.
*/
public static String getDownloadEndpoint(String key) {
return sTransferServerUrl + "download?key=" + key;
}
/**
* Returns the URL for GET requests to confirm a key is valid.
*
* @param key unique key to check
* @return URL for validity check
*/
public static String getValidKeyEndpoint(String key) {
return sTransferServerUrl + "valid?key=" + key;
}
/**
* Loads the base URL for the transfer API.
*
* @param resources to access strings
*/
public static void loadTransferServerUrl(Resources resources) {
sTransferServerUrl = resources.getString(R.string.transfer_url);
}
/**
* Performs a GET request to check if the provided key corresponds to valid data on the server.
*
* @param key unique key
* @return {@code true} if the app should continue with downloading data with the key, {@code false} otherwise.
*/
public static boolean isKeyValid(String key) {
try {
URL url = new URL(getValidKeyEndpoint(key));
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(CONNECTION_TIMEOUT);
connection.setReadTimeout(CONNECTION_TIMEOUT);
int responseCode = connection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
StringBuilder responseMsg = new StringBuilder();
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String line = reader.readLine();
while (line != null) {
responseMsg.append(line);
line = reader.readLine();
}
reader.close();
String response = responseMsg.toString().trim().toUpperCase();
Log.d(TAG, "Transfer server status response: " + response);
// The server is only ready to accept uploads if it responds with "VALID"
return response.equals("VALID");
} else {
Log.e(TAG, "Invalid response getting server status: " + responseCode);
}
} catch (MalformedURLException ex) {
Log.e(TAG, "Error parsing URL. This shouldn't happen.", ex);
} catch (IOException ex) {
Log.e(TAG, "Error opening or closing connection validating key.", ex);
}
return false;
}
/**
* Performs a GET request to check the status of the server and if uploading or downloading data is possible. Should
* NOT be run on the main thread.
*
* @return {@code true} if the app should continue with uploading/downloading data, {@code false} otherwise.
*/
public static boolean getServerStatus() {
try {
URL url = new URL(getStatusEndpoint());
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(CONNECTION_TIMEOUT);
connection.setReadTimeout(CONNECTION_TIMEOUT);
int responseCode = connection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
StringBuilder responseMsg = new StringBuilder();
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String line = reader.readLine();
while (line != null) {
responseMsg.append(line);
line = reader.readLine();
}
reader.close();
String response = responseMsg.toString().trim().toUpperCase();
Log.d(TAG, "Transfer server status response: " + response);
// The server is only ready to accept uploads if it responds with "OK"
return response.equals("OK");
} else {
Log.e(TAG, "Invalid response getting server status: " + responseCode);
}
} catch (MalformedURLException ex) {
Log.e(TAG, "Error parsing URL. This shouldn't happen.", ex);
} catch (IOException ex) {
Log.e(TAG, "Error opening or closing connection getting status.", ex);
}
return false;
}
/**
* Checks if the device currently has an available connection to the internet.
*
* @param context the current context
* @return {@code true} if an internet connection is available, {@code false} otherwise.
*/
public static boolean isConnectionAvailable(Context context) {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
return activeNetwork != null && activeNetwork.isConnectedOrConnecting();
}
/**
* Default private constructor.
*/
private TransferUtils() {
// does nothing
}
}