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;

}

}

}

}