https://www.alphavantage.co/query?function=FX_DAILY&from_symbol=USD&to_symbol=EUR&apikey=YOUR_API_KEY запрос проходит но package com.example.myapplication;

import android.annotation.SuppressLint;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log; // Добавлено для логирования
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.Spinner;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;

import com.example.myapplication.api.LoadCurrencyRatesTask;
import com.github.mikephil.charting.charts.LineChart;
import com.github.mikephil.charting.components.XAxis;
import com.github.mikephil.charting.data.Entry; // Импорт Entry для точек данных на графике
import com.github.mikephil.charting.data.LineData;
import com.github.mikephil.charting.data.LineDataSet; // Импорт LineDataSet для набора данных
import com.github.mikephil.charting.formatter.ValueFormatter; // Импорт ValueFormatter для пользовательских подписей оси
import com.github.mikephil.charting.utils.ColorTemplate; // Импорт ColorTemplate для цветов
import com.google.android.material.button.MaterialButton;
import com.google.android.material.button.MaterialButtonToggleGroup;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;

public class MainActivity extends AppCompatActivity {
    private Spinner spinnerFrom;
    private Spinner spinnerTo;
    private EditText inputFrom;
    private EditText inputTo;
    private ProgressBar loadingIndicator;
    private CurrencyRepository repository;
    private MaterialButtonToggleGroup timeRangeToggleGroup;
    private MaterialButton buttonSwap;

    private boolean isRatesLoaded = false;

    private LineChart lineChart; // Инициализация графика

    @SuppressLint("MissingInflatedId")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);




        repository = new CurrencyRepository(this);
        spinnerFrom = findViewById(R.id.spinner_from);
        spinnerTo = findViewById(R.id.spinner_to);
        inputFrom = findViewById(R.id.input_from);
        inputTo = findViewById(R.id.input_to);
        loadingIndicator = findViewById(R.id.loading_indicator);
        lineChart = findViewById(R.id.line_chart); // Инициализация графика
        timeRangeToggleGroup = findViewById(R.id.time_range_toggle_group);
        buttonSwap = findViewById(R.id.button_swap);

        MaterialButton buttonCurrencyList = findViewById(R.id.button_currency_list);
        buttonCurrencyList.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, CurrencyListActivity.class);
                startActivity(intent);
            }
        });

        buttonSwap.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int fromPosition = spinnerFrom.getSelectedItemPosition();
                int toPosition = spinnerTo.getSelectedItemPosition();

                spinnerFrom.setSelection(toPosition);
                spinnerTo.setSelection(fromPosition);

                // Обмен значениями в полях ввода (опционально)
                String inputFromValue = inputFrom.getText().toString();
                String inputToValue = inputTo.getText().toString();
                inputFrom.setText(inputToValue);
                inputTo.setText(inputFromValue);

                if (isRatesLoaded) {
                    performConversion();
                    loadChartData();
                } else {
                    showToast("Подождите, пока загрузятся курсы валют.");
                }
            }
        });

        // Показываем индикатор загрузки и загружаем курсы
        loadingIndicator.setVisibility(View.VISIBLE);
        repository.loadAllCurrencyRatesFromApi(new LoadCurrencyRatesTask.Callback() {
            @Override
            public void onRatesLoaded() {
                runOnUiThread(() -> {
                    new LoadCurrenciesTask().execute();
                });
            }

            @Override
            public void onError(Exception e) {
                runOnUiThread(() -> {
                    loadingIndicator.setVisibility(View.GONE);
                    showToast("Ошибка загрузки курсов валют: " + e.getMessage());
                });
            }
        });

        timeRangeToggleGroup.addOnButtonCheckedListener(new MaterialButtonToggleGroup.OnButtonCheckedListener() {
            @Override
            public void onButtonChecked(MaterialButtonToggleGroup group, int checkedId, boolean isChecked) {
                if (isChecked) {
                    Log.d("MainActivity", "Button checked: " + checkedId);
                    if (isRatesLoaded) {
                        performConversion();
                        loadChartData(); // Загружаем данные для графика при изменении выбора периода
                    } else {
                        showToast("Подождите, пока загрузятся курсы валют.");
                    }
                } else {
                    Log.d("MainActivity", "Button unchecked: " + checkedId);
                }
            }
        });

        inputFrom.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
            }

            @Override
            public void afterTextChanged(Editable s) {
                if (isRatesLoaded) {
                    performConversion();
                } else {
                    showToast("Подождите, пока загрузятся курсы валют.");
                }
            }
        });

        AdapterView.OnItemSelectedListener spinnerListener = new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                if (isRatesLoaded) {
                    performConversion();
                    loadChartData(); // Загружаем данные для графика при изменении выбора
                }
            }

            @Override
            public void onNothingSelected(AdapterView<?> parent) {}
        };

        spinnerFrom.setOnItemSelectedListener(spinnerListener);
        spinnerTo.setOnItemSelectedListener(spinnerListener);
    }

    private void loadChartData() {
        String fromCurrency = spinnerFrom.getSelectedItem().toString().toUpperCase();
        String toCurrency = spinnerTo.getSelectedItem().toString().toUpperCase();

        int timeRangeDays = 7;  // Значение по умолчанию
        int checkedButtonId = timeRangeToggleGroup.getCheckedButtonId();

        if (checkedButtonId == R.id.button_7_days) {
            timeRangeDays = 7;
        } else if (checkedButtonId == R.id.button_30_days) {
            timeRangeDays = 30;
        } else if (checkedButtonId == R.id.button_90_days) {
            timeRangeDays = 90;
        } else {
            timeRangeDays = 7; // По умолчанию 7 дней, если ничего не выбрано
        }

        new LoadChartDataTask(fromCurrency, toCurrency, timeRangeDays).execute();
    }

    private class LoadChartDataTask extends AsyncTask<Void, Void, LineData> {
        private final String fromCurrency;
        private final String toCurrency;
        private final int timeRangeDays;
        private List<Date> dates;

        public LoadChartDataTask(String fromCurrency, String toCurrency, int timeRangeDays) {
            this.fromCurrency = fromCurrency;
            this.toCurrency = toCurrency;
            this.timeRangeDays = timeRangeDays;
        }

        @Override
        protected LineData doInBackground(Void... voids) {
            Date endDate = new Date();  // Текущая дата
            Calendar cal = Calendar.getInstance();
            cal.setTime(endDate);
            cal.add(Calendar.DAY_OF_YEAR, -timeRangeDays);
            Date startDate = cal.getTime();

            List<CurrencyRate> historyFrom = repository.getCurrencyRateHistoryWithinDates("USD", fromCurrency, startDate, endDate);
            List<CurrencyRate> historyTo = repository.getCurrencyRateHistoryWithinDates("USD", toCurrency, startDate, endDate);

            if (fromCurrency.equals("USD")) {
                historyFrom = generateUsdHistory(startDate, endDate);
            }

            if (toCurrency.equals("USD")) {
                historyTo = generateUsdHistory(startDate, endDate);
            }

            if (historyFrom == null || historyTo == null ||
                    historyFrom.isEmpty() || historyTo.isEmpty()) {
                return null;
            }

            int size = Math.min(historyFrom.size(), historyTo.size());
            List<Entry> entries = new ArrayList<>();
            dates = new ArrayList<>();

            for (int i = 0; i < size; i++) {
                CurrencyRate rateFrom = historyFrom.get(i);
                CurrencyRate rateTo = historyTo.get(i);

                double rateFromUSD = rateFrom.rate;
                double rateToUSD = rateTo.rate;

                double crossRate = rateToUSD / rateFromUSD;

                entries.add(new Entry(i, (float) crossRate));
                dates.add(rateFrom.date);
            }

            LineDataSet dataSet = new LineDataSet(entries, "Курс " + fromCurrency + "/" + toCurrency);
            dataSet.setColor(ColorTemplate.getHoloBlue());
            dataSet.setLineWidth(2f);
            dataSet.setDrawCircles(false);
            dataSet.setMode(LineDataSet.Mode.CUBIC_BEZIER);
            dataSet.setDrawFilled(true);
            dataSet.setFillAlpha(50);
            dataSet.setFillColor(ColorTemplate.getHoloBlue());
            dataSet.setDrawValues(false);

            return new LineData(dataSet);
        }

        private List<CurrencyRate> generateUsdHistory(Date startDate, Date endDate) {
            List<Date> allDates = repository.getAllDatesWithinRange(startDate, endDate);
            List<CurrencyRate> usdHistory = new ArrayList<>();
            for (Date date : allDates) {
                usdHistory.add(new CurrencyRate("USD", "USD", 1.0, date));
            }
            return usdHistory;
        }

        @Override
        protected void onPostExecute(LineData lineData) {
            if (lineData != null && lineData.getEntryCount() > 0) {
                // Настраиваем ось X для отображения дат
                XAxis xAxis = lineChart.getXAxis();
                xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);
                xAxis.setLabelRotationAngle(-45f);
                xAxis.setGranularity(1f);
                xAxis.setDrawGridLines(false);
                xAxis.setValueFormatter(new ValueFormatter() {
                    private final SimpleDateFormat mFormat = new SimpleDateFormat("dd MMM", Locale.getDefault());

                    @Override
                    public String getFormattedValue(float value) {
                        int index = (int) value;
                        if (index >= 0 && index < dates.size()) {
                            Date date = dates.get(index);
                            return mFormat.format(date);
                        } else {
                            return "";
                        }
                    }
                });

                // Настраиваем ось Y
                lineChart.getAxisRight().setEnabled(false);

                // Обновляем данные графика
                lineChart.setData(lineData);

                // Разрешаем взаимодействие
                lineChart.setTouchEnabled(true);
                lineChart.setDragEnabled(true);
                lineChart.setScaleEnabled(true);

                // Устанавливаем кастомный MarkerView
                CustomMarkerView markerView = new CustomMarkerView(MainActivity.this, R.layout.custom_marker_view, dates);
                markerView.setChartView(lineChart);
                lineChart.setMarker(markerView);

                lineChart.invalidate();
            } else {
                lineChart.clear();
                lineChart.invalidate();
                showToast("Нет данных для отображения графика за выбранный период");
            }
        }
    }

    private class LoadCurrenciesTask extends AsyncTask<Void, Void, List<String>> {
        @Override
        protected void onPreExecute() {
            loadingIndicator.setVisibility(View.VISIBLE);
        }

        @Override
        protected List<String> doInBackground(Void... voids) {
            // Загрузка списка валют из репозитория
            return repository.getAllUniqueCurrencies();
        }

        @Override
        protected void onPostExecute(List<String> currencies) {
            loadingIndicator.setVisibility(View.GONE);
            if (currencies != null && !currencies.isEmpty()) {
                isRatesLoaded = true;
                ArrayAdapter<String> adapterFrom = new ArrayAdapter<>(MainActivity.this, android.R.layout.simple_spinner_item, currencies);
                adapterFrom.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
                spinnerFrom.setAdapter(adapterFrom);

                ArrayAdapter<String> adapterTo = new ArrayAdapter<>(MainActivity.this, android.R.layout.simple_spinner_item, currencies);
                adapterTo.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
                spinnerTo.setAdapter(adapterTo);

                // Устанавливаем стандартные значения
                spinnerFrom.setSelection(0);
                spinnerTo.setSelection(1);

                // Выполняем конвертацию и загружаем данные для графика
                performConversion();
                loadChartData();

                // Устанавливаем кнопку по умолчанию в ToggleGroup
                timeRangeToggleGroup.check(R.id.button_7_days);
            } else {
                showToast("Не удалось загрузить валюты");
            }
        }
    }

    private void performConversion() {
        String fromCurrency = spinnerFrom.getSelectedItem().toString().toUpperCase();
        String toCurrency = spinnerTo.getSelectedItem().toString().toUpperCase();
        String amountStr = inputFrom.getText().toString();

        if (amountStr.isEmpty()) {
            inputTo.setText("");
            return;
        }

        if (fromCurrency.equals(toCurrency)) {
            inputTo.setText(amountStr);
            return;
        }

        try {
            double amount = Double.parseDouble(amountStr);

            // Выполняем конвертацию
            new ConvertCurrencyTask(fromCurrency, toCurrency, amount, repository, result -> {
                if (result != null) {
                    inputTo.setText(String.format("%.2f", result));
                } else {
                    showToast("Ошибка при конвертации валюты: Курс не найден");
                    inputTo.setText("");
                }
            }).execute();
        } catch (NumberFormatException e) {
            inputTo.setText("");
        }
    }

    private void showToast(String message) {
        Toast.makeText(MainActivity.this, message, Toast.LENGTH_SHORT).show();
    }
}


// LoadCurrencyRatesTask.java
package com.example.myapplication.api;

import android.os.AsyncTask;
import android.util.Log;

import com.example.myapplication.CurrencyRate;
import com.example.myapplication.CurrencyRepository;

import org.json.JSONObject;
import org.json.JSONException;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.*;

public class LoadCurrencyRatesTask extends AsyncTask<Void, Void, Void> {
    private static final String TAG = "LoadCurrencyRatesTask";
    private static final String API_KEY = "HA7AJY6BZDLRDWY8"; // Replace with your Alpha Vantage API key
    private final CurrencyRepository repository;
    private final Callback callback;

    public interface Callback {
        void onRatesLoaded();

        void onError(Exception e);
    }

    public LoadCurrencyRatesTask(CurrencyRepository repository, Callback callback) {
        this.repository = repository;
        this.callback = callback;
    }

    @Override
    protected Void doInBackground(Void... voids) {
        try {
            // Clear existing rates in the database
            repository.clearAllRates();

            // Fetch list of currencies (you can customize this list)
            List<String> currencies = Arrays.asList("EUR", "GBP", "JPY", "AUD");

            // Fetch historical data for each currency pair relative to USD
            for (String targetCurrency : currencies) {
                fetchHistoricalRates("USD", targetCurrency);
            }

            if (callback != null) {
                callback.onRatesLoaded();
            }
        } catch (Exception e) {
            Log.e(TAG, "Error loading currency rates", e);
            if (callback != null) {
                callback.onError(e);
            }
        }
        return null;
    }

    private void fetchHistoricalRates(String baseCurrency, String targetCurrency) {
        try {
            String apiUrl = String.format(
                    Locale.US,
                    "https://www.alphavantage.co/query?function=FX_DAILY&from_symbol=%s&to_symbol=%s&apikey=%s",
                    baseCurrency,
                    targetCurrency,
                    API_KEY
            );

            URL url = new URL(apiUrl);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();

            Log.d(TAG, "rates fetched for " + baseCurrency + "/" + targetCurrency);
            connection.setRequestMethod("GET");


            // Read the response
            BufferedReader reader = new BufferedReader(
                    new InputStreamReader(connection.getInputStream())
            );
            StringBuilder response = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                response.append(line);
            }
            reader.close();

            // Parse the JSON response
            JSONObject jsonResponse = new JSONObject(response.toString());

            if (jsonResponse.has("Error Message")) {
                Log.e(TAG, "Error fetching data for " + targetCurrency + ": " + jsonResponse.getString("Error Message"));
                return;
            }

            JSONObject timeSeries = jsonResponse.getJSONObject("Time Series FX (Daily)");

            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.US);

            // Iterate over the dates
            Iterator<String> dates = timeSeries.keys();
            while (dates.hasNext()) {
                String dateStr = dates.next();
                JSONObject dailyData = timeSeries.getJSONObject(dateStr);

                double rate = dailyData.getDouble("4. close");

                Date date = dateFormat.parse(dateStr);

                // Create CurrencyRate object and insert into the database
                CurrencyRate currencyRate = new CurrencyRate(baseCurrency, targetCurrency, rate, date);
                repository.insertCurrencyRateSync(currencyRate);

                // Also store the reverse rate (targetCurrency to baseCurrency)
                double reverseRate = rate != 0 ? 1 / rate : 0;
                CurrencyRate reverseCurrencyRate = new CurrencyRate(targetCurrency, baseCurrency, reverseRate, date);
                repository.insertCurrencyRateSync(reverseCurrencyRate);
            }

            Log.d(TAG, "Historical rates fetched for " + baseCurrency + "/" + targetCurrency);

        } catch (JSONException e) {
            Log.e(TAG, "JSON parsing error for " + targetCurrency, e);
        } catch (Exception e) {
            Log.e(TAG, "Error fetching historical rates for " + targetCurrency, e);
        }
    }
}package com.example.myapplication;

import android.content.Context;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import androidx.room.TypeConverters;

@Database(entities = {CurrencyRate.class}, version = 3, exportSchema = false)
@TypeConverters({Converters.class})
public abstract class AppDatabase extends RoomDatabase {
    private static volatile AppDatabase INSTANCE;

    public abstract CurrencyRateDao currencyRateDao();

    public static AppDatabase getDatabase(final Context context) {
        if (INSTANCE == null) {
            synchronized (AppDatabase.class) {
                if (INSTANCE == null) {
                    INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
                                    AppDatabase.class, "currency_database")
                            .fallbackToDestructiveMigration()
                            .build();
                }
            }
        }
        return INSTANCE;
    }
}package com.example.myapplication;

import android.content.Context;

import com.example.myapplication.api.LoadCurrencyRatesTask;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class CurrencyRepository {
    private final CurrencyRateDao currencyRateDao;

    public CurrencyRepository(Context context) {
        AppDatabase db = AppDatabase.getDatabase(context);
        currencyRateDao = db.currencyRateDao();
    }

    public void insertCurrencyRateSync(CurrencyRate rate) {
        currencyRateDao.insertCurrencyRate(rate);
    }

    public void clearAllRates() {
        currencyRateDao.clearAllRates();
    }

    public List<Date> getAllDates() {
        return currencyRateDao.getAllDates();
    }

    // Добавляем метод для получения дат в заданном диапазоне
    public List<Date> getAllDatesWithinRange(Date startDate, Date endDate) {
        return currencyRateDao.getAllDatesWithinRange(startDate, endDate);
    }

    // Метод для получения исторических данных курса за заданный период
    public List<CurrencyRate> getCurrencyRateHistoryWithinDates(String baseCurrency, String targetCurrency, Date startDate, Date endDate) {
        return currencyRateDao.getCurrencyRateHistoryWithinDates(baseCurrency, targetCurrency, startDate, endDate);
    }

    // Метод для получения списка уникальных валют
    public List<String> getAllUniqueCurrencies() {
        List<String> baseCurrencies = currencyRateDao.getAllBaseCurrencies();
        List<String> targetCurrencies = currencyRateDao.getAllTargetCurrencies();
        Set<String> allCurrencies = new HashSet<>(baseCurrencies);
        allCurrencies.addAll(targetCurrencies);
        return new ArrayList<>(allCurrencies);
    }

    public Double getExchangeRate(String fromCurrency, String toCurrency) {
        return currencyRateDao.getLatestExchangeRate(fromCurrency, toCurrency);
    }

    // Загрузка курсов из API
    public void loadAllCurrencyRatesFromApi(LoadCurrencyRatesTask.Callback callback) {
        new LoadCurrencyRatesTask(this, callback).execute();
    }
} не удалось загрузить курсы валдт при запуске после того как добавил активность package com.example.myapplication;

import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import com.example.myapplication.CurrencyAdapter;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class CurrencyListActivity extends AppCompatActivity {

    private RecyclerView recyclerView;
    private CurrencyAdapter currencyAdapter;
    private CurrencyRepository repository;
    private Set<String> favoriteCurrencies;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_currency_list);

        repository = new CurrencyRepository(this);
        favoriteCurrencies = getFavoriteCurrencies();

        recyclerView = findViewById(R.id.currency_recycler_view);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));

        new LoadCurrenciesTask().execute();
    }

    private void loadCurrencies(List<String> currencies) {
        currencyAdapter = new CurrencyAdapter(currencies, favoriteCurrencies, new CurrencyAdapter.OnFavoriteClickListener() {
            @Override
            public void onFavoriteClick(String currency, boolean isFavorite) {
                if (isFavorite) {
                    favoriteCurrencies.add(currency);
                } else {
                    favoriteCurrencies.remove(currency);
                }
                saveFavoriteCurrencies(favoriteCurrencies);
            }
        });
        recyclerView.setAdapter(currencyAdapter);
    }

    private Set<String> getFavoriteCurrencies() {
        SharedPreferences prefs = getSharedPreferences("favorites", MODE_PRIVATE);
        return new HashSet<>(prefs.getStringSet("favorite_currencies", new HashSet<>()));
    }

    private void saveFavoriteCurrencies(Set<String> favorites) {
        SharedPreferences prefs = getSharedPreferences("favorites", MODE_PRIVATE);
        SharedPreferences.Editor editor = prefs.edit();
        editor.putStringSet("favorite_currencies", favorites);
        editor.apply();
    }

    private class LoadCurrenciesTask extends AsyncTask<Void, Void, List<String>> {
        @Override
        protected List<String> doInBackground(Void... voids) {
            return repository.getAllUniqueCurrencies();
        }

        @Override
        protected void onPostExecute(List<String> currencies) {
            loadCurrencies(currencies);
        }
    }
}