cz.maresmar.sfm.utils.MenuUtils.java Source code

Java tutorial

Introduction

Here is the source code for cz.maresmar.sfm.utils.MenuUtils.java

Source

/*
 * SmartFoodMenu - Android application for canteens extendable with plugins
 *
 * Copyright  2016-2018  Martin Mare <mmrmartin[at]gmail[dot]com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

package cz.maresmar.sfm.utils;

import android.app.PendingIntent;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.support.annotation.NonNull;
import android.support.annotation.StringRes;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationManagerCompat;
import android.support.v4.content.ContextCompat;
import android.text.format.DateFormat;

import cz.maresmar.sfm.Assert;
import cz.maresmar.sfm.BuildConfig;
import cz.maresmar.sfm.R;
import cz.maresmar.sfm.app.NotificationContract;
import cz.maresmar.sfm.plugin.BroadcastContract;
import cz.maresmar.sfm.provider.ProviderContract;
import cz.maresmar.sfm.view.MainActivity;

import static cz.maresmar.sfm.provider.ProviderContract.CREDENTIAL_FLAG_DISABLE_CREDIT_INCREASE_NOTIFICATION;
import static cz.maresmar.sfm.provider.ProviderContract.CREDENTIAL_FLAG_DISABLE_LOW_CREDIT_NOTIFICATION;
import static cz.maresmar.sfm.provider.ProviderContract.PORTAL_FLAG_DISABLE_NEW_MENU_NOTIFICATION;

/**
 * Utils for menu. Helps check menu for changes and shows more friendly data representations of data
 * columns used in menus.
 */
public class MenuUtils {

    // To prevent someone from accidentally instantiating the utils class,
    // make the constructor private.
    private MenuUtils() {
    }

    /**
     * Gets today date rounded to days
     *
     * @return Today's midnight in millis
     * @see System#currentTimeMillis()
     */
    public static long getTodayDate() {
        long actTime = System.currentTimeMillis();
        return actTime - (actTime % (1000 * 60 * 60 * 24));
    }

    /**
     * Translate date to user readable String using current locale
     *
     * @param context Valid context used to get locale
     * @param date    Date to be translated
     * @return String with date (without time)
     */
    @NonNull
    public static String getDateStr(Context context, long date) {
        return DateFormat.format("E, ", date) + DateFormat.getDateFormat(context).format(date);
    }

    /**
     * Translate date to user readable String using current locale
     *
     * @param context Valid context used to get locale
     * @param date    Date to be translated
     * @return String with date with time
     */
    @NonNull
    public static String getDateTimeStr(Context context, long date) {
        return getDateStr(context, date) + " " + DateFormat.getTimeFormat(context).format(date);
    }

    /**
     * Translate price to user readable String witch currency from current locale
     *
     * @param context  Valid context used to get locale
     * @param rawPrice Price to be translated (in farthing eg. {@code 5 CZK} is {@code 500})
     * @return String with price and currency
     */
    @NonNull
    public static String getPriceStr(@NonNull Context context, int rawPrice) {
        if (rawPrice != ProviderContract.NO_INFO) {
            float credit = ((float) rawPrice) / 100;
            return context.getString(R.string.credit_text, credit);
        } else {
            return context.getString(R.string.credit_no_info);
        }
    }

    /**
     * Returns error message if menu sync failed
     *
     * @param syncResult Worst sync result
     * @return String resource with error message
     * @see cz.maresmar.sfm.service.plugin.sync.SyncHandler.SyncResultListener#onSyncFinished(int)
     */
    @StringRes
    public static int getSyncErrorMessage(@BroadcastContract.SyncResult int syncResult) {
        switch (syncResult) {
        case BroadcastContract.RESULT_OK:
            throw new IllegalArgumentException("Cannot return error message when result is OK");
        case BroadcastContract.RESULT_WRONG_CREDENTIALS:
            return R.string.sync_result_wrong_credentials;
        case BroadcastContract.RESULT_UNKNOWN_PORTAL_FORMAT:
            return R.string.sync_result_unknown_portal_format;
        case BroadcastContract.RESULT_PORTAL_TEMPORALLY_INACCESSIBLE:
            return R.string.sync_result_temporally_inaccessible;
        case BroadcastContract.RESULT_NOT_SUPPORTED:
            return R.string.sync_result_not_supported;
        case BroadcastContract.RESULT_IO_EXCEPTION:
            return R.string.sync_result_io_exception;
        case BroadcastContract.RESULT_PLUGIN_TIMEOUT:
            return R.string.sync_result_plugin_timeout;
        default:
            throw new UnsupportedOperationException("Unknown result " + syncResult);
        }
    }

    /**
     * Check if credit is different from previous credit. If so it shows notification (if enabled)
     *
     * @param context        Some valid context
     * @param portalId       ID of portal with corresponding credential
     * @param credentialId   ID of credential to be checked
     * @param previousCredit Previous credit (before some plugin operation like sync happen)
     * @return {@code true} if credit changed, {@code false} otherwise
     */
    public static boolean checkCreditChanges(@NonNull Context context, long portalId, long credentialId,
            int previousCredit) {
        try (Cursor cursor = context.getContentResolver().query(
                ProviderContract.LogData.getUri(portalId, credentialId),
                new String[] { ProviderContract.LogData.CREDIT, ProviderContract.LogData.CREDENTIAL_NAME,
                        ProviderContract.Credentials.FLAGS, ProviderContract.Credentials.LOW_CREDIT_THRESHOLD },
                null, null, null)) {

            if (BuildConfig.DEBUG) {
                Assert.isOne(cursor.getCount());
            }

            cursor.moveToFirst();
            int actualCredit = cursor.getInt(0);
            String credentialName = cursor.getString(1);
            @ProviderContract.CredentialFlags
            int flags = cursor.getInt(2);
            int lowCreditThreshold = cursor.getInt(3) * 100;

            boolean increaseNotification = (flags
                    & CREDENTIAL_FLAG_DISABLE_CREDIT_INCREASE_NOTIFICATION) != CREDENTIAL_FLAG_DISABLE_CREDIT_INCREASE_NOTIFICATION;
            boolean lowCreditNotification = (flags
                    & CREDENTIAL_FLAG_DISABLE_LOW_CREDIT_NOTIFICATION) != CREDENTIAL_FLAG_DISABLE_LOW_CREDIT_NOTIFICATION;

            boolean changed = !(increaseNotification || lowCreditNotification);

            // Credit increased notification
            if (actualCredit > previousCredit && increaseNotification && actualCredit != ProviderContract.NO_INFO) {
                changed = true;

                // Prepare intent
                PendingIntent pendingIntent = MainActivity.getShowCreditIntent(context);

                NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context,
                        NotificationContract.CREDIT_CHANGE_CHANNEL_ID).setSmallIcon(R.drawable.notification_icon)
                                .setColor(ContextCompat.getColor(context, R.color.colorPrimary))
                                .setContentTitle(context.getString(R.string.notification_credit_icrease_title))
                                .setContentText(context.getString(R.string.notification_credit_icrease_text,
                                        getPriceStr(context, actualCredit), credentialName))
                                .setPriority(NotificationCompat.PRIORITY_DEFAULT)
                                // Set the intent that will fire when the user taps the notification
                                .setContentIntent(pendingIntent).setAutoCancel(true);

                NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
                // notificationId is a unique int for each notification that you must define
                notificationManager.notify((int) credentialId * 10, mBuilder.build());
            }

            if (lowCreditThreshold > actualCredit && lowCreditNotification
                    && actualCredit != ProviderContract.NO_INFO) {
                changed = true;

                // Prepare intent
                PendingIntent pendingIntent = MainActivity.getShowCreditIntent(context);

                NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context,
                        NotificationContract.CREDIT_CHANGE_CHANNEL_ID).setSmallIcon(R.drawable.notification_icon)
                                .setColor(ContextCompat.getColor(context, R.color.colorPrimary))
                                .setContentTitle(context.getString(R.string.notification_low_credit_title))
                                .setContentText(context.getString(R.string.notification_low_credit_text,
                                        getPriceStr(context, actualCredit), credentialName))
                                .setPriority(NotificationCompat.PRIORITY_DEFAULT)
                                // Set the intent that will fire when the user taps the notification
                                .setContentIntent(pendingIntent).setAutoCancel(true);

                NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
                // notificationId is a unique int for each notification that you must define
                notificationManager.notify((int) credentialId * 10, mBuilder.build());
            }

            return changed;
        }
    }

    /**
     * Check if portal has new menu available. If so it shows notification (if enabled)
     *
     * @param context              Some valid context
     * @param portalId             ID of portal to be checked
     * @param previousLastMenuDate Previous last menu entry date (before some plugin operation like sync happen)
     * @return {@code true} if new menu found, {@code false} otherwise
     */
    public static boolean checkMenuChanges(@NonNull Context context, long portalId, long previousLastMenuDate) {
        long actualLastMenuDate = getLastMenuDate(context, portalId);

        if (actualLastMenuDate > previousLastMenuDate) {
            try (Cursor cursor = context.getContentResolver().query(
                    ContentUris.withAppendedId(ProviderContract.Portal.getUri(), portalId),
                    new String[] { ProviderContract.Portal.NAME, ProviderContract.Portal.FLAGS }, null, null,
                    null)) {
                if (BuildConfig.DEBUG) {
                    Assert.isOne(cursor.getCount());
                }

                cursor.moveToFirst();
                String portalName = cursor.getString(0);
                @ProviderContract.PortalFlags
                int flags = cursor.getInt(1);

                if ((flags
                        & PORTAL_FLAG_DISABLE_NEW_MENU_NOTIFICATION) != PORTAL_FLAG_DISABLE_NEW_MENU_NOTIFICATION) {
                    // Prepare intent
                    PendingIntent pendingIntent = MainActivity.getDefaultIntent(context);

                    NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context,
                            NotificationContract.NEW_MENU_CHANNEL_ID).setSmallIcon(R.drawable.notification_icon)
                                    .setColor(ContextCompat.getColor(context, R.color.colorPrimary))
                                    .setContentTitle(context.getString(R.string.notification_new_menu_title))
                                    .setContentText(
                                            context.getString(R.string.notification_new_menu_text, portalName))
                                    .setPriority(NotificationCompat.PRIORITY_LOW)
                                    // Set the intent that will fire when the user taps the notification
                                    .setContentIntent(pendingIntent).setAutoCancel(true);

                    NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
                    // notificationId is a unique int for each notification that you must define
                    notificationManager.notify((int) portalId * 1000, mBuilder.build());
                }
            }
            return true;
        } else {
            return false;
        }
    }

    /**
     * Returns date of last menu entry in some portal
     *
     * @param context  Some valid context
     * @param portalId ID of portal
     * @return Date in millis or {@code 0} if portal hasn't any menu
     */
    public static long getLastMenuDate(@NonNull Context context, long portalId) {
        try (Cursor cursor = context.getContentResolver().query(ProviderContract.MenuEntry.getPortalUri(portalId),
                new String[] { "MAX(" + ProviderContract.MenuEntry.DATE + ")" }, null, null, null)) {
            if (BuildConfig.DEBUG) {
                Assert.isOne(cursor.getCount());
            }

            cursor.moveToFirst();

            if (cursor.isNull(0)) {
                return 0L;
            } else {
                return cursor.getLong(0);
            }
        }
    }
}