// 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();
            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);
        }
    }
}// 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();
            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.os.AsyncTask;
import android.util.Log;

public class ConvertCurrencyTask extends AsyncTask<Void, Void, Double> {
    private final String fromCurrency;
    private final String toCurrency;
    private final double amount;
    private final CurrencyRepository repository;
    private final Callback callback;

    public interface Callback {
        void onConversionResult(Double result);
    }

    public ConvertCurrencyTask(String fromCurrency, String toCurrency, double amount, CurrencyRepository repository, Callback callback) {
        this.fromCurrency = fromCurrency;
        this.toCurrency = toCurrency;
        this.amount = amount;
        this.repository = repository;
        this.callback = callback;
    }

    @Override
    protected Double doInBackground(Void... voids) {
        // Получаем курсы валют относительно USD
        Double rateFromUSD;
        if (fromCurrency.equals("USD")) {
            rateFromUSD = 1.0;
        } else {
            rateFromUSD = repository.getExchangeRate("USD", fromCurrency);
            if (rateFromUSD != null) {
                rateFromUSD = 1 / rateFromUSD; // Преобразуем в курс fromCurrency -> USD
            }
        }

        Double rateToUSD;
        if (toCurrency.equals("USD")) {
            rateToUSD = 1.0;
        } else {
            rateToUSD = repository.getExchangeRate("USD", toCurrency);
        }

        if (rateFromUSD == null || rateToUSD == null) {
            Log.e("ConvertCurrencyTask", "Conversion rate not found for " + fromCurrency + " or " + toCurrency);
            return null;
        }

        // Вычисляем кросс-курс
        double crossRate = rateToUSD / rateFromUSD;


        // Выполняем конвертацию
        return amount * crossRate;
    }



    @Override
    protected void onPostExecute(Double result) {
        callback.onConversionResult(result);
    }


}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;
    }
}// Converters.java
package com.example.myapplication;

import androidx.room.TypeConverter;
import java.util.Date;

import androidx.room.TypeConverter;
import java.util.Date;

public class Converters {
    @TypeConverter
    public static Date fromTimestamp(Long value) {
        return value == null ? null : new Date(value);
    }

    @TypeConverter
    public static Long dateToTimestamp(Date date) {
        return date == null ? null : date.getTime();
    }
}package com.example.myapplication;

import androidx.room.Entity;
import androidx.room.Index;
import androidx.room.PrimaryKey;
import androidx.room.TypeConverters;
import java.util.Date;

@Entity(tableName = "currency_rates",
        indices = {@Index(value = {"baseCurrency", "targetCurrency", "date"}, unique = true)})
@TypeConverters({Converters.class})
public class CurrencyRate {
    @PrimaryKey(autoGenerate = true)
    public int id;

    public String baseCurrency;
    public String targetCurrency;
    public double rate;
    public Date date;

    public CurrencyRate(String baseCurrency, String targetCurrency, double rate, Date date) {
        this.baseCurrency = baseCurrency;
        this.targetCurrency = targetCurrency;
        this.rate = rate;
        this.date = date;
    }
}// CurrencyRateDao.java
package com.example.myapplication;

import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import androidx.room.Query;
import java.util.Date;
import java.util.List;

@Dao
public interface CurrencyRateDao {

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    void insertCurrencyRate(CurrencyRate rate);

    @Query("SELECT DISTINCT baseCurrency FROM currency_rates")
    List<String> getAllBaseCurrencies();

    @Query("SELECT DISTINCT targetCurrency FROM currency_rates")
    List<String> getAllTargetCurrencies();

    @Query("SELECT rate FROM currency_rates WHERE baseCurrency = :fromCurrency AND targetCurrency = :toCurrency ORDER BY date DESC LIMIT 1")
    Double getLatestExchangeRate(String fromCurrency, String toCurrency);

    @Query("SELECT * FROM currency_rates WHERE baseCurrency = :baseCurrency AND targetCurrency = :targetCurrency ORDER BY date ASC")
    List<CurrencyRate> getCurrencyRateHistory(String baseCurrency, String targetCurrency);

    @Query("DELETE FROM currency_rates")
    void clearAllRates();

    @Query("SELECT DISTINCT date FROM currency_rates ORDER BY date ASC")
    List<Date> getAllDates();
}// CurrencyRepository.java
package com.example.myapplication;

import android.content.Context;
import android.util.Log;

import com.example.myapplication.api.LoadCurrencyRatesTask;

import java.util.Date;
import java.util.List;

import java.util.ArrayList;
import java.util.HashSet;
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 List<Date> getAllDates() {
        return currencyRateDao.getAllDates();
    }

    // Метод для получения исторических данных для валюты относительно USD
    public List<CurrencyRate> getCurrencyRateHistory(String currency) {
        if (currency.equals("USD")) {
            // Если валюта - USD, возвращаем null, мы обработаем это в MainActivity
            return null;
        } else {
            return currencyRateDao.getCurrencyRateHistory("USD", currency);
        }
    }


    // Метод для получения списка уникальных валют
    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);
    }

    public List<CurrencyRate> getCurrencyRateHistory(String baseCurrency, String targetCurrency) {
        return currencyRateDao.getCurrencyRateHistory(baseCurrency, targetCurrency);
    }

    // Загрузка курсов из API
    public void loadAllCurrencyRatesFromApi(LoadCurrencyRatesTask.Callback callback) {
        new LoadCurrencyRatesTask(this, callback).execute();
    }

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

} <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/constraint_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    android:background="@color/background_color"
    tools:context=".MainActivity">

    <!-- Загрузочный индикатор (ProgressBar) -->
    <ProgressBar
        android:id="@+id/loading_indicator"
        style="?android:attr/progressBarStyleLarge"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:indeterminateTint="@color/colorPrimary"
        android:visibility="gone"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <!-- Спиннеры выбора валют -->
    <Spinner
        android:id="@+id/spinner_from"
        style="@style/SpinnerStyle"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginEnd="8dp"
        app:layout_constraintEnd_toStartOf="@+id/spinner_to"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Spinner
        android:id="@+id/spinner_to"
        style="@style/SpinnerStyle"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@id/spinner_from"
        app:layout_constraintTop_toTopOf="@id/spinner_from" />

    <!-- Поле ввода суммы -->
    <com.google.android.material.textfield.TextInputLayout
        android:id="@+id/input_layout_from"
        style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.Dense"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:hint="Введите сумму"
        app:boxStrokeColor="@color/colorPrimary"
        android:layout_marginTop="16dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/spinner_from">

        <com.google.android.material.textfield.TextInputEditText
            android:id="@+id/input_from"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:inputType="numberDecimal"
            android:padding="12dp" />

    </com.google.android.material.textfield.TextInputLayout>

    <!-- Поле вывода конвертированной суммы -->
    <com.google.android.material.textfield.TextInputLayout
        android:id="@+id/input_layout_to"
        style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.Dense"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:hint="Конвертированная сумма"
        app:boxStrokeColor="@color/colorPrimary"
        android:layout_marginTop="16dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/input_layout_from">

        <com.google.android.material.textfield.TextInputEditText
            android:id="@+id/input_to"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:inputType="none"
            android:focusable="false"
            android:padding="12dp"
            android:background="@drawable/edittext_background_disabled" />

    </com.google.android.material.textfield.TextInputLayout>

    <!-- Layout for Time Range Selection -->
    <LinearLayout
        android:id="@+id/time_range_layout"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:gravity="center_vertical"
        android:layout_marginTop="16dp"
        app:layout_constraintTop_toBottomOf="@id/input_layout_to"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent">

        <TextView
            android:id="@+id/time_range_label"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Период:"
            android:textSize="16sp"
            android:layout_marginEnd="8dp"
            android:textColor="@color/black" />

        <Spinner
            android:id="@+id/spinner_time_range"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:entries="@array/time_ranges"
            android:background="@drawable/spinner_background" />

    </LinearLayout>

    <!-- График тренда -->
    <com.github.mikephil.charting.charts.LineChart
        android:id="@+id/line_chart"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="@color/chart_background"
        android:elevation="4dp"
        android:layout_marginTop="24dp"
        android:layout_marginBottom="16dp"
        app:layout_constraintTop_toBottomOf="@id/time_range_layout"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout> нужно добавить возможность просмтора курсов 90 дней 30 дней и неделя