From e14581d62af1f3265d74c1e397d180d1991541c0 Mon Sep 17 00:00:00 2001 From: uykek Date: Sat, 9 May 2020 12:13:08 +0200 Subject: [PATCH 001/219] Add new item titled 'Share Board' to the menu drawer of the GameActivity and a few Strings necessary for new the board-sharing feature in both English and German. --- app/src/main/res/menu/menu_drawer.xml | 8 ++++++-- app/src/main/res/values-de/strings.xml | 3 +++ app/src/main/res/values/strings.xml | 3 +++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/menu/menu_drawer.xml b/app/src/main/res/menu/menu_drawer.xml index 8c32e04..0ffccd2 100644 --- a/app/src/main/res/menu/menu_drawer.xml +++ b/app/src/main/res/menu/menu_drawer.xml @@ -1,7 +1,11 @@ - + + + + Bist du sicher, dass du den Spielstand löschen möchtest? Spiel Fortsetzen Zurücksetzen + Sudoku teilen + In Zwischenablage gespeichert Beste Zeit: Abgeschlossene Spiele: Benutzte Hinweise: @@ -49,6 +51,7 @@ Symbole Alles zurücksetzen Zurücksetzen + Teilen Bist du dir sicher, dass du das komplette Spielfeld zurücksetzen möchtest? Gesamtspielzeit: Fortsetzen diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index df9cd3f..46c67f1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -12,6 +12,7 @@ Help About Reset Board + Share Board Continue Game Open navigation drawer @@ -84,6 +85,8 @@ Use Are you sure you want to reset the game board? Reset + Share + Copied to clipboard From c1a37d3341ca900cf26b0143307118a889222044 Mon Sep 17 00:00:00 2001 From: uykek Date: Sat, 9 May 2020 12:19:56 +0200 Subject: [PATCH 002/219] Update gradle to include ConstraintLayouts and create layout for the board-sharing dialog. --- app/build.gradle | 2 + .../layout/dialog_fragment_share_board.xml | 41 +++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 app/src/main/res/layout/dialog_fragment_share_board.xml diff --git a/app/build.gradle b/app/build.gradle index 72ab140..d6a679d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -37,4 +37,6 @@ dependencies { implementation 'com.google.android.material:material:1.1.0' implementation 'androidx.legacy:legacy-support-core-ui:1.0.0' implementation 'androidx.legacy:legacy-support-core-utils:1.0.0' + implementation 'androidx.constraintlayout:constraintlayout:1.1.3' + } diff --git a/app/src/main/res/layout/dialog_fragment_share_board.xml b/app/src/main/res/layout/dialog_fragment_share_board.xml new file mode 100644 index 0000000..35cf194 --- /dev/null +++ b/app/src/main/res/layout/dialog_fragment_share_board.xml @@ -0,0 +1,41 @@ + + + + + + + \ No newline at end of file From 43f8d107454c924813565a9983d91173da769bb6 Mon Sep 17 00:00:00 2001 From: uykek Date: Sat, 9 May 2020 13:14:46 +0200 Subject: [PATCH 003/219] Add a subclass of DialogFragment using the previously added layout to GameActivity and a new interface to be implemented by all listeners for the subclass. --- .../ui/GameActivity.java | 93 ++++++++++++++++++- .../IShareDialogFragmentListener.java | 9 ++ 2 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/org/secuso/privacyfriendlysudoku/ui/listener/IShareDialogFragmentListener.java diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java index ece965e..904fed5 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java @@ -4,6 +4,9 @@ import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.app.DialogFragment; +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; @@ -19,14 +22,17 @@ import androidx.appcompat.app.ActionBarDrawerToggle; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; import android.view.Gravity; +import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.WindowManager; import android.widget.Button; +import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.RatingBar; import android.widget.TextView; +import android.widget.Toast; import org.secuso.privacyfriendlysudoku.controller.GameController; import org.secuso.privacyfriendlysudoku.controller.GameStateManager; @@ -39,6 +45,7 @@ import org.secuso.privacyfriendlysudoku.game.listener.IGameSolvedListener; import org.secuso.privacyfriendlysudoku.game.listener.ITimerListener; import org.secuso.privacyfriendlysudoku.ui.listener.IHintDialogFragmentListener; import org.secuso.privacyfriendlysudoku.ui.listener.IResetDialogFragmentListener; +import org.secuso.privacyfriendlysudoku.ui.listener.IShareDialogFragmentListener; import org.secuso.privacyfriendlysudoku.ui.view.R; import org.secuso.privacyfriendlysudoku.ui.view.SudokuFieldLayout; import org.secuso.privacyfriendlysudoku.ui.view.SudokuKeyboardLayout; @@ -48,7 +55,7 @@ import org.secuso.privacyfriendlysudoku.ui.view.WinDialog; import java.util.LinkedList; import java.util.List; -public class GameActivity extends BaseActivity implements NavigationView.OnNavigationItemSelectedListener, IGameSolvedListener ,ITimerListener, IHintDialogFragmentListener, IResetDialogFragmentListener { +public class GameActivity extends BaseActivity implements NavigationView.OnNavigationItemSelectedListener, IGameSolvedListener ,ITimerListener, IHintDialogFragmentListener, IResetDialogFragmentListener, IShareDialogFragmentListener { GameController gameController; SudokuFieldLayout layout; @@ -286,6 +293,25 @@ public class GameActivity extends BaseActivity implements NavigationView.OnNavig resetDialog.show(getFragmentManager(), "ResetDialogFragment"); break; + case R.id.menu_share: + ShareBoardDialog shareDialog = new ShareBoardDialog(); + shareDialog.setDisplayCode(gameController.getFieldAsString()); + shareDialog.setCopyClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + // remember to include alternate code for older android versions + String codeForClipboard = gameController.getFieldAsString().toString(); + ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); + ClipData clip = ClipData.newPlainText("BoardCode", codeForClipboard); + clipboard.setPrimaryClip(clip); + + Toast.makeText(GameActivity.this, R.string.copy_code_confirmation_toast, + Toast.LENGTH_LONG).show(); + } + }); + shareDialog.show(getFragmentManager(), "ShareDialogFragment"); + break; + case R.id.nav_newgame: //create new game intent = new Intent(this, MainActivity.class); @@ -433,11 +459,76 @@ public class GameActivity extends BaseActivity implements NavigationView.OnNavig gameController.resetLevel(); } + @Override + public void onShareDialogPositiveClick() { + gameController.resetLevel(); + } + @Override public void onDialogNegativeClick() { // do nothing } + public static class ShareBoardDialog extends DialogFragment { + private LinkedList listeners = new LinkedList<>(); + + /*declare empty display code and click listener in case anyone + * tries to call the ShareBoardDialog without setting those attributes first + */ + + private String displayCode = ""; + private View.OnClickListener copyClickListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + + } + }; + + public void setDisplayCode(String displayCode) { + this.displayCode = displayCode; + } + + public void setCopyClickListener(View.OnClickListener listener) { + copyClickListener = listener; + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + // Verify that the host activity implements the callback interface + if(activity instanceof IShareDialogFragmentListener) { + listeners.add((IShareDialogFragmentListener) activity); + } + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + + LayoutInflater inflater = getActivity().getLayoutInflater(); + View layout = inflater.inflate(R.layout.dialog_fragment_share_board, null); + + TextView displayText = (TextView)layout.findViewById(R.id.ver3_display_sudoku_text_view); + displayText.setText(displayCode); + ((ImageButton)layout.findViewById(R.id.ver3_copy_sudoku_to_clipboard_button)).setOnClickListener(copyClickListener); + builder.setView(layout); + + builder.setPositiveButton(R.string.share_confirmation_confirm, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + for(IShareDialogFragmentListener l : listeners) { + l.onShareDialogPositiveClick(); + } + } + }) + .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + // User cancelled the dialog + } + }); + return builder.create(); + } + } + public static class ResetConfirmationDialog extends DialogFragment { LinkedList listeners = new LinkedList<>(); diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/listener/IShareDialogFragmentListener.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/listener/IShareDialogFragmentListener.java new file mode 100644 index 0000000..9b2eb45 --- /dev/null +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/listener/IShareDialogFragmentListener.java @@ -0,0 +1,9 @@ +package org.secuso.privacyfriendlysudoku.ui.listener; + +/** + * Created by Chris on 19.01.2016. + */ +public interface IShareDialogFragmentListener { + public void onShareDialogPositiveClick(); + public void onDialogNegativeClick(); +} From 8f3371b38d2fecfda789635292e40734d9e5980c Mon Sep 17 00:00:00 2001 From: uykek Date: Sat, 9 May 2020 13:44:37 +0200 Subject: [PATCH 004/219] Enable data binding --- app/build.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/build.gradle b/app/build.gradle index d6a679d..ffdb524 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -25,6 +25,10 @@ android { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + + dataBinding { + enabled = true + } } dependencies { From 4134013fe9269cb250bf057bf8412a03a494ffae Mon Sep 17 00:00:00 2001 From: uykek Date: Sat, 9 May 2020 14:05:43 +0200 Subject: [PATCH 005/219] Change implementation of of ShareBoardDialogFragment to use databinding instead of 'findViewById' --- .../ui/GameActivity.java | 22 +++++++++++-------- .../layout/dialog_fragment_share_board.xml | 7 +++--- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java index 904fed5..51b8847 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java @@ -51,6 +51,7 @@ import org.secuso.privacyfriendlysudoku.ui.view.SudokuFieldLayout; import org.secuso.privacyfriendlysudoku.ui.view.SudokuKeyboardLayout; import org.secuso.privacyfriendlysudoku.ui.view.SudokuSpecialButtonLayout; import org.secuso.privacyfriendlysudoku.ui.view.WinDialog; +import org.secuso.privacyfriendlysudoku.ui.view.databinding.DialogFragmentShareBoardBinding; import java.util.LinkedList; import java.util.List; @@ -302,11 +303,15 @@ public class GameActivity extends BaseActivity implements NavigationView.OnNavig // remember to include alternate code for older android versions String codeForClipboard = gameController.getFieldAsString().toString(); ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); - ClipData clip = ClipData.newPlainText("BoardCode", codeForClipboard); - clipboard.setPrimaryClip(clip); - Toast.makeText(GameActivity.this, R.string.copy_code_confirmation_toast, - Toast.LENGTH_LONG).show(); + if (clipboard != null) { + ClipData clip = ClipData.newPlainText("BoardCode", codeForClipboard); + clipboard.setPrimaryClip(clip); + Toast.makeText(GameActivity.this, R.string.copy_code_confirmation_toast, + Toast.LENGTH_LONG).show(); + } else { + // + } } }); shareDialog.show(getFragmentManager(), "ShareDialogFragment"); @@ -506,12 +511,11 @@ public class GameActivity extends BaseActivity implements NavigationView.OnNavig AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); LayoutInflater inflater = getActivity().getLayoutInflater(); - View layout = inflater.inflate(R.layout.dialog_fragment_share_board, null); + DialogFragmentShareBoardBinding binding = DialogFragmentShareBoardBinding.inflate(inflater); - TextView displayText = (TextView)layout.findViewById(R.id.ver3_display_sudoku_text_view); - displayText.setText(displayCode); - ((ImageButton)layout.findViewById(R.id.ver3_copy_sudoku_to_clipboard_button)).setOnClickListener(copyClickListener); - builder.setView(layout); + binding.ver3DisplaySudokuTextView.setText(displayCode); + binding.ver3CopySudokuToClipboardButton.setOnClickListener(copyClickListener); + builder.setView(binding.getRoot()); builder.setPositiveButton(R.string.share_confirmation_confirm, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { diff --git a/app/src/main/res/layout/dialog_fragment_share_board.xml b/app/src/main/res/layout/dialog_fragment_share_board.xml index 35cf194..f6d811f 100644 --- a/app/src/main/res/layout/dialog_fragment_share_board.xml +++ b/app/src/main/res/layout/dialog_fragment_share_board.xml @@ -1,7 +1,7 @@ + @@ -38,4 +38,5 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="@+id/ver3_copy_sudoku_to_clipboard_button" app:layout_constraintVertical_bias="0.0" /> - \ No newline at end of file + + \ No newline at end of file From 5a098d8ac57a1ca3c01e7a7d0f054fcbed462e09 Mon Sep 17 00:00:00 2001 From: uykek Date: Sat, 9 May 2020 18:36:22 +0200 Subject: [PATCH 006/219] Add methods to generate the code necessary for sharing a specific sudoku board --- .../controller/GameController.java | 1 + .../privacyfriendlysudoku/game/GameBoard.java | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/GameController.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/GameController.java index 3ea8f65..b400d60 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/GameController.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/GameController.java @@ -414,6 +414,7 @@ public class GameController implements IModelChangedListener, Parcelable { return gameBoard.toString(); } + public String getCodeOfField() { return gameBoard.transformToCode(); } diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/game/GameBoard.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/game/GameBoard.java index ba7c64f..9bf90fc 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/game/GameBoard.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/game/GameBoard.java @@ -3,6 +3,7 @@ package org.secuso.privacyfriendlysudoku.game; import android.os.Parcel; import android.os.Parcelable; +import org.secuso.privacyfriendlysudoku.controller.Symbol; import org.secuso.privacyfriendlysudoku.game.listener.IModelChangedListener; import java.util.LinkedList; @@ -214,6 +215,20 @@ public class GameBoard implements Cloneable, Parcelable { return sb.toString(); } + public String transformToCode () { + StringBuilder sb = new StringBuilder(); + for(int i = 0; i < field.length; i++) { + for (int j = 0; j < field[0].length; j++) { + if (field[i][j].getValue() == 0) { + sb.append(0); + } else { + sb.append(Symbol.getSymbol(Symbol.SaveFormat, field[i][j].getValue()-1)); + } + } + } + return sb.toString(); + } + public boolean isFilled() { return actionOnCells(new ICellAction() { @Override From 9c9a0c12211c24e602f3114e8c874028e6813b9e Mon Sep 17 00:00:00 2001 From: uykek Date: Sat, 9 May 2020 18:37:00 +0200 Subject: [PATCH 007/219] Implement the functionality of the 'share' button of the ShareBoardDialogFragment, which is to share a board's code with other apps. --- .../ui/GameActivity.java | 20 +++++++++++++------ .../IShareDialogFragmentListener.java | 4 +++- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java index 51b8847..891ea49 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java @@ -296,12 +296,12 @@ public class GameActivity extends BaseActivity implements NavigationView.OnNavig case R.id.menu_share: ShareBoardDialog shareDialog = new ShareBoardDialog(); - shareDialog.setDisplayCode(gameController.getFieldAsString()); + shareDialog.setDisplayCode("sudoku://" + gameController.getCodeOfField()); shareDialog.setCopyClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // remember to include alternate code for older android versions - String codeForClipboard = gameController.getFieldAsString().toString(); + String codeForClipboard = "sudoku://" + gameController.getCodeOfField(); ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); if (clipboard != null) { @@ -310,7 +310,9 @@ public class GameActivity extends BaseActivity implements NavigationView.OnNavig Toast.makeText(GameActivity.this, R.string.copy_code_confirmation_toast, Toast.LENGTH_LONG).show(); } else { - // + //remember to replace hardcoded string + Toast.makeText(GameActivity.this, "Cannot access clipboard", + Toast.LENGTH_LONG).show(); } } }); @@ -465,8 +467,14 @@ public class GameActivity extends BaseActivity implements NavigationView.OnNavig } @Override - public void onShareDialogPositiveClick() { - gameController.resetLevel(); + public void onShareDialogPositiveClick(String input) { + Intent sendBoardIntent = new Intent(); + sendBoardIntent.setAction(Intent.ACTION_SEND); + sendBoardIntent.putExtra(Intent.EXTRA_TEXT, input); + sendBoardIntent.setType("text/plain"); + + Intent shareBoardIntent = Intent.createChooser(sendBoardIntent, null); + startActivity(shareBoardIntent); } @Override @@ -520,7 +528,7 @@ public class GameActivity extends BaseActivity implements NavigationView.OnNavig builder.setPositiveButton(R.string.share_confirmation_confirm, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { for(IShareDialogFragmentListener l : listeners) { - l.onShareDialogPositiveClick(); + l.onShareDialogPositiveClick(binding.ver3DisplaySudokuTextView.getText().toString()); } } }) diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/listener/IShareDialogFragmentListener.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/listener/IShareDialogFragmentListener.java index 9b2eb45..d5c1629 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/listener/IShareDialogFragmentListener.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/listener/IShareDialogFragmentListener.java @@ -1,9 +1,11 @@ package org.secuso.privacyfriendlysudoku.ui.listener; +import org.secuso.privacyfriendlysudoku.ui.view.databinding.DialogFragmentShareBoardBinding; + /** * Created by Chris on 19.01.2016. */ public interface IShareDialogFragmentListener { - public void onShareDialogPositiveClick(); + public void onShareDialogPositiveClick(String input); public void onDialogNegativeClick(); } From c0743f7a088a4cd229a9527e2c0bcde6b44e99c6 Mon Sep 17 00:00:00 2001 From: uykek Date: Sat, 9 May 2020 19:37:57 +0200 Subject: [PATCH 008/219] Make a few improvements on the implementation of the 'copy to clipboard' button. --- .../org/secuso/privacyfriendlysudoku/ui/GameActivity.java | 7 +++---- .../ui/listener/IShareDialogFragmentListener.java | 5 ----- app/src/main/res/values-de/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java index 891ea49..1effafe 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java @@ -295,13 +295,13 @@ public class GameActivity extends BaseActivity implements NavigationView.OnNavig break; case R.id.menu_share: + String codeForClipboard = "sudoku://" + gameController.getCodeOfField(); ShareBoardDialog shareDialog = new ShareBoardDialog(); - shareDialog.setDisplayCode("sudoku://" + gameController.getCodeOfField()); + shareDialog.setDisplayCode(codeForClipboard); shareDialog.setCopyClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // remember to include alternate code for older android versions - String codeForClipboard = "sudoku://" + gameController.getCodeOfField(); ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); if (clipboard != null) { @@ -310,8 +310,7 @@ public class GameActivity extends BaseActivity implements NavigationView.OnNavig Toast.makeText(GameActivity.this, R.string.copy_code_confirmation_toast, Toast.LENGTH_LONG).show(); } else { - //remember to replace hardcoded string - Toast.makeText(GameActivity.this, "Cannot access clipboard", + Toast.makeText(GameActivity.this, R.string.copy_code_error_toast, Toast.LENGTH_LONG).show(); } } diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/listener/IShareDialogFragmentListener.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/listener/IShareDialogFragmentListener.java index d5c1629..c14edbd 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/listener/IShareDialogFragmentListener.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/listener/IShareDialogFragmentListener.java @@ -1,10 +1,5 @@ package org.secuso.privacyfriendlysudoku.ui.listener; -import org.secuso.privacyfriendlysudoku.ui.view.databinding.DialogFragmentShareBoardBinding; - -/** - * Created by Chris on 19.01.2016. - */ public interface IShareDialogFragmentListener { public void onShareDialogPositiveClick(String input); public void onDialogNegativeClick(); diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 3b92912..04ea1c9 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -37,6 +37,7 @@ Zurücksetzen Sudoku teilen In Zwischenablage gespeichert + Kein Zugriff auf Zwischenablage möglich Beste Zeit: Abgeschlossene Spiele: Benutzte Hinweise: diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 46c67f1..5e45fd0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -87,6 +87,7 @@ Reset Share Copied to clipboard + Cannot access clipboard From 385959d9ebb0c5b1dc4104572b3e668ef2804ed9 Mon Sep 17 00:00:00 2001 From: uykek Date: Wed, 13 May 2020 23:24:03 +0200 Subject: [PATCH 009/219] Expand GameActivity so that it accepts a string containing an enconded sudoku board (for now with default difficulty 'easy') --- .../ui/GameActivity.java | 53 +++++++++++++------ 1 file changed, 37 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java index 1effafe..dc73e8c 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java @@ -12,6 +12,7 @@ import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Configuration; import android.graphics.Point; +import android.net.Uri; import android.os.Bundle; import android.preference.PreferenceActivity; import android.preference.PreferenceManager; @@ -21,6 +22,7 @@ import androidx.drawerlayout.widget.DrawerLayout; import androidx.appcompat.app.ActionBarDrawerToggle; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; + import android.view.Gravity; import android.view.LayoutInflater; import android.view.Menu; @@ -28,7 +30,6 @@ import android.view.MenuItem; import android.view.View; import android.view.WindowManager; import android.widget.Button; -import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.RatingBar; import android.widget.TextView; @@ -53,6 +54,7 @@ import org.secuso.privacyfriendlysudoku.ui.view.SudokuSpecialButtonLayout; import org.secuso.privacyfriendlysudoku.ui.view.WinDialog; import org.secuso.privacyfriendlysudoku.ui.view.databinding.DialogFragmentShareBoardBinding; +import java.util.Date; import java.util.LinkedList; import java.util.List; @@ -104,25 +106,44 @@ public class GameActivity extends BaseActivity implements NavigationView.OnNavig if(savedInstanceState == null) { Bundle extras = getIntent().getExtras(); - if (extras != null) { - gameType = GameType.valueOf(extras.getString("gameType", GameType.Default_9x9.name())); - gameDifficulty = GameDifficulty.valueOf(extras.getString("gameDifficulty", GameDifficulty.Moderate.name())); - loadLevel = extras.getBoolean("loadLevel", false); - if (loadLevel) { - loadLevelID = extras.getInt("loadLevelID"); - } - } - + Uri data = getIntent().getData(); gameController = new GameController(sharedPref, getApplicationContext()); - List loadableGames = GameStateManager.getLoadableGameList(); + boolean intentReceivedFromMainActivity = extras != null && + (extras.containsKey("gameType") || extras.containsKey("loadLevel")); + + if (data != null && !intentReceivedFromMainActivity) { + String input = data.toString(); + input = input.replace("sudoku.de://", ""); + int sectionSize = (int)Math.sqrt(input.length()); + int boardSize = sectionSize * sectionSize; + + GameInfoContainer container = new GameInfoContainer(0, null, null, null, new int [boardSize], new boolean [boardSize][sectionSize]); + container.parseFixedValues(input); + container.parseGameType("Default_" + sectionSize + "x" + sectionSize); + container.parseDifficulty("Easy"); + + gameController.loadLevel(container); - if (loadLevel && loadableGames.size() > loadLevelID) { - // load level from GameStateManager - gameController.loadLevel(loadableGames.get(loadLevelID)); } else { - // load a new level - gameController.loadNewLevel(gameType, gameDifficulty); + if (extras != null) { + gameType = GameType.valueOf(extras.getString("gameType", GameType.Default_9x9.name())); + gameDifficulty = GameDifficulty.valueOf(extras.getString("gameDifficulty", GameDifficulty.Moderate.name())); + loadLevel = extras.getBoolean("loadLevel", false); + if (loadLevel) { + loadLevelID = extras.getInt("loadLevelID"); + } + } + + List loadableGames = GameStateManager.getLoadableGameList(); + + if (loadLevel && loadableGames.size() > loadLevelID) { + // load level from GameStateManager + gameController.loadLevel(loadableGames.get(loadLevelID)); + } else { + // load a new level + gameController.loadNewLevel(gameType, gameDifficulty); + } } } else { gameController = savedInstanceState.getParcelable("gameController"); From a1b13a49e5046d0bf2ef12ebe0ac3187638f0326 Mon Sep 17 00:00:00 2001 From: uykek Date: Thu, 14 May 2020 10:31:25 +0200 Subject: [PATCH 010/219] Compute difficulty of sudokus which are imported via deeplink --- .../privacyfriendlysudoku/ui/GameActivity.java | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java index dc73e8c..8907380 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java @@ -40,6 +40,7 @@ import org.secuso.privacyfriendlysudoku.controller.GameStateManager; import org.secuso.privacyfriendlysudoku.controller.SaveLoadStatistics; import org.secuso.privacyfriendlysudoku.controller.Symbol; import org.secuso.privacyfriendlysudoku.controller.helper.GameInfoContainer; +import org.secuso.privacyfriendlysudoku.controller.qqwing.QQWing; import org.secuso.privacyfriendlysudoku.game.GameDifficulty; import org.secuso.privacyfriendlysudoku.game.GameType; import org.secuso.privacyfriendlysudoku.game.listener.IGameSolvedListener; @@ -114,16 +115,25 @@ public class GameActivity extends BaseActivity implements NavigationView.OnNavig if (data != null && !intentReceivedFromMainActivity) { String input = data.toString(); - input = input.replace("sudoku.de://", ""); + input = input.replace("sudoku://", ""); + int sectionSize = (int)Math.sqrt(input.length()); int boardSize = sectionSize * sectionSize; GameInfoContainer container = new GameInfoContainer(0, null, null, null, new int [boardSize], new boolean [boardSize][sectionSize]); container.parseFixedValues(input); container.parseGameType("Default_" + sectionSize + "x" + sectionSize); - container.parseDifficulty("Easy"); - gameController.loadLevel(container); + QQWing difficultyCheck = new QQWing(container.getGameType(), GameDifficulty.Unspecified); + difficultyCheck.setRecordHistory(true); + difficultyCheck.setPuzzle(container.getFixedValues()); + boolean possibleToSolve = difficultyCheck.solve(); + + if (possibleToSolve) { + String difficulty = difficultyCheck.getDifficulty().toString(); + container.parseDifficulty(difficulty); + gameController.loadLevel(container); + } } else { if (extras != null) { From 26d5cab36930ffd49fed870673efc4ed0a39278e Mon Sep 17 00:00:00 2001 From: uykek Date: Fri, 15 May 2020 19:07:50 +0200 Subject: [PATCH 011/219] Notify user if a sudoku they tried to import is impossible to solve and close app afterwards. --- .../ui/GameActivity.java | 20 +++++++++++++++---- app/src/main/res/values-de/strings.xml | 1 + app/src/main/res/values/strings.xml | 2 ++ 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java index 8907380..da45e48 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java @@ -129,10 +129,22 @@ public class GameActivity extends BaseActivity implements NavigationView.OnNavig difficultyCheck.setPuzzle(container.getFixedValues()); boolean possibleToSolve = difficultyCheck.solve(); - if (possibleToSolve) { - String difficulty = difficultyCheck.getDifficulty().toString(); - container.parseDifficulty(difficulty); - gameController.loadLevel(container); + String difficulty = difficultyCheck.getDifficulty().toString(); + container.parseDifficulty(difficulty); + gameController.loadLevel(container); + + if (!possibleToSolve) { + + AlertDialog.Builder builder = new AlertDialog.Builder(GameActivity.this); + builder.setMessage(R.string.impossible_import_notice) + .setCancelable(false) + .setPositiveButton(R.string.okay, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + finish(); + } + }); + AlertDialog alert = builder.create(); + alert.show(); } } else { diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 04ea1c9..4f0215d 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -32,6 +32,7 @@ Benutzen Löschen Wähle ein gültiges Feld aus und drücke dann auf den Hinweis Knopf um dann das makierte Feld aufzulösen. + Das Sudoku, das du importieren möchtest, hat keine Lösung. Bist du sicher, dass du den Spielstand löschen möchtest? Spiel Fortsetzen Zurücksetzen diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5e45fd0..b8595ba 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -20,6 +20,8 @@ Generating new level in the background… + The sudoku you wish to import has no solution. + Settings From 064fcd9971b53fb1325ab1dacfd6601fd80f15d4 Mon Sep 17 00:00:00 2001 From: Erik Waegerle Date: Sat, 16 May 2020 20:13:33 +0200 Subject: [PATCH 012/219] Extension of the GameAcitivity with an Intent Filter in the Manifest.xml Intent-Filter is responsible for DeepLinking --- app/src/main/AndroidManifest.xml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 9d32955..fb20384 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,7 +3,8 @@ package="org.secuso.privacyfriendlysudoku.ui.view"> - + + + android:theme="@style/AppTheme.NoActionBar"> + + + + + + + + Date: Tue, 19 May 2020 18:23:41 +0200 Subject: [PATCH 013/219] Fix mistake which caused impossible imported sudokus to be saved to database. --- .../ui/GameActivity.java | 38 ++++++++++++------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java index da45e48..8b2bd2d 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java @@ -69,6 +69,7 @@ public class GameActivity extends BaseActivity implements NavigationView.OnNavig TextView viewName ; RatingBar ratingBar; private boolean gameSolved = false; + private boolean startGame = true; SaveLoadStatistics statistics = new SaveLoadStatistics(this); WinDialog dialog = null; @@ -119,22 +120,29 @@ public class GameActivity extends BaseActivity implements NavigationView.OnNavig int sectionSize = (int)Math.sqrt(input.length()); int boardSize = sectionSize * sectionSize; + QQWing difficultyCheck; + GameInfoContainer container = new GameInfoContainer(0, GameDifficulty.Unspecified, + GameType.Unspecified, new int [boardSize], new int [boardSize], new boolean [boardSize][sectionSize]); - GameInfoContainer container = new GameInfoContainer(0, null, null, null, new int [boardSize], new boolean [boardSize][sectionSize]); - container.parseFixedValues(input); - container.parseGameType("Default_" + sectionSize + "x" + sectionSize); + try { + container.parseFixedValues(input); + container.parseGameType("Default_" + sectionSize + "x" + sectionSize); - QQWing difficultyCheck = new QQWing(container.getGameType(), GameDifficulty.Unspecified); - difficultyCheck.setRecordHistory(true); - difficultyCheck.setPuzzle(container.getFixedValues()); - boolean possibleToSolve = difficultyCheck.solve(); + difficultyCheck = new QQWing(container.getGameType(), GameDifficulty.Unspecified); + difficultyCheck.setRecordHistory(true); + difficultyCheck.setPuzzle(container.getFixedValues()); + startGame = difficultyCheck.solve(); + container.parseDifficulty(difficultyCheck.getDifficulty().toString()); - String difficulty = difficultyCheck.getDifficulty().toString(); - container.parseDifficulty(difficulty); - gameController.loadLevel(container); - - if (!possibleToSolve) { + } catch (IllegalArgumentException e) { + startGame = false; + sectionSize = GameType.Default_9x9.getSize(); + boardSize = sectionSize * sectionSize; + container = new GameInfoContainer(0, GameDifficulty.Unspecified, + GameType.Default_9x9, new int [boardSize], new int [boardSize], new boolean [boardSize][sectionSize]); + } + if (!startGame) { AlertDialog.Builder builder = new AlertDialog.Builder(GameActivity.this); builder.setMessage(R.string.impossible_import_notice) .setCancelable(false) @@ -147,6 +155,8 @@ public class GameActivity extends BaseActivity implements NavigationView.OnNavig alert.show(); } + gameController.loadLevel(container); + } else { if (extras != null) { gameType = GameType.valueOf(extras.getString("gameType", GameType.Default_9x9.name())); @@ -268,7 +278,7 @@ public class GameActivity extends BaseActivity implements NavigationView.OnNavig @Override public void onPause(){ super.onPause(); - if(!gameSolved) { + if(!gameSolved && startGame) { gameController.saveGame(this); } gameController.deleteTimer(); @@ -493,7 +503,7 @@ public class GameActivity extends BaseActivity implements NavigationView.OnNavig // display the time timerView.setText(timeToString(time)); - if(gameSolved) return; + if(gameSolved || !startGame) return; // save time gameController.saveGame(this); } From 2a92374f12b532f55aed9a05e3c6899f6e24ffa6 Mon Sep 17 00:00:00 2001 From: Erik Waegerle Date: Tue, 19 May 2020 21:50:23 +0200 Subject: [PATCH 014/219] removed not necessary permisson --- app/src/main/AndroidManifest.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index fb20384..c929de3 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,8 +3,7 @@ package="org.secuso.privacyfriendlysudoku.ui.view"> - - + Date: Tue, 19 May 2020 22:36:24 +0200 Subject: [PATCH 015/219] Add setter for QQWing's Random attribute that lets you initialize it with a seed --- .../privacyfriendlysudoku/controller/qqwing/QQWing.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/qqwing/QQWing.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/qqwing/QQWing.java index f57efd5..ee5dfb8 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/qqwing/QQWing.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/qqwing/QQWing.java @@ -199,6 +199,10 @@ public class QQWing { return reset(); } + public void setRandom(int seed) { + random = new Random(seed); + } + /** * Reset the board to its initial state with only the givens. This method * clears any solution, resets statistics, and clears any history messages. From eb5aa197f0f848716467921dda1774f37c35fb03 Mon Sep 17 00:00:00 2001 From: Erik Waegerle Date: Wed, 20 May 2020 12:32:41 +0200 Subject: [PATCH 016/219] The database will be extended by DailySudokus. A new table was created for this purpose --- .../controller/database/DatabaseHelper.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/database/DatabaseHelper.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/database/DatabaseHelper.java index 85dd131..27041b4 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/database/DatabaseHelper.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/database/DatabaseHelper.java @@ -27,6 +27,17 @@ public class DatabaseHelper extends SQLiteOpenHelper { @Override public void onCreate(SQLiteDatabase db) { + + String createTable = + "CREATE TABLE DAILYSUDOKU" + + "( DATE TEXT PRIMARY KEY " + + " , LEVEL_difficulty TEXT " + + " , LEVEL_gametype TEXT" + + " , LEVEL_time INTEGER " + + " , LEVEL_puzzle TEXT" + + ")" + ; + db.execSQL(LevelColumns.SQL_CREATE_ENTRIES); } From 6dbf66cb7f2d285d230e0e2b7cbaf16da6922d74 Mon Sep 17 00:00:00 2001 From: Erik Waegerle Date: Wed, 20 May 2020 12:45:21 +0200 Subject: [PATCH 017/219] The menu item Daily Sudoku was added to the navigation drawer --- app/src/main/res/menu/menu_drawer_main.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/menu/menu_drawer_main.xml b/app/src/main/res/menu/menu_drawer_main.xml index e4dd2f1..065b116 100644 --- a/app/src/main/res/menu/menu_drawer_main.xml +++ b/app/src/main/res/menu/menu_drawer_main.xml @@ -5,6 +5,8 @@ android:checkableBehavior="all"> + Date: Wed, 20 May 2020 18:21:09 +0200 Subject: [PATCH 018/219] Add DailySudokuColumns and DailySudoku classes, which extend LevelColumns and Level by adding the attributes "hints used" and "time needed", for the daily sudoku database table --- .../database/columns/DailySudokuColumns.java | 57 +++++++++++++++++++ .../database/model/DailySudoku.java | 31 ++++++++++ 2 files changed, 88 insertions(+) create mode 100644 app/src/main/java/org/secuso/privacyfriendlysudoku/controller/database/columns/DailySudokuColumns.java create mode 100644 app/src/main/java/org/secuso/privacyfriendlysudoku/controller/database/model/DailySudoku.java diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/database/columns/DailySudokuColumns.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/database/columns/DailySudokuColumns.java new file mode 100644 index 0000000..0c342d8 --- /dev/null +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/database/columns/DailySudokuColumns.java @@ -0,0 +1,57 @@ +package org.secuso.privacyfriendlysudoku.controller.database.columns; + +import android.content.ContentValues; +import android.database.Cursor; +import android.provider.BaseColumns; + +import org.secuso.privacyfriendlysudoku.controller.database.model.DailySudoku; +import org.secuso.privacyfriendlysudoku.controller.database.model.Level; + + + +public class DailySudokuColumns extends LevelColumns { + + public static final String TABLE_NAME = "ds_levels"; + + public static final String HINTS_USED = "ds_hints_used"; + public static final String TIME_NEEDED = "ds_time_needed"; + public static final String[] PROJECTION = { + _ID, + DIFFICULTY, + GAMETYPE, + PUZZLE, + HINTS_USED, + TIME_NEEDED + }; + + private static final String TEXT_TYPE = " TEXT "; + private static final String INTEGER_TYPE = " INTEGER "; + private static final String TIME_TYPE = " TIME (0) "; + private static final String COMMA_SEP = ","; + + public static String SQL_CREATE_ENTRIES = + "CREATE TABLE " + TABLE_NAME + " (" + + _ID + INTEGER_TYPE + " PRIMARY KEY" + COMMA_SEP + + DIFFICULTY + TEXT_TYPE + COMMA_SEP + + GAMETYPE + TEXT_TYPE + COMMA_SEP + + PUZZLE + TEXT_TYPE + COMMA_SEP + + HINTS_USED + INTEGER_TYPE + COMMA_SEP + + TIME_NEEDED + TIME_TYPE + " )"; + + + public static DailySudoku getLevel(Cursor c) { + Level level = LevelColumns.getLevel(c); + int hintsUsed = c.getInt(c.getColumnIndexOrThrow(HINTS_USED)); + String timeNeeded = c.getString(c.getColumnIndexOrThrow(TIME_NEEDED)); + return new DailySudoku(level.getId(), level.getDifficulty(), level.getGameType(), level.getPuzzle(), hintsUsed, timeNeeded); + } + + public static ContentValues getValues(DailySudoku record) { + ContentValues result = LevelColumns.getValues(record); + result.put(HINTS_USED, record.getHintsUsed()); + result.put(TIME_NEEDED, record.getTimeNeeded()); + + return result; + } + +} diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/database/model/DailySudoku.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/database/model/DailySudoku.java new file mode 100644 index 0000000..f8546c3 --- /dev/null +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/database/model/DailySudoku.java @@ -0,0 +1,31 @@ +package org.secuso.privacyfriendlysudoku.controller.database.model; + +import org.secuso.privacyfriendlysudoku.game.GameDifficulty; +import org.secuso.privacyfriendlysudoku.game.GameType; + +public class DailySudoku extends Level { + private int hintsUsed; + private String timeNeeded; + + public DailySudoku(int id, GameDifficulty gameDifficulty, GameType gameType, int[] puzzle, int hintsUsed, String timeNeeded) { + super(id, gameDifficulty, gameType, puzzle); + this.hintsUsed = hintsUsed; + this.timeNeeded = timeNeeded; + } + + public int getHintsUsed() { + return hintsUsed; + } + + public void setHintsUsed(int hintsUsed) { + this.hintsUsed = hintsUsed; + } + + public String getTimeNeeded() { + return timeNeeded; + } + + public void setTimeNeeded(String timeNeeded) { + this.timeNeeded = timeNeeded; + } +} From 8b8f72b2e72acb4a08743a688bcb5f9789301685 Mon Sep 17 00:00:00 2001 From: uykek Date: Thu, 21 May 2020 12:33:32 +0200 Subject: [PATCH 019/219] Add methods to fetch and save daily sudokus to the DatabaseHelper class --- .../controller/database/DatabaseHelper.java | 47 ++++++++++++++----- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/database/DatabaseHelper.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/database/DatabaseHelper.java index 27041b4..2e740c2 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/database/DatabaseHelper.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/database/DatabaseHelper.java @@ -6,7 +6,9 @@ import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; +import org.secuso.privacyfriendlysudoku.controller.database.columns.DailySudokuColumns; import org.secuso.privacyfriendlysudoku.controller.database.columns.LevelColumns; +import org.secuso.privacyfriendlysudoku.controller.database.model.DailySudoku; import org.secuso.privacyfriendlysudoku.controller.database.model.Level; import org.secuso.privacyfriendlysudoku.game.GameDifficulty; import org.secuso.privacyfriendlysudoku.game.GameType; @@ -27,22 +29,13 @@ public class DatabaseHelper extends SQLiteOpenHelper { @Override public void onCreate(SQLiteDatabase db) { - - String createTable = - "CREATE TABLE DAILYSUDOKU" + - "( DATE TEXT PRIMARY KEY " + - " , LEVEL_difficulty TEXT " + - " , LEVEL_gametype TEXT" + - " , LEVEL_time INTEGER " + - " , LEVEL_puzzle TEXT" + - ")" - ; - db.execSQL(LevelColumns.SQL_CREATE_ENTRIES); + db.execSQL(DailySudokuColumns.SQL_CREATE_ENTRIES); } public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL(LevelColumns.SQL_DELETE_ENTRIES); + db.execSQL(DailySudokuColumns.SQL_DELETE_ENTRIES); onCreate(db); } @@ -91,6 +84,33 @@ public class DatabaseHelper extends SQLiteOpenHelper { return levelList.get(0); } + public synchronized List getDailySudokus() { + List dailySudokuList = new LinkedList<>(); + SQLiteDatabase database = getWritableDatabase(); + String order = DailySudokuColumns._ID + " DESC"; + + // How you want the results sorted in the resulting Cursor + Cursor c = database.query( + DailySudokuColumns.TABLE_NAME, // The table to query + DailySudokuColumns.PROJECTION, // The columns to return + null, // select all rows + null, // select all rows + null, // don't group the rows + null, // don't filter by row groups + order // The sort order + ); + + if (c != null) { + while(c.moveToNext()) { + dailySudokuList.add(DailySudokuColumns.getLevel(c)); + } + } + + c.close(); + return dailySudokuList; + + } + public synchronized void deleteLevel(int id) { SQLiteDatabase database = getWritableDatabase(); @@ -104,5 +124,10 @@ public class DatabaseHelper extends SQLiteOpenHelper { SQLiteDatabase database = getWritableDatabase(); return database.insert(LevelColumns.TABLE_NAME, null, LevelColumns.getValues(level)); } + + public synchronized long addDailySudoku(DailySudoku ds) { + SQLiteDatabase database = getWritableDatabase(); + return database.insert(DailySudokuColumns.TABLE_NAME, null, DailySudokuColumns.getValues(ds)); + } } From 9c31f0d869a27da1720dc77bf3da37aff07ed34b Mon Sep 17 00:00:00 2001 From: uykek Date: Fri, 22 May 2020 14:50:59 +0200 Subject: [PATCH 020/219] Update database version --- .../controller/database/DatabaseHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/database/DatabaseHelper.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/database/DatabaseHelper.java index 2e740c2..55ac340 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/database/DatabaseHelper.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/database/DatabaseHelper.java @@ -20,7 +20,7 @@ import java.util.List; public class DatabaseHelper extends SQLiteOpenHelper { - public static final int DATABASE_VERSION = 1; + public static final int DATABASE_VERSION = 2; public static final String DATABASE_NAME = "Database.db"; public DatabaseHelper(Context context) { From a2c52f0891bc1b4d4fd8e9a9647567116a5c7793 Mon Sep 17 00:00:00 2001 From: uykek Date: Mon, 25 May 2020 16:53:34 +0200 Subject: [PATCH 021/219] Reserve maximum id to identify daily sudoku within app, add method which saves a sudoku to the daily sudoku data base using the current date as its id --- .../controller/GameController.java | 26 ++++++++++++++++++- .../ui/GameActivity.java | 8 +++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/GameController.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/GameController.java index b400d60..f2bb7f0 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/GameController.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/GameController.java @@ -6,6 +6,8 @@ import android.os.Handler; import android.os.Parcel; import android.os.Parcelable; +import org.secuso.privacyfriendlysudoku.controller.database.DatabaseHelper; +import org.secuso.privacyfriendlysudoku.controller.database.model.DailySudoku; import org.secuso.privacyfriendlysudoku.controller.helper.GameInfoContainer; import org.secuso.privacyfriendlysudoku.game.CellConflict; import org.secuso.privacyfriendlysudoku.game.CellConflictList; @@ -19,7 +21,10 @@ import org.secuso.privacyfriendlysudoku.game.listener.IHighlightChangedListener; import org.secuso.privacyfriendlysudoku.game.listener.IHintListener; import org.secuso.privacyfriendlysudoku.game.listener.IModelChangedListener; import org.secuso.privacyfriendlysudoku.game.listener.ITimerListener; +import org.secuso.privacyfriendlysudoku.ui.GameActivity; +import java.util.Calendar; +import java.util.Date; import java.util.LinkedList; import java.util.List; import java.util.Timer; @@ -32,6 +37,7 @@ import java.util.concurrent.atomic.AtomicBoolean; public class GameController implements IModelChangedListener, Parcelable { // General + public static final int DAILY_SUDOKU_ID = Integer.MAX_VALUE - 1; private SharedPreferences settings; // View @@ -305,7 +311,7 @@ public class GameController implements IModelChangedListener, Parcelable { SharedPreferences.Editor editor = settings.edit(); // is anyone ever gonna play so many levels? :) - if(gameID == Integer.MAX_VALUE-1) { + if(gameID == DAILY_SUDOKU_ID - 1) { editor.putInt("lastGameID", 1); } else { editor.putInt("lastGameID", gameID); @@ -318,6 +324,24 @@ public class GameController implements IModelChangedListener, Parcelable { fm.saveGameState(this); } + public void saveDailySudoku(Context context) { + int amountOfCells = size * size; + int[] encodedBoard = new int[amountOfCells]; + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + encodedBoard[i * size + j] = gameBoard.getCell(i, j).getValue(); + } + } + + Calendar currentDate = Calendar.getInstance(); + int id = currentDate.get(Calendar.DAY_OF_MONTH) * 1000000 + + (currentDate.get(Calendar.MONTH) + 1) * 10000 + currentDate.get(Calendar.YEAR); + + DatabaseHelper db = new DatabaseHelper(context); + DailySudoku dailySudoku = new DailySudoku(id, difficulty, gameType, encodedBoard, usedHints, GameActivity.timeToString(time)); + db.addDailySudoku(dailySudoku); + } + public void deleteGame(Context context) { if(gameID == 0) { throw new IllegalArgumentException("GameID may not be 0."); diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java index 8b2bd2d..7481a83 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java @@ -369,6 +369,7 @@ public class GameActivity extends BaseActivity implements NavigationView.OnNavig } }); shareDialog.show(getFragmentManager(), "ShareDialogFragment"); + break; case R.id.nav_newgame: @@ -435,6 +436,11 @@ public class GameActivity extends BaseActivity implements NavigationView.OnNavig gameController.deleteGame(this); disableReset(); + //Save solved sudoku, if it happens to be a daily sudoku, to daily sudoku database + if(gameController.getGameID() == GameController.DAILY_SUDOKU_ID) { + gameController.saveDailySudoku(GameActivity.this); + } + //Show time hints new plus old best time statistics.saveGameStats(); @@ -480,7 +486,7 @@ public class GameActivity extends BaseActivity implements NavigationView.OnNavig specialButtonLayout.setButtonsEnabled(false); } - public String timeToString(int time) { + public static String timeToString(int time) { int seconds = time % 60; int minutes = ((time - seconds) / 60) % 60; int hours = (time - minutes - seconds) / (3600); From 5ed7b5542a1b3e17aad27bcfdbc8181a59ae5e83 Mon Sep 17 00:00:00 2001 From: ErikWaegerle Date: Mon, 25 May 2020 19:42:42 +0200 Subject: [PATCH 022/219] Customization of the icon in the NavigationDrawer menu --- app/src/main/res/menu/menu_drawer_main.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/menu/menu_drawer_main.xml b/app/src/main/res/menu/menu_drawer_main.xml index 065b116..f8e8a43 100644 --- a/app/src/main/res/menu/menu_drawer_main.xml +++ b/app/src/main/res/menu/menu_drawer_main.xml @@ -5,7 +5,7 @@ android:checkableBehavior="all"> - From 514750650d04cb094b2560f3865110d905f4608e Mon Sep 17 00:00:00 2001 From: ErikWaegerle Date: Mon, 25 May 2020 19:47:34 +0200 Subject: [PATCH 023/219] Inserting a new activity in the Manifest for the Daily Sudoku. --- app/src/main/AndroidManifest.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c929de3..a1cc005 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -30,6 +30,11 @@ android:label="@string/app_name" android:theme="@style/AppTheme.NoActionBar"> + + Date: Mon, 25 May 2020 20:04:02 +0200 Subject: [PATCH 024/219] Extension of the method goToNavigateItem() with dailySudoku Item. Adds NavigationDrawer functionality. --- .../org/secuso/privacyfriendlysudoku/ui/MainActivity.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/MainActivity.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/MainActivity.java index 70878d3..4a378c4 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/MainActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/MainActivity.java @@ -30,6 +30,7 @@ import org.secuso.privacyfriendlysudoku.controller.NewLevelManager; import org.secuso.privacyfriendlysudoku.controller.helper.GameInfoContainer; import org.secuso.privacyfriendlysudoku.game.GameDifficulty; import org.secuso.privacyfriendlysudoku.game.GameType; +import org.secuso.privacyfriendlysudoku.ui.view.DailySudokuActivity; import org.secuso.privacyfriendlysudoku.ui.view.R; import java.util.LinkedList; @@ -330,6 +331,11 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig overridePendingTransition(0, 0); break; + case R.id.nav_dailySudoku_main: + intent = new Intent(this, DailySudokuActivity.class); + startActivity(intent); + break; + default: } return true; From dd9a66eb8cdcf160234d9a4268d253de2bcf99e9 Mon Sep 17 00:00:00 2001 From: ErikWaegerle Date: Mon, 25 May 2020 20:12:50 +0200 Subject: [PATCH 025/219] 1.) Creating a Daily Sudoku Activity XML 2.) Adaptation of the layout 3.) Inserting the app and toolbar into the layout 4.) Use the layout of fragment_stats.xml 5.) Adaptation and modification of the layout see Mockup 3 --- .../main/res/layout/activity_daily_sudoku.xml | 417 ++++++++++++++++++ 1 file changed, 417 insertions(+) create mode 100644 app/src/main/res/layout/activity_daily_sudoku.xml diff --git a/app/src/main/res/layout/activity_daily_sudoku.xml b/app/src/main/res/layout/activity_daily_sudoku.xml new file mode 100644 index 0000000..16983d5 --- /dev/null +++ b/app/src/main/res/layout/activity_daily_sudoku.xml @@ -0,0 +1,417 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From f9744bc894190016eb8b8714b83c7bfcf045bcf1 Mon Sep 17 00:00:00 2001 From: ErikWaegerle Date: Mon, 25 May 2020 20:16:16 +0200 Subject: [PATCH 026/219] Embedding a vector graphic for the tab "Daily Sudoku" in the NavigationDrawer menu --- app/src/main/res/drawable/ic_menu_daily_sudoku.xml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 app/src/main/res/drawable/ic_menu_daily_sudoku.xml diff --git a/app/src/main/res/drawable/ic_menu_daily_sudoku.xml b/app/src/main/res/drawable/ic_menu_daily_sudoku.xml new file mode 100644 index 0000000..2239a4f --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_daily_sudoku.xml @@ -0,0 +1,9 @@ + + + From 5d620829c446378f0fc52c55a11011cbe46cab0e Mon Sep 17 00:00:00 2001 From: ErikWaegerle Date: Mon, 25 May 2020 20:21:12 +0200 Subject: [PATCH 027/219] 1.) Creating the class DailySudokuActivity 2.) Implementing the onCreate() method --- .../ui/view/DailySudokuActivity.java | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/DailySudokuActivity.java diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/DailySudokuActivity.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/DailySudokuActivity.java new file mode 100644 index 0000000..96aeba9 --- /dev/null +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/DailySudokuActivity.java @@ -0,0 +1,32 @@ +package org.secuso.privacyfriendlysudoku.ui.view; + +import android.content.SharedPreferences; +import android.os.Bundle; +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; +import org.secuso.privacyfriendlysudoku.controller.helper.GameInfoContainer; +import java.util.List; + +public class DailySudokuActivity extends AppCompatActivity { + + List loadableGameList; + SharedPreferences settings; + + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.activity_daily_sudoku); + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + + + androidx.appcompat.app.ActionBar actionBar = getSupportActionBar(); + actionBar.setTitle("Daily Sudoku"); + actionBar.setDisplayHomeAsUpEnabled(true); + + + + } +} \ No newline at end of file From b41ff4b238b7740984eb9862e2a4123dbfd84819 Mon Sep 17 00:00:00 2001 From: ErikWaegerle Date: Tue, 26 May 2020 01:13:13 +0200 Subject: [PATCH 028/219] Changes to the layout 1.) Increase the number of stars to 4 2.) Creation of the button "Start DailySudoku" 3.) Positioning of the individual elements in the layout --- .../main/res/layout/activity_daily_sudoku.xml | 850 ++++++++++-------- 1 file changed, 479 insertions(+), 371 deletions(-) diff --git a/app/src/main/res/layout/activity_daily_sudoku.xml b/app/src/main/res/layout/activity_daily_sudoku.xml index 16983d5..18bfd7d 100644 --- a/app/src/main/res/layout/activity_daily_sudoku.xml +++ b/app/src/main/res/layout/activity_daily_sudoku.xml @@ -1,417 +1,525 @@ - + app:statusBarBackground="?attr/colorPrimary"> - - - - - - - - - - - + app:layout_constraintBottom_toTopOf="@+id/main_content" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent"> + + + + + + + + + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + android:paddingLeft="@dimen/activity_horizontal_margin" + android:paddingTop="@dimen/activity_vertical_margin" + android:paddingRight="@dimen/activity_horizontal_margin" + android:paddingBottom="@dimen/activity_vertical_margin" + android:weightSum="10" + tools:context="org.secuso.privacyfriendlysudoku.ui.StatsActivity$PlaceholderFragment"> - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + android:foregroundGravity="center_vertical" /> + + - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - + android:weightSum="3"> - - - - - - - - - - - - - + android:gravity="center_vertical"> - - - - + + - - - - - - - - - - - - - + android:layout_weight="1" + android:gravity="center_vertical"> - - - - + - - - - + + - - - - - - - - + + + + + + + + + + + - + android:gravity="center_vertical"> - - - - + + - + + + + + + + + + + + + + + + + - - - + android:weightSum="3"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + android:layout_width="wrap_content" + android:layout_height="0dp" + app:layout_behavior="@string/appbar_scrolling_view_behavior" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toStartOf="@+id/linearLayout6" + app:layout_constraintTop_toBottomOf="@+id/linearLayout6" > + + - \ No newline at end of file + \ No newline at end of file From b863c7a88cb96580ccc6fe47836ea235074655fe Mon Sep 17 00:00:00 2001 From: ErikWaegerle Date: Tue, 26 May 2020 16:43:18 +0200 Subject: [PATCH 029/219] Implementing/adding the functionality of the "Menu Button" and the "Back Button" in the ActionBar. --- .../ui/view/DailySudokuActivity.java | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/DailySudokuActivity.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/DailySudokuActivity.java index 96aeba9..aa86113 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/DailySudokuActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/DailySudokuActivity.java @@ -1,16 +1,22 @@ package org.secuso.privacyfriendlysudoku.ui.view; + import android.content.SharedPreferences; import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; +import org.secuso.privacyfriendlysudoku.controller.SaveLoadStatistics; import org.secuso.privacyfriendlysudoku.controller.helper.GameInfoContainer; +import org.secuso.privacyfriendlysudoku.ui.StatsActivity; import java.util.List; public class DailySudokuActivity extends AppCompatActivity { List loadableGameList; SharedPreferences settings; + private StatsActivity.SectionsPagerAdapter mSectionsPagerAdapter; @Override @@ -29,4 +35,32 @@ public class DailySudokuActivity extends AppCompatActivity { } + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.menu_stats, menu); + //getMenuInflater().inflate(R.menu.menu_stats, menu); + return true; + //return false; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + + //noinspection SimplifiableIfStatement + switch(item.getItemId()) { + case R.id.action_reset: + SaveLoadStatistics.resetStats(this); + mSectionsPagerAdapter.refresh(this); + return true; + case android.R.id.home: + finish(); + return true; + } + + return super.onOptionsItemSelected(item); + } + } \ No newline at end of file From 88a7f7813e02fbfdcb2175f4285dda08920cb17d Mon Sep 17 00:00:00 2001 From: uykek Date: Tue, 26 May 2020 20:15:33 +0200 Subject: [PATCH 030/219] Add methods which generate a fixed sudoku based on the current date --- .../controller/NewLevelManager.java | 12 ++++++++++++ .../controller/QQWingController.java | 14 ++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/NewLevelManager.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/NewLevelManager.java index b74319b..b656bb5 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/NewLevelManager.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/NewLevelManager.java @@ -15,6 +15,9 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; import java.util.LinkedList; import java.util.List; import java.util.Random; @@ -75,6 +78,15 @@ public class NewLevelManager { return false; } + public int[] loadDailySudoku() { + DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy"); + Date date = new Date(); + String toHash = "Sudoku/.PrivacyFriendly/." + dateFormat.format(date); + + QQWingController controller = new QQWingController(); + return controller.generateFromSeed(toHash.hashCode()); + } + public int[] loadLevel(GameType type, GameDifficulty diff) { Level level = dbHelper.getLevel(diff, type); dbHelper.deleteLevel(level.getId()); diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/QQWingController.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/QQWingController.java index 2a2f1d4..2f44929 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/QQWingController.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/QQWingController.java @@ -53,6 +53,20 @@ public class QQWingController { return generated; } + public int[] generateFromSeed(int seed) { + generated.clear(); + + QQWing generator = new QQWing(GameType.Default_9x9, GameDifficulty.Unspecified); + generator.setRandom(seed); + generator.setRecordHistory(true); + generator.generatePuzzle(); + + generated.add(generator.getPuzzle()); + opts.gameType = GameType.Default_9x9; + opts.gameDifficulty = generator.getDifficulty(); + return generated.poll(); + } + public int[] solve(GameBoard gameBoard) { level = new int[gameBoard.getSize()*gameBoard.getSize()]; From 9f999b5512b090dfd0b621a28154cd71f7cbd9a6 Mon Sep 17 00:00:00 2001 From: ErikWaegerle Date: Wed, 27 May 2020 20:04:08 +0200 Subject: [PATCH 031/219] Added the method loadNewDailySudokuLevel. Is responsible for loading the DailySudoku. --- .../controller/GameController.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/GameController.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/GameController.java index f2bb7f0..e58ddd3 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/GameController.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/GameController.java @@ -111,6 +111,17 @@ public class GameController implements IModelChangedListener, Parcelable { newLevelManager.checkAndRestock(); } + public void loadNewDailySudokuLevel(GameDifficulty gameDifficulty) { + NewLevelManager newLevelManager = NewLevelManager.getInstance(context, settings); + + int[] level = newLevelManager.loadDailySudoku(); + + loadLevel(new GameInfoContainer(DAILY_SUDOKU_ID, gameDifficulty, GameType.Default_9x9, level, null, null)); + + newLevelManager.checkAndRestock(); + + } + public int getTime() { return time; } From 1dc60679d38c6993bb6521de6ef33e4e48cd2ede Mon Sep 17 00:00:00 2001 From: ErikWaegerle Date: Wed, 27 May 2020 20:15:28 +0200 Subject: [PATCH 032/219] Modify the onCreate() method to ensure that only in case of the DailySudoku the DailySudoku is loaded --- .../ui/GameActivity.java | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java index 7481a83..441e2ae 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java @@ -55,7 +55,7 @@ import org.secuso.privacyfriendlysudoku.ui.view.SudokuSpecialButtonLayout; import org.secuso.privacyfriendlysudoku.ui.view.WinDialog; import org.secuso.privacyfriendlysudoku.ui.view.databinding.DialogFragmentShareBoardBinding; -import java.util.Date; + import java.util.LinkedList; import java.util.List; @@ -158,23 +158,29 @@ public class GameActivity extends BaseActivity implements NavigationView.OnNavig gameController.loadLevel(container); } else { + boolean isDailySudoku = false; if (extras != null) { gameType = GameType.valueOf(extras.getString("gameType", GameType.Default_9x9.name())); gameDifficulty = GameDifficulty.valueOf(extras.getString("gameDifficulty", GameDifficulty.Moderate.name())); + isDailySudoku = extras.getBoolean("isDailySudoku", false); loadLevel = extras.getBoolean("loadLevel", false); if (loadLevel) { loadLevelID = extras.getInt("loadLevelID"); } } + if (isDailySudoku) { + gameController.loadNewDailySudokuLevel(gameDifficulty); + } else { - List loadableGames = GameStateManager.getLoadableGameList(); + List loadableGames = GameStateManager.getLoadableGameList(); - if (loadLevel && loadableGames.size() > loadLevelID) { - // load level from GameStateManager - gameController.loadLevel(loadableGames.get(loadLevelID)); - } else { - // load a new level - gameController.loadNewLevel(gameType, gameDifficulty); + if (loadLevel && loadableGames.size() > loadLevelID) { + // load level from GameStateManager + gameController.loadLevel(loadableGames.get(loadLevelID)); + } else { + // load a new level + gameController.loadNewLevel(gameType, gameDifficulty); + } } } } else { From e1ded11056d64e5a7e052c1027972725a823f021 Mon Sep 17 00:00:00 2001 From: uykek Date: Wed, 27 May 2020 20:36:58 +0200 Subject: [PATCH 033/219] Fix bug that caused an exception if the app tries to upgrade the database --- .../controller/database/columns/DailySudokuColumns.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/database/columns/DailySudokuColumns.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/database/columns/DailySudokuColumns.java index 0c342d8..248371a 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/database/columns/DailySudokuColumns.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/database/columns/DailySudokuColumns.java @@ -38,6 +38,9 @@ public class DailySudokuColumns extends LevelColumns { HINTS_USED + INTEGER_TYPE + COMMA_SEP + TIME_NEEDED + TIME_TYPE + " )"; + public static String SQL_DELETE_ENTRIES = + "DROP TABLE IF EXISTS " + TABLE_NAME; + public static DailySudoku getLevel(Cursor c) { Level level = LevelColumns.getLevel(c); From 56db7277caecebab1df4622f11d47108e9c70930 Mon Sep 17 00:00:00 2001 From: uykek Date: Wed, 27 May 2020 20:50:42 +0200 Subject: [PATCH 034/219] Exclude daily sudoku from the sudokus displayed in the LoadGameActivity --- .../privacyfriendlysudoku/ui/LoadGameActivity.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/LoadGameActivity.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/LoadGameActivity.java index f04fa0b..5549373 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/LoadGameActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/LoadGameActivity.java @@ -21,7 +21,9 @@ import android.widget.ListView; import android.widget.RatingBar; import android.widget.TextView; +import org.secuso.privacyfriendlysudoku.controller.GameController; import org.secuso.privacyfriendlysudoku.controller.GameStateManager; +import org.secuso.privacyfriendlysudoku.controller.NewLevelManager; import org.secuso.privacyfriendlysudoku.controller.helper.GameInfoContainer; import org.secuso.privacyfriendlysudoku.game.GameDifficulty; import org.secuso.privacyfriendlysudoku.ui.listener.IDeleteDialogFragmentListener; @@ -70,6 +72,13 @@ public class LoadGameActivity extends BaseActivity implements IDeleteDialogFragm GameStateManager gameStateManager = new GameStateManager(this, settings); loadableGameList = gameStateManager.loadGameStateInfo(); + for (GameInfoContainer container : loadableGameList) { + if (container.getID() == GameController.DAILY_SUDOKU_ID) { + loadableGameList.remove(container); + break; + } + } + AdapterView.OnItemClickListener clickListener = new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { From 644358ced8d9b3aad66805cadcd1e17af9b3eaff Mon Sep 17 00:00:00 2001 From: ErikWaegerle Date: Wed, 27 May 2020 21:41:40 +0200 Subject: [PATCH 035/219] Adjustment of the layout by adding the TextView from list_entry_layout.xml Is responsible for displaying the games already completed in Daily Sudoku. --- .../main/res/layout/activity_daily_sudoku.xml | 315 ++---------------- 1 file changed, 23 insertions(+), 292 deletions(-) diff --git a/app/src/main/res/layout/activity_daily_sudoku.xml b/app/src/main/res/layout/activity_daily_sudoku.xml index 18bfd7d..308b70d 100644 --- a/app/src/main/res/layout/activity_daily_sudoku.xml +++ b/app/src/main/res/layout/activity_daily_sudoku.xml @@ -52,7 +52,7 @@ + android:textSize="32dp" /> @@ -192,7 +200,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -505,6 +235,7 @@ android:layout_marginRight="30dp" android:layout_marginBottom="290dp" android:clickable="true" + android:onClick="onClick" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:srcCompat="@android:drawable/ic_media_play" /> From 29933c01eb9fc3558d1251fd944ab62128a306f6 Mon Sep 17 00:00:00 2001 From: uykek Date: Wed, 27 May 2020 21:44:11 +0200 Subject: [PATCH 036/219] Allow eleven (instead of ten) sudokus to be saved by the GameStateManager if one of them is the daily sudoku --- .../controller/GameStateManager.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/GameStateManager.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/GameStateManager.java index bebec31..c50ba6c 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/GameStateManager.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/GameStateManager.java @@ -20,6 +20,7 @@ public class GameStateManager { Context context; private SharedPreferences settings; + private boolean includesDaily; private static String FILE_EXTENSION = ".txt"; private static String SAVE_PREFIX = "save_"; @@ -87,6 +88,11 @@ public class GameStateManager { gic.parseSetValues(values[i++]); gic.parseNotes(values[i++]); gic.parseHintsUsed(values[i++]); + + if (gic.getID() == GameController.DAILY_SUDOKU_ID) { + includesDaily = true; + } + } catch(IllegalArgumentException e) { file.delete(); continue; @@ -108,7 +114,7 @@ public class GameStateManager { LinkedList removeList = new LinkedList<>(); for(int i = 0; i < list.size(); i++) { - if(i >= MAX_NUM_OF_SAVED_GAMES) { + if((i >= MAX_NUM_OF_SAVED_GAMES && !includesDaily) || i > MAX_NUM_OF_SAVED_GAMES) { deleteGameStateFile(list.get(i)); removeList.add(list.get(i)); } From f179518f5aa659c10dc7e7068f599131c60965e4 Mon Sep 17 00:00:00 2001 From: ErikWaegerle Date: Wed, 27 May 2020 21:58:32 +0200 Subject: [PATCH 037/219] Adding the onClick() method, the SudokuListAdapter() and the getView() method This adds functionality to the button and the customization/implementation to display the completed DailySudoku in list form. --- .../ui/view/DailySudokuActivity.java | 107 +++++++++++++++++- 1 file changed, 104 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/DailySudokuActivity.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/DailySudokuActivity.java index aa86113..3cc73d9 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/DailySudokuActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/DailySudokuActivity.java @@ -1,22 +1,44 @@ package org.secuso.privacyfriendlysudoku.ui.view; +import android.content.Context; +import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; +import android.os.Handler; +import android.preference.PreferenceManager; +import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ImageView; +import android.widget.ListView; +import android.widget.TextView; +import android.view.View; +import android.widget.RatingBar; +import org.secuso.privacyfriendlysudoku.controller.database.DatabaseHelper; +import org.secuso.privacyfriendlysudoku.controller.database.model.DailySudoku; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; import org.secuso.privacyfriendlysudoku.controller.SaveLoadStatistics; -import org.secuso.privacyfriendlysudoku.controller.helper.GameInfoContainer; +import org.secuso.privacyfriendlysudoku.game.GameDifficulty; +import org.secuso.privacyfriendlysudoku.ui.GameActivity; import org.secuso.privacyfriendlysudoku.ui.StatsActivity; import java.util.List; -public class DailySudokuActivity extends AppCompatActivity { - List loadableGameList; +public class DailySudokuActivity extends AppCompatActivity { + + List sudokuList; SharedPreferences settings; + private final DatabaseHelper dbHelper = new DatabaseHelper(this); + RatingBar difficultyBar; + static final int MAIN_CONTENT_FADEOUT_DURATION = 150; + static final int MAIN_CONTENT_FADEIN_DURATION = 250; + private Handler mHandler; private StatsActivity.SectionsPagerAdapter mSectionsPagerAdapter; + private SudokuListAdapter sudokuListAdapter; @Override @@ -28,13 +50,39 @@ public class DailySudokuActivity extends AppCompatActivity { setSupportActionBar(toolbar); + List sudokus = dbHelper.getDailySudokus(); + TextView tw = findViewById(R.id.first_diff_text); + tw.setText(String.valueOf(sudokus.size())); + androidx.appcompat.app.ActionBar actionBar = getSupportActionBar(); actionBar.setTitle("Daily Sudoku"); actionBar.setDisplayHomeAsUpEnabled(true); + difficultyBar = findViewById(R.id.first_diff_bar); + settings = PreferenceManager.getDefaultSharedPreferences(this); + mHandler = new Handler(); + sudokuList = dbHelper.getDailySudokus(); + + ListView listView = (ListView)findViewById(R.id.sudoku_list); + sudokuListAdapter = new DailySudokuActivity.SudokuListAdapter(this, sudokuList); + listView.setAdapter(sudokuListAdapter); } + + public void onClick(View view) { + + int index = difficultyBar.getProgress()-1; + GameDifficulty gameDifficulty = GameDifficulty.getValidDifficultyList().get(index < 0 ? 0 : index); + //send everything to game activity + final Intent intent = new Intent(this,GameActivity.class); + intent.putExtra("gameDifficulty", gameDifficulty.name()); + intent.putExtra("isDailySudoku", true); + + startActivity(intent); + + } + public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.menu_stats, menu); @@ -63,4 +111,57 @@ public class DailySudokuActivity extends AppCompatActivity { return super.onOptionsItemSelected(item); } + private class SudokuListAdapter extends BaseAdapter { + + private Context context; + private List sudokuList; + + public SudokuListAdapter(Context context, List sudokuList) { + this.context = context; + this.sudokuList = sudokuList; + } + + @Override + public int getCount() { + return sudokuList.size(); + } + + @Override + public Object getItem(int position) { + return sudokuList.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + DailySudoku sudoku = sudokuList.get(position); + + if (convertView == null) { + LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + convertView = (View) inflater.inflate(R.layout.list_entry_layout, null); + } + + TextView gameType = (TextView)convertView.findViewById(R.id.loadgame_listentry_gametype); + TextView difficulty =(TextView)convertView.findViewById(R.id.loadgame_listentry_difficultytext); + RatingBar difficultyBar =(RatingBar)convertView.findViewById(R.id.loadgame_listentry_difficultybar); + TextView playedTime = (TextView)convertView.findViewById(R.id.loadgame_listentry_timeplayed); + TextView lastTimePlayed = (TextView)convertView.findViewById(R.id.loadgame_listentry_lasttimeplayed); + ImageView image = (ImageView)convertView.findViewById(R.id.loadgame_listentry_gametypeimage); + + + image.setImageResource(R.drawable.icon_default_9x9); + + gameType.setText(sudoku.getGameType().getStringResID()); + difficulty.setText(sudoku.getDifficulty().getStringResID()); + difficultyBar.setNumStars(GameDifficulty.getValidDifficultyList().size()); + difficultyBar.setMax(GameDifficulty.getValidDifficultyList().size()); + difficultyBar.setRating(GameDifficulty.getValidDifficultyList().indexOf(sudoku.getDifficulty())+1); + + return convertView; + } + } } \ No newline at end of file From 94b7b82bb45aec081cd24d2ed9c07f3565bf88cf Mon Sep 17 00:00:00 2001 From: uykek Date: Wed, 27 May 2020 23:09:09 +0200 Subject: [PATCH 038/219] Only calculate the daily sudoku once a day; after the DailySudokuActivity's floating action button has been pressed for the first time a day, load the daily sudoku from the 'saves' directory --- .../ui/GameActivity.java | 15 +++++++++--- .../ui/view/DailySudokuActivity.java | 23 ++++++++++++++++--- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java index 441e2ae..df80fc6 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java @@ -174,9 +174,18 @@ public class GameActivity extends BaseActivity implements NavigationView.OnNavig List loadableGames = GameStateManager.getLoadableGameList(); - if (loadLevel && loadableGames.size() > loadLevelID) { - // load level from GameStateManager - gameController.loadLevel(loadableGames.get(loadLevelID)); + if (loadLevel) { + if (loadableGames.size() > loadLevelID) { + // load level from GameStateManager + gameController.loadLevel(loadableGames.get(loadLevelID)); + } else if (loadLevelID == GameController.DAILY_SUDOKU_ID) { + for (GameInfoContainer container : loadableGames) { + if (container.getID() == loadLevelID) { + gameController.loadLevel(container); + break; + } + } + } } else { // load a new level gameController.loadNewLevel(gameType, gameDifficulty); diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/DailySudokuActivity.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/DailySudokuActivity.java index 3cc73d9..02eed27 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/DailySudokuActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/DailySudokuActivity.java @@ -17,6 +17,8 @@ import android.widget.ListView; import android.widget.TextView; import android.view.View; import android.widget.RatingBar; + +import org.secuso.privacyfriendlysudoku.controller.GameController; import org.secuso.privacyfriendlysudoku.controller.database.DatabaseHelper; import org.secuso.privacyfriendlysudoku.controller.database.model.DailySudoku; import androidx.appcompat.app.AppCompatActivity; @@ -25,6 +27,8 @@ import org.secuso.privacyfriendlysudoku.controller.SaveLoadStatistics; import org.secuso.privacyfriendlysudoku.game.GameDifficulty; import org.secuso.privacyfriendlysudoku.ui.GameActivity; import org.secuso.privacyfriendlysudoku.ui.StatsActivity; + +import java.util.Calendar; import java.util.List; @@ -74,13 +78,26 @@ public class DailySudokuActivity extends AppCompatActivity { int index = difficultyBar.getProgress()-1; GameDifficulty gameDifficulty = GameDifficulty.getValidDifficultyList().get(index < 0 ? 0 : index); + //send everything to game activity + Calendar currentDate = Calendar.getInstance(); + int id = currentDate.get(Calendar.DAY_OF_MONTH) * 1000000 + + (currentDate.get(Calendar.MONTH) + 1) * 10000 + currentDate.get(Calendar.YEAR); final Intent intent = new Intent(this,GameActivity.class); - intent.putExtra("gameDifficulty", gameDifficulty.name()); - intent.putExtra("isDailySudoku", true); + + if (settings.getInt("lastPlayed", 0) == id) { + intent.putExtra("loadLevel", true); + intent.putExtra("loadLevelID", GameController.DAILY_SUDOKU_ID); + } else { + SharedPreferences.Editor editor = settings.edit(); + editor.putInt("lastPlayed", id); + editor.apply(); + + intent.putExtra("gameDifficulty", gameDifficulty.name()); + intent.putExtra("isDailySudoku", true); + } startActivity(intent); - } public boolean onCreateOptionsMenu(Menu menu) { From 861015ae6684d186431784e8aafbdcc85b711544 Mon Sep 17 00:00:00 2001 From: uykek Date: Wed, 27 May 2020 23:22:26 +0200 Subject: [PATCH 039/219] Stop player from playing the daily sudoku again if they have solved it already. --- .../privacyfriendlysudoku/ui/GameActivity.java | 4 ++++ .../ui/view/DailySudokuActivity.java | 17 +++++++++++------ app/src/main/res/values-de/strings.xml | 3 +++ app/src/main/res/values/strings.xml | 4 ++++ 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java index df80fc6..9512ce5 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java @@ -454,6 +454,10 @@ public class GameActivity extends BaseActivity implements NavigationView.OnNavig //Save solved sudoku, if it happens to be a daily sudoku, to daily sudoku database if(gameController.getGameID() == GameController.DAILY_SUDOKU_ID) { gameController.saveDailySudoku(GameActivity.this); + SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this); + SharedPreferences.Editor editor = sharedPref.edit(); + editor.putBoolean("finishedForToday", true); + editor.apply(); } //Show time hints new plus old best time diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/DailySudokuActivity.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/DailySudokuActivity.java index 02eed27..e5197fe 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/DailySudokuActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/DailySudokuActivity.java @@ -17,6 +17,7 @@ import android.widget.ListView; import android.widget.TextView; import android.view.View; import android.widget.RatingBar; +import android.widget.Toast; import org.secuso.privacyfriendlysudoku.controller.GameController; import org.secuso.privacyfriendlysudoku.controller.database.DatabaseHelper; @@ -85,19 +86,23 @@ public class DailySudokuActivity extends AppCompatActivity { + (currentDate.get(Calendar.MONTH) + 1) * 10000 + currentDate.get(Calendar.YEAR); final Intent intent = new Intent(this,GameActivity.class); - if (settings.getInt("lastPlayed", 0) == id) { - intent.putExtra("loadLevel", true); - intent.putExtra("loadLevelID", GameController.DAILY_SUDOKU_ID); - } else { + if (settings.getInt("lastPlayed", 0) != id) { SharedPreferences.Editor editor = settings.edit(); editor.putInt("lastPlayed", id); + editor.putBoolean("finishedForToday", false); editor.apply(); intent.putExtra("gameDifficulty", gameDifficulty.name()); intent.putExtra("isDailySudoku", true); - } + startActivity(intent); - startActivity(intent); + } else if (!settings.getBoolean("finishedForToday", true)) { + intent.putExtra("loadLevel", true); + intent.putExtra("loadLevelID", GameController.DAILY_SUDOKU_ID); + startActivity(intent); + } else { + Toast.makeText(this, R.string.finished_daily_sudoku, Toast.LENGTH_LONG).show(); + } } public boolean onCreateOptionsMenu(Menu menu) { diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 4f0215d..dcf4e73 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -102,4 +102,7 @@ Skip Tutorial + + Du hast das Sudoku des Tages bereits gelöst. + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b8595ba..07f27d4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -152,4 +152,8 @@ Next Skip Tutorial + + + You have already solved the sudoku of the day + From a015bd5597840259fcae81efe0963ab9b96445ca Mon Sep 17 00:00:00 2001 From: uykek Date: Thu, 28 May 2020 00:18:01 +0200 Subject: [PATCH 040/219] Calculate difficulty of daily sudoku after sudoku has been generated instead of assigning a difficulty pre-generation --- .../controller/GameController.java | 11 +++++++++-- .../secuso/privacyfriendlysudoku/ui/GameActivity.java | 2 +- .../ui/view/DailySudokuActivity.java | 11 ++++++----- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/GameController.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/GameController.java index e58ddd3..1a0fbbc 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/GameController.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/GameController.java @@ -9,6 +9,7 @@ import android.os.Parcelable; import org.secuso.privacyfriendlysudoku.controller.database.DatabaseHelper; import org.secuso.privacyfriendlysudoku.controller.database.model.DailySudoku; import org.secuso.privacyfriendlysudoku.controller.helper.GameInfoContainer; +import org.secuso.privacyfriendlysudoku.controller.qqwing.QQWing; import org.secuso.privacyfriendlysudoku.game.CellConflict; import org.secuso.privacyfriendlysudoku.game.CellConflictList; import org.secuso.privacyfriendlysudoku.game.GameBoard; @@ -111,12 +112,18 @@ public class GameController implements IModelChangedListener, Parcelable { newLevelManager.checkAndRestock(); } - public void loadNewDailySudokuLevel(GameDifficulty gameDifficulty) { + public void loadNewDailySudokuLevel() { NewLevelManager newLevelManager = NewLevelManager.getInstance(context, settings); int[] level = newLevelManager.loadDailySudoku(); - loadLevel(new GameInfoContainer(DAILY_SUDOKU_ID, gameDifficulty, GameType.Default_9x9, level, null, null)); + QQWing difficultyCheck = new QQWing(GameType.Default_9x9, GameDifficulty.Unspecified); + difficultyCheck.setRecordHistory(true); + difficultyCheck.setPuzzle(level); + difficultyCheck.solve(); + + loadLevel(new GameInfoContainer(DAILY_SUDOKU_ID, difficultyCheck.getDifficulty(), + GameType.Default_9x9, level, null, null)); newLevelManager.checkAndRestock(); diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java index 9512ce5..3837b42 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java @@ -169,7 +169,7 @@ public class GameActivity extends BaseActivity implements NavigationView.OnNavig } } if (isDailySudoku) { - gameController.loadNewDailySudokuLevel(gameDifficulty); + gameController.loadNewDailySudokuLevel(); } else { List loadableGames = GameStateManager.getLoadableGameList(); diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/DailySudokuActivity.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/DailySudokuActivity.java index e5197fe..6534d59 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/DailySudokuActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/DailySudokuActivity.java @@ -77,22 +77,23 @@ public class DailySudokuActivity extends AppCompatActivity { public void onClick(View view) { - int index = difficultyBar.getProgress()-1; - GameDifficulty gameDifficulty = GameDifficulty.getValidDifficultyList().get(index < 0 ? 0 : index); - - //send everything to game activity + // Calculate the current date as an int id Calendar currentDate = Calendar.getInstance(); int id = currentDate.get(Calendar.DAY_OF_MONTH) * 1000000 + (currentDate.get(Calendar.MONTH) + 1) * 10000 + currentDate.get(Calendar.YEAR); final Intent intent = new Intent(this,GameActivity.class); + /* + If the 'lastPlayed' key does not return the calculated id, then the player has not played + the sudoku of the day yet, meaning it has yet to be generated + */ if (settings.getInt("lastPlayed", 0) != id) { SharedPreferences.Editor editor = settings.edit(); editor.putInt("lastPlayed", id); editor.putBoolean("finishedForToday", false); editor.apply(); - intent.putExtra("gameDifficulty", gameDifficulty.name()); + //send everything to game activity intent.putExtra("isDailySudoku", true); startActivity(intent); From 91f7cc6d2b5230fc82742c9661ca740e7715b48e Mon Sep 17 00:00:00 2001 From: uykek Date: Thu, 28 May 2020 00:25:12 +0200 Subject: [PATCH 041/219] Add method which calculates the amount of seconds that were needed to solve a DailySudoku object based on its "timeNeeded" attribute --- .../controller/database/model/DailySudoku.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/database/model/DailySudoku.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/database/model/DailySudoku.java index f8546c3..a78c660 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/database/model/DailySudoku.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/database/model/DailySudoku.java @@ -25,6 +25,20 @@ public class DailySudoku extends Level { return timeNeeded; } + public int getTimeNeededInSeconds() { + if (timeNeeded.matches("/d/d:/d/d:/d/d")) { + String[] timeInstances = timeNeeded.split(":"); + int hourIndex = 0; + int minuteIndex = 1; + int secondIndex = 2; + + int minutes = Integer.parseInt(timeInstances[hourIndex]) * 60 + Integer.parseInt(timeInstances[minuteIndex]); + return minutes * 60 + Integer.parseInt(timeInstances[secondIndex]); + } + + return 0; + } + public void setTimeNeeded(String timeNeeded) { this.timeNeeded = timeNeeded; } From c0faeacce83c2982c1a50c0cad0f5b460b9a5bc7 Mon Sep 17 00:00:00 2001 From: uykek Date: Sat, 30 May 2020 11:02:37 +0200 Subject: [PATCH 042/219] Let AppTheme extend DayNightTheme and add a new actionbar style with the attributes the AppTheme previously inherited from DarkActionBar. --- app/src/main/res/values/styles.xml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index cba9695..75b60d3 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -1,11 +1,17 @@ - + + From ef344db7599c9322e1a7c8c53cdae508c6530fb0 Mon Sep 17 00:00:00 2001 From: ErikWaegerle Date: Tue, 2 Jun 2020 20:39:32 +0200 Subject: [PATCH 056/219] The color references of the following vector files have been replaced by attribute references --- app/src/main/res/drawable-v21/ic_info_black_24dp.xml | 2 +- app/src/main/res/drawable/ic_accessibility_black_48dp.xml | 8 ++++---- app/src/main/res/drawable/ic_create_black_48dp.xml | 8 ++++---- app/src/main/res/drawable/ic_delete_black_48dp.xml | 8 ++++---- app/src/main/res/drawable/ic_info_black_24dp.xml | 4 ++-- .../main/res/drawable/ic_lightbulb_outline_black_48dp.xml | 8 ++++---- app/src/main/res/drawable/ic_redo_black_48dp.xml | 4 ++-- .../drawable/ic_settings_backup_restore_black_48dp.xml | 8 ++++---- 8 files changed, 25 insertions(+), 25 deletions(-) diff --git a/app/src/main/res/drawable-v21/ic_info_black_24dp.xml b/app/src/main/res/drawable-v21/ic_info_black_24dp.xml index 8024b5b..7ca7548 100644 --- a/app/src/main/res/drawable-v21/ic_info_black_24dp.xml +++ b/app/src/main/res/drawable-v21/ic_info_black_24dp.xml @@ -4,6 +4,6 @@ android:viewportWidth="24.0" android:viewportHeight="24.0"> diff --git a/app/src/main/res/drawable/ic_accessibility_black_48dp.xml b/app/src/main/res/drawable/ic_accessibility_black_48dp.xml index 8725da4..fc2f0fe 100644 --- a/app/src/main/res/drawable/ic_accessibility_black_48dp.xml +++ b/app/src/main/res/drawable/ic_accessibility_black_48dp.xml @@ -5,10 +5,10 @@ android:viewportHeight="192"> + android:fillColor= "?attr/standardVectorGraphic" + android:strokeColor="?attr/standardVectorGraphic"/> + android:fillColor="?attr/standardVectorGraphic" + android:strokeColor="?attr/standardVectorGraphic"/> diff --git a/app/src/main/res/drawable/ic_create_black_48dp.xml b/app/src/main/res/drawable/ic_create_black_48dp.xml index 6e4ebd2..b424e09 100644 --- a/app/src/main/res/drawable/ic_create_black_48dp.xml +++ b/app/src/main/res/drawable/ic_create_black_48dp.xml @@ -5,10 +5,10 @@ android:viewportHeight="192"> + android:fillColor="?attr/standardVectorGraphic" + android:strokeColor="?attr/standardVectorGraphic"/> + android:fillColor="?attr/standardVectorGraphic" + android:strokeColor="?attr/standardVectorGraphic"/> diff --git a/app/src/main/res/drawable/ic_delete_black_48dp.xml b/app/src/main/res/drawable/ic_delete_black_48dp.xml index 74263a9..7a1ae3e 100644 --- a/app/src/main/res/drawable/ic_delete_black_48dp.xml +++ b/app/src/main/res/drawable/ic_delete_black_48dp.xml @@ -5,10 +5,10 @@ android:viewportHeight="192"> + android:fillColor="?attr/standardVectorGraphic" + android:strokeColor="?attr/standardVectorGraphic"/> + android:fillColor="?attr/standardVectorGraphic" + android:strokeColor="?attr/standardVectorGraphic"/> diff --git a/app/src/main/res/drawable/ic_info_black_24dp.xml b/app/src/main/res/drawable/ic_info_black_24dp.xml index 80e5b17..43e0f32 100644 --- a/app/src/main/res/drawable/ic_info_black_24dp.xml +++ b/app/src/main/res/drawable/ic_info_black_24dp.xml @@ -5,6 +5,6 @@ android:viewportHeight="36"> + android:fillColor="?attr/standardVectorGraphic" + android:strokeColor="?attr/standardVectorGraphic"/> diff --git a/app/src/main/res/drawable/ic_lightbulb_outline_black_48dp.xml b/app/src/main/res/drawable/ic_lightbulb_outline_black_48dp.xml index b148d19..4ce4359 100644 --- a/app/src/main/res/drawable/ic_lightbulb_outline_black_48dp.xml +++ b/app/src/main/res/drawable/ic_lightbulb_outline_black_48dp.xml @@ -5,10 +5,10 @@ android:viewportHeight="192"> + android:fillColor="?attr/standardVectorGraphic" + android:strokeColor="?attr/standardVectorGraphic"/> + android:fillColor="?attr/standardVectorGraphic" + android:strokeColor="?attr/standardVectorGraphic"/> diff --git a/app/src/main/res/drawable/ic_redo_black_48dp.xml b/app/src/main/res/drawable/ic_redo_black_48dp.xml index e06137b..3e752a7 100644 --- a/app/src/main/res/drawable/ic_redo_black_48dp.xml +++ b/app/src/main/res/drawable/ic_redo_black_48dp.xml @@ -5,6 +5,6 @@ android:viewportHeight="192"> + android:fillColor="?attr/standardVectorGraphic" + android:strokeColor="?attr/standardVectorGraphic"/> diff --git a/app/src/main/res/drawable/ic_settings_backup_restore_black_48dp.xml b/app/src/main/res/drawable/ic_settings_backup_restore_black_48dp.xml index 7839a5a..5095e32 100644 --- a/app/src/main/res/drawable/ic_settings_backup_restore_black_48dp.xml +++ b/app/src/main/res/drawable/ic_settings_backup_restore_black_48dp.xml @@ -5,10 +5,10 @@ android:viewportHeight="192"> + android:fillColor="?attr/standardVectorGraphic" + android:strokeColor="?attr/standardVectorGraphic"/> + android:fillColor="?attr/standardVectorGraphic" + android:strokeColor="?attr/standardVectorGraphic"/> From d587b4b2dc3b4b20559bc3c9cda894d5795001f4 Mon Sep 17 00:00:00 2001 From: uykek Date: Tue, 2 Jun 2020 21:13:02 +0200 Subject: [PATCH 057/219] Add all the theme attributes which are necessary for the win screen layout. --- app/src/main/res/values/attrs.xml | 2 ++ app/src/main/res/values/styles.xml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 2f0dcbc..6dbe1d6 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -4,5 +4,7 @@ + + \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index dacce95..d5042f9 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -9,6 +9,8 @@ @color/white @color/white @color/black + @color/middleblue + @color/transparent @style/ToolbarStyle From ebcca01f52521cc57ab9bfdb6bc994d65063e3ca Mon Sep 17 00:00:00 2001 From: uykek Date: Tue, 2 Jun 2020 21:16:00 +0200 Subject: [PATCH 058/219] Replace color references in the win screen layout and stats fragment layout with attribute references --- .../main/res/layout-land/fragment_stats.xml | 4 +-- .../res/layout-xlarge-land/fragment_stats.xml | 4 +-- .../main/res/layout-xlarge/fragment_stats.xml | 4 +-- .../res/layout-xlarge/win_screen_layout.xml | 34 +++++++++---------- app/src/main/res/layout/fragment_stats.xml | 4 +-- app/src/main/res/layout/win_screen_layout.xml | 34 +++++++++---------- 6 files changed, 42 insertions(+), 42 deletions(-) diff --git a/app/src/main/res/layout-land/fragment_stats.xml b/app/src/main/res/layout-land/fragment_stats.xml index 80160d4..409b7a6 100644 --- a/app/src/main/res/layout-land/fragment_stats.xml +++ b/app/src/main/res/layout-land/fragment_stats.xml @@ -86,7 +86,7 @@ android:foregroundGravity="center_vertical" android:layout_centerVertical="true" android:layout_centerHorizontal="true" - android:background="@color/colorPrimary" /> + android:background="?attr/colorPrimary" /> diff --git a/app/src/main/res/layout-xlarge-land/fragment_stats.xml b/app/src/main/res/layout-xlarge-land/fragment_stats.xml index 035860f..6932511 100644 --- a/app/src/main/res/layout-xlarge-land/fragment_stats.xml +++ b/app/src/main/res/layout-xlarge-land/fragment_stats.xml @@ -32,7 +32,7 @@ android:foregroundGravity="center_vertical" android:layout_centerVertical="true" android:layout_centerHorizontal="true" - android:background="@color/colorPrimary" + android:background="?attr/colorPrimary" android:layout_marginTop="@dimen/activity_horizontal_margin" android:layout_marginBottom="@dimen/activity_horizontal_margin"/> @@ -97,7 +97,7 @@ android:foregroundGravity="center_vertical" android:layout_centerVertical="true" android:layout_centerHorizontal="true" - android:background="@color/colorPrimary" /> + android:background="?attr/colorPrimary" /> diff --git a/app/src/main/res/layout-xlarge/fragment_stats.xml b/app/src/main/res/layout-xlarge/fragment_stats.xml index 26997f5..b75bacb 100644 --- a/app/src/main/res/layout-xlarge/fragment_stats.xml +++ b/app/src/main/res/layout-xlarge/fragment_stats.xml @@ -32,7 +32,7 @@ android:foregroundGravity="center_vertical" android:layout_centerVertical="true" android:layout_centerHorizontal="true" - android:background="@color/colorPrimary" + android:background="?attr/colorPrimary" android:layout_marginTop="@dimen/activity_horizontal_margin" android:layout_marginBottom="@dimen/activity_horizontal_margin"/> @@ -97,7 +97,7 @@ android:foregroundGravity="center_vertical" android:layout_centerVertical="true" android:layout_centerHorizontal="true" - android:background="@color/colorPrimary" /> + android:background="?attr/colorPrimary" /> diff --git a/app/src/main/res/layout-xlarge/win_screen_layout.xml b/app/src/main/res/layout-xlarge/win_screen_layout.xml index 9e8562a..7481b06 100644 --- a/app/src/main/res/layout-xlarge/win_screen_layout.xml +++ b/app/src/main/res/layout-xlarge/win_screen_layout.xml @@ -2,7 +2,7 @@ @@ -24,12 +24,12 @@ android:layout_width="40dp" android:layout_height="40dp" android:layout_gravity="bottom" - android:background="@color/transparent" + android:background="?attr/backgroundSurroundingDialog" android:src="@drawable/ic_trophy_award_black_48dp" /> @@ -40,7 +40,7 @@ android:layout_height="fill_parent" android:layout_weight="3" android:gravity="center" - android:background="@color/transparent" + android:background="?attr/backgroundSurroundingDialog" android:orientation="vertical" android:weightSum="5" > @@ -57,32 +57,32 @@ android:text="@string/win_text" android:textAlignment="center" android:layout_gravity="center_horizontal" - android:textColor="@color/white"/> + android:textColor="?attr/lightestFontColor"/> + android:textColor="?attr/lightestFontColor"/> + android:textColor="?attr/lightestFontColor"/> @@ -111,7 +111,7 @@ android:layout_width="0dp" android:layout_height="fill_parent" android:layout_weight="1" - android:background="@color/transparent" + android:background="?attr/backgroundSurroundingDialog" android:orientation="vertical" android:gravity="bottom"> + android:background="?attr/backgroundSurroundingDialog" /> @@ -142,7 +142,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:background="@drawable/standalone_button" - android:textColor="@color/white" + android:textColor="?attr/lightestFontColor" android:layout_weight="1" android:text="@string/win_button_text" android:layout_marginRight="5dp"/> @@ -151,7 +151,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:background="@drawable/standalone_button" - android:textColor="@color/white" + android:textColor="?attr/lightestFontColor" android:layout_weight="1" android:text="@string/win_show_game" android:layout_marginLeft="5dp"/> diff --git a/app/src/main/res/layout/fragment_stats.xml b/app/src/main/res/layout/fragment_stats.xml index 9e12962..efea7f0 100644 --- a/app/src/main/res/layout/fragment_stats.xml +++ b/app/src/main/res/layout/fragment_stats.xml @@ -32,7 +32,7 @@ android:foregroundGravity="center_vertical" android:layout_centerVertical="true" android:layout_centerHorizontal="true" - android:background="@color/colorPrimary" + android:background="?attr/colorPrimary" android:layout_marginTop="@dimen/activity_horizontal_margin" android:layout_marginBottom="@dimen/activity_horizontal_margin"/> @@ -98,7 +98,7 @@ android:foregroundGravity="center_vertical" android:layout_centerVertical="true" android:layout_centerHorizontal="true" - android:background="@color/colorPrimary" /> + android:background="?attr/colorPrimary" /> diff --git a/app/src/main/res/layout/win_screen_layout.xml b/app/src/main/res/layout/win_screen_layout.xml index eb2e5da..df0f5a9 100644 --- a/app/src/main/res/layout/win_screen_layout.xml +++ b/app/src/main/res/layout/win_screen_layout.xml @@ -2,7 +2,7 @@ @@ -25,12 +25,12 @@ android:layout_width="40dp" android:layout_height="40dp" android:layout_gravity="bottom" - android:background="@color/transparent" + android:background="?attr/backgroundSurroundingDialog" android:src="@drawable/ic_trophy_award_black_48dp" /> @@ -41,7 +41,7 @@ android:layout_height="fill_parent" android:layout_weight="3" android:gravity="center" - android:background="@color/transparent" + android:background="?attr/backgroundSurroundingDialog" android:orientation="vertical" android:weightSum="5" > @@ -60,32 +60,32 @@ android:text="@string/win_text" android:textAlignment="center" android:layout_gravity="center_horizontal" - android:textColor="@color/white"/> + android:textColor="?attr/lightestFontColor"/> + android:textColor="?attr/lightestFontColor"/> + android:textColor="?attr/lightestFontColor"/> @@ -114,7 +114,7 @@ android:layout_width="0dp" android:layout_height="fill_parent" android:layout_weight="1" - android:background="@color/transparent" + android:background="?attr/backgroundSurroundingDialog" android:orientation="vertical" android:gravity="bottom"> + android:background="?attr/backgroundSurroundingDialog" /> @@ -145,7 +145,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:background="@drawable/standalone_button" - android:textColor="@color/white" + android:textColor="?attr/lightestFontColor" android:layout_weight="1" android:text="@string/win_button_text" android:layout_marginRight="5dp"/> @@ -154,7 +154,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:background="@drawable/standalone_button" - android:textColor="@color/white" + android:textColor="?attr/lightestFontColor" android:layout_weight="1" android:text="@string/win_show_game" android:layout_marginLeft="5dp"/> From b7189338f7ade8bd60b8c4c94f7f541969434ca5 Mon Sep 17 00:00:00 2001 From: ErikWaegerle Date: Wed, 3 Jun 2020 00:36:59 +0200 Subject: [PATCH 059/219] The color references of the following layouts files have been replaced by attribute references --- app/src/main/res/layout-land/activity_about.xml | 4 ++-- app/src/main/res/layout-land/activity_main_menu.xml | 6 +++--- app/src/main/res/layout-small/activity_main_menu.xml | 6 +++--- app/src/main/res/layout-xlarge-land/activity_about.xml | 4 ++-- app/src/main/res/layout-xlarge/activity_about.xml | 4 ++-- app/src/main/res/layout-xlarge/activity_main_menu.xml | 6 +++--- app/src/main/res/layout/activity_about.xml | 4 ++-- app/src/main/res/layout/activity_daily_sudoku.xml | 4 ++-- app/src/main/res/layout/activity_game_view.xml | 2 +- app/src/main/res/layout/activity_main_menu.xml | 6 +++--- app/src/main/res/layout/activity_tutorial.xml | 6 +++--- app/src/main/res/layout/nav_header.xml | 6 +++--- app/src/main/res/layout/tutorial_slide1.xml | 6 +++--- app/src/main/res/layout/tutorial_slide2.xml | 6 +++--- app/src/main/res/layout/tutorial_slide3.xml | 8 ++++---- 15 files changed, 39 insertions(+), 39 deletions(-) diff --git a/app/src/main/res/layout-land/activity_about.xml b/app/src/main/res/layout-land/activity_about.xml index 97b5716..c7ac404 100644 --- a/app/src/main/res/layout-land/activity_about.xml +++ b/app/src/main/res/layout-land/activity_about.xml @@ -3,13 +3,13 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="#FFFFFF"> + android:background="?attr/lightestBackGround">