CatalogService
package com.spawnlabs.endpoint.gamestick.player.service.catalog;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import com.spawnlabs.endpoint.gamestick.player.Constants;
import com.spawnlabs.endpoint.gamestick.player.R;
import com.spawnlabs.endpoint.gamestick.player.db.CatalogDatabase;
import com.spawnlabs.endpoint.gamestick.player.model.catalog.CatalogPackage;
import com.spawnlabs.endpoint.gamestick.player.model.catalog.Game;
import com.spawnlabs.endpoint.gamestick.player.service.spawnrest.SpawnRestException;
import com.spawnlabs.endpoint.gamestick.player.util.CatalogSeedUtil;
import com.spawnlabs.endpoint.gamestick.player.util.HttpUtil;
import com.spawnlabs.endpoint.gamestick.player.util.JsonUtil;
import java.io.IOException;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
/**
*
*
*/
public class CatalogService
extends Service {
private static final String TAG = "GS-" + CatalogService.class.getSimpleName();
public static final String PATH_GENRES = "/genres";
public static final String PATH_GAMES = "/games";
private static final String QUERY_DETAIL = "detail=true";
private static final long ten_min = 600000;
private static final String ACTION_SCHEDULE_CHECK = "schedule";
private CatalogServiceHandler catalogServiceHandler;
private String catalogServiceGamesUrl;
private String catalogServiceGenresUrl;
// ================================================================================================================================================================
// Lifecycle / Service
// ================================================================================================================================================================
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
String serviceUrl = getString(R.string.spawnRestApirul);
catalogServiceGamesUrl = serviceUrl + PATH_GAMES;
catalogServiceGenresUrl = serviceUrl + PATH_GENRES;
// Handler ==============================================================
HandlerThread thread = new HandlerThread(getClass().getName() + "-PollThread", THREAD_PRIORITY_BACKGROUND);
thread.start();
catalogServiceHandler = new CatalogServiceHandler(thread.getLooper());
// Check catalog seed ===================================================
if (!CatalogSeedUtil.isSeedReady()) {
// The seed files are missing. Try to get them from - tho, strictly-speaking,
// this is not a scenario we support. This is more just to help in dev environment.
Log.w(TAG, "Seed files missing. Updating catalog...");
forceUpdateCatalog();
}
// Start update timer ===================================================
scheduleNextTimeCheck();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
String intentExtra = intent.getStringExtra(Constants.KEY_ACTION);
Log.v(TAG, "onStartCommand() intent: " + intentExtra);
if (ACTION_SCHEDULE_CHECK.equals(intentExtra)) {
scheduleNextTimeCheck();
}
else if (Constants.ACTION_INIT.equals(intentExtra)) {
// Only force an update check on *later* calls to startService(). Not the init one.
// So do nothing here; we're just starting it up.
}
else /*if (all other cases / null intent)*/ {
forceUpdateCatalog();
}
return START_STICKY;
}
// ================================================================================================================================================================
// Internal methods
// ================================================================================================================================================================
private void scheduleNextTimeCheck() {
catalogServiceHandler.sendEmptyMessageDelayed(CatalogServiceHandler.CATALOG_SERVICE_HANDLER_CHECK_FOR_UPDATE, ten_min);
}
private void forceUpdateCatalog() {
Log.i(TAG, "forceUpdateCatalog()");
catalogServiceHandler.sendEmptyMessage(CatalogServiceHandler.CATALOG_SERVICE_HANDLER_FORCE_UPDATE);
}
/**
* Call only from within Handler
*/
private void updateCatalog() {
Log.v(TAG, "updateCatalog()");
new GetCatalogAsyncTask().execute();
}
private boolean isWifiConnected() {
return ((ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE)).getNetworkInfo(ConnectivityManager.TYPE_WIFI).isConnected();
}
// ============================================================================================================================================================
// Inner classes
// ============================================================================================================================================================
class CatalogServiceHandler
extends Handler {
static final int CATALOG_SERVICE_HANDLER_CHECK_FOR_UPDATE = 0;
static final int CATALOG_SERVICE_HANDLER_FORCE_UPDATE = 1;
public CatalogServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case (CATALOG_SERVICE_HANDLER_CHECK_FOR_UPDATE):
try {
if (timeIsGoodForUpdate()) {
updateCatalog();
}
} finally {
// Even if something fails, trigger the next poll
scheduleNextTimeCheck();
}
break;
case (CATALOG_SERVICE_HANDLER_FORCE_UPDATE):
updateCatalog();
break;
}
}
/**
* Call only on worker thread
*/
private boolean timeIsGoodForUpdate() {
// Create time markers every time we check.
TimeZone cst = TimeZone.getTimeZone("CST");
Calendar twoAM = new GregorianCalendar(cst);
twoAM.set(GregorianCalendar.HOUR_OF_DAY, 2);
twoAM.set(GregorianCalendar.MINUTE, 0);
twoAM.set(GregorianCalendar.SECOND, 0);
Calendar twoTenAM = new GregorianCalendar(cst);
twoTenAM.set(GregorianCalendar.HOUR_OF_DAY, 2);
twoTenAM.set(GregorianCalendar.MINUTE, 10);
twoTenAM.set(GregorianCalendar.SECOND, 0);
Calendar now = Calendar.getInstance(cst);
return now.after(twoAM) && now.before(twoTenAM);
}
}
private class GetCatalogAsyncTask
extends AsyncTask<Void, Integer, CatalogPackage> {
private final String TAG = "GS-" + CatalogService.GetCatalogAsyncTask.class.getSimpleName();
@Override
protected CatalogPackage doInBackground(Void... params) {
if (!isWifiConnected()) {
Log.e(TAG, "Wifi not connected. Will try again later.");// todo -EJT decide Not sure how this works with OOBE.
startService(new Intent(CatalogService.this, CatalogService.class).putExtra(Constants.KEY_ACTION, ACTION_SCHEDULE_CHECK));
return null;
}
// --- games
// Get json
Log.v(TAG, "Getting catalog...");
String jsonCatalog = getGamesJsonFromCatalogService();
if (jsonCatalog == null) {
return null;
}
// make games from json
List<Game> games = JsonUtil.gamesFrom(jsonCatalog);
if (games != null) {// if that succeeds...
// write json to seed file
Log.v(TAG, "Writing json to catalog seed file");
try {
CatalogSeedUtil.overwriteJsonCatalogSeed(jsonCatalog);
} catch (IOException e) {
Log.e(TAG, "Exception writing new Game Catalog to file.", e);
}
} else {
Log.e(TAG, "Exception updating Catalog games. (games = null)");
return null;
}
// --- genres
// Get json
Log.v(TAG, "Getting genres...");
String jsonGenres = getGenresJsonFromCatalogService();
// Get genres from json
Map<String, String[]> genres = JsonUtil.genresFrom(jsonGenres);
// make persistable string from json
String genresString = CatalogPackage.staticGetGenresAsString(genres.keySet());
int size = genres.keySet().size();
if (size != 0 && genresString.split(",").length == size) {// if that succeeds...
// write json to seed file
Log.v(TAG, "Writing json to genre seed file");
try {
CatalogSeedUtil.overwriteJsonGenreSeed(jsonGenres);
} catch (IOException e) {
Log.e(TAG, "Exception writing new genre list to file.", e);
}
} else {
Log.e(TAG, "Exception updating Catalog genres. " + size + " : " + genresString.split(",").length);
return null;
}
CatalogPackage catalogPackage = new CatalogPackage(games, genres);
// Update the CatalogDatabase
CatalogDatabase.getCatalogDatabase().repopulateDatabase(catalogPackage);
return catalogPackage;
}
@Override
protected void onPostExecute(CatalogPackage catalogPackage) {
}
private String getGamesJsonFromCatalogService() {
try {
return HttpUtil.httpGet(catalogServiceGamesUrl + "?" + QUERY_DETAIL);
} catch (SpawnRestException e) {
Log.wtf(TAG, "Failed to get games from Catalog Service.", e);
return null;
}
}
private String getGenresJsonFromCatalogService() {
try {
return HttpUtil.httpGet(catalogServiceGenresUrl);
} catch (SpawnRestException e) {
Log.wtf(TAG, "Failed to get genres from Catalog Service.", e);
return null;
}
}
}
}