diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml
index b268ef3..9621f9b 100644
--- a/.idea/deploymentTargetSelector.xml
+++ b/.idea/deploymentTargetSelector.xml
@@ -4,6 +4,14 @@
+
+
+
+
+
+
+
+
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index b6a8a0d..8e7121a 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -14,6 +14,8 @@ android {
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+
+
}
buildTypes {
@@ -35,7 +37,6 @@ android {
}
dependencies {
-
implementation(libs.appcompat)
implementation(libs.material)
implementation(libs.constraintlayout)
@@ -43,6 +44,11 @@ dependencies {
implementation(libs.lifecycle.viewmodel.ktx)
implementation(libs.navigation.fragment)
implementation(libs.navigation.ui)
+
+ implementation("androidx.room:room-runtime:2.7.1")
+ implementation(libs.room.common.jvm)
+ annotationProcessor("androidx.room:room-compiler:2.7.1")
+
testImplementation(libs.junit)
androidTestImplementation(libs.ext.junit)
androidTestImplementation(libs.espresso.core)
diff --git a/app/src/main/java/com/kolobochki/memory/AppDatabase.java b/app/src/main/java/com/kolobochki/memory/AppDatabase.java
new file mode 100644
index 0000000..dd63e82
--- /dev/null
+++ b/app/src/main/java/com/kolobochki/memory/AppDatabase.java
@@ -0,0 +1,32 @@
+package com.kolobochki.memory;
+
+import android.content.Context;
+
+import androidx.room.Database;
+import androidx.room.Room;
+import androidx.room.RoomDatabase;
+
+@Database(
+ entities = {LevelRecord.class},
+ version = 1,
+ exportSchema = true
+)
+public abstract class AppDatabase extends RoomDatabase {
+ public abstract LevelRecordDao levelRecordDao();
+
+ private static volatile AppDatabase INSTANCE;
+
+ public static AppDatabase getDatabase(Context context) {
+ if (INSTANCE == null) {
+ synchronized (AppDatabase.class) {
+ if (INSTANCE == null) {
+ INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
+ AppDatabase.class, "memory-db")
+ .fallbackToDestructiveMigration()
+ .build();
+ }
+ }
+ }
+ return INSTANCE;
+ }
+}
diff --git a/app/src/main/java/com/kolobochki/memory/GameRepository.java b/app/src/main/java/com/kolobochki/memory/GameRepository.java
new file mode 100644
index 0000000..a544959
--- /dev/null
+++ b/app/src/main/java/com/kolobochki/memory/GameRepository.java
@@ -0,0 +1,50 @@
+package com.kolobochki.memory;
+
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.MutableLiveData;
+
+import java.util.List;
+
+public class GameRepository {
+ private final LevelRecordDao recordDao;
+
+ public GameRepository(LevelRecordDao recordDao) {
+ this.recordDao = recordDao;
+ }
+
+ // Сохранить результат уровня
+ public void saveLevelResult(int levelId, int score) {
+ new Thread(() -> {
+ LevelRecord existing = recordDao.getRecordForLevel(levelId);
+
+ if (existing == null) {
+ // Первая попытка на этом уровне
+ recordDao.insert(new LevelRecord(levelId, score, 1));
+ } else {
+ // Обновляем если результат лучше
+ recordDao.updateIfBetter(levelId, score);
+ if (score <= existing.bestScore) {
+ // Просто увеличиваем счётчик попыток
+ existing.attemptsCount++;
+ recordDao.update(existing);
+ }
+ }
+ }).start();
+ }
+
+ // Получить все записи
+ public LiveData> getAllRecords() {
+ MutableLiveData> liveData = new MutableLiveData<>();
+ new Thread(() -> {
+ liveData.postValue(recordDao.getAllRecords());
+ }).start();
+ return liveData;
+ }
+
+ // Сбросить все данные
+ public void resetAllData() {
+ new Thread(() -> {
+ recordDao.deleteAllRecords();
+ }).start();
+ }
+}
diff --git a/app/src/main/java/com/kolobochki/memory/LevelRecord.java b/app/src/main/java/com/kolobochki/memory/LevelRecord.java
new file mode 100644
index 0000000..41850f9
--- /dev/null
+++ b/app/src/main/java/com/kolobochki/memory/LevelRecord.java
@@ -0,0 +1,21 @@
+package com.kolobochki.memory;
+
+import androidx.room.Entity;
+import androidx.room.PrimaryKey;
+
+@Entity(tableName = "level_records")
+public class LevelRecord {
+ @PrimaryKey
+ public int levelId;
+
+ public int bestScore;
+ public int attemptsCount;
+ public long lastPlayedTime;
+
+ public LevelRecord(int levelId, int bestScore, int attemptsCount) {
+ this.levelId = levelId;
+ this.bestScore = bestScore;
+ this.attemptsCount = attemptsCount;
+ this.lastPlayedTime = System.currentTimeMillis();
+ }
+}
diff --git a/app/src/main/java/com/kolobochki/memory/LevelRecordDao.java b/app/src/main/java/com/kolobochki/memory/LevelRecordDao.java
new file mode 100644
index 0000000..b386807
--- /dev/null
+++ b/app/src/main/java/com/kolobochki/memory/LevelRecordDao.java
@@ -0,0 +1,30 @@
+package com.kolobochki.memory;
+
+import androidx.room.Dao;
+import androidx.room.Insert;
+import androidx.room.Query;
+import androidx.room.Update;
+
+import java.util.List;
+
+@Dao
+public interface LevelRecordDao {
+ @Insert
+ void insert(LevelRecord record);
+
+ @Update
+ void update(LevelRecord record);
+
+ @Query("SELECT * FROM level_records WHERE levelId = :levelId")
+ LevelRecord getRecordForLevel(int levelId);
+
+ @Query("SELECT * FROM level_records ORDER BY levelId ASC")
+ List getAllRecords();
+
+ @Query("DELETE FROM level_records")
+ void deleteAllRecords();
+
+ @Query("UPDATE level_records SET bestScore = :newScore, attemptsCount = attemptsCount + 1 WHERE levelId = :levelId AND :newScore > bestScore")
+ void updateIfBetter(int levelId, int newScore);
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/kolobochki/memory/MainActivity.java b/app/src/main/java/com/kolobochki/memory/MainActivity.java
index bea7d77..227b56d 100644
--- a/app/src/main/java/com/kolobochki/memory/MainActivity.java
+++ b/app/src/main/java/com/kolobochki/memory/MainActivity.java
@@ -10,18 +10,33 @@ import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import androidx.navigation.ui.AppBarConfiguration;
import androidx.navigation.ui.NavigationUI;
+import androidx.room.Room;
import com.kolobochki.memory.databinding.ActivityMainBinding;
+import com.kolobochki.memory.AppDatabase;
+import com.kolobochki.memory.GameRepository;
+
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding binding;
private NavController navController;
+ private GameRepository gameRepository;
+ private static AppDatabase database;
+
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ database = Room.databaseBuilder(getApplicationContext(),
+ AppDatabase.class, "memory-db")
+ .fallbackToDestructiveMigration()
+ .build();
+
+ gameRepository = new GameRepository(database.levelRecordDao());
+
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
@@ -33,7 +48,9 @@ public class MainActivity extends AppCompatActivity {
R.id.navigation_task1,
R.id.navigation_task2,
R.id.navigation_task3,
- R.id.navigation_task4)
+ R.id.navigation_task4,
+ R.id.navigation_info,
+ R.id.navigation_records)
.build();
NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
NavigationUI.setupWithNavController(binding.navView, navController);
@@ -42,12 +59,31 @@ public class MainActivity extends AppCompatActivity {
if (destination.getId() == R.id.navigation_task1
|| destination.getId() == R.id.navigation_task2
|| destination.getId() == R.id.navigation_task3
- || destination.getId() == R.id.navigation_task4) {
+ || destination.getId() == R.id.navigation_task4
+ || destination.getId() == R.id.navigation_info
+ || destination.getId() == R.id.navigation_records) {
binding.navView.setVisibility(View.GONE);
} else {
binding.navView.setVisibility(View.VISIBLE);
}
});
+
+ }
+
+ public GameRepository getGameRepository() {
+ return gameRepository;
+ }
+
+ public static AppDatabase getDatabase() {
+ return database;
+ }
+
+ @Override
+ protected void onDestroy() {
+ if (database != null && database.isOpen()) {
+ database.close();
+ }
+ super.onDestroy();
}
@Override
diff --git a/app/src/main/java/com/kolobochki/memory/fragment_info.java b/app/src/main/java/com/kolobochki/memory/fragment_info.java
new file mode 100644
index 0000000..1d4fc24
--- /dev/null
+++ b/app/src/main/java/com/kolobochki/memory/fragment_info.java
@@ -0,0 +1,132 @@
+package com.kolobochki.memory;
+
+import android.os.Bundle;
+
+import androidx.activity.OnBackPressedCallback;
+import androidx.appcompat.app.ActionBar;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.fragment.app.Fragment;
+import androidx.navigation.NavController;
+import androidx.navigation.Navigation;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+import android.os.Bundle;
+import com.kolobochki.memory.R;
+import com.kolobochki.memory.databinding.FragmentDashboardBinding;
+import com.kolobochki.memory.databinding.FragmentInfoBinding;
+
+public class fragment_info extends Fragment {
+
+ private FragmentInfoBinding binding;
+ private static final String ARG_PARAM1 = "param1";
+ private static final String ARG_PARAM2 = "param2";
+
+ private TextView helpText;
+
+ private String mParam1;
+ private String mParam2;
+ private OnBackPressedCallback backPressedCallback;
+ private String previousTitle;
+
+ public fragment_info() {
+ // Required empty public constructor
+ }
+
+ public static fragment_info newInstance(String param1, String param2) {
+ fragment_info fragment = new fragment_info();
+ Bundle args = new Bundle();
+ args.putString(ARG_PARAM1, param1);
+ args.putString(ARG_PARAM2, param2);
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (getArguments() != null) {
+ mParam1 = getArguments().getString(ARG_PARAM1);
+ mParam2 = getArguments().getString(ARG_PARAM2);
+ }
+
+ if (getActivity() instanceof AppCompatActivity) {
+ ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setTitle("Инструкция");
+ }
+ }
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ // Inflate the layout for this fragment
+ binding = com.kolobochki.memory.databinding.FragmentInfoBinding .inflate(inflater, container, false);
+ View root = binding.getRoot();
+
+ String instructions =
+ "1. Найти пары:\n" +
+ " - На экране карточки перевернуты рубашкой вверх\n" +
+ " - Открывайте по две карточки, чтобы найти пары\n" +
+ " - Совпавшие карточки остаются открытыми\n" +
+ " - Цель: открыть все пары за минимальное время\n\n" +
+
+ "2. Саймон говорит:\n" +
+ " - Саймон показывает последовательность цветов\n" +
+ " - Повторите показанную последовательность\n" +
+ " - С каждым уровнем последовательность удлиняется\n" +
+ " - Ошибка завершает игру\n\n" +
+
+ "3. Числовая матрица:\n" +
+ " - Запомните расположение чисел в матрице\n" +
+ " - Восстановите числа после их исчезновения\n" +
+ " - Сложность повышается с каждым уровнем\n\n" +
+
+ "4. Найти лишнее:\n" +
+ " - Среди нескольких объектов найдите отличающийся\n" +
+ " - Выберите лишний объект\n" +
+ " - Время на ответ ограничено";
+
+ binding.textView.setText(instructions);
+
+ return root;
+ }
+
+ private void navigateHome() {
+ NavController navController = Navigation.findNavController(requireView());
+ navController.popBackStack();
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ if (backPressedCallback != null) {
+ backPressedCallback.remove();
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (getActivity() != null && ((AppCompatActivity)getActivity()).getSupportActionBar() != null) {
+ ((AppCompatActivity)getActivity()).getSupportActionBar().hide();
+ }
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ if (getActivity() != null && ((AppCompatActivity)getActivity()).getSupportActionBar() != null) {
+ ((AppCompatActivity)getActivity()).getSupportActionBar().show();
+ }
+
+ if (getActivity() instanceof AppCompatActivity) {
+ ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
+ if (actionBar != null && previousTitle != null) {
+ actionBar.setTitle(previousTitle);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/kolobochki/memory/fragment_records.java b/app/src/main/java/com/kolobochki/memory/fragment_records.java
new file mode 100644
index 0000000..9a89e59
--- /dev/null
+++ b/app/src/main/java/com/kolobochki/memory/fragment_records.java
@@ -0,0 +1,145 @@
+package com.kolobochki.memory;
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.activity.OnBackPressedCallback;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.ActionBar;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.fragment.app.Fragment;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import com.kolobochki.memory.AppDatabase;
+import com.kolobochki.memory.LevelRecord;
+import com.kolobochki.memory.LevelRecordDao;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+public class fragment_records extends Fragment {
+
+ private RecyclerView recyclerView;
+ private StatsAdapter adapter;
+ private LevelRecordDao levelRecordDao;
+ private Executor executor = Executors.newSingleThreadExecutor();
+
+ private OnBackPressedCallback backPressedCallback;
+ private String previousTitle;
+
+ // Названия уровней
+ private final String[] levelNames = {
+ "Найти пары",
+ "Саймон говорит",
+ "Числовая матрица",
+ "Найти лишнее"
+ };
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Инициализация базы данных
+ AppDatabase db = AppDatabase.getDatabase(requireContext().getApplicationContext());
+ levelRecordDao = db.levelRecordDao();
+
+ if (getActivity() instanceof AppCompatActivity) {
+ ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setTitle("Мой прогресс");
+ }
+ }
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.fragment_records, container, false);
+
+ recyclerView = view.findViewById(R.id.stats_recycler);
+ recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
+
+ adapter = new StatsAdapter();
+ recyclerView.setAdapter(adapter);
+
+ loadStats();
+
+ return view;
+ }
+
+ private void loadStats() {
+ executor.execute(() -> {
+ try {
+ List records = levelRecordDao.getAllRecords();
+
+ // Если записей нет - создаем пустые
+ if (records == null || records.isEmpty()) {
+ for (int i = 1; i <= 4; i++) {
+ LevelRecord record = new LevelRecord(i, 0, 0);
+ levelRecordDao.insert(record);
+ }
+ records = levelRecordDao.getAllRecords();
+ }
+
+ List finalRecords = records;
+ requireActivity().runOnUiThread(() -> {
+ if (adapter != null) {
+ adapter.setRecords(finalRecords);
+ }
+ });
+ } catch (Exception e) {
+ requireActivity().runOnUiThread(() ->
+ Toast.makeText(requireContext(), "Ошибка загрузки данных", Toast.LENGTH_SHORT).show()
+ );
+ }
+ });
+ }
+
+ // Адаптер для RecyclerView
+ private class StatsAdapter extends RecyclerView.Adapter {
+
+ private List records;
+
+ public void setRecords(List records) {
+ this.records = records;
+ notifyDataSetChanged();
+ }
+
+ @NonNull
+ @Override
+ public StatsViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ View view = LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.item_level_stat, parent, false);
+ return new StatsViewHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull StatsViewHolder holder, int position) {
+ LevelRecord record = records.get(position);
+ holder.levelName.setText(levelNames[record.levelId - 1]);
+ holder.bestScore.setText("Рекорд: " + record.bestScore);
+ holder.attempts.setText("Попыток: " + record.attemptsCount);
+ }
+
+ @Override
+ public int getItemCount() {
+ return records != null ? records.size() : 0;
+ }
+
+ class StatsViewHolder extends RecyclerView.ViewHolder {
+ TextView levelName, bestScore, attempts;
+
+ StatsViewHolder(View itemView) {
+ super(itemView);
+ levelName = itemView.findViewById(R.id.level_name);
+ bestScore = itemView.findViewById(R.id.best_score);
+ attempts = itemView.findViewById(R.id.attempts_count);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/kolobochki/memory/fragment_task1.java b/app/src/main/java/com/kolobochki/memory/fragment_task1.java
index 4b815b7..02dffc9 100644
--- a/app/src/main/java/com/kolobochki/memory/fragment_task1.java
+++ b/app/src/main/java/com/kolobochki/memory/fragment_task1.java
@@ -1,10 +1,8 @@
package com.kolobochki.memory;
import android.os.Bundle;
-
import androidx.appcompat.app.ActionBar;
import androidx.fragment.app.Fragment;
-
import android.os.Handler;
import android.view.LayoutInflater;
import android.view.View;
@@ -14,17 +12,23 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
-
+import android.widget.Toast;
import androidx.activity.OnBackPressedCallback;
import androidx.appcompat.app.AlertDialog;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import androidx.appcompat.app.AppCompatActivity;
+import androidx.room.Room;
+
+import com.kolobochki.memory.AppDatabase;
+import com.kolobochki.memory.LevelRecord;
+import com.kolobochki.memory.LevelRecordDao;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-
-import com.kolobochki.memory.R;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
public class fragment_task1 extends Fragment {
@@ -35,43 +39,34 @@ public class fragment_task1 extends Fragment {
private OnBackPressedCallback backPressedCallback;
private String previousTitle;
- // ----------------------------------------------
-
- private static final int PAIRS_COUNT = 6; // Кол-во пар
- private static final int GRID_COLUMNS = 4; // Кол-во колонок
- private static final int PREVIEW_TIME = 5000; // Задержка в начале в мс
- private static final int SUCCESS_DELAY = 4000; // Задержка в конце в мс
- private static final int INITIAL_LIVES = 3; // Кол-во жизек
-
- // ----------------------------------------------
+ // Game constants
+ private static final int PAIRS_COUNT = 6;
+ private static final int GRID_COLUMNS = 4;
+ private static final int PREVIEW_TIME = 5000;
+ private static final int SUCCESS_DELAY = 4000;
+ private static final int INITIAL_LIVES = 3;
+ private static final int LEVEL_ID = 1; // ID для первого уровня
+ // Game variables
private int score = 0;
private int lives = INITIAL_LIVES;
- private int flippedCount = 0;
private ImageView firstFlipped = null;
private boolean isPreview = true;
private boolean isGameComplete = false;
- // ----------------------------------------------
-
+ // Views
private GridLayout gridLayout;
private ProgressBar progressBar;
private List tileImages = new ArrayList<>();
private List tiles = new ArrayList<>();
private LinearLayout livesLayout;
- // -----------------------------------------------
+ // Database
+ private AppDatabase database;
+ private LevelRecordDao levelRecordDao;
+ private Executor executor = Executors.newSingleThreadExecutor();
- public fragment_task1() { }
-
- public static fragment_task1 newInstance(String param1, String param2) {
- fragment_task1 fragment = new fragment_task1();
- Bundle args = new Bundle();
- args.putString(ARG_PARAM1, param1);
- args.putString(ARG_PARAM2, param2);
- fragment.setArguments(args);
- return fragment;
- }
+ public fragment_task1() {}
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -81,7 +76,10 @@ public class fragment_task1 extends Fragment {
mParam2 = getArguments().getString(ARG_PARAM2);
}
- OnBackPressedCallback backPressedCallback = new OnBackPressedCallback(true) {
+ database = AppDatabase.getDatabase(requireContext());
+ levelRecordDao = database.levelRecordDao();
+
+ backPressedCallback = new OnBackPressedCallback(true) {
@Override
public void handleOnBackPressed() {
if (!isGameComplete) {
@@ -103,7 +101,7 @@ public class fragment_task1 extends Fragment {
}
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_task1, container, false);
gridLayout = view.findViewById(R.id.gridLayout);
@@ -168,7 +166,6 @@ public class fragment_task1 extends Fragment {
}, PREVIEW_TIME);
}
-
private void flipAllTiles() {
for (ImageView tile : tiles) {
tile.setImageResource(R.drawable.tile_back);
@@ -233,11 +230,13 @@ public class fragment_task1 extends Fragment {
private void gameComplete() {
isGameComplete = true;
+ saveGameResult(true);
new Handler().postDelayed(this::navigateHome, 1500);
}
private void gameOver() {
isGameComplete = true;
+ saveGameResult(false);
new AlertDialog.Builder(requireContext())
.setTitle("Игра окончена")
.setMessage("Вы проиграли. Попробуйте еще раз!")
@@ -246,6 +245,49 @@ public class fragment_task1 extends Fragment {
.show();
}
+ private void saveGameResult(boolean isWin) {
+ executor.execute(() -> {
+ try {
+ LevelRecord existingRecord = levelRecordDao.getRecordForLevel(LEVEL_ID);
+
+ if (existingRecord == null) {
+ // Первая запись для этого уровня
+ LevelRecord newRecord = new LevelRecord(LEVEL_ID, isWin ? score : 0, 1);
+ levelRecordDao.insert(newRecord);
+
+ if (isWin) {
+ showNewRecordToast(score);
+ }
+ } else {
+ boolean isNewRecord = false;
+
+ if (isWin && score > existingRecord.bestScore) {
+ existingRecord.bestScore = score;
+ isNewRecord = true;
+ }
+
+ existingRecord.attemptsCount++;
+ levelRecordDao.update(existingRecord);
+
+ if (isNewRecord) {
+ showNewRecordToast(score);
+ }
+ }
+ } catch (Exception e) {
+ requireActivity().runOnUiThread(() ->
+ Toast.makeText(requireContext(), "Ошибка сохранения: " + e.getMessage(), Toast.LENGTH_SHORT).show()
+ );
+ }
+ });
+ }
+
+ private void showNewRecordToast(int score) {
+ requireActivity().runOnUiThread(() -> {
+ Toast.makeText(requireContext(),
+ "Новый рекорд: " + score, Toast.LENGTH_SHORT).show();
+ });
+ }
+
private void navigateHome() {
NavController navController = Navigation.findNavController(requireView());
navController.popBackStack();
@@ -257,6 +299,9 @@ public class fragment_task1 extends Fragment {
if (backPressedCallback != null) {
backPressedCallback.remove();
}
+ if (database != null) {
+ database.close();
+ }
}
private void showExitConfirmationDialog() {
diff --git a/app/src/main/java/com/kolobochki/memory/fragment_task2.java b/app/src/main/java/com/kolobochki/memory/fragment_task2.java
index d42b06d..ad200f8 100644
--- a/app/src/main/java/com/kolobochki/memory/fragment_task2.java
+++ b/app/src/main/java/com/kolobochki/memory/fragment_task2.java
@@ -3,35 +3,38 @@ package com.kolobochki.memory;
import static com.kolobochki.memory.R.*;
import android.os.Bundle;
-
import androidx.activity.OnBackPressedCallback;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
-
import android.os.Handler;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
-import androidx.appcompat.app.AppCompatActivity;
import android.widget.Button;
import android.widget.GridLayout;
import android.widget.ImageView;
import android.widget.TextView;
+import android.widget.Toast;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
-import com.kolobochki.memory.R;
+import com.kolobochki.memory.AppDatabase;
+import com.kolobochki.memory.LevelRecord;
+import com.kolobochki.memory.LevelRecordDao;
public class fragment_task2 extends Fragment {
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
+ private static final int LEVEL_ID = 2; // ID для второго уровня
private String mParam1;
private String mParam2;
@@ -50,6 +53,11 @@ public class fragment_task2 extends Fragment {
private TextView roundText, livesText;
private GridLayout buttonsGrid;
+ // Database
+ private AppDatabase database;
+ private LevelRecordDao levelRecordDao;
+ private Executor executor = Executors.newSingleThreadExecutor();
+
private final int[] colorImages = {
R.drawable.simon_red,
R.drawable.simon_green,
@@ -62,15 +70,6 @@ public class fragment_task2 extends Fragment {
// Required empty public constructor
}
- public static fragment_task2 newInstance(String param1, String param2) {
- fragment_task2 fragment = new fragment_task2();
- Bundle args = new Bundle();
- args.putString(ARG_PARAM1, param1);
- args.putString(ARG_PARAM2, param2);
- fragment.setArguments(args);
- return fragment;
- }
-
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -79,6 +78,9 @@ public class fragment_task2 extends Fragment {
mParam2 = getArguments().getString(ARG_PARAM2);
}
+ database = AppDatabase.getDatabase(requireContext());
+ levelRecordDao = database.levelRecordDao();
+
OnBackPressedCallback backPressedCallback = new OnBackPressedCallback(true) {
@Override
public void handleOnBackPressed() {
@@ -92,6 +94,14 @@ public class fragment_task2 extends Fragment {
};
requireActivity().getOnBackPressedDispatcher().addCallback(this, backPressedCallback);
+ executor.execute(() -> {
+ LevelRecord existingRecord = levelRecordDao.getRecordForLevel(LEVEL_ID);
+ if (existingRecord != null) {
+ existingRecord.attemptsCount++;
+ levelRecordDao.update(existingRecord);
+ }
+ });
+
if (getActivity() instanceof AppCompatActivity) {
ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
if (actionBar != null) {
@@ -101,7 +111,7 @@ public class fragment_task2 extends Fragment {
}
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_task2, container, false);
redButton = view.findViewById(R.id.redButton);
@@ -128,6 +138,7 @@ public class fragment_task2 extends Fragment {
round = 1;
lives = 3;
currentStep = 0;
+ isGameComplete = false;
updateUI();
for (int i = 0; i < 3; i++) {
@@ -207,8 +218,10 @@ public class fragment_task2 extends Fragment {
round++;
currentStep = 0;
updateUI();
- generateNextSequence();
+ checkAndUpdateRecord(round);
+
+ generateNextSequence();
new Handler().postDelayed(() -> {
if (!isGameComplete) {
showSequence();
@@ -231,6 +244,17 @@ public class fragment_task2 extends Fragment {
}
}
+ private void checkAndUpdateRecord(int currentRound) {
+ executor.execute(() -> {
+ LevelRecord record = levelRecordDao.getRecordForLevel(LEVEL_ID);
+ if (record != null && currentRound > record.bestScore) {
+ record.bestScore = currentRound;
+ levelRecordDao.update(record);
+ showNewRecordToast(currentRound);
+ }
+ });
+ }
+
private void updateUI() {
roundText.setText("Раунд: " + round);
@@ -257,6 +281,45 @@ public class fragment_task2 extends Fragment {
.show();
}
+ private void saveGameResult(int roundsCompleted) {
+ executor.execute(() -> {
+ try {
+ LevelRecord existingRecord = levelRecordDao.getRecordForLevel(LEVEL_ID);
+
+ if (existingRecord == null) {
+ LevelRecord newRecord = new LevelRecord(LEVEL_ID, roundsCompleted, 1);
+ levelRecordDao.insert(newRecord);
+ showNewRecordToast(roundsCompleted);
+ } else {
+ boolean isNewRecord = false;
+
+ if (roundsCompleted > existingRecord.bestScore) {
+ existingRecord.bestScore = roundsCompleted;
+ isNewRecord = true;
+ }
+
+ existingRecord.attemptsCount++;
+ levelRecordDao.update(existingRecord);
+
+ if (isNewRecord) {
+ showNewRecordToast(roundsCompleted);
+ }
+ }
+ } catch (Exception e) {
+ requireActivity().runOnUiThread(() ->
+ Toast.makeText(requireContext(), "Ошибка сохранения: " + e.getMessage(), Toast.LENGTH_SHORT).show()
+ );
+ }
+ });
+ }
+
+ private void showNewRecordToast(int score) {
+ requireActivity().runOnUiThread(() -> {
+ Toast.makeText(requireContext(),
+ "Новый рекорд: " + score + " раундов", Toast.LENGTH_SHORT).show();
+ });
+ }
+
private void showExitConfirmationDialog() {
new AlertDialog.Builder(requireContext())
.setTitle("Закончить упражнение?")
@@ -296,4 +359,12 @@ public class fragment_task2 extends Fragment {
}
}
}
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ if (database != null) {
+ database.close();
+ }
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/kolobochki/memory/fragment_task3.java b/app/src/main/java/com/kolobochki/memory/fragment_task3.java
index 5aae46c..1ecc0e5 100644
--- a/app/src/main/java/com/kolobochki/memory/fragment_task3.java
+++ b/app/src/main/java/com/kolobochki/memory/fragment_task3.java
@@ -23,11 +23,18 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
public class fragment_task3 extends Fragment {
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
+ private static final int LEVEL_ID = 3;
+
+ private AppDatabase database;
+ private LevelRecordDao levelRecordDao;
+ private Executor executor = Executors.newSingleThreadExecutor();
private String mParam1;
private String mParam2;
@@ -66,6 +73,20 @@ public class fragment_task3 extends Fragment {
mParam2 = getArguments().getString(ARG_PARAM2);
}
+ database = AppDatabase.getDatabase(requireContext());
+ levelRecordDao = database.levelRecordDao();
+
+ executor.execute(() -> {
+ LevelRecord existingRecord = levelRecordDao.getRecordForLevel(LEVEL_ID);
+ if (existingRecord != null) {
+ existingRecord.attemptsCount++;
+ levelRecordDao.update(existingRecord);
+ } else {
+ LevelRecord newRecord = new LevelRecord(LEVEL_ID, 0, 1);
+ levelRecordDao.insert(newRecord);
+ }
+ });
+
OnBackPressedCallback backPressedCallback = new OnBackPressedCallback(true) {
@Override
public void handleOnBackPressed() {
@@ -172,6 +193,9 @@ public class fragment_task3 extends Fragment {
if (isCorrect) {
currentRound++;
+ // Проверяем и обновляем рекорд сразу при увеличении раунда
+ checkAndUpdateRecord(currentRound);
+
Toast.makeText(getContext(), "Правильно! Раунд " + currentRound, Toast.LENGTH_SHORT).show();
new Handler().postDelayed(this::setupGame, 1500);
} else {
@@ -185,6 +209,24 @@ public class fragment_task3 extends Fragment {
}
}
+ private void checkAndUpdateRecord(int currentRound) {
+ executor.execute(() -> {
+ LevelRecord record = levelRecordDao.getRecordForLevel(LEVEL_ID);
+ if (record != null && currentRound > record.bestScore) {
+ record.bestScore = currentRound;
+ levelRecordDao.update(record);
+ showNewRecordToast(currentRound);
+ }
+ });
+ }
+
+ private void showNewRecordToast(int round) {
+ requireActivity().runOnUiThread(() -> {
+ Toast.makeText(requireContext(),
+ "Новый рекорд: " + round + " раундов", Toast.LENGTH_SHORT).show();
+ });
+ }
+
private void gameOver() {
isGameComplete = true;
new AlertDialog.Builder(requireContext())
@@ -244,4 +286,12 @@ public class fragment_task3 extends Fragment {
NavController navController = Navigation.findNavController(requireView());
navController.popBackStack();
}
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ if (database != null) {
+ database.close();
+ }
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/kolobochki/memory/fragment_task4.java b/app/src/main/java/com/kolobochki/memory/fragment_task4.java
index 8ac5d56..1099204 100644
--- a/app/src/main/java/com/kolobochki/memory/fragment_task4.java
+++ b/app/src/main/java/com/kolobochki/memory/fragment_task4.java
@@ -8,6 +8,8 @@ import android.view.ViewGroup;
import android.widget.GridLayout;
import android.widget.ImageView;
import android.widget.TextView;
+import android.widget.Toast;
+
import androidx.activity.OnBackPressedCallback;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
@@ -19,11 +21,18 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
public class fragment_task4 extends Fragment {
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
+ private static final int LEVEL_ID = 4;
+
+ private AppDatabase database;
+ private LevelRecordDao levelRecordDao;
+ private Executor executor = Executors.newSingleThreadExecutor();
private String mParam1;
private String mParam2;
@@ -62,6 +71,20 @@ public class fragment_task4 extends Fragment {
mParam2 = getArguments().getString(ARG_PARAM2);
}
+ database = AppDatabase.getDatabase(requireContext());
+ levelRecordDao = database.levelRecordDao();
+
+ executor.execute(() -> {
+ LevelRecord existingRecord = levelRecordDao.getRecordForLevel(LEVEL_ID);
+ if (existingRecord != null) {
+ existingRecord.attemptsCount++;
+ levelRecordDao.update(existingRecord);
+ } else {
+ LevelRecord newRecord = new LevelRecord(LEVEL_ID, 0, 1);
+ levelRecordDao.insert(newRecord);
+ }
+ });
+
OnBackPressedCallback backPressedCallback = new OnBackPressedCallback(true) {
@Override
public void handleOnBackPressed() {
@@ -181,8 +204,9 @@ public class fragment_task4 extends Fragment {
setCardsClickable(false);
if (row == changedRow && col == changedCol) {
- // Correct guess
round++;
+ checkAndUpdateRecord(round);
+
handler.postDelayed(() -> {
if (isAdded()) startNewRound();
}, 1000);
@@ -206,6 +230,33 @@ public class fragment_task4 extends Fragment {
}
}
+ private void checkAndUpdateRecord(int currentRound) {
+ executor.execute(() -> {
+ LevelRecord record = levelRecordDao.getRecordForLevel(LEVEL_ID);
+ if (record != null && currentRound > record.bestScore) {
+ record.bestScore = currentRound;
+ levelRecordDao.update(record);
+ showNewRecordToast(currentRound);
+ }
+ });
+ }
+
+ private void showNewRecordToast(int round) {
+ requireActivity().runOnUiThread(() -> {
+ Toast.makeText(requireContext(),
+ "Новый рекорд: " + round + " раундов", Toast.LENGTH_SHORT).show();
+ });
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ handler.removeCallbacksAndMessages(null);
+ if (database != null) {
+ database.close();
+ }
+ }
+
private void setCardImage(int row, int col, String imageName) {
cards[row][col].setImageResource(getResourceId(imageName));
}
@@ -281,10 +332,4 @@ public class fragment_task4 extends Fragment {
NavController navController = Navigation.findNavController(requireView());
navController.popBackStack();
}
-
- @Override
- public void onDestroyView() {
- super.onDestroyView();
- handler.removeCallbacksAndMessages(null);
- }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/kolobochki/memory/ui/dashboard/DashboardFragment.java b/app/src/main/java/com/kolobochki/memory/ui/dashboard/DashboardFragment.java
index 3f19fc8..89b2ab6 100644
--- a/app/src/main/java/com/kolobochki/memory/ui/dashboard/DashboardFragment.java
+++ b/app/src/main/java/com/kolobochki/memory/ui/dashboard/DashboardFragment.java
@@ -1,34 +1,106 @@
package com.kolobochki.memory.ui.dashboard;
+import android.app.AlertDialog;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
+import android.widget.Toast;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
+import androidx.navigation.Navigation;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+import com.kolobochki.memory.LevelRecord;
+import com.kolobochki.memory.MainActivity;
import com.kolobochki.memory.databinding.FragmentDashboardBinding;
+import com.kolobochki.memory.R;
+import com.kolobochki.memory.AppDatabase;
+import com.kolobochki.memory.GameRepository;
+import com.kolobochki.memory.LevelRecordDao;
+
public class DashboardFragment extends Fragment {
private FragmentDashboardBinding binding;
+ private Executor executor = Executors.newSingleThreadExecutor();
+ private LevelRecordDao levelRecordDao;
- public View onCreateView(@NonNull LayoutInflater inflater,
- ViewGroup container, Bundle savedInstanceState) {
- DashboardViewModel dashboardViewModel =
- new ViewModelProvider(this).get(DashboardViewModel.class);
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ // Инициализация базы данных
+ AppDatabase db = MainActivity.getDatabase();
+ levelRecordDao = db.levelRecordDao();
+ }
+ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
binding = FragmentDashboardBinding.inflate(inflater, container, false);
View root = binding.getRoot();
- //final TextView textView = binding.textDashboard;
- //dashboardViewModel.getText().observe(getViewLifecycleOwner(), textView::setText);
+ binding.chip.setOnClickListener(view -> {
+ try {
+ Navigation.findNavController(view).navigate(R.id.action_navigation_dashboard_to_navigation_info);
+ } catch (Exception e) {
+ //e.printStackTrace();
+ }
+ });
+
+ binding.chip2.setOnClickListener(view -> {
+ Navigation.findNavController(view).navigate(R.id.action_navigation_dashboard_to_navigation_records);
+ });
+
+ binding.chip4.setOnClickListener(view -> {
+ showResetConfirmationDialog();
+ });
+
+ binding.chip3.setOnClickListener(view -> {
+ new AlertDialog.Builder(requireContext())
+ .setTitle("Переход на другую платформу")
+ .setMessage("Скоро...")
+ .setPositiveButton("OK", null)
+ .show();
+ });
+
+
return root;
}
+ private void showResetConfirmationDialog() {
+ new AlertDialog.Builder(requireContext())
+ .setTitle("Сброс статистики")
+ .setMessage("Вы уверены, что хотите сбросить все рекорды и статистику?")
+ .setPositiveButton("Сбросить", (dialog, which) -> resetAllStats())
+ .setNegativeButton("Отмена", null)
+ .show();
+ }
+
+ private void resetAllStats() {
+ executor.execute(() -> {
+ // Удаляем все записи
+ levelRecordDao.deleteAllRecords();
+
+ // Создаем новые пустые записи для каждого уровня
+ for (int i = 1; i <= 4; i++) {
+ LevelRecord record = new LevelRecord(i, 0, 0);
+ levelRecordDao.insert(record);
+ }
+
+ // Показываем уведомление в UI потоке
+ requireActivity().runOnUiThread(() -> {
+ Toast.makeText(requireContext(),
+ "Все данные сброшены", Toast.LENGTH_SHORT).show();
+ });
+ });
+ }
+
@Override
public void onDestroyView() {
super.onDestroyView();
diff --git a/app/src/main/res/layout/fragment_dashboard.xml b/app/src/main/res/layout/fragment_dashboard.xml
index b94dd9c..9cfc2b2 100644
--- a/app/src/main/res/layout/fragment_dashboard.xml
+++ b/app/src/main/res/layout/fragment_dashboard.xml
@@ -79,4 +79,5 @@
android:text="Сбросить мои данные"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView" />
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_info.xml b/app/src/main/res/layout/fragment_info.xml
new file mode 100644
index 0000000..78303f3
--- /dev/null
+++ b/app/src/main/res/layout/fragment_info.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_records.xml b/app/src/main/res/layout/fragment_records.xml
new file mode 100644
index 0000000..49bd17a
--- /dev/null
+++ b/app/src/main/res/layout/fragment_records.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_level_stat.xml b/app/src/main/res/layout/item_level_stat.xml
new file mode 100644
index 0000000..ba5c368
--- /dev/null
+++ b/app/src/main/res/layout/item_level_stat.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/navigation/mobile_navigation.xml b/app/src/main/res/navigation/mobile_navigation.xml
index 1b6bbd5..988f786 100644
--- a/app/src/main/res/navigation/mobile_navigation.xml
+++ b/app/src/main/res/navigation/mobile_navigation.xml
@@ -35,6 +35,12 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 1851377..7e963bf 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -10,6 +10,7 @@ lifecycleLivedataKtx = "2.8.7"
lifecycleViewmodelKtx = "2.8.7"
navigationFragment = "2.8.8"
navigationUi = "2.8.8"
+roomCommonJvm = "2.7.1"
[libraries]
junit = { group = "junit", name = "junit", version.ref = "junit" }
@@ -22,6 +23,7 @@ lifecycle-livedata-ktx = { group = "androidx.lifecycle", name = "lifecycle-lived
lifecycle-viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "lifecycleViewmodelKtx" }
navigation-fragment = { group = "androidx.navigation", name = "navigation-fragment", version.ref = "navigationFragment" }
navigation-ui = { group = "androidx.navigation", name = "navigation-ui", version.ref = "navigationUi" }
+room-common-jvm = { group = "androidx.room", name = "room-common-jvm", version.ref = "roomCommonJvm" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }