Search in sources :

Example 1 with ApiStatus

use of com.thebluealliance.androidclient.models.ApiStatus in project the-blue-alliance-android by the-blue-alliance.

the class DatafeedActivity method onResume.

@Override
protected void onResume() {
    super.onResume();
    mEventBus.register(this);
    ApiStatus status = mStatusController.fetchApiStatus();
    commonStatusUpdate(status);
}
Also used : ApiStatus(com.thebluealliance.androidclient.models.ApiStatus)

Example 2 with ApiStatus

use of com.thebluealliance.androidclient.models.ApiStatus in project the-blue-alliance-android by the-blue-alliance.

the class StatusRefreshService method updateTbaStatus.

@WorkerThread
private void updateTbaStatus() {
    if (!ConnectionDetector.isConnectedToInternet(this)) {
        return;
    }
    /* Updating FirebaseRemoteConfig */
    try {
        mAppConfig.updateRemoteDataBlocking();
    } catch (ExecutionException | InterruptedException e) {
        TbaLogger.w("Error updating FirebaseRemoteConfig: " + e.getMessage(), e);
    }
    Response<ApiStatus> response;
    try {
        response = mRetrofitAPI.fetchApiStatus().toBlocking().first();
    } catch (Exception ex) {
        TbaLogger.w("Error updating TBA status: " + ex.getMessage(), ex);
        return;
    }
    if (!response.isSuccessful()) {
        TbaLogger.w("Unable to update myTBA Status\n" + response.code() + " " + response.message());
        return;
    }
    ApiStatus status = response.body();
    /* Write the new data to shared prefs */
    mPrefs.edit().putString(TBAStatusController.STATUS_PREF_KEY, status.getJsonBlob()).apply();
    /* Post the update to the EventBus */
    mEventBus.post(status);
    /* Update Champs pit locations if necessary */
    PitLocationHelper.updateRemoteDataIfNeeded(getApplicationContext(), mAppConfig, mHttpClient);
    if (status.getMinAppVersion() != null && BuildConfig.VERSION_CODE < status.getMinAppVersion()) {
        startActivity(new Intent(this, UpdateRequiredActivity.class));
    }
}
Also used : ApiStatus(com.thebluealliance.androidclient.models.ApiStatus) Intent(android.content.Intent) ExecutionException(java.util.concurrent.ExecutionException) ExecutionException(java.util.concurrent.ExecutionException) UpdateRequiredActivity(com.thebluealliance.androidclient.activities.UpdateRequiredActivity) WorkerThread(androidx.annotation.WorkerThread)

Example 3 with ApiStatus

use of com.thebluealliance.androidclient.models.ApiStatus in project the-blue-alliance-android by the-blue-alliance.

the class TBAStatusController method onActivityResumed.

@Override
public void onActivityResumed(Activity activity) {
    /* Update myTBA Status */
    double timeout = mUserIsLoggedIn ? UPDATE_TIMEOUT_LOGGED_IN_NS : UPDATE_TIMEOUT_LOGGED_OUT_NS;
    if (mLastUpdateTime + timeout < System.nanoTime()) {
        scheduleStatusUpdate(activity);
        mLastUpdateTime = System.nanoTime();
    }
    /* App updates required/recommended */
    if (!Utilities.isDebuggable() && BuildConfig.VERSION_CODE < getMinAppVersion()) {
        activity.startActivity(new Intent(activity, UpdateRequiredActivity.class));
    } else if (!Utilities.isDebuggable() && BuildConfig.VERSION_CODE < getLatestAppVersion() && mLastDialogTime + DIALOG_TIMEOUT < System.nanoTime()) {
        /* Show an app update dialog */
        new AlertDialog.Builder(activity).setTitle(R.string.update_dialog_title).setMessage(R.string.update_dialog_text).setPositiveButton(R.string.update_dialog_action, (dialog, which) -> {
            /* Open Play Store page */
            dialog.dismiss();
            Intent i = new Intent(Intent.ACTION_VIEW);
            i.setData(Uri.parse("https://play.google.com/store/apps/details?id=com.thebluealliance.androidclient"));
            activity.startActivity(i);
        }).setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss()).show();
        mLastDialogTime = System.nanoTime();
    }
    /* Admin push message */
    ApiStatus status = fetchApiStatus();
    Date now = new Date();
    if (status != null && status.getHasMessage() && mLastMessageTime + DIALOG_TIMEOUT < System.nanoTime() && status.getMessageExpiration().compareTo(now.getTime()) > 0) {
        new AlertDialog.Builder(activity).setMessage(status.getMessageText()).setCancelable(false).setPositiveButton(R.string.ok, ((dialog, which) -> dialog.dismiss())).show();
        mLastMessageTime = System.nanoTime();
    }
    /* Clear OkHttp Cache when commanded */
    clearOkCacheIfNeeded(status, false);
}
Also used : Context(android.content.Context) Bundle(android.os.Bundle) AlertDialog(androidx.appcompat.app.AlertDialog) Cache(okhttp3.Cache) Date(java.util.Date) Uri(android.net.Uri) Intent(android.content.Intent) AccountController(com.thebluealliance.androidclient.accounts.AccountController) Singleton(javax.inject.Singleton) Inject(javax.inject.Inject) Calendar(java.util.Calendar) Gson(com.google.gson.Gson) Schedulers(rx.schedulers.Schedulers) ApiStatus(com.thebluealliance.androidclient.models.ApiStatus) R(com.thebluealliance.androidclient.R) TbaLogger(com.thebluealliance.androidclient.TbaLogger) IOException(java.io.IOException) Nullable(androidx.annotation.Nullable) BuildConfig(com.thebluealliance.androidclient.BuildConfig) SharedPreferences(android.content.SharedPreferences) Application(android.app.Application) Utilities(com.thebluealliance.androidclient.Utilities) AnalyticsActions(com.thebluealliance.androidclient.background.AnalyticsActions) UpdateRequiredActivity(com.thebluealliance.androidclient.activities.UpdateRequiredActivity) Activity(android.app.Activity) ApiStatus(com.thebluealliance.androidclient.models.ApiStatus) Intent(android.content.Intent) UpdateRequiredActivity(com.thebluealliance.androidclient.activities.UpdateRequiredActivity) Date(java.util.Date)

Example 4 with ApiStatus

use of com.thebluealliance.androidclient.models.ApiStatus in project the-blue-alliance-android by the-blue-alliance.

the class LoadTBADataWorker method doWork.

@NonNull
@Override
public Result doWork() {
    mStartTime = System.currentTimeMillis();
    int[] params = getInputData().getIntArray(DATA_TO_LOAD);
    int[] dataToLoad;
    if (params == null || params.length == 0) {
        dataToLoad = new int[] { LOAD_TEAMS, LOAD_EVENTS, LOAD_DISTRICTS };
    } else {
        dataToLoad = params;
    }
    try {
        /* First, do a blocking update of Remote Config */
        publishProgress(new LoadProgressInfo(LoadProgressInfo.STATE_LOADING, mApplicationContext.getString(R.string.loading_config)));
        mAppConfig.updateRemoteDataBlocking();
        Call<ApiStatus> statusCall = mDatafeed.fetchApiStatus();
        Response<ApiStatus> statusResponse = statusCall.execute();
        if (!statusResponse.isSuccessful() || statusResponse.body() == null || statusResponse.body().getMaxSeason() == null) {
            TbaLogger.e("Bad API status response: " + statusResponse);
            return onConnectionError();
        }
        int maxCompYear = statusResponse.body().getMaxSeason();
        List<Team> allTeams = new ArrayList<>();
        int maxPageNum = 0;
        if (Arrays.binarySearch(dataToLoad, LOAD_TEAMS) != -1) {
            mDb.getTeamsTable().deleteAllRows();
            // First we will load all the teams
            for (int pageNum = 0; pageNum < 20; pageNum++) {
                // limit to 20 pages to prevent potential infinite loop
                if (isStopped()) {
                    return Result.failure();
                }
                int start = pageNum * Constants.API_TEAM_LIST_PAGE_SIZE;
                int end = start + Constants.API_TEAM_LIST_PAGE_SIZE - 1;
                start = start == 0 ? 1 : start;
                publishProgress(new LoadProgressInfo(LoadProgressInfo.STATE_LOADING, mApplicationContext.getString(R.string.loading_teams, start, end)));
                Call<List<Team>> teamListCall = mDatafeed.fetchTeamPage(pageNum, ApiConstants.TBA_CACHE_WEB);
                Response<List<Team>> teamListResponse = teamListCall.execute();
                if (!teamListResponse.isSuccessful()) {
                    return onConnectionError();
                }
                if (teamListResponse.body() == null || teamListResponse.body().isEmpty()) {
                    // No teams found for a page; we are done
                    break;
                }
                Date lastModified = teamListResponse.headers().getDate("Last-Modified");
                List<Team> responseBody = teamListResponse.body();
                if (lastModified != null) {
                    long lastModifiedTimestamp = lastModified.getTime();
                    for (int i = 0; i < responseBody.size(); i++) {
                        responseBody.get(i).setLastModified(lastModifiedTimestamp);
                    }
                }
                allTeams.addAll(responseBody);
                maxPageNum = Math.max(maxPageNum, pageNum);
            }
        }
        List<Event> allEvents = new ArrayList<>();
        if (Arrays.binarySearch(dataToLoad, LOAD_EVENTS) != -1) {
            mDb.getEventsTable().deleteAllRows();
            // Now we load all events
            for (int year = Constants.FIRST_COMP_YEAR; year <= maxCompYear; year++) {
                if (isStopped()) {
                    return Result.failure();
                }
                publishProgress(new LoadProgressInfo(LoadProgressInfo.STATE_LOADING, mApplicationContext.getString(R.string.loading_events, Integer.toString(year))));
                Call<List<Event>> eventListCall = mDatafeed.fetchEventsInYear(year, ApiConstants.TBA_CACHE_WEB);
                Response<List<Event>> eventListResponse = eventListCall.execute();
                if (!eventListResponse.isSuccessful()) {
                    return onConnectionError();
                }
                if (eventListResponse.body() == null) {
                    continue;
                }
                Date lastModified = eventListResponse.headers().getDate("Last-Modified");
                List<Event> responseBody = eventListResponse.body();
                if (lastModified != null) {
                    long lastModifiedTimestamp = lastModified.getTime();
                    for (int i = 0; i < responseBody.size(); i++) {
                        responseBody.get(i).setLastModified(lastModifiedTimestamp);
                    }
                }
                allEvents.addAll(responseBody);
                TbaLogger.i(String.format("Loaded %1$d events in %2$d", eventListResponse.body().size(), year));
            }
        }
        List<District> allDistricts = new ArrayList<>();
        if (Arrays.binarySearch(dataToLoad, LOAD_DISTRICTS) != -1) {
            mDb.getDistrictsTable().deleteAllRows();
            // load all districts
            for (int year = Constants.FIRST_DISTRICT_YEAR; year <= maxCompYear; year++) {
                if (isStopped()) {
                    return Result.failure();
                }
                publishProgress(new LoadProgressInfo(LoadProgressInfo.STATE_LOADING, mApplicationContext.getString(R.string.loading_districts, year)));
                AddDistrictKeys keyAdder = new AddDistrictKeys(year);
                Call<List<District>> districtListCall = mDatafeed.fetchDistrictList(year, ApiConstants.TBA_CACHE_WEB);
                Response<List<District>> districtListResponse = districtListCall.execute();
                if (!districtListResponse.isSuccessful() || districtListResponse.body() == null) {
                    return onConnectionError();
                }
                List<District> newDistrictList = districtListResponse.body();
                keyAdder.call(newDistrictList);
                Date lastModified = districtListResponse.headers().getDate("Last-Modified");
                if (lastModified != null) {
                    long lastModifiedTimestamp = lastModified.getTime();
                    for (int i = 0; i < newDistrictList.size(); i++) {
                        newDistrictList.get(i).setLastModified(lastModifiedTimestamp);
                    }
                }
                allDistricts.addAll(newDistrictList);
                TbaLogger.i(String.format("Loaded %1$d districts in %2$d", newDistrictList.size(), year));
            }
        }
        if (isStopped()) {
            return Result.failure();
        }
        // If no exception has been thrown at this point, we have all the data. We can now
        // insert it into the database. Pass a 0 as the last-modified time here, because we set
        // it individually above
        publishProgress(new LoadProgressInfo(LoadProgressInfo.STATE_LOADING, mApplicationContext.getString(R.string.loading_almost_finished)));
        TbaLogger.i("Writing " + allTeams.size() + " teams");
        mTeamWriter.write(allTeams, 0L);
        TbaLogger.i("Writing " + allEvents.size() + " events");
        mEventWriter.write(allEvents, 0L);
        TbaLogger.i("Writing " + allDistricts.size() + " districts");
        mDistrictWriter.write(allDistricts, 0L);
        SharedPreferences.Editor editor = mSharedPreferences.edit();
        // Write TBA Status
        editor.putString(TBAStatusController.STATUS_PREF_KEY, statusResponse.body().getJsonBlob());
        editor.putInt(Constants.LAST_YEAR_KEY, statusResponse.body().getMaxSeason());
        // Loop through all pages
        for (int pageNum = 0; pageNum <= maxPageNum; pageNum++) {
            editor.putBoolean(Database.ALL_TEAMS_LOADED_TO_DATABASE_FOR_PAGE + pageNum, true);
        }
        // Loop through all years
        for (int year = Constants.FIRST_COMP_YEAR; year <= maxCompYear; year++) {
            editor.putBoolean(Database.ALL_EVENTS_LOADED_TO_DATABASE_FOR_YEAR + year, true);
        }
        // Loop through years for districts
        for (int year = Constants.FIRST_DISTRICT_YEAR; year <= maxCompYear; year++) {
            editor.putBoolean(Database.ALL_DISTRICTS_LOADED_TO_DATABASE_FOR_YEAR + year, true);
        }
        editor.putInt(Constants.APP_VERSION_KEY, BuildConfig.VERSION_CODE);
        editor.apply();
        AnalyticsHelper.sendTimingUpdate(mApplicationContext, System.currentTimeMillis() - mStartTime, "load all data", "");
        return Result.success(new LoadProgressInfo(LoadProgressInfo.STATE_FINISHED, mApplicationContext.getString(R.string.loading_finished)).toData());
    } catch (RuntimeException ex) {
        // This is bad, probably an error in the response from the server
        TbaLogger.e("Error loading initial data", ex);
        return onFailure(new LoadProgressInfo(LoadProgressInfo.STATE_ERROR, Utilities.exceptionStacktraceToString(ex)));
    } catch (IOException | InterruptedException | ExecutionException e) {
        /* Some sort of network error */
        TbaLogger.e("Error loading initial data", e);
        return onConnectionError();
    }
}
Also used : ApiStatus(com.thebluealliance.androidclient.models.ApiStatus) ArrayList(java.util.ArrayList) Team(com.thebluealliance.androidclient.models.Team) ArrayList(java.util.ArrayList) List(java.util.List) ExecutionException(java.util.concurrent.ExecutionException) SharedPreferences(android.content.SharedPreferences) IOException(java.io.IOException) AddDistrictKeys(com.thebluealliance.androidclient.datafeed.maps.AddDistrictKeys) Date(java.util.Date) Event(com.thebluealliance.androidclient.models.Event) District(com.thebluealliance.androidclient.models.District) NonNull(androidx.annotation.NonNull)

Example 5 with ApiStatus

use of com.thebluealliance.androidclient.models.ApiStatus in project the-blue-alliance-android by the-blue-alliance.

the class APIStatusDeserializer method deserialize.

@Override
public ApiStatus deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
    if (!json.isJsonObject()) {
        throw new JsonParseException("Data is not JsonObject");
    }
    JsonObject data = json.getAsJsonObject();
    ApiStatus status = new ApiStatus();
    JsonElement maxSeason = data.get(MAX_SEASON_TAG);
    if (maxSeason == null || !maxSeason.isJsonPrimitive()) {
        throw new JsonParseException("Max Season is not a primitive");
    }
    status.setMaxSeason(maxSeason.getAsInt());
    JsonElement currentSeason = data.get(CURRENT_SEASON_TAG);
    if (currentSeason == null || !currentSeason.isJsonPrimitive()) {
        // Default to the current year if not set
        int currentYear = Calendar.getInstance().get(Calendar.YEAR);
        status.setCurrentSeason(currentYear);
    } else {
        status.setCurrentSeason(currentSeason.getAsInt());
    }
    // Ensure that maxSeason always is max(current, given max)
    int maxSeasonCheck = Math.max(status.getMaxSeason(), status.getCurrentSeason());
    status.setMaxSeason(maxSeasonCheck);
    JsonElement fmsApiDown = data.get(FMS_API_DOWN_TAG);
    if (fmsApiDown == null || !fmsApiDown.isJsonPrimitive()) {
        throw new JsonParseException("Is Datafeed Down is not a primitive");
    }
    status.setFmsApiDown(fmsApiDown.getAsBoolean());
    JsonElement downEvents = data.get(DOWN_EVENTS_TAG);
    if (downEvents == null || !downEvents.isJsonArray()) {
        throw new JsonParseException("Down Events is not an array");
    }
    List<String> downKeys = new ArrayList<>();
    for (JsonElement eventKey : downEvents.getAsJsonArray()) {
        if (!eventKey.isJsonPrimitive()) {
            continue;
        }
        downKeys.add(eventKey.getAsString());
    }
    status.setDownEvents(downKeys);
    JsonElement androidSpecific = data.get(ANDROID_SETTINGS_TAG);
    if (androidSpecific == null || !androidSpecific.isJsonObject()) {
        throw new JsonParseException("No Android specific settings");
    }
    JsonObject androidSettings = androidSpecific.getAsJsonObject();
    JsonElement minAppVersion = androidSettings.get(MIN_APP_VERSION_TAG);
    if (minAppVersion == null || !minAppVersion.isJsonPrimitive()) {
        throw new JsonParseException("Min App Version not found");
    }
    status.setMinAppVersion(minAppVersion.getAsInt());
    JsonElement latestAppVersion = androidSettings.get(LATEST_APP_VERSION_TAG);
    if (latestAppVersion == null || !latestAppVersion.isJsonPrimitive()) {
        throw new JsonParseException("Latest app version not found");
    }
    status.setLatestAppVersion(latestAppVersion.getAsInt());
    JsonElement message = data.get(MESSAGE_DICT);
    if (message != null && !message.isJsonNull() && message.isJsonObject()) {
        JsonObject adminMessage = data.get(MESSAGE_DICT).getAsJsonObject();
        JsonElement messageText = adminMessage.get(MESSAGE_TEXT);
        JsonElement messageExpiration = adminMessage.get(MESSAGE_EXPIRATION);
        if (messageText == null || messageText.isJsonNull() || messageExpiration == null || messageExpiration.isJsonNull()) {
            throw new JsonParseException("Message requires text and expiration");
        }
        status.setHasMessage(true);
        status.setMessageText(messageText.getAsString());
        status.setMessageExpiration(new Date(messageExpiration.getAsLong() * MS_PER_SECOND).getTime());
    } else {
        status.setHasMessage(false);
    }
    JsonElement lastCacheClear = data.get(LAST_OKHTTP_CACHE_CLEAR);
    if (lastCacheClear != null && !lastCacheClear.isJsonNull() && lastCacheClear.isJsonPrimitive()) {
        JsonPrimitive lastCacheTime = lastCacheClear.getAsJsonPrimitive();
        long lastTimestamp = lastCacheTime.getAsLong();
        status.setLastOkHttpCacheClear(lastTimestamp);
    } else {
        status.setLastOkHttpCacheClear((long) -1);
    }
    status.setJsonBlob(json.toString());
    return status;
}
Also used : JsonPrimitive(com.google.gson.JsonPrimitive) JsonElement(com.google.gson.JsonElement) ApiStatus(com.thebluealliance.androidclient.models.ApiStatus) ArrayList(java.util.ArrayList) JsonObject(com.google.gson.JsonObject) JsonParseException(com.google.gson.JsonParseException) Date(java.util.Date)

Aggregations

ApiStatus (com.thebluealliance.androidclient.models.ApiStatus)5 Date (java.util.Date)3 Intent (android.content.Intent)2 SharedPreferences (android.content.SharedPreferences)2 UpdateRequiredActivity (com.thebluealliance.androidclient.activities.UpdateRequiredActivity)2 IOException (java.io.IOException)2 ArrayList (java.util.ArrayList)2 ExecutionException (java.util.concurrent.ExecutionException)2 Activity (android.app.Activity)1 Application (android.app.Application)1 Context (android.content.Context)1 Uri (android.net.Uri)1 Bundle (android.os.Bundle)1 NonNull (androidx.annotation.NonNull)1 Nullable (androidx.annotation.Nullable)1 WorkerThread (androidx.annotation.WorkerThread)1 AlertDialog (androidx.appcompat.app.AlertDialog)1 Gson (com.google.gson.Gson)1 JsonElement (com.google.gson.JsonElement)1 JsonObject (com.google.gson.JsonObject)1