public class App extends Application {
    private static App appInstance;

    public static App getInstance() {
        return appInstance;
    }

    @NonNull
    public static Context context() {
        return appInstance.getApplicationContext();
    }

    public AlarmsRepository provideAlarmsRepository() {
        return AlarmsRepository.getInstance(getApplicationContext());
    }

    @SuppressLint("CheckResult")
    @Override
    public void onCreate() {
        super.onCreate();
        appInstance = this;

        RxJavaPlugins.setErrorHandler(throwable -> L.e("App", throwable.getLocalizedMessage()));
    }

    /**
     * Enum used to identify the tracker that needs to be used for tracking.
     * <p/>
     * A single tracker is usually enough for most purposes. In case you do need multiple trackers,
     * storing them all in Application object helps ensure that they are created only once per
     * application instance.
     */
    public enum TrackerName {
        APP_TRACKER, // Tracker used only in this app.
        GLOBAL_TRACKER, // Tracker used by all the apps from a company. eg: roll-up tracking.
    }


} public class SourcesManager {

    private static final AtomicBoolean isInitialized = new AtomicBoolean(false);

    private static ArrayList<CurrencySource> currencySources = null;

    private static final PublishSubject<Boolean> sourceUpdates = PublishSubject.create();

    public static void init() {
        if (isInitialized.get()) return;// Already initialized
        initDataSourceSelections();
    }

    public static PublishSubject<Boolean> getSourceUpdates() {
        return sourceUpdates;
    }

    @Nullable
    public static IPollingSource getProviderForSource(List<IPollingSource> providers, CurrencySource source) {
        IPollingSource pollingSource = null;
        switch (source.getType()) {
            case CurrencyType.PARAGARANTI:
                pollingSource = CollectionUtils.getInstance(providers, ParaGarantiRateProvider.class);
                break;
            case CurrencyType.ALTININ:
                pollingSource = CollectionUtils.getInstance(providers, YorumlarRateProvider.class);
                break;
            case CurrencyType.ENPARA:
                pollingSource = CollectionUtils.getInstance(providers, EnparaRateProvider.class);
                break;
            case CurrencyType.BIGPARA:
                pollingSource = CollectionUtils.getInstance(providers, BigparaRateProvider.class);
                break;
            case CurrencyType.TLKUR:
                pollingSource = CollectionUtils.getInstance(providers, DolarTlKurRateProvider.class);
                break;
            case CurrencyType.YAPIKREDI:
                pollingSource = CollectionUtils.getInstance(providers, YapıKrediRateProvider.class);
                break;
            case CurrencyType.YAHOO:
                pollingSource = CollectionUtils.getInstance(providers, YahooRateProvider.class);
                break;
            case CurrencyType.BLOOMBERGHT:
                pollingSource = CollectionUtils.getInstance(providers, BloombergRateProvider.class);
                break;
        }
        return pollingSource;
    }

    public static void selectSources(final Activity activity) {
        final ArrayList<CurrencySource> currencySources = getCurrencySources();
        final boolean[] temp_data_source_states = new boolean[currencySources.size()];
        for (int i = 0; i < temp_data_source_states.length; i++) {
            temp_data_source_states[i] = currencySources.get(i).isEnabled();
        }
        String[] data_set_names = new String[currencySources.size()];
        for (int i = 0; i < currencySources.size(); i++) {
            data_set_names[i] = currencySources.get(i).getName();
        }
        AlertDialog.Builder builder = new AlertDialog.Builder(activity, R.style.AppTheme_Alert);

        builder.setMultiChoiceItems(data_set_names, temp_data_source_states, (dialog, which, isChecked) -> temp_data_source_states[which] = isChecked);

        builder.setCancelable(true);
        builder.setTitle(R.string.select_sources);
        builder.setPositiveButton(R.string.apply, null);
        builder.setNegativeButton(R.string.dismiss, null);

        final AlertDialog dialog = builder.create();
        dialog.setOnShowListener(dialogInterface -> {
            Button button = dialog.getButton(AlertDialog.BUTTON_POSITIVE);
            button.setOnClickListener(view -> {

                int enabled_count = 0;
                for (int i = 0; i < currencySources.size(); i++) {
                    if (temp_data_source_states[i]) enabled_count++;
                }
                if (enabled_count == 0) {
                    Toast.makeText(activity, R.string.select_at_least_one_source, Toast.LENGTH_SHORT).show();
                } else {
                    for (int i = 0; i < currencySources.size(); i++) {
                        currencySources.get(i).setEnabled(temp_data_source_states[i]);
                    }
                    sourceUpdates.onNext(true);
                    saveSources(currencySources);
                    dialog.dismiss();
                }
            });
        });
        dialog.getWindow().setWindowAnimations(R.style.DialogAnimationFade);
        dialog.show();
    }

    private static void saveSources(List<CurrencySource> currencySources) {
        StringBuilder sources = new StringBuilder();
        for (int i = 0; i < currencySources.size(); i++) {
            CurrencySource currencySource = currencySources.get(i);
            if (currencySource.isEnabled()) {
                sources.append(currencySource.getType()).append(";");
            }
        }
        Prefs.saveSources(sources.toString());
    }


    /**
     * @param value_type is one of {@link ValueType}
     */
    public static String getSourceName(int type, int value_type) {
        String postFix = "";
        if (value_type == ValueType.SELL) {
            postFix = " " + App.context().getString(R.string.sell);
        } else if (value_type == ValueType.BUY) {
            postFix = " " + App.context().getString(R.string.buy);
        }
        return getSourceName(type) + postFix;
    }

    public static String getSourceName(int type) {
        for (CurrencySource source : getCurrencySources()) {
            if (type == source.getType()) {
                return source.getName();
            }
        }
        return "";
    }

    public static ArrayList<CurrencySource> getCurrencySources() {
        if (!isInitialized.get()) {
            init();
        }
        return currencySources;
    }

    public static CurrencySource getSource(int source_type) {
        for (CurrencySource currencySource : getCurrencySources()) {
            if (currencySource.getType() == source_type) return currencySource;
        }
        return null;
    }

    @ColorInt
    private static int color(@ColorRes int colorRes) {
        return ContextCompat.getColor(App.context(), colorRes);
    }

    private static void initDataSourceSelections() {

        int[] only_usd_try = {IRate.USD};
        int[] altinInSupported = {IRate.USD, IRate.EUR, IRate.EUR_USD, IRate.ONS};
        int[] paragarantiSupported = {IRate.USD, IRate.EUR, IRate.EUR_USD};
        int[] enparaSupported = {IRate.USD, IRate.EUR, IRate.EUR_USD, IRate.ONS_TRY};
        int[] yapikrediSupported = {IRate.USD, IRate.EUR, IRate.ONS_TRY};
        int[] bloombergSupported = {IRate.USD, IRate.EUR, IRate.EUR_USD, IRate.ONS};
        currencySources = new ArrayList<>();
        currencySources.add(new CurrencySource("Altın.in", CurrencyType.ALTININ, color(R.color.colorYorumlar), true, altinInSupported));
        currencySources.add(new CurrencySource("Enpara", CurrencyType.ENPARA, color(R.color.colorEnpara), false, enparaSupported));
        currencySources.add(new CurrencySource("Bigpara", CurrencyType.BIGPARA, color(R.color.colorBigPara), false, only_usd_try));
        currencySources.add(new CurrencySource("TlKur", CurrencyType.TLKUR, color(R.color.colorDolarTlKur), false, altinInSupported));
        currencySources.add(new CurrencySource("Yapı Kredi", CurrencyType.YAPIKREDI, color(R.color.colorYapiKredi), false, yapikrediSupported));
        currencySources.add(new CurrencySource("Yahoo", CurrencyType.YAHOO, color(R.color.colorYahoo), false, altinInSupported));
        currencySources.add(new CurrencySource("Paragaranti", CurrencyType.PARAGARANTI, color(R.color.colorParagaranti), false, paragarantiSupported)); // update supported ones
        currencySources.add(new CurrencySource("Bloomberg HT", CurrencyType.BLOOMBERGHT, color(R.color.colorBloomberg), true, bloombergSupported));
        int index = 0;
        for (CurrencySource source : currencySources) {
            source.setChartIndex(index);
            index += isAvgType(source.getType()) ? 1 : 2;
        }
        updateSourceStatesFromPrefs();
        isInitialized.set(true);
    }

    private static void updateSourceStatesFromPrefs() {
        String prefSources = Prefs.getSources();
        if (!TextUtils.isEmpty(prefSources)) {
            String[] splits = prefSources.split(";");
            for (CurrencySource currencySource : currencySources) {
                currencySource.setEnabled(false);
            }
            for (String str : splits) {
                if (TextUtils.isEmpty(str)) continue;
                int sourceTypeTemp;
                try {
                    sourceTypeTemp = Integer.parseInt(str);
                    for (CurrencySource currencySource : currencySources) {
                        if (currencySource.getType() == sourceTypeTemp) {
                            currencySource.setEnabled(true);
                        }
                    }
                } catch (Exception ignored) {
                }
            }
        }
    }

    public static boolean isAvgType(int type) {
        return type == CurrencyType.ALTININ || type == CurrencyType.PARAGARANTI ||
                type == CurrencyType.TLKUR || type == CurrencyType.YAHOO | type == CurrencyType.BLOOMBERGHT;
    }
} public class CurrencySource {
    private final String name;
    private boolean enabled;
    private final int type;
    private final int color;
    private final int[] supportedRates;
    private int chartIndex = 0;

    public CurrencySource(String name, int type, @ColorInt int colorInt, boolean enabled, int[] supportedRates) {
        this.name = name;
        this.type = type;
        this.enabled = enabled;
        this.color = colorInt;
        this.supportedRates = supportedRates;
    }

    public int getChartIndex() {
        return chartIndex;
    }

    public void setChartIndex(int chartIndex) {
        this.chartIndex = chartIndex;
    }

    public int[] getSupportedRates() {
        return supportedRates;
    }

    public int getColor() {
        return color;
    }

    public int getType() {
        return type;
    }

    public String getName() {
        return name;
    }

    public boolean isEnabled() {
        return enabled;
    }

    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }

    @Override
    public String toString() {
        return getName();
    }

    public boolean isRateSupported(int rateType) {
        if (supportedRates == null) return true;
        if (supportedRates.length <= 0) return true;
        for (int i : supportedRates) {
            if (rateType == i) {
                return true;
            }
        }
        return false;
    }
} public interface ValueType {
    int NONE = -1;
    int BUY = 1;
    int SELL = 2;
    int AVG = 3;
} public class SimpleSpinnerAdapter<T> extends ArrayAdapter<T> {

    public SimpleSpinnerAdapter(Context context, List<T> objects) {
        super(context, R.layout.item_spinner_dropdown, 0, objects);
    }

    @Override
    public View getView(int position, View convertView, @NonNull ViewGroup parent) {
        convertView = super.getView(position, convertView, parent);
        View vTxt = convertView.findViewById(R.id.tv_item_spinner);
        if (vTxt != null) {
            TextView tv = ((TextView) vTxt);
            T item = getItem(position);
            tv.setText(item.toString());
//            if (position == 0) {
//                tv.setTextColor(ContextCompat.getColor(getContext(), R.color.colorPrimary));
//            } else {
//                tv.setTextColor(ContextCompat.getColor(getContext(), R.color.colorPrimaryDark));
//                tv.setTextSize(16);
//            }
        }
        return convertView;
    }

    @Override
    public View getDropDownView(int position, View convertView, @NonNull ViewGroup parent) {
        if (convertView == null) {
            convertView = LayoutInflater.from(getContext()).inflate(R.layout.item_spinner_dropdown, parent, false);
        }
        View vTxt = convertView.findViewById(R.id.tv_item_spinner);
        if (vTxt != null) {
            TextView tv = ((TextView) vTxt);
            T item = getItem(position);
            tv.setText(item.toString());
//            if (position == 0) {
//                tv.setTextColor(ContextCompat.getColor(getContext(), R.color.colorPrimary));
//            } else {
//                tv.setTextColor(ContextCompat.getColor(getContext(), R.color.colorPrimaryDark));
//            }
        }
        return convertView;
    }
} public class CollectionUtils {
    public static <E> E getInstance(List<E> list, Class<?> clazz) {
        for (E e : list) {
            if (clazz.isInstance(e)) {
                return e;
            }
        }
        return null;
    }

    public static <E> int size(List<E> list) {
        return list == null ? 0 : list.size();
    }

    public static <E> int size(E[] array) {
        return array == null ? 0 : array.length;
    }

    public static <E> boolean isNullOrEmpty(List<E> list) {
        return list == null || list.size() < 1;
    }

    public static <E> boolean isNullOrEmpty(E[] array) {
        return array == null || array.length < 1;
    }

} public class DecimalDigitsInputFilter implements InputFilter {

    private final int decimalDigits;

    /**
     * Constructor.
     *
     * @param decimalDigits maximum decimal digits
     */
    public DecimalDigitsInputFilter(int decimalDigits) {
        this.decimalDigits = decimalDigits;
    }

    @Override
    public CharSequence filter(CharSequence source,
                               int start,
                               int end,
                               Spanned dest,
                               int dstart,
                               int dend) {


        int dotPos = -1;
        int len = dest.length();
        for (int i = 0; i < len; i++) {
            char c = dest.charAt(i);
            if (c == '.' || c == ',') {
                dotPos = i;
                break;
            }
        }
        if (dotPos >= 0) {

            // protects against many dots
            if (source.equals(".") || source.equals(",")) {
                return "";
            }
            // if the text is entered before the dot
            if (dend <= dotPos) {
                return null;
            }
            if (len - dotPos > decimalDigits) {
                return "";
            }
        }

        return null;
    }

} public class InputFilterMinMax implements InputFilter {

    private final double min, max;

    public InputFilterMinMax(double min, double max) {
        this.min = Math.min(min, max);
        this.max = Math.max(min, max);
    }

    @Override
    public CharSequence filter(CharSequence source, int i, int i2, Spanned spanned, int i3, int i4) {
        try {
            String str = spanned.toString() + source.toString();
            Float input = RateUtils.toFloat(str);
            if (input != null && isInRange(min, max, input)) {
                return null;
            }
        } catch (NumberFormatException nfe) {
            nfe.printStackTrace();
        }
        return "";
    }

    private boolean isInRange(double min, double max, double value) {
        return value >= min && value <= max;
    }
} public class L {

    private static final String TAG = "ExchangeRates";

    private static final String LOG_PREFIX = "";
    private static final int LOG_PREFIX_LENGTH = LOG_PREFIX.length();
    private static final int MAX_LOG_TAG_LENGTH = 23;
    private static final int MAX_CHAR_LENGTH = 320;

    public static String makeLogTag(String str) {
        if (str.length() > MAX_LOG_TAG_LENGTH - LOG_PREFIX_LENGTH) {
            return LOG_PREFIX + str.substring(0, MAX_LOG_TAG_LENGTH - LOG_PREFIX_LENGTH - 1);
        }

        return LOG_PREFIX + str;
    }

    public static void e(String tag, String msg) {
        Log.e(makeLogTag(tag), msg);
    }

    public static void e(String tag, Throwable th) {
        Log.e(tag, "", th);
    }

    public static void i(String tag, String msg) {
        Log.i(makeLogTag(tag), msg);
    }

    public static void ex(Exception ex) {
        Log.e(TAG, "", ex);
    }

    public static void ex(Exception ex, String message) {
        Log.e(TAG, message, ex);
    }
} public class RateUtils {
    private static final Formatter formatter2 = new Formatter(2, 0);
    private static final Formatter formatter5 = new Formatter(5, 2);

    public static <T extends BaseRate> T getRate(List<T> rates, @IRate.RateDef int rateType) {
        if (rates == null) return null;
        for (T rate : rates) {
            if (rate.getRateType() == rateType) {
                return rate;
            }
        }
        return null;
    }

    public static String valueToUI(Float val, @IRate.RateDef int rateType) {
        if (val == null) return "";
        String formatted = formatValue(val, rateType);
        if (rateType == IRate.EUR_USD) {
            return formatted;
        } else if (rateType == IRate.ONS) {
            return App.context().getString(R.string.placeholder_dollar, formatted);
        } else {
            return App.context().getString(R.string.placeholder_tl, formatted);
        }
    }

    public static String formatValue(float val, int rateType) {
        if (rateType == IRate.ONS || rateType == IRate.ONS_TRY) {
            return formatter2.format(val);
        } else {
            return formatter5.format(val);
        }
    }

    public static String rateName(int rate_type) {
        if (rate_type == IRate.EUR) {
            return "EUR"; // todo refactor with side menu names.
        } else if (rate_type == IRate.USD) {
            return "USD";
        } else if (rate_type == IRate.EUR_USD) {
            return "EUR_USD";
        } else if (rate_type == IRate.ONS) {
            return "ONS";
        }
        return "";
    }

    public static
    @DrawableRes
    int getRateIcon(int rateType) {
        if (rateType == IRate.EUR) {
            return R.drawable.ic_euro;
        } else if (rateType == IRate.ONS) {
            return R.drawable.ic_gold;
        } else if (rateType == IRate.USD) {
            return R.drawable.ic_dollar;
        } else if (rateType == IRate.EUR_USD) {
            return R.drawable.ic_exchange_eur_usd;
        }
        return -1;
    }

    @Nullable
    public static Float toFloat(String str) {
        if (TextUtils.isEmpty(str)) return null;
        str = str.trim();

        try {
            return Float.parseFloat(str);
        } catch (Exception e) {
            try {
                DecimalFormatSymbols symbols = new DecimalFormatSymbols();
                symbols.setDecimalSeparator(DecimalFormatSymbols.getInstance().getDecimalSeparator());
                symbols.setGroupingSeparator(DecimalFormatSymbols.getInstance().getGroupingSeparator());
                DecimalFormat format = new DecimalFormat("###,###,###,##0.#####");
                format.setDecimalFormatSymbols(symbols);
                return format.parse(str).floatValue();
            } catch (Exception ignored) {

            }
        }
        return null;
    }
}
import com.google.android.material.textfield.TextInputLayout;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.text.Editable;
import android.text.InputFilter;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RadioGroup;
import android.widget.Spinner;
import android.widget.Toast;
import java.util.ArrayList;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat;
import androidx.core.graphics.drawable.DrawableCompat;
import dynoapps.exchange_rates.App;
import dynoapps.exchange_rates.R;
import dynoapps.exchange_rates.SourcesManager;
import dynoapps.exchange_rates.data.CurrencySource;
import dynoapps.exchange_rates.interfaces.ValueType;
import dynoapps.exchange_rates.ui.SimpleSpinnerAdapter;
import dynoapps.exchange_rates.util.CollectionUtils;
import dynoapps.exchange_rates.util.DecimalDigitsInputFilter;
import dynoapps.exchange_rates.util.InputFilterMinMax;
import dynoapps.exchange_rates.util.L;
import dynoapps.exchange_rates.util.RateUtils;
package dynoapps.exchange_rates.alarm;





/**
 * Created by erdemmac on 13/12/2016.
 */

public class AlarmManager {

    public static void addAlarmDialog(final Context context) {
        addAlarmDialog(context, -1, -1, ValueType.NONE, null, null);
    }

    public static void addAlarmDialog(final Context context, final AlarmsDataSource.AlarmUpdateInsertCallback alarmUpdateInsertCallback) {
        addAlarmDialog(context, -1, -1, ValueType.NONE, null, alarmUpdateInsertCallback);
    }

    public static void addAlarmDialog(@NonNull final Context context,
                                      int sourceType, final int rateType, final int valueType, Float defaultValue,
                                      final AlarmsDataSource.AlarmUpdateInsertCallback alarmUpdateInsertCallback) {
        @SuppressLint("InflateParams") final View v = LayoutInflater.from(context).inflate(R.layout.layout_alarm_selection, null);
        final EditText etAlarm = v.findViewById(R.id.et_alarm_value);
        final TextInputLayout tilValue = v.findViewById(R.id.til_alarm_value);
        if (defaultValue != null) {
            String val = RateUtils.formatValue(defaultValue, rateType);
            etAlarm.setText(val);
            etAlarm.setSelection(etAlarm.getText().length());
        }
        etAlarm.setFilters(new InputFilter[]{new DecimalDigitsInputFilter(4), new InputFilterMinMax(1, 10000)});