diff --git a/app/build.gradle b/app/build.gradle index 72ab140..bf5c5ab 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -11,8 +11,8 @@ android { applicationId "org.secuso.privacyfriendlysudoku" minSdkVersion 16 targetSdkVersion 29 - versionCode 8 - versionName "2.2.1" + versionCode 9 + versionName "3.0.0" vectorDrawables.useSupportLibrary = true } buildTypes { @@ -25,16 +25,23 @@ android { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + + dataBinding { + enabled = true + } } dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') + implementation 'androidx.preference:preference:1.1.1' testImplementation 'junit:junit:4.12' - implementation 'androidx.core:core:1.2.0' + implementation 'androidx.core:core:1.3.2' implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation 'androidx.legacy:legacy-support-v13:1.0.0' - implementation 'androidx.appcompat:appcompat:1.1.0' - implementation 'com.google.android.material:material:1.1.0' + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation 'com.google.android.material:material:1.2.1' implementation 'androidx.legacy:legacy-support-core-ui:1.0.0' implementation 'androidx.legacy:legacy-support-core-utils:1.0.0' + implementation 'androidx.constraintlayout:constraintlayout:2.0.1' + } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 9d32955..43a0057 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,34 +2,39 @@ - + + android:theme="@style/AppTheme"> - - + android:theme="@style/AppTheme.NoActionBar" /> - + android:theme="@style/AppTheme.NoActionBar" /> + + + android:launchMode="singleTask" + android:theme="@style/AppTheme.NoActionBar"> + + + + + + + + + + + + + + . + */ package org.secuso.privacyfriendlysudoku; import android.app.Application; 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..ed0d54d 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/GameController.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/GameController.java @@ -1,3 +1,19 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.controller; import android.content.Context; @@ -6,7 +22,10 @@ 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.controller.qqwing.QQWing; import org.secuso.privacyfriendlysudoku.game.CellConflict; import org.secuso.privacyfriendlysudoku.game.CellConflictList; import org.secuso.privacyfriendlysudoku.game.GameBoard; @@ -19,7 +38,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 +54,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 @@ -57,6 +80,7 @@ public class GameController implements IModelChangedListener, Parcelable { private GameType gameType; private GameDifficulty difficulty; private CellConflictList errorList = new CellConflictList(); + private boolean gameIsCustom; // Undo Redo private UndoRedoManager undoRedoManager; @@ -84,6 +108,7 @@ public class GameController implements IModelChangedListener, Parcelable { public GameController(GameType type, SharedPreferences pref, Context context) { this.context = context; this.gameBoard = new GameBoard(type); + this.gameIsCustom = false; setGameType(type); setSettings(pref); @@ -95,6 +120,8 @@ public class GameController implements IModelChangedListener, Parcelable { return gameID; } + public boolean gameIsCustom() { return gameIsCustom; } + public void loadNewLevel(GameType type, GameDifficulty difficulty) { NewLevelManager newLevelManager = NewLevelManager.getInstance(context, settings); @@ -105,6 +132,23 @@ public class GameController implements IModelChangedListener, Parcelable { newLevelManager.checkAndRestock(); } + public void loadNewDailySudokuLevel() { + NewLevelManager newLevelManager = NewLevelManager.getInstance(context, settings); + + // generate the daily sudoku + int[] level = newLevelManager.loadDailySudoku(); + + // calculate the difficulty of the daily sudoku + 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)); + + } + public int getTime() { return time; } @@ -117,6 +161,7 @@ public class GameController implements IModelChangedListener, Parcelable { this.difficulty = gic.getDifficulty(); this.time = gic.getTimePlayed(); this.usedHints = gic.getHintsUsed(); + this.gameIsCustom = gic.isCustom(); setGameType(gic.getGameType()); this.gameBoard = new GameBoard(gic.getGameType()); @@ -305,7 +350,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 +363,31 @@ public class GameController implements IModelChangedListener, Parcelable { fm.saveGameState(this); } + /** + * Save progress on the current daily sudoku + * @param context the context in which this method is called + */ + 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(); + } + } + + // turn the current date into an id + Calendar currentDate = Calendar.getInstance(); + int id = currentDate.get(Calendar.DAY_OF_MONTH) * 1000000 + + (currentDate.get(Calendar.MONTH) + 1) * 10000 + currentDate.get(Calendar.YEAR); + + // save the sudoku to the database using the previously calculated id + 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."); @@ -414,6 +484,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/controller/GameStateManager.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/GameStateManager.java index bebec31..bfff728 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/GameStateManager.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/GameStateManager.java @@ -1,3 +1,19 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.controller; import android.content.Context; @@ -20,6 +36,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 +104,15 @@ public class GameStateManager { gic.parseSetValues(values[i++]); gic.parseNotes(values[i++]); gic.parseHintsUsed(values[i++]); + + if (values.length > i) { + gic.setCustom(true); + } + + if (gic.getID() == GameController.DAILY_SUDOKU_ID) { + includesDaily = true; + } + } catch(IllegalArgumentException e) { file.delete(); continue; @@ -108,7 +134,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)); } diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/GeneratorService.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/GeneratorService.java index f6dbead..79435b1 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/GeneratorService.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/GeneratorService.java @@ -1,3 +1,19 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.controller; import android.app.IntentService; diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/Highscore.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/Highscore.java index 9c3831c..e30d8d4 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/Highscore.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/Highscore.java @@ -1,3 +1,19 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.controller; /** 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..6e9464d 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/NewLevelManager.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/NewLevelManager.java @@ -1,3 +1,19 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.controller; import android.content.Context; @@ -15,6 +31,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; @@ -37,6 +56,10 @@ public class NewLevelManager { public static int PRE_SAVES_MIN = 3; public static int PRE_SAVES_MAX = 10; + private final double CHALLENGE_GENERATION_PROBABILITY = 0.25; + private final int CHALLENGE_ITERATIONS = 4; + + public static NewLevelManager getInstance(Context context, SharedPreferences settings) { if(instance == null) { instance = new NewLevelManager(context, settings); @@ -75,6 +98,16 @@ public class NewLevelManager { return false; } + public int[] loadDailySudoku() { + // create a seed from the current date + DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy"); + String toHash = "Sudoku/.PrivacyFriendly/." + dateFormat.format(new Date()); + QQWingController controller = new QQWingController(); + + // generate new sudoku using the previously computed seed + return controller.generateFromSeed(toHash.hashCode(), CHALLENGE_GENERATION_PROBABILITY, CHALLENGE_ITERATIONS); + } + 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..3b8c35e 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/QQWingController.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/QQWingController.java @@ -1,9 +1,26 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.controller; import android.os.Parcelable; import android.util.Log; import java.util.LinkedList; +import java.util.Random; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.Date; @@ -53,6 +70,49 @@ public class QQWingController { return generated; } + /** + * Generate a new sudoku based on a given seed regardless of outcome difficulty + * @param seed the seed based on which the sudoku should be calculated + * @return the generated sudoku + */ + public int[] generateFromSeed(int seed) { + return generateFromSeed(seed, 1, 1); + } + + /** + * Generate a new sudoku based on a given seed, but only accept challenge sudokus with a certain probability + * @param seed the seed based on which the sudoku should be calculated + * @param challengePermission the probability with which a challenge sudoku is accepted upon calculation + * @param challengeIterations the amount of times a challenge sudoku can be rejected in a row before being + * accepted with a probability of 100% + * @return the generated sudoku + */ + public int[] generateFromSeed(int seed, double challengePermission, int challengeIterations) { + generated.clear(); + QQWing generator = new QQWing(GameType.Default_9x9, GameDifficulty.Unspecified); + boolean continueSearch = true; + Random random = new Random(seed); + int seedFactor = 2; + + while(continueSearch && challengeIterations > 0) { + seed *= seedFactor; + generator.setRandom(seed); + generator.setRecordHistory(true); + generator.generatePuzzle(); + + if (generator.getDifficulty() != GameDifficulty.Challenge || random.nextDouble() < challengePermission) { + continueSearch = false; + } else { + challengeIterations--; + } + } + + 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()]; @@ -200,6 +260,10 @@ public class QQWingController { } } + public boolean isImpossible() { + return solveImpossible; + } + private static class QQWingOptions { // defaults for options boolean needNow = false; diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/SaveLoadStatistics.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/SaveLoadStatistics.java index 7819531..ee233f4 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/SaveLoadStatistics.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/SaveLoadStatistics.java @@ -1,3 +1,19 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.controller; import android.content.Context; @@ -204,12 +220,12 @@ public class SaveLoadStatistics implements ITimerListener, IHintListener { @Override public void onTick(int time) { - incTime(gc.getDifficulty(), gc.getGameType()); + if (!gc.gameIsCustom()) incTime(gc.getDifficulty(), gc.getGameType()); //gc.getUsedHints(); } @Override public void onHintUsed() { - incHints(gc.getDifficulty(),gc.getGameType()); + if (!gc.gameIsCustom()) incHints(gc.getDifficulty(),gc.getGameType()); } } diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/Symbol.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/Symbol.java index 96265f0..adac2c4 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/Symbol.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/Symbol.java @@ -1,3 +1,19 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.controller; /** diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/UndoRedoManager.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/UndoRedoManager.java index c6c4ce1..e41dade 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/UndoRedoManager.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/UndoRedoManager.java @@ -1,3 +1,19 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.controller; import android.os.Parcel; 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..5601101 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 @@ -1,3 +1,19 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.controller.database; import android.content.ContentValues; @@ -6,7 +22,10 @@ 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.migration.MigrationUtil; +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; @@ -18,7 +37,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) { @@ -28,11 +47,16 @@ public class DatabaseHelper extends SQLiteOpenHelper { @Override public void onCreate(SQLiteDatabase db) { 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); - onCreate(db); + // fallback to destructive migration if no migration could be executed + if(!MigrationUtil.executeMigration(db, oldVersion, newVersion)) { + db.execSQL(LevelColumns.SQL_DELETE_ENTRIES); + db.execSQL(DailySudokuColumns.SQL_DELETE_ENTRIES); + onCreate(db); + } } public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { @@ -80,6 +104,39 @@ public class DatabaseHelper extends SQLiteOpenHelper { return levelList.get(0); } + /** + * Returns a list of all the daily sudokus that have been solved and thus saved to the database + * @return a list of all the daily sudokus that have been solved so far + */ + public synchronized List getDailySudokus() { + List dailySudokuList = new LinkedList<>(); + SQLiteDatabase database = getWritableDatabase(); + + // order results from most to least recent + 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(); @@ -93,5 +150,15 @@ public class DatabaseHelper extends SQLiteOpenHelper { SQLiteDatabase database = getWritableDatabase(); return database.insert(LevelColumns.TABLE_NAME, null, LevelColumns.getValues(level)); } + + /** + * Adds a new daily sudoku to the database + * @param ds the daily sudoku which is to be added to the database + * @return the row id of the newly inserted sudoku (or -1 if an error occurred) + */ + public synchronized long addDailySudoku(DailySudoku ds) { + SQLiteDatabase database = getWritableDatabase(); + return database.insert(DailySudokuColumns.TABLE_NAME, null, DailySudokuColumns.getValues(ds)); + } } 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..cfd2fcd --- /dev/null +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/database/columns/DailySudokuColumns.java @@ -0,0 +1,89 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ + +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; + + +/** + * Defines a database schema for saving daily sudokus + */ +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 String SQL_DELETE_ENTRIES = + "DROP TABLE IF EXISTS " + TABLE_NAME; + + + /** + * Create a new DailySudoku object using the data stored in a specific database row + * @param c the cursor pointing to the row whose data should be used + * @return the DailySudoku object created using the data from the database row + */ + 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); + } + + /** + * Given a specific DailySudoku instance, extracts all relevant parameters and saves them to a ContentValues object + * @param record the DailySudoku instance whose parameters should be extracted + * @return the ContentValues instance containing the extracted parameters + */ + 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/columns/LevelColumns.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/database/columns/LevelColumns.java index d6deebc..f824533 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/database/columns/LevelColumns.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/database/columns/LevelColumns.java @@ -1,3 +1,20 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ + package org.secuso.privacyfriendlysudoku.controller.database.columns; import android.content.ContentValues; diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/database/migration/Migration.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/database/migration/Migration.java new file mode 100644 index 0000000..794bedf --- /dev/null +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/database/migration/Migration.java @@ -0,0 +1,35 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ +package org.secuso.privacyfriendlysudoku.controller.database.migration; + +import android.database.sqlite.SQLiteDatabase; + +/** + * Similar idea to room migration class. + * @author Christopher Beckmann + */ +public abstract class Migration { + int from = 0; + int to = 0; + + public Migration(int from, int to) { + this.from = from; + this.to = to; + } + + public abstract void migrate(SQLiteDatabase db); +} diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/database/migration/MigrationUtil.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/database/migration/MigrationUtil.java new file mode 100644 index 0000000..386e5cb --- /dev/null +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/database/migration/MigrationUtil.java @@ -0,0 +1,52 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ +package org.secuso.privacyfriendlysudoku.controller.database.migration; + +import android.database.sqlite.SQLiteDatabase; + +import java.util.Arrays; +import java.util.List; + +import static org.secuso.privacyfriendlysudoku.controller.database.columns.DailySudokuColumns.SQL_CREATE_ENTRIES; + +/** + * @author Christopher Beckmann + */ +public class MigrationUtil { + + public static List migrations = Arrays.asList( + new Migration(1,2) { + @Override + public void migrate(SQLiteDatabase db) { + db.execSQL(SQL_CREATE_ENTRIES); + } + } + ); + + //TODO: for now just try to find the desired migration from the list. + // -> When more migrations are added, a chain could be found, e.g. 1->2->3 + public static boolean executeMigration(SQLiteDatabase db, int from, int to) { + for(Migration m : migrations) { + if(m.from == from && m.to == to) { + m.migrate(db); + return true; + } + } + return false; + } + +} 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..3c40446 --- /dev/null +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/database/model/DailySudoku.java @@ -0,0 +1,86 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ + +package org.secuso.privacyfriendlysudoku.controller.database.model; + +import org.secuso.privacyfriendlysudoku.game.GameDifficulty; +import org.secuso.privacyfriendlysudoku.game.GameType; + +/** + * Models the content of a single row of the daily sudoku database + */ +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; + } + + /** + * Return the amount of hints the user needed to solve this sudoku + * @return the amount of hints the user needed to solve this sudoku + */ + public int getHintsUsed() { + return hintsUsed; + } + + /** + * Set a new value for the hintsUsed attribute of this daily sudoku + * @param hintsUsed the new value for the hintsUsed attribute + */ + public void setHintsUsed(int hintsUsed) { + this.hintsUsed = hintsUsed; + } + + /** + * Return the time the user needed to solve this sudoku as a string + * @return the time the user needed to solve this sudoku as a string + */ + public String getTimeNeeded() { + return timeNeeded; + } + + /** + * Return the time the user needed to solve this sudoku in seconds + * @return the time the user needed to solve this sudoku in seconds (or 0 if the timeNeeded parameter + * does not have the right format) + */ + public int getTimeNeededInSeconds() { + if (timeNeeded.matches("[0-9]{2}:[0-5][0-9]:[0-5][0-9]")) { + 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; + } + + /** + * Set a new value for the timeNeeded attribute of this daily sudoku + * @param timeNeeded the new value for the timeNeeded attribute + */ + public void setTimeNeeded(String timeNeeded) { + this.timeNeeded = timeNeeded; + } +} diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/database/model/Level.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/database/model/Level.java index 589d365..be46794 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/database/model/Level.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/database/model/Level.java @@ -1,3 +1,19 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.controller.database.model; import org.secuso.privacyfriendlysudoku.controller.Symbol; diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/helper/GameInfoContainer.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/helper/GameInfoContainer.java index 43ed14e..345805c 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/helper/GameInfoContainer.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/helper/GameInfoContainer.java @@ -1,3 +1,19 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.controller.helper; import android.util.Log; @@ -25,6 +41,7 @@ public class GameInfoContainer { int[] setValues; boolean[][] setNotes; int hintsUsed; + boolean isCustom; public GameInfoContainer() {} public GameInfoContainer(int ID, GameDifficulty difficulty, GameType gameType, int[] fixedValues, int[] setValues, boolean[][] setNotes) { @@ -40,17 +57,19 @@ public class GameInfoContainer { this.setValues = setValues; this.setNotes = setNotes; this.hintsUsed = hintsUsed; + isCustom = false; } public void setID(int ID) { this.ID = ID; } + public void setCustom (boolean isCustom) { this.isCustom = isCustom; } + + public boolean isCustom () { return isCustom; } + public void parseGameType(String s) { gameType = Enum.valueOf(GameType.class, s); - if(gameType == null) { - throw new IllegalArgumentException("GameInfoContainer: gameType could not be set."); - } } public int getTimePlayed() { @@ -63,7 +82,7 @@ public class GameInfoContainer { public void parseTime(String s) { try { - this.timePlayed = Integer.valueOf(s); + this.timePlayed = Integer.parseInt(s); } catch(NumberFormatException e) { throw new IllegalArgumentException("GameInfoContainer: Can not parse time.", e); } @@ -71,7 +90,7 @@ public class GameInfoContainer { public void parseHintsUsed(String s) { try { - this.hintsUsed = Integer.valueOf(s); + this.hintsUsed = Integer.parseInt(s); } catch(NumberFormatException e) { throw new IllegalArgumentException("GameInfoContainer: Can not parse hints used.", e); } @@ -79,7 +98,7 @@ public class GameInfoContainer { public void parseDate(String s) { try { - this.lastTimePlayed = new Date(Long.valueOf(s)); + this.lastTimePlayed = new Date(Long.parseLong(s)); } catch(NumberFormatException e) { throw new IllegalArgumentException("GameInfoContainer: LastTimePlayed Date can not be extracted.", e); } @@ -87,9 +106,6 @@ public class GameInfoContainer { public void parseDifficulty(String s) { difficulty = Enum.valueOf(GameDifficulty.class, s); - if(difficulty == null) { - throw new IllegalArgumentException("GameInfoContainer: difficulty could not be set."); - } } public void parseFixedValues(String s){ @@ -104,6 +120,11 @@ public class GameInfoContainer { fixedValues = new int[s.length()]; for(int i = 0; i < s.length(); i++) { fixedValues[i] = Symbol.getValue(Symbol.SaveFormat, String.valueOf(s.charAt(i)))+1; + if (gameType != GameType.Unspecified && gameType != null) { + if (fixedValues[i] < 0 || fixedValues[i] > gameType.getSize()) { + throw new IllegalArgumentException("Fixed values must each be smaller than " + gameType.getSize() + "."); + } + } } } @@ -128,7 +149,7 @@ public class GameInfoContainer { int size = gameType.getSize(); int sq = size*size; - if(gameType != GameType.Unspecified && gameType != null) { + if(gameType != GameType.Unspecified) { if(strings.length != sq) { throw new IllegalArgumentException("The string array must have "+sq+" entries."); } @@ -174,6 +195,7 @@ public class GameInfoContainer { public static String getGameInfo(GameController controller) { StringBuilder sb = new StringBuilder(); Date today = new Date(); + boolean custom = controller.gameIsCustom(); sb.append(controller.getGameType().name()); sb.append("/"); @@ -191,6 +213,12 @@ public class GameInfoContainer { sb.append("/"); sb.append(controller.getUsedHints()); + // add additional information to custom sudokus to ensure they can be distinguished from 'regular' sudokus + if (custom) { + sb.append("/"); + sb.append(custom); + } + String result = sb.toString(); Log.d("getGameInfo", result); diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/helper/HighscoreInfoContainer.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/helper/HighscoreInfoContainer.java index b2bd929..2c20914 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/helper/HighscoreInfoContainer.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/helper/HighscoreInfoContainer.java @@ -1,3 +1,19 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.controller.helper; import org.secuso.privacyfriendlysudoku.controller.GameController; diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/qqwing/Action.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/qqwing/Action.java index 8b3dd25..a054d82 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/qqwing/Action.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/qqwing/Action.java @@ -5,7 +5,7 @@ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or + * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/qqwing/LogItem.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/qqwing/LogItem.java index 93184df..bf05a1e 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/qqwing/LogItem.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/qqwing/LogItem.java @@ -5,7 +5,7 @@ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or + * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/qqwing/LogType.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/qqwing/LogType.java index 0e7ae49..be0f66d 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/qqwing/LogType.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/qqwing/LogType.java @@ -5,7 +5,7 @@ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or + * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/qqwing/PrintStyle.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/qqwing/PrintStyle.java index fb242b3..0166f55 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/qqwing/PrintStyle.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/qqwing/PrintStyle.java @@ -5,7 +5,7 @@ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or + * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, 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..dfee30e 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 @@ -7,7 +7,7 @@ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or + * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, @@ -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. diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/qqwing/Symmetry.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/qqwing/Symmetry.java index d78fc3f..04d0992 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/qqwing/Symmetry.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/qqwing/Symmetry.java @@ -5,7 +5,7 @@ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or + * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/game/CellConflict.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/game/CellConflict.java index 554dd55..63537dc 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/game/CellConflict.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/game/CellConflict.java @@ -1,3 +1,19 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.game; import android.os.Parcel; diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/game/CellConflictList.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/game/CellConflictList.java index 14bcc63..309360d 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/game/CellConflictList.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/game/CellConflictList.java @@ -1,3 +1,19 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.game; import java.util.ArrayList; 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..a2aaa68 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/game/GameBoard.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/game/GameBoard.java @@ -1,8 +1,25 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ 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 +231,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 diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/game/GameCell.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/game/GameCell.java index de2a4f0..82ce514 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/game/GameCell.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/game/GameCell.java @@ -1,3 +1,19 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.game; import android.os.Parcel; diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/game/GameDifficulty.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/game/GameDifficulty.java index f296149..ad3a33f 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/game/GameDifficulty.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/game/GameDifficulty.java @@ -1,3 +1,19 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.game; import android.os.Parcel; diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/game/GameSettings.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/game/GameSettings.java index 0b40c2e..c0e69d7 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/game/GameSettings.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/game/GameSettings.java @@ -1,3 +1,19 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.game; /** diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/game/GameStatus.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/game/GameStatus.java index 3fa213d..e681e6b 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/game/GameStatus.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/game/GameStatus.java @@ -1,3 +1,19 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.game; /** diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/game/GameType.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/game/GameType.java index a93478a..5f67a3c 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/game/GameType.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/game/GameType.java @@ -1,3 +1,19 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.game; import android.os.Parcel; diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/game/ICellAction.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/game/ICellAction.java index 0cf6815..d028e9f 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/game/ICellAction.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/game/ICellAction.java @@ -1,3 +1,19 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.game; /** diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/game/listener/IGameErrorListener.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/game/listener/IGameErrorListener.java index 628a35c..568e50d 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/game/listener/IGameErrorListener.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/game/listener/IGameErrorListener.java @@ -1,3 +1,19 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.game.listener; import org.secuso.privacyfriendlysudoku.game.CellConflict; diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/game/listener/IGameSolvedListener.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/game/listener/IGameSolvedListener.java index 7a5eecb..e0ec751 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/game/listener/IGameSolvedListener.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/game/listener/IGameSolvedListener.java @@ -1,3 +1,19 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.game.listener; /** diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/game/listener/IHighlightChangedListener.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/game/listener/IHighlightChangedListener.java index 1740595..b782310 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/game/listener/IHighlightChangedListener.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/game/listener/IHighlightChangedListener.java @@ -1,3 +1,19 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.game.listener; /** diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/game/listener/IHintListener.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/game/listener/IHintListener.java index f3957d3..f9548a3 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/game/listener/IHintListener.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/game/listener/IHintListener.java @@ -1,3 +1,19 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.game.listener; /** diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/game/listener/IModelChangedListener.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/game/listener/IModelChangedListener.java index 0c25859..178c61c 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/game/listener/IModelChangedListener.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/game/listener/IModelChangedListener.java @@ -1,3 +1,19 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.game.listener; import org.secuso.privacyfriendlysudoku.game.GameCell; diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/game/listener/ITimerListener.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/game/listener/ITimerListener.java index 8d0743c..3f47603 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/game/listener/ITimerListener.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/game/listener/ITimerListener.java @@ -1,3 +1,19 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.game.listener; /** diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/AboutActivity.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/AboutActivity.java index e42d898..9ab0693 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/AboutActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/AboutActivity.java @@ -1,3 +1,19 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.ui; import android.graphics.Color; @@ -11,6 +27,10 @@ import android.widget.TextView; import org.secuso.privacyfriendlysudoku.ui.view.BuildConfig; import org.secuso.privacyfriendlysudoku.ui.view.R; +/** + * The AboutActivity is extended by the BaseActivity and is responsible for the content of the AboutActivity. + */ + public class AboutActivity extends BaseActivity { @Override diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/AppCompatPreferenceActivity.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/AppCompatPreferenceActivity.java index 4133998..5a7f7de 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/AppCompatPreferenceActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/AppCompatPreferenceActivity.java @@ -1,3 +1,19 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.ui; import android.content.res.Configuration; diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/BaseActivity.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/BaseActivity.java index 38750a5..b101855 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/BaseActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/BaseActivity.java @@ -1,3 +1,19 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.ui; import android.os.Bundle; diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/CreateSudokuActivity.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/CreateSudokuActivity.java new file mode 100644 index 0000000..05a89f1 --- /dev/null +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/CreateSudokuActivity.java @@ -0,0 +1,294 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ +package org.secuso.privacyfriendlysudoku.ui; + +import android.content.Context; +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.PreferenceManager; +import android.view.View; +import android.view.WindowManager; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.appcompat.app.AppCompatDelegate; +import androidx.appcompat.widget.Toolbar; + +import org.secuso.privacyfriendlysudoku.controller.GameController; +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.ui.listener.IFinalizeDialogFragmentListener; +import org.secuso.privacyfriendlysudoku.ui.listener.IImportDialogFragmentListener; +import org.secuso.privacyfriendlysudoku.ui.view.CreateSudokuSpecialButtonLayout; +import org.secuso.privacyfriendlysudoku.ui.view.R; +import org.secuso.privacyfriendlysudoku.ui.view.SudokuFieldLayout; +import org.secuso.privacyfriendlysudoku.ui.view.SudokuKeyboardLayout; + +/** + * The CreateSudokuActivity is an activity which extends the BaseActivity and implements the + * IFinalizeDialogFragementListener. It is used to create custom sudokus, which are passed to the + * GameActivity afterwards. + */ +public class CreateSudokuActivity extends BaseActivity implements IFinalizeDialogFragmentListener, IImportDialogFragmentListener { + + GameController gameController; + SharedPreferences sharedPref; + SudokuFieldLayout layout; + SudokuKeyboardLayout keyboard; + CreateSudokuSpecialButtonLayout specialButtonLayout; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + sharedPref = PreferenceManager.getDefaultSharedPreferences(this); + + if (sharedPref.getBoolean("pref_keep_screen_on", true)) { + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } + + if(savedInstanceState == null) { + + gameController = new GameController(sharedPref, getApplicationContext()); + + Bundle extras = getIntent().getExtras(); + GameType gameType = GameType.valueOf(extras.getString("gameType", GameType.Default_9x9.name())); + int sectionSize = gameType.getSize(); + int boardSize = sectionSize * sectionSize; + + GameInfoContainer container = new GameInfoContainer(0, GameDifficulty.Moderate, + gameType, new int[boardSize], new int[boardSize], new boolean[boardSize][sectionSize]); + gameController.loadLevel(container); + } else { + gameController = savedInstanceState.getParcelable("gameController"); + if(gameController != null) { + gameController.removeAllListeners(); + gameController.setContextAndSettings(getApplicationContext(), sharedPref); + } + } + + setUpLayout(); + } + + private void setUpLayout() { + + setContentView(R.layout.activity_create_sudoku); + + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + getSupportActionBar().setTitle(getString(gameController.getGameType().getStringResID())); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + + layout = (SudokuFieldLayout)findViewById(R.id.sudokuLayout); + layout.setSettingsAndGame(sharedPref, gameController); + + keyboard = (SudokuKeyboardLayout) findViewById(R.id.sudokuKeyboardLayout); + keyboard.removeAllViews(); + keyboard.setGameController(gameController); + Point p = new Point(); + getWindowManager().getDefaultDisplay().getSize(p); + + int orientation = getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT ? + LinearLayout.HORIZONTAL : LinearLayout.VERTICAL; + + keyboard.setKeyBoard(gameController.getSize(), p.x,layout.getHeight()-p.y, orientation); + + specialButtonLayout = (CreateSudokuSpecialButtonLayout) findViewById(R.id.createSudokuLayout); + specialButtonLayout.setButtons(p.x, gameController, keyboard, getFragmentManager(), orientation, + CreateSudokuActivity.this, this, this); + + gameController.notifyHighlightChangedListeners(); + } + + @Override + public boolean onSupportNavigateUp() { + onBackPressed(); + return true; + } + + @Override + public void onResume(){ + super.onResume(); + + View mainContent = findViewById(R.id.main_content); + if (mainContent != null) { + mainContent.animate().alpha(1).setDuration(MAIN_CONTENT_FADEOUT_DURATION); + } + + SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this); + Symbol s; + try { + s = Symbol.valueOf(sharedPref.getString("pref_symbols", Symbol.Default.name())); + } catch(IllegalArgumentException e) { + s = Symbol.Default; + } + layout.setSymbols(s); + keyboard.setSymbols(s); + } + + @Override + public void onBackPressed() { + finish(); + super.onBackPressed(); + } + + /** + * Verifies an encoded sudoku board by testing whether or not it is uniquely solvable + * @param gameType the type of the encoded sudoku + * @param boardContent the encoded sudoku + * @return whether or not the sudoku is uniquely solvable + */ + public static boolean verify(GameType gameType, String boardContent) { + int boardSize = gameType.getSize() * gameType.getSize(); + + GameInfoContainer container = new GameInfoContainer(0, GameDifficulty.Unspecified, + gameType, new int [boardSize], new int [boardSize], new boolean [boardSize][gameType.getSize()]); + + try { + container.parseFixedValues(boardContent); + } catch (IllegalArgumentException e) { + return false; + } + + QQWing verifier = new QQWing(gameType, GameDifficulty.Unspecified); + verifier.setRecordHistory(true); + verifier.setPuzzle(container.getFixedValues()); + verifier.solve(); + + return verifier.hasUniqueSolution(); + } + + /** + * If the positive button of the FinalizeDialog is clicked, verify the sudoku. Immediately pass + * it to the GameActivity, if the verification process is successful, and do nothing apart from + * notifying the user if not. + * Implements the onFinalizeDialogPositiveClick() method of the IFinalizeDialogFragmentListener + * interface. + */ + public void onFinalizeDialogPositiveClick() { + Toast.makeText(CreateSudokuActivity.this, R.string.verify_custom_sudoku_process_toast, Toast.LENGTH_SHORT).show(); + String boardContent = gameController.getCodeOfField(); + boolean distinctlySolvable = verify(gameController.getGameType(), boardContent); + + if(distinctlySolvable) { + Toast.makeText(CreateSudokuActivity.this, R.string.finished_verifying_custom_sudoku_toast, Toast.LENGTH_LONG).show(); + final Intent intent = new Intent(this, GameActivity.class); + + /* + Since the GameActivity expects the links of imported sudokus to start with an url scheme, + add one to the start of the encoded board + */ + String scheme = GameActivity.validUris.size() > 0 ? GameActivity.validUris.get(0).getScheme() + + "://" + GameActivity.validUris.get(0).getHost() : ""; + if (!scheme.equals("") && !scheme.endsWith("/")) scheme = scheme + "/"; + + intent.setData(Uri.parse(scheme + boardContent)); + intent.putExtra("isCustom", true); + startActivity(intent); + finish(); + } else { + Toast.makeText(CreateSudokuActivity.this, R.string.failed_to_verify_custom_sudoku_toast, Toast.LENGTH_LONG).show(); + } + + } + public void onImportDialogPositiveClick(String input) { + String inputSudoku = null; + String prefix; + StringBuilder errorMessage = new StringBuilder(); + + /* remove the present prefix, or, if the input contains none of the valid prefixes, notify the user + that their input is not valid */ + for (int i = 0; i < GameActivity.validUris.size(); i++) { + prefix = GameActivity.validUris.get(i).getHost().equals("") ? + GameActivity.validUris.get(i).getScheme() + "://" : + GameActivity.validUris.get(i).getScheme() + "://" + GameActivity.validUris.get(i).getHost() + "/"; + if (input.startsWith(prefix)) { + inputSudoku = input.replace(prefix, ""); + break; + } + + String endOfRecord = (i == GameActivity.validUris.size() - 1) ? "" : ", "; + errorMessage.append(prefix); + errorMessage.append(endOfRecord); + } + + // the inputSudoku variable being null means the input did not match any of the valid prefixes + if (inputSudoku == null) { + Toast.makeText(CreateSudokuActivity.this, + this.getString(R.string.menu_import_wrong_format_custom_sudoku) + " " + errorMessage.toString(), Toast.LENGTH_LONG).show(); + return; + } + + boolean validSize = Math.sqrt(inputSudoku.length()) == gameController.getSize(); + + if (!validSize) { + Toast.makeText(CreateSudokuActivity.this, R.string.failed_to_verify_custom_sudoku_toast, Toast.LENGTH_LONG).show(); + return; + } + + //check whether or not the sudoku is valid and has a unique solution + boolean solvable = verify(gameController.getGameType(), inputSudoku); + + // if the encoded sudoku is solvable, sent the code directly to the GameActivity; if not, notify the user + if (solvable) { + int boardSize = gameController.getGameType().getSize() * gameController.getGameType().getSize(); + GameInfoContainer container = new GameInfoContainer(0, GameDifficulty.Unspecified, + gameController.getGameType(), new int [boardSize], new int [boardSize], + new boolean [boardSize][gameController.getGameType().getSize()]); + container.parseSetValues(inputSudoku); + + gameController.loadLevel(container); + setUpLayout(); + + } else { + Toast.makeText(CreateSudokuActivity.this, R.string.failed_to_verify_custom_sudoku_toast, Toast.LENGTH_LONG).show(); + } + } + + /** + * Implements the onDialogNegativeClick() method of the IFinalizeDialogFragmentListener + * interface. + */ + public void onDialogNegativeClick() { + + } + + @Override + public void onSaveInstanceState(Bundle savedInstanceState) { + // Always call the superclass so it can save the view hierarchy state + super.onSaveInstanceState(savedInstanceState); + + // Save the user's current game state + savedInstanceState.putParcelable("gameController", gameController); + + } + + @Override + public void onRestoreInstanceState(Bundle savedInstanceState) { + super.onRestoreInstanceState(savedInstanceState); + + gameController = savedInstanceState.getParcelable("gameController"); + } +} diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/DailySudokuActivity.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/DailySudokuActivity.java new file mode 100644 index 0000000..e684b06 --- /dev/null +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/DailySudokuActivity.java @@ -0,0 +1,272 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ +package org.secuso.privacyfriendlysudoku.ui; + +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.Bundle; +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 android.widget.Toast; +import org.secuso.privacyfriendlysudoku.controller.GameController; +import org.secuso.privacyfriendlysudoku.controller.GameStateManager; +import org.secuso.privacyfriendlysudoku.controller.NewLevelManager; +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.qqwing.QQWing; +import org.secuso.privacyfriendlysudoku.game.GameDifficulty; +import org.secuso.privacyfriendlysudoku.game.GameType; +import org.secuso.privacyfriendlysudoku.ui.view.R; + +import java.text.DateFormat; +import java.util.Calendar; +import java.util.List; +import java.util.Locale; +import java.util.TimeZone; + +/** + *The DailySudokuActivity is an activity that extends the AppCompatActivity. + *The activity is responsible for the logic of the DailySudoku. + *DailySudoku is a game mode where every day a different Sudoku is created for the user + */ +public class DailySudokuActivity extends AppCompatActivity { + + List sudokuList; + SharedPreferences settings; + private final DatabaseHelper dbHelper = new DatabaseHelper(this); + RatingBar difficultyBar; + private SudokuListAdapter sudokuListAdapter; + private int dailyId; + + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.activity_daily_sudoku); + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + + sudokuList = dbHelper.getDailySudokus(); + TextView totalGamesTextView = findViewById(R.id.numb_of_total_games); + TextView hintsTextView = findViewById(R.id.numb_of_hints); + TextView totalTimeTextView = findViewById(R.id.numb_of_total_time); + totalGamesTextView.setText(String.valueOf(sudokuList.size())); + + + int sumHints = 0; + int sumTime = 0; + + for (DailySudoku sudoku : sudokuList){ + sumHints += sudoku.getHintsUsed(); + sumTime += sudoku.getTimeNeededInSeconds(); + } + + int hours = sumTime / 3600; + int minutes = ( sumTime / 60 ) % 60; + int seconds = sumTime % 60; + String str = String.format(Locale.getDefault(), "%02d:%02d:%02d", hours, minutes, seconds); + + hintsTextView.setText(String.valueOf(sumHints)); + totalTimeTextView.setText(str); + + androidx.appcompat.app.ActionBar actionBar = getSupportActionBar(); + actionBar.setTitle(R.string.menu_daily_sudoku); + actionBar.setDisplayHomeAsUpEnabled(true); + + difficultyBar = findViewById(R.id.first_diff_bar); + settings = PreferenceManager.getDefaultSharedPreferences(this); + + ListView listView = (ListView)findViewById(R.id.sudoku_list); + sudokuListAdapter = new SudokuListAdapter(this, sudokuList); + listView.setAdapter(sudokuListAdapter); + + // Calculate the current date as an int id + Calendar currentDate = Calendar.getInstance(); + dailyId = currentDate.get(Calendar.DAY_OF_MONTH) * 1000000 + + (currentDate.get(Calendar.MONTH) + 1) * 10000 + currentDate.get(Calendar.YEAR); + + GameDifficulty dailyDifficulty; + + //only calculate the difficulty of the daily sudoku once a day + if (settings.getInt("lastCalculated", 0) != dailyId) { + // generate the daily sudoku + NewLevelManager newLevelManager = NewLevelManager.getInstance(getApplicationContext(), settings); + int[] level = newLevelManager.loadDailySudoku(); + QQWing difficultyCheck = new QQWing(GameType.Default_9x9, GameDifficulty.Unspecified); + + difficultyCheck.setRecordHistory(true); + difficultyCheck.setPuzzle(level); + difficultyCheck.solve(); + + dailyDifficulty = difficultyCheck.getDifficulty(); + + //save the index of the daily difficulty (in the valid difficulty list) and the day it was calculated for + SharedPreferences.Editor editor = settings.edit(); + editor.putInt("lastCalculated", dailyId); + editor.putInt("dailyDifficultyIndex", GameDifficulty.getValidDifficultyList().indexOf(dailyDifficulty)); + editor.apply(); + + } else { + // if the daily sudoku has been calculated already, the difficulty can be read from the settings attribute + int index = settings.getInt("dailyDifficultyIndex", GameDifficulty.getValidDifficultyList() + .indexOf(GameDifficulty.Unspecified)); + dailyDifficulty = GameDifficulty.getValidDifficultyList().get(index); + } + + TextView diffTextView = findViewById(R.id.first_diff_text); + + diffTextView.setText(dailyDifficulty.getStringResID()); + difficultyBar.setNumStars(GameDifficulty.getValidDifficultyList().size()); + difficultyBar.setMax(GameDifficulty.getValidDifficultyList().size()); + difficultyBar.setRating(GameDifficulty.getValidDifficultyList().indexOf(dailyDifficulty)+1); + } + + public void onClick(View view) { + 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 saved on their phone and needs to be generated again + */ + if (settings.getInt("lastPlayed", 0) != dailyId) { + SharedPreferences.Editor editor = settings.edit(); + editor.putInt("lastPlayed", dailyId); + + //as the player just started a new daily sudoku, set the 'finishedForToday' setting to 'false' + editor.putBoolean("finishedForToday", false); + editor.apply(); + + //send everything to game activity, which calculates the daily sudoku + intent.putExtra("isDailySudoku", true); + startActivity(intent); + + } else if (!settings.getBoolean("finishedForToday", true)) { + /* + if the 'finished for today' setting is 'false', the player has already started the sudoku + but has yet to finish it -> send the designated daily sudoku ID to the GameActivity + */ + GameStateManager fm = new GameStateManager(getBaseContext(), settings); + fm.loadGameStateInfo(); + 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) { + return true; + } + + @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 + if (item.getItemId() == android.R.id.home) { + finish(); + return true; + } + + return super.onOptionsItemSelected(item); + } + + private static 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); + ImageView customImage = (ImageView)convertView.findViewById(R.id.loadgame_listentry_custom_icon); + + + 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); + customImage.setVisibility(View.INVISIBLE); + + + int id = sudoku.getId(); + Calendar cal = Calendar.getInstance(); + cal.set(id%10000, (id/10000) % 100, id/1000000, 0, 0, 0 ); + + + DateFormat format = DateFormat.getDateInstance(); + format.setTimeZone(TimeZone.getDefault()); + + lastTimePlayed.setText(format.format(cal.getTime())); + + playedTime.setText(sudoku.getTimeNeeded()); + return convertView; + } + } +} \ No newline at end of file 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..2016e4a 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java @@ -1,24 +1,49 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.ui; 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; 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; import com.google.android.material.navigation.NavigationView; + +import androidx.appcompat.app.AppCompatDelegate; import androidx.core.view.GravityCompat; import androidx.drawerlayout.widget.DrawerLayout; import androidx.appcompat.app.ActionBarDrawerToggle; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; + +import android.util.Log; import android.view.Gravity; +import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; @@ -27,28 +52,40 @@ import android.widget.Button; 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; 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; 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; 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.Arrays; 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 { + + public static final List validUris = Arrays.asList( + Uri.parse("https://sudoku.secuso.org"), + Uri.parse("http://sudoku.secuso.org"), + Uri.parse("sudoku://") + ); GameController gameController; SudokuFieldLayout layout; @@ -57,15 +94,27 @@ public class GameActivity extends BaseActivity implements NavigationView.OnNavig TextView timerView; TextView viewName ; RatingBar ratingBar; - private boolean gameSolved = false; SaveLoadStatistics statistics = new SaveLoadStatistics(this); WinDialog dialog = null; + private boolean gameSolved = false; + private boolean startGame = true; + + public static String timeToString(int time) { + int seconds = time % 60; + int minutes = ((time - seconds) / 60) % 60; + int hours = (time - minutes - seconds) / (3600); + String h, m, s; + s = (seconds < 10) ? "0" + String.valueOf(seconds) : String.valueOf(seconds); + m = (minutes < 10) ? "0" + String.valueOf(minutes) : String.valueOf(minutes); + h = (hours < 10) ? "0" + String.valueOf(hours) : String.valueOf(hours); + return h + ":" + m + ":" + s; + } @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); - if(gameSolved) { + if(gameSolved || !startGame) { gameController.pauseTimer(); } else { // start the game @@ -80,41 +129,165 @@ public class GameActivity extends BaseActivity implements NavigationView.OnNavig @Override protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this); + /* + If the app is started via a deeplink, the GameActivity is the first activity the user accesses, + so we need to set the dark mode settings in this activity as well + */ + if (sharedPref.getBoolean("pref_dark_mode_setting", false )) { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); + } else if (sharedPref.getBoolean("pref_dark_mode_automatically_by_system", false)) { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM); + + } else if(sharedPref.getBoolean("pref_dark_mode_automatically_by_battery", false)){ + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY); + } else { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO); + } + if(sharedPref.getBoolean("pref_keep_screen_on", true)) { getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } + super.onCreate(savedInstanceState); + GameType gameType = GameType.Unspecified; GameDifficulty gameDifficulty = GameDifficulty.Unspecified; int loadLevelID = 0; boolean loadLevel = false; - if(savedInstanceState == null) { + 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"); - } - } + /* + If a (deep) link is used to access the activity, the content of the link cannot be accessed + as a part of the getExtras() bundle. Instead, it needs to be saved as an URI object + */ + Uri data = getIntent().getData(); gameController = new GameController(sharedPref, getApplicationContext()); - List loadableGames = GameStateManager.getLoadableGameList(); + // Intents coming from the LoadGameActivity and MainActivity can be identified based on the keys the getExtras() bundle contains + boolean intentReceivedFromMainActivity = extras != null && + (extras.containsKey("gameType") || extras.containsKey("loadLevel")); + + /* + If data is not null and the intent was not received from the MainActivity/ LoadGameActivity, the source of the intent must be the + CreateSudokuActivity, the ImportBoardDialog or a deep link, meaning data carries an URI containing an encoded sudoku + */ + if (data != null && !intentReceivedFromMainActivity) { + // extract encoded sudoku board from the URI + String input = ""; + + for (int i = 0; i < validUris.size(); i++) { + if (data.getScheme().equals(validUris.get(i).getScheme())) { + if (validUris.get(i).getHost().equals("")) { + input = data.getHost(); + break; + } + else if (data.getHost().equals(validUris.get(i).getHost())) { + input = data.getPath(); + input =input.replace("/", ""); + break; + } + } + } + + // Save all of the information that can be extracted from the encoded board in a GameInfoContainer object + 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]); + container.setCustom(extras != null && extras.getBoolean("isCustom", false)); + + try { + container.parseGameType("Default_" + sectionSize + "x" + sectionSize); + container.parseFixedValues(input); + difficultyCheck = new QQWing(container.getGameType(), GameDifficulty.Unspecified); + + // calculate difficulty of the imported sudoku + difficultyCheck.setRecordHistory(true); + difficultyCheck.setPuzzle(container.getFixedValues()); + difficultyCheck.solve(); + + container.parseDifficulty(difficultyCheck.getDifficulty().toString()); + + // A sudoku is that does not have a unique solution is deemed 'unplayable' and may not be started + startGame = difficultyCheck.hasUniqueSolution(); + + + } catch (IllegalArgumentException e) { + // If the imported code does not actually encode a valid sudoku, it needs to be rejected + startGame = false; + + /* + set up a blank sudoku field that can be displayed in the activity while the player is notified that + the link they imported does not encode a valid sudoku + */ + 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]); + } + + // Notify the user if the sudoku they tried to import cannot be played and finish the activity + if (!startGame) { + AlertDialog.Builder builder = new AlertDialog.Builder(GameActivity.this, R.style.AppTheme_Dialog); + 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(); + } + + 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); + 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"); + } + } + + /* + The 'isDailySudoku' key is only set by the DailySudokuActivity if a new daily sudoku needs to be calculated; + otherwise, the extras simply contain the id of the daily sudoku. Therefore, calculate the new daily sudoko if + 'isDailySudoku' is true + */ + if (isDailySudoku) { + gameController.loadNewDailySudokuLevel(); + } else { + + List loadableGames = GameStateManager.getLoadableGameList(); + + 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); + } + } } } else { gameController = savedInstanceState.getParcelable("gameController"); @@ -170,7 +343,7 @@ public class GameActivity extends BaseActivity implements NavigationView.OnNavig //set Special keys specialButtonLayout = (SudokuSpecialButtonLayout) findViewById(R.id.sudokuSpecialLayout); - specialButtonLayout.setButtons(p.x, gameController, keyboard, getFragmentManager(), orientation); + specialButtonLayout.setButtons(p.x, gameController, keyboard, getFragmentManager(), orientation, GameActivity.this); //set TimerView timerView = (TextView)findViewById(R.id.timerView); @@ -217,11 +390,21 @@ public class GameActivity extends BaseActivity implements NavigationView.OnNavig @Override public void onPause(){ super.onPause(); - if(!gameSolved) { + + // Do not save solved or unplayable sudokus + if(!gameSolved && startGame) { gameController.saveGame(this); } gameController.deleteTimer(); } + + @Override + public void onNewIntent(Intent intent) { + super.onNewIntent(intent); + startActivity(intent); + finish(); + } + @Override public void onResume(){ super.onResume(); @@ -233,7 +416,7 @@ public class GameActivity extends BaseActivity implements NavigationView.OnNavig gameController.initTimer(); - if(!gameSolved) { + if(!gameSolved && startGame) { mHandler.postDelayed(new Runnable() { @Override public void run() { @@ -253,6 +436,12 @@ public class GameActivity extends BaseActivity implements NavigationView.OnNavig keyboard.setSymbols(s); } + /*@Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.game_view, menu); + return true; + }*/ @Override public void onBackPressed() { @@ -265,13 +454,6 @@ public class GameActivity extends BaseActivity implements NavigationView.OnNavig } } - /*@Override - public boolean onCreateOptionsMenu(Menu menu) { - // Inflate the menu; this adds items to the action bar if it is present. - getMenuInflater().inflate(R.menu.game_view, menu); - return true; - }*/ - @SuppressWarnings("StatementWithEmptyBody") @Override public boolean onNavigationItemSelected(MenuItem item) { @@ -286,6 +468,39 @@ public class GameActivity extends BaseActivity implements NavigationView.OnNavig resetDialog.show(getFragmentManager(), "ResetDialogFragment"); break; + case R.id.menu_share: + // Create import link from current sudoku board + String scheme = GameActivity.validUris.size() > 0 ? GameActivity.validUris.get(0).getScheme() + + "://" + GameActivity.validUris.get(0).getHost() : ""; + if (!scheme.equals("") && !scheme.endsWith("/")) scheme = scheme + "/"; + String codeForClipboard = scheme + gameController.getCodeOfField(); + + // Create new ShareBoardDialog using the previously created links + ShareBoardDialog shareDialog = new ShareBoardDialog(); + shareDialog.setDisplayCode(codeForClipboard); + shareDialog.setCopyClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + // remember to include alternate code for older android versions + + //save link to clipboard + ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); + + 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 { + Toast.makeText(GameActivity.this, R.string.copy_code_error_toast, + Toast.LENGTH_LONG).show(); + } + } + }); + shareDialog.show(getFragmentManager(), "ShareDialogFragment"); + + break; + case R.id.nav_newgame: //create new game intent = new Intent(this, MainActivity.class); @@ -296,8 +511,6 @@ public class GameActivity extends BaseActivity implements NavigationView.OnNavig case R.id.menu_settings: //open settings intent = new Intent(this,SettingsActivity.class); - intent.putExtra( PreferenceActivity.EXTRA_SHOW_FRAGMENT, SettingsActivity.GamePreferenceFragment.class.getName() ); - intent.putExtra( PreferenceActivity.EXTRA_NO_HEADERS, true ); break; case R.id.nav_highscore: @@ -341,7 +554,6 @@ public class GameActivity extends BaseActivity implements NavigationView.OnNavig return true; } - @Override public void onSolved() { gameSolved = true; @@ -350,12 +562,33 @@ public class GameActivity extends BaseActivity implements NavigationView.OnNavig gameController.deleteGame(this); disableReset(); - //Show time hints new plus old best time + //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(); + /* + set 'finishedForToday' setting to 'true', signifying that the player has solved the daily + sudoku and can no longer play it today + */ + editor.putBoolean("finishedForToday", true); + editor.apply(); + } - statistics.saveGameStats(); + //Don't save statistics if game is custom + boolean isNewBestTime; + + if (!gameController.gameIsCustom()) { + //Show time hints new plus old best time + statistics.saveGameStats(); + isNewBestTime = gameController.getUsedHints() == 0 + && statistics.loadStats(gameController.getGameType(),gameController.getDifficulty()).getMinTime() >= gameController.getTime(); + + } else { + // cannot be best time if sudoku is custom + isNewBestTime = false; + } - boolean isNewBestTime = gameController.getUsedHints() == 0 - && statistics.loadStats(gameController.getGameType(),gameController.getDifficulty()).getMinTime() >= gameController.getTime(); dialog = new WinDialog(this, R.style.WinDialog , timeToString(gameController.getTime()), String.valueOf(gameController.getUsedHints()), isNewBestTime); @@ -395,18 +628,6 @@ public class GameActivity extends BaseActivity implements NavigationView.OnNavig specialButtonLayout.setButtonsEnabled(false); } - public String timeToString(int time) { - int seconds = time % 60; - int minutes = ((time - seconds) / 60) % 60; - int hours = (time - minutes - seconds) / (3600); - String h, m, s; - s = (seconds < 10) ? "0" + String.valueOf(seconds) : String.valueOf(seconds); - m = (minutes < 10) ? "0" + String.valueOf(minutes) : String.valueOf(minutes); - h = (hours < 10) ? "0" + String.valueOf(hours) : String.valueOf(hours); - return h + ":" + m + ":" + s; - } - - private void disableReset(){ NavigationView navView = (NavigationView)findViewById(R.id.nav_view); Menu navMenu = navView.getMenu(); @@ -418,7 +639,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); } @@ -433,11 +654,100 @@ public class GameActivity extends BaseActivity implements NavigationView.OnNavig gameController.resetLevel(); } + @Override + 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 public void onDialogNegativeClick() { // do nothing } + @Override + public void onSaveInstanceState(Bundle savedInstanceState) { + // Always call the superclass so it can save the view hierarchy state + super.onSaveInstanceState(savedInstanceState); + + // Save the user's current game state + savedInstanceState.putParcelable("gameController", gameController); + savedInstanceState.putBoolean("gameSolved", gameSolved); + } + + @Override + public void onRestoreInstanceState(Bundle savedInstanceState) { + super.onRestoreInstanceState(savedInstanceState); + + gameController = savedInstanceState.getParcelable("gameController"); + gameSolved = savedInstanceState.getBoolean("gameSolved"); + } + + 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(), R.style.AppTheme_Dialog); + + LayoutInflater inflater = getActivity().getLayoutInflater(); + DialogFragmentShareBoardBinding binding = DialogFragmentShareBoardBinding.inflate(inflater); + + binding.ver3DisplaySudokuTextView.setText(displayCode); + binding.ver3DisplaySudokuTextView.setEnabled(false); + 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) { + for(IShareDialogFragmentListener l : listeners) { + l.onShareDialogPositiveClick(binding.ver3DisplaySudokuTextView.getText().toString()); + } + } + }) + .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<>(); @@ -454,7 +764,7 @@ public class GameActivity extends BaseActivity implements NavigationView.OnNavig @Override public Dialog onCreateDialog(Bundle savedInstanceState) { // Use the Builder class for convenient dialog construction - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), R.style.AppTheme_Dialog); builder.setMessage(R.string.reset_confirmation) .setPositiveButton(R.string.reset_confirmation_confirm, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { @@ -472,20 +782,4 @@ public class GameActivity extends BaseActivity implements NavigationView.OnNavig } } - @Override - public void onSaveInstanceState(Bundle savedInstanceState) { - // Save the user's current game state - - savedInstanceState.putParcelable("gameController", gameController); - savedInstanceState.putInt("gameSolved", gameSolved ? 1 : 0); - - // Always call the superclass so it can save the view hierarchy state - super.onSaveInstanceState(savedInstanceState); - } - - @Override - public void onRestoreInstanceState(Bundle savedInstanceState) { - //super.onRestoreInstanceState(savedInstanceState); - } - } diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/HelpActivity.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/HelpActivity.java index d731b12..8499e4c 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/HelpActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/HelpActivity.java @@ -1,3 +1,19 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.ui; 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..5f47dc3 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/LoadGameActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/LoadGameActivity.java @@ -1,3 +1,19 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.ui; import android.app.Activity; @@ -21,6 +37,7 @@ 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.helper.GameInfoContainer; import org.secuso.privacyfriendlysudoku.game.GameDifficulty; @@ -70,6 +87,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) { @@ -137,7 +161,7 @@ public class LoadGameActivity extends BaseActivity implements IDeleteDialogFragm @Override public Dialog onCreateDialog(Bundle savedInstanceState) { // Use the Builder class for convenient dialog construction - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), R.style.AppTheme_Dialog); builder.setMessage(R.string.loadgame_delete_confirmation) .setPositiveButton(R.string.loadgame_delete_confirm, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { @@ -201,6 +225,8 @@ public class LoadGameActivity extends BaseActivity implements IDeleteDialogFragm 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); + ImageView customImage = (ImageView)convertView.findViewById(R.id.loadgame_listentry_custom_icon); + TextView customLabel = (TextView) convertView.findViewById(R.id.loadgame_listentry_custom_label); switch(gic.getGameType()) { case Default_6x6: @@ -221,6 +247,9 @@ public class LoadGameActivity extends BaseActivity implements IDeleteDialogFragm difficultyBar.setMax(GameDifficulty.getValidDifficultyList().size()); difficultyBar.setRating(GameDifficulty.getValidDifficultyList().indexOf(gic.getDifficulty())+1); + customImage.setImageResource(gic.isCustom() ? R.drawable.ic_circle_blue_36dp : R.drawable.ic_circle_grey_36dp); + customLabel.setVisibility(gic.isCustom() ? View.VISIBLE : View.GONE); + int time = gic.getTimePlayed(); int seconds = time % 60; int minutes = ((time -seconds)/60)%60 ; 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..be2b943 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/MainActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/MainActivity.java @@ -1,11 +1,34 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.ui; +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.DialogFragment; +import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; +import android.net.Uri; import android.os.Bundle; -import android.preference.PreferenceActivity; import android.preference.PreferenceManager; import com.google.android.material.navigation.NavigationView; + +import androidx.appcompat.app.AppCompatDelegate; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentPagerAdapter; @@ -13,31 +36,36 @@ import androidx.core.view.GravityCompat; import androidx.viewpager.widget.ViewPager; import androidx.drawerlayout.widget.DrawerLayout; import androidx.appcompat.app.ActionBarDrawerToggle; -import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; + import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.Button; +import android.widget.CheckBox; +import android.widget.CompoundButton; import android.widget.ImageView; import android.widget.RatingBar; import android.widget.TextView; import android.widget.Toast; +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.game.GameType; +import org.secuso.privacyfriendlysudoku.ui.listener.IImportDialogFragmentListener; import org.secuso.privacyfriendlysudoku.ui.view.R; +import org.secuso.privacyfriendlysudoku.ui.view.databinding.DialogFragmentImportBoardBinding; import java.util.LinkedList; import java.util.List; import static org.secuso.privacyfriendlysudoku.ui.TutorialActivity.ACTION_SHOW_ANYWAYS; -public class MainActivity extends BaseActivity implements NavigationView.OnNavigationItemSelectedListener{ +public class MainActivity extends BaseActivity implements NavigationView.OnNavigationItemSelectedListener, IImportDialogFragmentListener{ RatingBar difficultyBar; TextView difficultyText; @@ -53,9 +81,20 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig @Override protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - settings = PreferenceManager.getDefaultSharedPreferences(this); + if (settings.getBoolean("pref_dark_mode_setting", false )) { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); + + } else if (settings.getBoolean("pref_dark_mode_automatically_by_system", false)) { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM); + + } else if(settings.getBoolean("pref_dark_mode_automatically_by_battery", false)){ + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY); + } else { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO); + } + + super.onCreate(savedInstanceState); NewLevelManager newLevelManager = NewLevelManager.getInstance(getApplicationContext(), settings); @@ -121,17 +160,44 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig final LinkedList difficultyList = GameDifficulty.getValidDifficultyList(); difficultyBar.setNumStars(difficultyList.size()); difficultyBar.setMax(difficultyList.size()); + CheckBox createGameBar = (CheckBox) findViewById(R.id.circleButton); difficultyBar.setOnRatingBarChangeListener(new RatingBar.OnRatingBarChangeListener() { @Override public void onRatingChanged(RatingBar ratingBar, float rating, boolean fromUser) { - if (rating < 1) { - ratingBar.setRating(1); + createGameBar.setChecked(false); + ((Button) findViewById(R.id.playButton)).setText(R.string.new_game); + + if (rating >= 1) { + difficultyText.setText(getString(difficultyList.get((int) ratingBar.getRating() - 1).getStringResID())); + } else { + difficultyText.setText(R.string.difficulty_custom); + createGameBar.setChecked(true); + ((Button)findViewById(R.id.playButton)).setText(R.string.create_game); } - difficultyText.setText(getString(difficultyList.get((int) ratingBar.getRating() - 1).getStringResID())); } }); - GameDifficulty lastChosenDifficulty = GameDifficulty.valueOf(settings.getString("lastChosenDifficulty", "Moderate")); - difficultyBar.setRating(GameDifficulty.getValidDifficultyList().indexOf(lastChosenDifficulty) + 1); + + createGameBar.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + if (isChecked) { + + difficultyBar.setRating(0); + ((Button)findViewById(R.id.playButton)).setText(R.string.create_game); + } + createGameBar.setChecked(isChecked); + }}); + + String retrievedDifficulty = settings.getString("lastChosenDifficulty", "Moderate"); + GameDifficulty lastChosenDifficulty = GameDifficulty.valueOf( + retrievedDifficulty.equals("Custom")? GameDifficulty.Unspecified.toString() : retrievedDifficulty); + + if (lastChosenDifficulty == GameDifficulty.Unspecified) { + difficultyBar.setRating(0); + createGameBar.setChecked(true); + } else { + difficultyBar.setRating(GameDifficulty.getValidDifficultyList().indexOf(lastChosenDifficulty) + 1); + } /*LayerDrawable stars = (LayerDrawable)difficultyBar.getProgressDrawable(); stars.getDrawable(2).setColorFilter(getResources().getColor(R.color.colorPrimary), PorterDuff.Mode.SRC_ATOP);//Color for Stars fully selected stars.getDrawable(1).setColorFilter(getResources().getColor(R.color.middleblue), PorterDuff.Mode.SRC_ATOP);//Color for Stars partially selected @@ -166,8 +232,6 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig winScreen.show(fm,"win_screen_layout");*/ } - - public void onClick(View view) { Intent i = null; @@ -184,6 +248,18 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig break; case R.id.playButton: GameType gameType = GameType.getValidGameTypes().get(mViewPager.getCurrentItem()); + if (((CheckBox)findViewById(R.id.circleButton)).isChecked()) { + // start CreateSudokuActivity + i = new Intent(this, CreateSudokuActivity.class); + i.putExtra("gameType", gameType.name()); + + SharedPreferences.Editor editor = settings.edit(); + editor.putString("lastChosenGameType", gameType.name()); + editor.putString("lastChosenDifficulty", "Custom"); + editor.apply(); + //i.putExtra("gameDifficulty", GameDifficulty.Easy); + break; + } int index = difficultyBar.getProgress()-1; GameDifficulty gameDifficulty = GameDifficulty.getValidDifficultyList().get(index < 0 ? 0 : index); @@ -242,7 +318,7 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig Button continueButton = (Button)findViewById(R.id.continueButton); GameStateManager fm = new GameStateManager(getBaseContext(), settings); List gic = fm.loadGameStateInfo(); - if(gic.size() > 0) { + if(gic.size() > 0 && !(gic.size() == 1 && gic.get(0).getID() == GameController.DAILY_SUDOKU_ID)) { continueButton.setEnabled(true); continueButton.setBackgroundResource(R.drawable.standalone_button); } else { @@ -254,7 +330,7 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig @Override public boolean onNavigationItemSelected(MenuItem item) { // Handle navigation view item clicks here. - final int id = item.getItemId(); + int id = item.getItemId(); drawer.closeDrawer(GravityCompat.START); @@ -271,9 +347,9 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig } }, NAVDRAWER_LAUNCH_DELAY); - // fade out the active activity + // fade out the active activity (but not if the user chose to open the ImportBoardDialog) View mainContent = findViewById(R.id.main_content); - if (mainContent != null) { + if (mainContent != null && id != R.id.nav_import_sudoku) { mainContent.animate().alpha(0).setDuration(MAIN_CONTENT_FADEOUT_DURATION); } @@ -292,11 +368,13 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig Intent intent; switch(id) { + case R.id.nav_import_sudoku: + ImportBoardDialog dialog = new ImportBoardDialog(); + dialog.show(getFragmentManager(), "ImportDialogFragment"); + break; case R.id.menu_settings_main: //open settings - intent = new Intent(this,SettingsActivity.class); - intent.putExtra( PreferenceActivity.EXTRA_SHOW_FRAGMENT, SettingsActivity.GamePreferenceFragment.class.getName() ); - intent.putExtra( PreferenceActivity.EXTRA_NO_HEADERS, true ); + intent = new Intent(this, SettingsActivity.class); startActivity(intent); overridePendingTransition(0, 0); break; @@ -330,6 +408,12 @@ 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); + overridePendingTransition(0, 0); + break; + default: } return true; @@ -345,6 +429,106 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig return super.onOptionsItemSelected(item); }*/ + public void onImportDialogPositiveClick(String input) { + String inputSudoku = null; + String prefix = ""; + StringBuilder errorMessage = new StringBuilder(); + + // a valid input needs to contain exactly one of the valid prefixes + for (int i = 0; i < GameActivity.validUris.size(); i++) { + prefix = GameActivity.validUris.get(i).getHost().equals("") ? + GameActivity.validUris.get(i).getScheme() + "://" : + GameActivity.validUris.get(i).getScheme() + "://" + GameActivity.validUris.get(i).getHost() + "/"; + if (input.startsWith(prefix)) { + inputSudoku = input.replace(prefix, ""); + break; + } + + String endOfRecord = i == GameActivity.validUris.size() - 1 ? "" : ", "; + errorMessage.append(prefix); + errorMessage.append(endOfRecord); + } + + if (inputSudoku == null) { + Toast.makeText(MainActivity.this, + this.getString(R.string.menu_import_wrong_format_custom_sudoku) + " " + errorMessage.toString(), Toast.LENGTH_LONG).show(); + return; + } + + double size = Math.sqrt(inputSudoku.length()); + boolean validSize = false; + + // check whether or not the size of the encoded sudoku is valid; if not, notify the user + for (GameType type : GameType.getValidGameTypes()) { + if (type.getSize() == size) { + validSize = true; + break; + } + } + + if (!validSize) { + Toast.makeText(MainActivity.this, R.string.failed_to_verify_custom_sudoku_toast, Toast.LENGTH_LONG).show(); + return; + } + + GameType gameType = Enum.valueOf(GameType.class, "Default_" + (int)size + "x" + (int)size); + + //check whether or not the sudoku is valid and has a unique solution + boolean solvable = CreateSudokuActivity.verify(gameType, inputSudoku); + + // if the encoded sudoku is solvable, sent the code directly to the GameActivity; if not, notify the user + if (solvable) { + Toast.makeText(MainActivity.this, R.string.finished_verifying_custom_sudoku_toast, Toast.LENGTH_LONG).show(); + final Intent intent = new Intent(this, GameActivity.class); + intent.setData(Uri.parse(prefix + inputSudoku)); + startActivity(intent); + finish(); + } else { + Toast.makeText(MainActivity.this, R.string.failed_to_verify_custom_sudoku_toast, Toast.LENGTH_LONG).show(); + } + } + + public void onDialogNegativeClick() { + mNavigationView.setCheckedItem(R.id.nav_newgame_main); + } + + public static class ImportBoardDialog extends DialogFragment { + private LinkedList listeners = new LinkedList<>(); + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + // Verify that the host activity implements the callback interface + if(activity instanceof IImportDialogFragmentListener) { + listeners.add((IImportDialogFragmentListener) activity); + } + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), R.style.AppTheme_Dialog); + LayoutInflater inflater = getActivity().getLayoutInflater(); + DialogFragmentImportBoardBinding binding = DialogFragmentImportBoardBinding.inflate(inflater); + builder.setView(binding.getRoot()); + builder.setMessage(R.string.dialog_import_custom_sudoku); + builder.setPositiveButton(R.string.dialog_import_custom_sudoku_positive_button, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + for(IImportDialogFragmentListener l : listeners) { + l.onImportDialogPositiveClick(binding.ver3ImportSudokuEditText.getText().toString()); + } + } + }) + .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + for(IImportDialogFragmentListener l : listeners) { + l.onDialogNegativeClick(); + } + } + }); + return builder.create(); + } + + } /** * A {@link FragmentPagerAdapter} that returns a fragment corresponding to diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/PrefManager.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/PrefManager.java index 39b0929..8280b54 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/PrefManager.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/PrefManager.java @@ -1,3 +1,19 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.ui; import android.content.Context; diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/SettingsActivity.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/SettingsActivity.java index 448b560..7f1d963 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/SettingsActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/SettingsActivity.java @@ -1,192 +1,120 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.ui; - -import android.annotation.TargetApi; -import android.content.Context; -import android.content.res.Configuration; +import android.content.SharedPreferences; import android.os.Build; import android.os.Bundle; -import android.preference.ListPreference; -import android.preference.Preference; -import android.preference.PreferenceActivity; -import androidx.appcompat.app.ActionBar; import android.preference.PreferenceFragment; import android.preference.PreferenceManager; import android.view.MenuItem; -import android.view.View; +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.app.AppCompatDelegate; +import androidx.preference.Preference; +import androidx.preference.PreferenceFragmentCompat; +import androidx.preference.PreferenceGroup; +import androidx.preference.PreferenceScreen; + +import org.secuso.privacyfriendlysudoku.ui.SettingsActivity; import org.secuso.privacyfriendlysudoku.ui.view.R; -import java.util.List; - -/** - * A {@link PreferenceActivity} that presents a set of application settings. On - * handset devices, settings are presented as a single list. On tablets, - * settings are split by category, with category headers shown to the left of - * the list of settings. - *

- * See - * Android Design: Settings for design guidelines and the Settings - * API Guide for more information on developing a Settings UI. - */ -public class SettingsActivity extends AppCompatPreferenceActivity { - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setupActionBar(); - - overridePendingTransition(0, 0); - - View mainContent = findViewById(R.id.main_content); - if (mainContent != null) { - mainContent.setAlpha(0); - mainContent.animate().alpha(1).setDuration(BaseActivity.MAIN_CONTENT_FADEIN_DURATION); - } - } - - /** - * Set up the {@link android.app.ActionBar}, if the API is available. - */ - private void setupActionBar() { - ActionBar actionBar = getSupportActionBar(); - if (actionBar != null) { - // Show the Up button in the action bar. - actionBar.setDisplayHomeAsUpEnabled(true); - } - } - - @Override - public boolean onMenuItemSelected(int featureId, MenuItem item) { - int id = item.getItemId(); - if (id == android.R.id.home) { - if (!super.onMenuItemSelected(featureId, item)) { - finish(); - //NavUtils.navigateUpFromSameTask(this); - } - return true; - } - return super.onMenuItemSelected(featureId, item); - } +public class SettingsActivity extends AppCompatActivity { - /** - * {@inheritDoc} - */ - @Override - public boolean onIsMultiPane() { - return isXLargeTablet(this); - } + private static SettingsFragment settingsFragment; - /** - * Helper method to determine if the device has an extra-large screen. For - * example, 10" tablets are extra-large. - */ - private static boolean isXLargeTablet(Context context) { - return (context.getResources().getConfiguration().screenLayout - & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_XLARGE; - } + private static void recheckNightModeProperties(SharedPreferences sharedPreferences) { - /** - * {@inheritDoc} - */ - @Override - @TargetApi(Build.VERSION_CODES.HONEYCOMB) - public void onBuildHeaders(List

target) { - loadHeadersFromResource(R.xml.pref_settings_headers, target); - } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - /** - * A preference value change listener that updates the preference's summary - * to reflect its new value. - */ - private static Preference.OnPreferenceChangeListener sBindPreferenceSummaryToValueListener = new Preference.OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object value) { - String stringValue = value.toString(); - - if (preference instanceof ListPreference) { - // For list preferences, look up the correct display value in - // the preference's 'entries' list. - ListPreference listPreference = (ListPreference) preference; - int index = listPreference.findIndexOfValue(stringValue); - - // Set the summary to reflect the new value. - preference.setSummary( - index >= 0 - ? listPreference.getEntries()[index] - : null); + if (sharedPreferences.getBoolean("pref_dark_mode_setting", false)) { + settingsFragment.findPreference("pref_dark_mode_automatically_by_system").setEnabled(false); + settingsFragment.findPreference("pref_dark_mode_automatically_by_battery").setEnabled(false); } else { - // For all other preferences, set the summary to the value's - // simple string representation. - preference.setSummary(stringValue); + if (sharedPreferences.getBoolean("pref_dark_mode_automatically_by_battery", false) && sharedPreferences.getBoolean("pref_dark_mode_automatically_by_system", false) ) { + sharedPreferences.edit().putBoolean("pref_dark_mode_automatically_by_battery", false).commit(); + } + + settingsFragment.findPreference("pref_dark_mode_automatically_by_system").setEnabled(!sharedPreferences.getBoolean("pref_dark_mode_automatically_by_battery", false)); + settingsFragment.findPreference("pref_dark_mode_automatically_by_battery").setEnabled(!sharedPreferences.getBoolean("pref_dark_mode_automatically_by_system", false)); + }} + + if (sharedPreferences.getBoolean("pref_dark_mode_setting", false )) { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); + + } else if (sharedPreferences.getBoolean("pref_dark_mode_automatically_by_system", false)) { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM); + } else if(sharedPreferences.getBoolean("pref_dark_mode_automatically_by_battery", false)){ + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY); + } else { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO); + } + } + + static SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() { + + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { + if (key.equals("pref_dark_mode_setting")|| key.equals("pref_dark_mode_automatically_by_system")||key.equals("pref_dark_mode_automatically_by_battery")) { + recheckNightModeProperties(sharedPreferences); } - return true; } }; - /** - * Binds a preference's summary to its value. More specifically, when the - * preference's value is changed, its summary (line of text below the - * preference title) is updated to reflect the value. The summary is also - * immediately updated upon calling this method. The exact display format is - * dependent on the type of preference. - * - * @see #sBindPreferenceSummaryToValueListener - */ - private static void bindPreferenceSummaryToValue(Preference preference) { - // Set the listener to watch for value changes. - preference.setOnPreferenceChangeListener(sBindPreferenceSummaryToValueListener); - - // Trigger the listener immediately with the preference's - // current value. - sBindPreferenceSummaryToValueListener.onPreferenceChange(preference, - PreferenceManager - .getDefaultSharedPreferences(preference.getContext()) - .getString(preference.getKey(), "")); - } - - /** - * This method stops fragment injection in malicious applications. - * Make sure to deny any unknown fragments here. - */ - protected boolean isValidFragment(String fragmentName) { - return PreferenceFragment.class.getName().equals(fragmentName) - || GamePreferenceFragment.class.getName().equals(fragmentName); - } - - /** - * This fragment shows general preferences only. It is used when the - * activity is showing a two-pane settings UI. - */ - @TargetApi(Build.VERSION_CODES.HONEYCOMB) - public static class GamePreferenceFragment extends PreferenceFragment { - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - addPreferencesFromResource(R.xml.pref_settings_general); - setHasOptionsMenu(true); - - // Bind the summaries of EditText/List/Dialog/Ringtone preferences - // to their values. When their values change, their summaries are - // updated to reflect the new value, per the Android Design - // guidelines. - - //bindPreferenceSummaryToValue(findPreference("example_text")); - //bindPreferenceSummaryToValue(findPreference("example_list")); - + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.settings_activity); + settingsFragment = new SettingsFragment(); + getSupportFragmentManager() + .beginTransaction() + .replace(R.id.settings, settingsFragment) + .commit(); + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + actionBar.setDisplayHomeAsUpEnabled(true); } + + + } + @Override + public boolean onOptionsItemSelected(MenuItem item) { + int id = item.getItemId(); + if (id == android.R.id.home) { + finish(); + return true; + } + return super.onOptionsItemSelected(item); + } + + + public static class SettingsFragment extends PreferenceFragmentCompat { + @Override - public boolean onOptionsItemSelected(MenuItem item) { - int id = item.getItemId(); - if (id == android.R.id.home) { - getActivity().finish(); - //startActivity(new Intent(getActivity(), SettingsActivity.class)); - return true; - } - return super.onOptionsItemSelected(item); + public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { + setPreferencesFromResource(R.xml.pref_settings_general, rootKey); + SharedPreferences preferenceManager = PreferenceManager + .getDefaultSharedPreferences(getActivity()); + preferenceManager.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener); + recheckNightModeProperties(preferenceManager); } } -} +} \ No newline at end of file diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/SplashActivity.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/SplashActivity.java index 032fc39..2c4a3d6 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/SplashActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/SplashActivity.java @@ -1,3 +1,19 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.ui; import android.content.Intent; diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/StatsActivity.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/StatsActivity.java index 1119d5a..17c504b 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/StatsActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/StatsActivity.java @@ -1,3 +1,19 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.ui; import android.content.Context; diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/TutorialActivity.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/TutorialActivity.java index 20b16b6..609d750 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/TutorialActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/TutorialActivity.java @@ -1,3 +1,19 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.ui; import android.content.Context; @@ -20,7 +36,6 @@ import android.widget.TextView; import org.secuso.privacyfriendlysudoku.ui.view.R; - /** * Class structure taken from tutorial at http://www.androidhive.info/2016/05/android-build-intro-slider-app/ * @author Karola Marky diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/listener/IDeleteDialogFragmentListener.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/listener/IDeleteDialogFragmentListener.java index 1005c65..5b545d9 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/listener/IDeleteDialogFragmentListener.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/listener/IDeleteDialogFragmentListener.java @@ -1,3 +1,19 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.ui.listener; /** diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/listener/IFinalizeDialogFragmentListener.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/listener/IFinalizeDialogFragmentListener.java new file mode 100644 index 0000000..d61ffd3 --- /dev/null +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/listener/IFinalizeDialogFragmentListener.java @@ -0,0 +1,22 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ +package org.secuso.privacyfriendlysudoku.ui.listener; + +public interface IFinalizeDialogFragmentListener { + public void onFinalizeDialogPositiveClick(); + public void onDialogNegativeClick(); +} \ No newline at end of file diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/listener/IHintDialogFragmentListener.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/listener/IHintDialogFragmentListener.java index c22825e..0d73b15 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/listener/IHintDialogFragmentListener.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/listener/IHintDialogFragmentListener.java @@ -1,3 +1,19 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.ui.listener; /** diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/listener/IImportDialogFragmentListener.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/listener/IImportDialogFragmentListener.java new file mode 100644 index 0000000..8100f87 --- /dev/null +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/listener/IImportDialogFragmentListener.java @@ -0,0 +1,22 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ +package org.secuso.privacyfriendlysudoku.ui.listener; + +public interface IImportDialogFragmentListener { + public void onImportDialogPositiveClick(String input); + public void onDialogNegativeClick(); +} diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/listener/IResetDialogFragmentListener.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/listener/IResetDialogFragmentListener.java index 89800a3..149c9bd 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/listener/IResetDialogFragmentListener.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/listener/IResetDialogFragmentListener.java @@ -1,3 +1,19 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.ui.listener; /** 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..1520da9 --- /dev/null +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/listener/IShareDialogFragmentListener.java @@ -0,0 +1,22 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ +package org.secuso.privacyfriendlysudoku.ui.listener; + +public interface IShareDialogFragmentListener { + public void onShareDialogPositiveClick(String input); + public void onDialogNegativeClick(); +} diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/CellHighlightTypes.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/CellHighlightTypes.java index 9536477..f51ec3e 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/CellHighlightTypes.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/CellHighlightTypes.java @@ -1,3 +1,19 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.ui.view; /** diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/CreateSudokuButtonType.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/CreateSudokuButtonType.java new file mode 100644 index 0000000..aa7912e --- /dev/null +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/CreateSudokuButtonType.java @@ -0,0 +1,69 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ +package org.secuso.privacyfriendlysudoku.ui.view; + +import androidx.annotation.DrawableRes; + +import java.util.ArrayList; +import java.util.List; + +public enum CreateSudokuButtonType { + Unspecified(R.drawable.ic_accessibility_black_48dp),// placeholder + Value(R.drawable.ic_accessibility_black_48dp), // should be non picture + Do(R.drawable.ic_redo_black_48dp), + Undo(R.drawable.ic_undo_black_48dp), + Import(R.drawable.ic_import), + Spacer(R.drawable.ic_accessibility_black_48dp),//placeholder + Delete(R.drawable.ic_delete_black_48dp), + Finalize(R.drawable.ic_finalize), + Reset(R.drawable.ic_settings_backup_restore_black_48dp); + + + private int resID; + + CreateSudokuButtonType(@DrawableRes int res){ + this.resID = res; + } + + public int getResID() { + return resID; + } + + public static List getSpecialButtons() { + ArrayList result = new ArrayList(); + result.add(Undo); + result.add(Do); + result.add(Finalize); + //result.add(Spacer); + result.add(Delete); + result.add(Import); + return result; + } + public static String getName(CreateSudokuButtonType type) { + switch (type) { + case Do: return "Do"; + case Undo: return "Un"; + case Finalize: return "Fi"; + case Import: return "Im"; + case Spacer: return ""; + case Delete: return "Del"; + default:return "NotSet"; + } + } +} + + diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/CreateSudokuSpecialButton.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/CreateSudokuSpecialButton.java new file mode 100644 index 0000000..cde1c5a --- /dev/null +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/CreateSudokuSpecialButton.java @@ -0,0 +1,36 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ +package org.secuso.privacyfriendlysudoku.ui.view; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.ImageButton; + +public class CreateSudokuSpecialButton extends androidx.appcompat.widget.AppCompatImageButton { + private int value = -1; + private CreateSudokuButtonType type = CreateSudokuButtonType.Unspecified; + + public CreateSudokuSpecialButton(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public void setValue(int value) { this.value = value; } + public void setType(CreateSudokuButtonType type) { this.type = type; } + public int getValue () { return value; } + public CreateSudokuButtonType getType() { return type; } + +} diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/CreateSudokuSpecialButtonLayout.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/CreateSudokuSpecialButtonLayout.java new file mode 100644 index 0000000..0a92c1e --- /dev/null +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/CreateSudokuSpecialButtonLayout.java @@ -0,0 +1,233 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ +package org.secuso.privacyfriendlysudoku.ui.view; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.DialogFragment; +import android.app.FragmentManager; +import android.content.Context; +import android.content.DialogInterface; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.Toast; + +import androidx.core.content.ContextCompat; + +import org.secuso.privacyfriendlysudoku.controller.GameController; +import org.secuso.privacyfriendlysudoku.game.listener.IHighlightChangedListener; +import org.secuso.privacyfriendlysudoku.ui.GameActivity; +import org.secuso.privacyfriendlysudoku.ui.MainActivity; +import org.secuso.privacyfriendlysudoku.ui.listener.IFinalizeDialogFragmentListener; +import org.secuso.privacyfriendlysudoku.ui.listener.IHintDialogFragmentListener; +import org.secuso.privacyfriendlysudoku.ui.listener.IImportDialogFragmentListener; + +import java.util.LinkedList; + +import static org.secuso.privacyfriendlysudoku.ui.view.CreateSudokuButtonType.Spacer; +import static org.secuso.privacyfriendlysudoku.ui.view.CreateSudokuButtonType.getSpecialButtons; + +public class CreateSudokuSpecialButtonLayout extends LinearLayout implements IHighlightChangedListener { + + IFinalizeDialogFragmentListener finalizeDialogFragmentListener; + IImportDialogFragmentListener importDialogFragmentListener; + CreateSudokuSpecialButton[] fixedButtons; + public int fixedButtonsCount = getSpecialButtons().size(); + GameController gameController; + SudokuKeyboardLayout keyboard; + Bitmap bitMap,bitResult; + Canvas canvas; + FragmentManager fragmentManager; + Context context; + + OnClickListener listener = new OnClickListener() { + @Override + public void onClick(View v) { + if(v instanceof CreateSudokuSpecialButton) { + CreateSudokuSpecialButton btn = (CreateSudokuSpecialButton)v; + + //int row = gameController.getSelectedRow(); + //int col = gameController.getSelectedCol(); + + switch(btn.getType()) { + case Delete: + gameController.deleteSelectedCellsValue(); + break; + case Import: + MainActivity.ImportBoardDialog impDialog = new MainActivity.ImportBoardDialog(); + impDialog.show(fragmentManager, "ImportDialogFragment"); + break; + case Do: + gameController.ReDo(); + break; + case Undo: + gameController.UnDo(); + break; + case Finalize: + FinalizeConfirmationDialog dialog = new FinalizeConfirmationDialog(); + dialog.show(fragmentManager, "FinalizeDialogFragment"); + default: + break; + } + } + } + }; + + + public CreateSudokuSpecialButtonLayout(Context context, AttributeSet attrs) { + super(context, attrs); + setWeightSum(fixedButtonsCount); + this.context = context; + } + + public void setButtonsEnabled(boolean enabled) { + for(CreateSudokuSpecialButton b : fixedButtons) { + b.setEnabled(enabled); + } + } + + public void setButtons(int width, GameController gc, SudokuKeyboardLayout key, FragmentManager fm, + int orientation, Context cxt, IFinalizeDialogFragmentListener finalizeListener, + IImportDialogFragmentListener importListener) { + fragmentManager = fm; + keyboard=key; + gameController = gc; + context = cxt; + finalizeDialogFragmentListener = finalizeListener; + importDialogFragmentListener = importListener; + if(gameController != null) { + gameController.registerHighlightChangedListener(this); + } + fixedButtons = new CreateSudokuSpecialButton[fixedButtonsCount]; + LayoutParams p; + int i = 0; + //ArrayList type = (ArrayList) SudokuButtonType.getSpecialButtons(); + for (CreateSudokuButtonType t : getSpecialButtons()){ + fixedButtons[i] = new CreateSudokuSpecialButton(getContext(),null); + if(orientation == LinearLayout.HORIZONTAL) { + p = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, 1); + } else { + p = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, 1); + fixedButtons[i].setPadding(25, 0, 25, 0); + } + p.setMargins(5, 5, 5, 5); + + //int width2 =width/(fixedButtonsCount); + //p.width= width2-15; + if(t == Spacer) { + fixedButtons[i].setVisibility(View.INVISIBLE); + } + /*if(t == SudokuButtonType.Do && !gameController.isRedoAvailable()) { + fixedButtons[i].setEnabled(false); + } + if(t == SudokuButtonType.Undo && !gameController.isUndoAvailable()) { + fixedButtons[i].setEnabled(false); + }*/ + fixedButtons[i].setLayoutParams(p); + fixedButtons[i].setType(t); + fixedButtons[i].setImageDrawable(getResources().getDrawable(t.getResID())); + // fixedButtons[i].setText(SudokuButtonType.getName(t)); + fixedButtons[i].setScaleType(ImageView.ScaleType.CENTER); + fixedButtons[i].setAdjustViewBounds(true); + fixedButtons[i].setOnClickListener(listener); + fixedButtons[i].setBackgroundResource(R.drawable.numpad_highlighted_four); + addView(fixedButtons[i]); + + if(fixedButtons[i].getVisibility() == View.VISIBLE) { + Drawable drawable = ContextCompat.getDrawable(context, fixedButtons[i].getType().getResID()); + setUpVectorDrawable(drawable); + drawable.draw(canvas); + fixedButtons[i].setImageBitmap(bitResult); + } + i++; + } + + } + + @Override + public void onHighlightChanged() { + for(int i = 0; i < fixedButtons.length; i++) { + switch(fixedButtons[i].getType()) { + case Undo: + fixedButtons[i].setBackgroundResource(gameController.isUndoAvailable() ? + R.drawable.numpad_highlighted_four : R.drawable.inactive_button); + break; + case Do: + fixedButtons[i].setBackgroundResource(gameController.isRedoAvailable() ? + R.drawable.numpad_highlighted_four : R.drawable.inactive_button); + break; + default: + break; + } + } + } + + /* + Set up the vector drawables so that they can be properly displayed despite using theme attributes for their fill color + */ + private void setUpVectorDrawable(Drawable drawable) { + drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); + bitMap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), + drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); + bitResult = Bitmap.createBitmap(bitMap.getWidth(), bitMap.getHeight(), Bitmap.Config.ARGB_8888); + + canvas = new Canvas(bitResult); + } + + public static class FinalizeConfirmationDialog extends DialogFragment { + + LinkedList listeners = new LinkedList<>(); + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + // Verify that the host activity implements the callback interface + if(activity instanceof IFinalizeDialogFragmentListener) { + listeners.add((IFinalizeDialogFragmentListener) activity); + } + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + // Use the Builder class for convenient dialog construction + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), R.style.AppTheme_Dialog); + builder.setMessage(R.string.finalize_custom_sudoku_dialog) + .setPositiveButton(R.string.okay, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + for(IFinalizeDialogFragmentListener l : listeners) { + l.onFinalizeDialogPositiveClick(); + } + } + }) + .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + // User cancelled the dialog + } + }); + return builder.create(); + } + } +} diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/SudokuButton.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/SudokuButton.java index 4391125..1b91726 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/SudokuButton.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/SudokuButton.java @@ -1,3 +1,19 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.ui.view; import android.content.Context; @@ -12,7 +28,7 @@ import android.widget.Button; -public class SudokuButton extends Button { +public class SudokuButton extends androidx.appcompat.widget.AppCompatButton { private int value = -1; private SudokuButtonType type = SudokuButtonType.Unspecified; diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/SudokuButtonType.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/SudokuButtonType.java index 5fb3854..79d43b9 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/SudokuButtonType.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/SudokuButtonType.java @@ -1,3 +1,19 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.ui.view; import androidx.annotation.DrawableRes; diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/SudokuCellView.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/SudokuCellView.java index 06b9a0a..a9ec0c1 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/SudokuCellView.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/SudokuCellView.java @@ -1,6 +1,24 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.ui.view; import android.content.Context; +import android.content.res.Configuration; +import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; @@ -31,15 +49,32 @@ public class SudokuCellView extends View { Symbol symbolsToUse = Symbol.Default; RelativeLayout.LayoutParams params; - - public SudokuCellView(Context context) { - super(context); - } + int backgroundColor; + int backgroundErrorColor; + int backgroundSelectedColor; + int backgroundConnectedOuterColor; + int backgroundConnectedInnerColor; + int backgroundValueHighlightedColor; + int backgroundValueHighlightedSelectedColor; + int textColor; public SudokuCellView(Context context, AttributeSet attrs){ super(context, attrs); + + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SudokuCellView); + backgroundColor = a.getColor(R.styleable.SudokuCellView_sudokuCellBackgroundColor, Color.argb(255, 200, 200, 200)); + backgroundErrorColor = a.getColor(R.styleable.SudokuCellView_sudokuCellBackgroundErrorColor, Color.argb(255, 200, 200, 200)); + backgroundSelectedColor = a.getColor(R.styleable.SudokuCellView_sudokuCellBackgroundSelectedColor, Color.argb(255, 200, 200, 200)); + backgroundConnectedOuterColor = a.getColor(R.styleable.SudokuCellView_sudokuCellBackgroundConnectedOuterColor, Color.argb(255, 200, 200, 200)); + backgroundConnectedInnerColor = a.getColor(R.styleable.SudokuCellView_sudokuCellBackgroundConnectedInnerColor, Color.argb(255, 200, 200, 200)); + backgroundValueHighlightedColor = a.getColor(R.styleable.SudokuCellView_sudokuCellBackgroundValueHighlightedColor, Color.argb(255, 200, 200, 200)); + backgroundValueHighlightedSelectedColor = a.getColor(R.styleable.SudokuCellView_sudokuCellBackgroundValueHighlightedSelectedColor, Color.argb(255, 200, 200, 200)); + textColor = a.getColor(R.styleable.SudokuCellView_sudokuCellTextColor, Color.argb(255, 200, 200, 200)); + a.recycle(); } + + public void setSelected(boolean b) { this.selected = b; } @@ -89,13 +124,6 @@ public class SudokuCellView extends View { return true; }*/ - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - - - } - @Override public void onDraw(Canvas canvas) { super.onDraw(canvas); @@ -108,27 +136,28 @@ public class SudokuCellView extends View { Paint p = new Paint(); switch(highlightType) { case Default: - p.setColor(Color.WHITE); + p.setColor(backgroundColor); break; case Error: - p.setColor(Color.LTGRAY); + p.setColor(backgroundErrorColor); break; case Selected: - p.setColor(Color.GREEN); + p.setColor(backgroundSelectedColor); break; case Connected: - p.setColor(Color.WHITE); + p.setColor(backgroundConnectedOuterColor); drawBackground(canvas, 3, 3, mWidth - 3, mHeight - 3, p); - p.setColor(Color.YELLOW); + p.setColor(backgroundConnectedInnerColor); p.setAlpha(100); break; case Value_Highlighted: - p.setColor(Color.YELLOW); + p.setColor(backgroundValueHighlightedColor); break; case Value_Highlighted_Selected: - p.setColor(Color.CYAN); + p.setColor(backgroundValueHighlightedSelectedColor); + break; default: - p.setColor(Color.WHITE); + p.setColor(backgroundColor); } @@ -147,6 +176,7 @@ public class SudokuCellView extends View { public void drawValue(Canvas canvas) { Paint p = new Paint(); + p.setColor(textColor); int root = (int) Math.sqrt(size); int j= root+1; int k = root; diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/SudokuFieldLayout.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/SudokuFieldLayout.java index 0aef74f..9d78d98 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/SudokuFieldLayout.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/SudokuFieldLayout.java @@ -1,7 +1,25 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.ui.view; import android.content.Context; import android.content.SharedPreferences; +import android.content.res.Configuration; +import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; @@ -31,6 +49,9 @@ public class SudokuFieldLayout extends RelativeLayout implements IHighlightChang private int gameCellHeight; private SharedPreferences settings; private Paint p = new Paint(); + int backgroundColor; + int errorColor; + int sectionLineColor; private OnTouchListener listener = new OnTouchListener() { @Override @@ -55,8 +76,15 @@ public class SudokuFieldLayout extends RelativeLayout implements IHighlightChang public SudokuFieldLayout(Context context, AttributeSet attrs) { super(context, attrs); this.attrs=attrs; + + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SudokuFieldLayout); + backgroundColor = a.getColor(R.styleable.SudokuFieldLayout_sudokuFieldGridColor, Color.argb(255, 200, 200, 200)); + errorColor = a.getColor(R.styleable.SudokuFieldLayout_sudokuFieldErrorColor, Color.RED); + sectionLineColor = a.getColor(R.styleable.SudokuFieldLayout_sudokuFieldSectionLineColor, Color.BLACK); + a.recycle(); + setWillNotDraw(false); - setBackgroundColor(Color.argb(255, 200, 200, 200)); + setBackgroundColor(backgroundColor); } public void setSettingsAndGame(SharedPreferences sharedPref, GameController gc) { @@ -98,7 +126,7 @@ public class SudokuFieldLayout extends RelativeLayout implements IHighlightChang if(gameController == null) return; - p.setColor(Color.BLACK); + p.setColor(sectionLineColor); p.setStrokeWidth(5); int horizontalSections = gameController.getSize() / sectionWidth; @@ -222,7 +250,7 @@ public class SudokuFieldLayout extends RelativeLayout implements IHighlightChang p = new Paint(Paint.ANTI_ALIAS_FLAG); p.setStyle(Paint.Style.STROKE); p.setStrokeWidth(4); - p.setColor(Color.RED); + p.setColor(errorColor); float offsetX = 0; float offsetY = 0; diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/SudokuKeyboardLayout.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/SudokuKeyboardLayout.java index f6adb3b..f54aca5 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/SudokuKeyboardLayout.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/SudokuKeyboardLayout.java @@ -1,3 +1,19 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.ui.view; import android.content.Context; diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/SudokuSpecialButton.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/SudokuSpecialButton.java index d177ce2..920be2d 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/SudokuSpecialButton.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/SudokuSpecialButton.java @@ -1,3 +1,19 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.ui.view; import android.content.Context; @@ -7,7 +23,7 @@ import android.widget.ImageButton; /** * Created by TMZ_LToP on 07.12.2015. */ -public class SudokuSpecialButton extends ImageButton { +public class SudokuSpecialButton extends androidx.appcompat.widget.AppCompatImageButton { private int value = -1; private SudokuButtonType type = SudokuButtonType.Unspecified; diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/SudokuSpecialButtonLayout.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/SudokuSpecialButtonLayout.java index 4bf1ba5..b676b89 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/SudokuSpecialButtonLayout.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/SudokuSpecialButtonLayout.java @@ -1,3 +1,19 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.ui.view; import android.app.Activity; @@ -10,6 +26,7 @@ import android.content.DialogInterface; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; +import android.graphics.drawable.Drawable; import android.os.Bundle; import android.util.AttributeSet; import android.view.View; @@ -18,8 +35,11 @@ import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.Toast; +import androidx.core.content.ContextCompat; + import org.secuso.privacyfriendlysudoku.controller.GameController; import org.secuso.privacyfriendlysudoku.game.listener.IHighlightChangedListener; +import org.secuso.privacyfriendlysudoku.ui.GameActivity; import org.secuso.privacyfriendlysudoku.ui.listener.IHintDialogFragmentListener; import java.util.LinkedList; @@ -40,6 +60,7 @@ public class SudokuSpecialButtonLayout extends LinearLayout implements IHighligh Bitmap bitMap,bitResult; Canvas canvas; FragmentManager fragmentManager; + Context context; OnClickListener listener = new OnClickListener() { @Override @@ -68,7 +89,7 @@ public class SudokuSpecialButtonLayout extends LinearLayout implements IHighligh break; case Hint: if(gameController.isValidCellSelected()) { - if(gameController.getUsedHints() == 0) { + if(gameController.getUsedHints() == 0 && !gameController.gameIsCustom()) { // are you sure you want to use a hint? HintConfirmationDialog hintDialog = new HintConfirmationDialog(); hintDialog.show(fragmentManager, "HintDialogFragment"); @@ -93,6 +114,7 @@ public class SudokuSpecialButtonLayout extends LinearLayout implements IHighligh public SudokuSpecialButtonLayout(Context context, AttributeSet attrs) { super(context, attrs); setWeightSum(fixedButtonsCount); + this.context = context; } public void setButtonsEnabled(boolean enabled) { @@ -101,10 +123,11 @@ public class SudokuSpecialButtonLayout extends LinearLayout implements IHighligh } } - public void setButtons(int width, GameController gc, SudokuKeyboardLayout key, FragmentManager fm, int orientation) { + public void setButtons(int width, GameController gc, SudokuKeyboardLayout key, FragmentManager fm, int orientation, Context cxt) { fragmentManager = fm; keyboard=key; gameController = gc; + context = cxt; if(gameController != null) { gameController.registerHighlightChangedListener(this); } @@ -142,6 +165,13 @@ public class SudokuSpecialButtonLayout extends LinearLayout implements IHighligh fixedButtons[i].setOnClickListener(listener); fixedButtons[i].setBackgroundResource(R.drawable.numpad_highlighted_four); addView(fixedButtons[i]); + + if(fixedButtons[i].getVisibility() == View.VISIBLE) { + Drawable drawable = ContextCompat.getDrawable(context, fixedButtons[i].getType().getResID()); + setUpVectorDrawable(drawable); + drawable.draw(canvas); + fixedButtons[i].setImageBitmap(bitResult); + } i++; } @@ -160,12 +190,13 @@ public class SudokuSpecialButtonLayout extends LinearLayout implements IHighligh R.drawable.numpad_highlighted_four : R.drawable.inactive_button); break; case NoteToggle: - bitMap = BitmapFactory.decodeResource(getResources(), fixedButtons[i].getType().getResID()); - bitResult = Bitmap.createBitmap(bitMap.getWidth(), bitMap.getHeight(), Bitmap.Config.ARGB_8888); + Drawable drawable = ContextCompat.getDrawable(context, fixedButtons[i].getType().getResID()); + // prepare canvas for the rotation of the note drawable + setUpVectorDrawable(drawable); - canvas = new Canvas(bitResult); canvas.rotate(gameController.getNoteStatus() ? 45.0f : 0.0f, bitMap.getWidth()/2, bitMap.getHeight()/2); canvas.drawBitmap(bitMap, 0, 0, null); + drawable.draw(canvas); fixedButtons[i].setImageBitmap(bitResult); fixedButtons[i].setBackgroundResource(gameController.getNoteStatus() ? R.drawable.numpad_highlighted_three : R.drawable.numpad_highlighted_four); @@ -179,6 +210,18 @@ public class SudokuSpecialButtonLayout extends LinearLayout implements IHighligh } } + /* + Set up the vector drawables so that they can be properly displayed despite using theme attributes for their fill color + */ + private void setUpVectorDrawable(Drawable drawable) { + drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); + bitMap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), + drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); + bitResult = Bitmap.createBitmap(bitMap.getWidth(), bitMap.getHeight(), Bitmap.Config.ARGB_8888); + + canvas = new Canvas(bitResult); + } + public static class HintConfirmationDialog extends DialogFragment { LinkedList listeners = new LinkedList<>(); @@ -195,7 +238,7 @@ public class SudokuSpecialButtonLayout extends LinearLayout implements IHighligh @Override public Dialog onCreateDialog(Bundle savedInstanceState) { // Use the Builder class for convenient dialog construction - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), R.style.AppTheme_Dialog); builder.setMessage(R.string.hint_confirmation) .setPositiveButton(R.string.hint_confirmation_confirm, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/WinDialog.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/WinDialog.java index e3645f7..a94e085 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/WinDialog.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/view/WinDialog.java @@ -1,3 +1,19 @@ +/* + This file is part of Privacy Friendly Sudoku. + + Privacy Friendly Sudoku is free software: + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, + either version 3 of the License, or any later version. + + Privacy Friendly Sudoku is distributed in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Privacy Friendly Sudoku. If not, see . + */ package org.secuso.privacyfriendlysudoku.ui.view; import android.app.AlertDialog; 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-v21/ratingbar.xml b/app/src/main/res/drawable-v21/ratingbar.xml index 5c66e0e..fe14f96 100644 --- a/app/src/main/res/drawable-v21/ratingbar.xml +++ b/app/src/main/res/drawable-v21/ratingbar.xml @@ -18,17 +18,17 @@ + android:tint="?attr/rankingStarNotSelected" /> + android:tint="?attr/rankingStarNotSelected" /> + android:tint="?attr/colorAccent" /> diff --git a/app/src/main/res/drawable/create_game_src.xml b/app/src/main/res/drawable/create_game_src.xml new file mode 100644 index 0000000..b5214d0 --- /dev/null +++ b/app/src/main/res/drawable/create_game_src.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_accessibility_black_48dp.png b/app/src/main/res/drawable/ic_accessibility_black_48dp.png deleted file mode 100644 index 46d736a..0000000 Binary files a/app/src/main/res/drawable/ic_accessibility_black_48dp.png and /dev/null differ diff --git a/app/src/main/res/drawable/ic_accessibility_black_48dp.xml b/app/src/main/res/drawable/ic_accessibility_black_48dp.xml new file mode 100644 index 0000000..fc2f0fe --- /dev/null +++ b/app/src/main/res/drawable/ic_accessibility_black_48dp.xml @@ -0,0 +1,14 @@ + + + + diff --git a/app/src/main/res/drawable/ic_circle_blue_36dp.xml b/app/src/main/res/drawable/ic_circle_blue_36dp.xml new file mode 100644 index 0000000..5cfcc8e --- /dev/null +++ b/app/src/main/res/drawable/ic_circle_blue_36dp.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_circle_grey_36dp.xml b/app/src/main/res/drawable/ic_circle_grey_36dp.xml new file mode 100644 index 0000000..b2962e0 --- /dev/null +++ b/app/src/main/res/drawable/ic_circle_grey_36dp.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_create_black_48dp.png b/app/src/main/res/drawable/ic_create_black_48dp.png deleted file mode 100644 index a09d362..0000000 Binary files a/app/src/main/res/drawable/ic_create_black_48dp.png and /dev/null differ diff --git a/app/src/main/res/drawable/ic_create_black_48dp.xml b/app/src/main/res/drawable/ic_create_black_48dp.xml new file mode 100644 index 0000000..b424e09 --- /dev/null +++ b/app/src/main/res/drawable/ic_create_black_48dp.xml @@ -0,0 +1,14 @@ + + + + diff --git a/app/src/main/res/drawable/ic_delete_black_48dp.png b/app/src/main/res/drawable/ic_delete_black_48dp.png deleted file mode 100644 index c259012..0000000 Binary files a/app/src/main/res/drawable/ic_delete_black_48dp.png and /dev/null differ diff --git a/app/src/main/res/drawable/ic_delete_black_48dp.xml b/app/src/main/res/drawable/ic_delete_black_48dp.xml new file mode 100644 index 0000000..7a1ae3e --- /dev/null +++ b/app/src/main/res/drawable/ic_delete_black_48dp.xml @@ -0,0 +1,14 @@ + + + + diff --git a/app/src/main/res/drawable/ic_finalize.xml b/app/src/main/res/drawable/ic_finalize.xml new file mode 100644 index 0000000..c74a7bb --- /dev/null +++ b/app/src/main/res/drawable/ic_finalize.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_import.xml b/app/src/main/res/drawable/ic_import.xml new file mode 100644 index 0000000..45d8360 --- /dev/null +++ b/app/src/main/res/drawable/ic_import.xml @@ -0,0 +1,4 @@ + + + diff --git a/app/src/main/res/drawable/ic_info_black_24dp.png b/app/src/main/res/drawable/ic_info_black_24dp.png deleted file mode 100644 index da56077..0000000 Binary files a/app/src/main/res/drawable/ic_info_black_24dp.png and /dev/null differ diff --git a/app/src/main/res/drawable/ic_info_black_24dp.xml b/app/src/main/res/drawable/ic_info_black_24dp.xml new file mode 100644 index 0000000..43e0f32 --- /dev/null +++ b/app/src/main/res/drawable/ic_info_black_24dp.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable-hdpi/ic_keyboard_arrow_left_black_24dp.xml b/app/src/main/res/drawable/ic_keyboard_arrow_left_black_24dp.xml similarity index 84% rename from app/src/main/res/drawable-hdpi/ic_keyboard_arrow_left_black_24dp.xml rename to app/src/main/res/drawable/ic_keyboard_arrow_left_black_24dp.xml index c9f7747..3dafd87 100644 --- a/app/src/main/res/drawable-hdpi/ic_keyboard_arrow_left_black_24dp.xml +++ b/app/src/main/res/drawable/ic_keyboard_arrow_left_black_24dp.xml @@ -4,6 +4,6 @@ android:viewportWidth="24.0" android:viewportHeight="24.0"> diff --git a/app/src/main/res/drawable-hdpi/ic_keyboard_arrow_right_black_24dp.xml b/app/src/main/res/drawable/ic_keyboard_arrow_right_black_24dp.xml similarity index 84% rename from app/src/main/res/drawable-hdpi/ic_keyboard_arrow_right_black_24dp.xml rename to app/src/main/res/drawable/ic_keyboard_arrow_right_black_24dp.xml index a3d1622..9d4dc66 100644 --- a/app/src/main/res/drawable-hdpi/ic_keyboard_arrow_right_black_24dp.xml +++ b/app/src/main/res/drawable/ic_keyboard_arrow_right_black_24dp.xml @@ -4,6 +4,6 @@ android:viewportWidth="24.0" android:viewportHeight="24.0"> diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml index 4457f4c..f780f84 100644 --- a/app/src/main/res/drawable/ic_launcher_foreground.xml +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -5,32 +5,32 @@ android:viewportHeight="512"> + android:fillColor="?attr/lightestBackGround"/> + android:fillColor="?attr/lightestBackGround"/> + android:fillColor="?attr/lightestBackGround"/> + android:fillColor="?attr/lightestBackGround"/> + android:fillColor="?attr/lightestBackGround"/> + android:fillColor="?attr/lightestBackGround"/> + android:fillColor="?attr/lightestBackGround"/> + android:fillColor="?attr/lightestBackGround"/> + android:fillColor="?attr/lightestBackGround"/> + android:fillColor="?attr/lightestBackGround"/> diff --git a/app/src/main/res/drawable/ic_launcher_foreground_shadow.xml b/app/src/main/res/drawable/ic_launcher_foreground_shadow.xml index 4a5b108..207775b 100644 --- a/app/src/main/res/drawable/ic_launcher_foreground_shadow.xml +++ b/app/src/main/res/drawable/ic_launcher_foreground_shadow.xml @@ -2,16 +2,16 @@ xmlns:aapt="http://schemas.android.com/aapt" android:width="512dp" android:height="512dp" - android:viewportWidth="512" - android:viewportHeight="512"> + android:viewportWidth="600" + android:viewportHeight="600"> + android:pathData="M659,552.9V364.5L336.3,41.8h-12.6V93l-34.4,-34.4h-13.4v40.3l-40.3,-40.3h-13.4v16.8l-33.6,-33.6h-13.4l0.1,54.7l-40.8,-40.8L88.6,62L71.8,76.2l2.5,17.6l5,6.7l-5.9,-0.8l38.6,38.6l-44.5,-2.5v13.4l27,27l-53.8,-0.1v13.4l133.5,133.5H40.7v14.3l26.9,26.9v94l141.5,141.6L633,611.9C655.2,611.8 658.9,575.1 659,552.9z"> @@ -25,33 +25,33 @@ + android:pathData="M174.6,41.6h13.4v429.9h-13.4z" + android:fillColor="#FFFFFF"/> + android:pathData="M322.4,41.6h13.4v429.9h-13.4z" + android:fillColor="#FFFFFF"/> + android:pathData="M40.3,323.7h429.9v13.4h-429.9z" + android:fillColor="#FFFFFF"/> + android:pathData="M40.3,176h429.9v13.4h-429.9z" + android:fillColor="#FFFFFF"/> + android:pathData="M94.1,68.5h40.3V55.1H94.1c-14.8,0.3 -26.6,12.5 -26.3,27.4c0.3,14.4 11.9,26.1 26.3,26.3h13.4c7.4,-0.2 13.6,5.6 13.8,13.1c0.2,7.4 -5.6,13.6 -13.1,13.8c-0.2,0 -0.5,0 -0.7,0H67.2v13.4h40.3c14.8,-0.3 26.6,-12.5 26.3,-27.4c-0.3,-14.4 -11.9,-26.1 -26.3,-26.3H94.1C86.6,95.2 80.8,89 81,81.6C81.2,74.4 86.9,68.7 94.1,68.5z" + android:fillColor="#FFFFFF"/> + android:pathData="M262,206.2h-40.3v94H262c14.8,0 26.9,-12 26.9,-26.9c0,0 0,0 0,0V233C288.8,218.2 276.8,206.2 262,206.2zM262,286.8h-26.9v-67.2H262c7.4,0 13.4,6 13.4,13.4v40.3C275.4,280.8 269.4,286.8 262,286.8C262,286.8 262,286.8 262,286.8z" + android:fillColor="#FFFFFF"/> + android:pathData="M406.4,206.2h-13.4c-14.8,0 -26.9,12 -26.9,26.9v40.3c0,14.8 12,26.9 26.9,26.9c0,0 0,0 0,0h13.4c14.8,0 26.9,-12 26.9,-26.9v-40.3C433.2,218.2 421.2,206.2 406.4,206.2zM419.8,273.4c0,7.4 -6,13.4 -13.4,13.4h-13.4c-7.4,0 -13.4,-6 -13.4,-13.4v-40.3c0,-7.4 6,-13.4 13.4,-13.4h13.4c7.4,0 13.4,6 13.4,13.4V273.4z" + android:fillColor="#FFFFFF"/> + android:pathData="M134.4,374l0,0l0,0l0,0l0,-9.5l-9.4,0l-0.1,0.1l-0.1,-0.1l-37.9,37.9l-6.3,6.3l0,-12.5l0,-31.7l-13.4,0l0,19.8l0,17.4l0,20.5l0,17.6l0,18.7l13.4,0l0,-30.8l6.5,-6.4l37.7,37.2l0,0l9.6,0l0,-9.3l-37.9,-37.3l37.8,-37.9z" + android:fillColor="#FFFFFF"/> + android:pathData="M275.4,58.4v60.4c0,11.1 -9,20.2 -20.1,20.2s-20.2,-9 -20.2,-20.2l0,0V58.4h-13.4v60.4c0,18.5 15,33.6 33.6,33.6c18.5,0 33.6,-15 33.6,-33.6V58.4H275.4z" + android:fillColor="#FFFFFF"/> + android:pathData="M275.4,364.5V425c0,11.1 -9,20.1 -20.1,20.1s-20.2,-9 -20.2,-20.1l0,0v-60.5h-13.4V425c0,18.5 15,33.6 33.6,33.6c18.5,0 33.6,-15 33.6,-33.6v-60.5H275.4z" + android:fillColor="#FFFFFF"/> diff --git a/app/src/main/res/drawable/ic_launcher_foreground_shadow_old.xml b/app/src/main/res/drawable/ic_launcher_foreground_shadow_old.xml new file mode 100644 index 0000000..4a5b108 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_foreground_shadow_old.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_lightbulb_outline_black_48dp.png b/app/src/main/res/drawable/ic_lightbulb_outline_black_48dp.png deleted file mode 100644 index 1116049..0000000 Binary files a/app/src/main/res/drawable/ic_lightbulb_outline_black_48dp.png and /dev/null differ 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 new file mode 100644 index 0000000..4ce4359 --- /dev/null +++ b/app/src/main/res/drawable/ic_lightbulb_outline_black_48dp.xml @@ -0,0 +1,14 @@ + + + + diff --git a/app/src/main/res/drawable/ic_menu_about.xml b/app/src/main/res/drawable/ic_menu_about.xml index 2851ab6..6c08f3d 100644 --- a/app/src/main/res/drawable/ic_menu_about.xml +++ b/app/src/main/res/drawable/ic_menu_about.xml @@ -4,6 +4,6 @@ android:viewportWidth="24.0" android:viewportHeight="24.0"> \ No newline at end of file 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..59db42b --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_daily_sudoku.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_menu_help.xml b/app/src/main/res/drawable/ic_menu_help.xml index 4148ec9..fdc9afa 100644 --- a/app/src/main/res/drawable/ic_menu_help.xml +++ b/app/src/main/res/drawable/ic_menu_help.xml @@ -4,6 +4,6 @@ android:viewportWidth="24.0" android:viewportHeight="24.0"> \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_menu_home.xml b/app/src/main/res/drawable/ic_menu_home.xml index 5a425b4..2ea2788 100644 --- a/app/src/main/res/drawable/ic_menu_home.xml +++ b/app/src/main/res/drawable/ic_menu_home.xml @@ -4,6 +4,6 @@ android:viewportWidth="24.0" android:viewportHeight="24.0"> \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_menu_settings.xml b/app/src/main/res/drawable/ic_menu_settings.xml index 8e4a68a..7715e5e 100644 --- a/app/src/main/res/drawable/ic_menu_settings.xml +++ b/app/src/main/res/drawable/ic_menu_settings.xml @@ -1,5 +1,6 @@ - + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_redo_black_48dp.png b/app/src/main/res/drawable/ic_redo_black_48dp.png deleted file mode 100644 index a8ab30c..0000000 Binary files a/app/src/main/res/drawable/ic_redo_black_48dp.png and /dev/null differ diff --git a/app/src/main/res/drawable/ic_redo_black_48dp.xml b/app/src/main/res/drawable/ic_redo_black_48dp.xml new file mode 100644 index 0000000..3e752a7 --- /dev/null +++ b/app/src/main/res/drawable/ic_redo_black_48dp.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_school_black_24dp.xml b/app/src/main/res/drawable/ic_school_black_24dp.xml index f8d18b2..7b73270 100644 --- a/app/src/main/res/drawable/ic_school_black_24dp.xml +++ b/app/src/main/res/drawable/ic_school_black_24dp.xml @@ -5,6 +5,6 @@ android:viewportWidth="24.0" android:viewportHeight="24.0"> \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_settings_backup_restore_black_48dp.png b/app/src/main/res/drawable/ic_settings_backup_restore_black_48dp.png deleted file mode 100644 index be0716c..0000000 Binary files a/app/src/main/res/drawable/ic_settings_backup_restore_black_48dp.png and /dev/null differ 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 new file mode 100644 index 0000000..5095e32 --- /dev/null +++ b/app/src/main/res/drawable/ic_settings_backup_restore_black_48dp.xml @@ -0,0 +1,14 @@ + + + + diff --git a/app/src/main/res/drawable/ic_star_black_48dp.png b/app/src/main/res/drawable/ic_star_black_48dp.png deleted file mode 100644 index 54d3065..0000000 Binary files a/app/src/main/res/drawable/ic_star_black_48dp.png and /dev/null differ diff --git a/app/src/main/res/drawable/ic_star_black_48dp.xml b/app/src/main/res/drawable/ic_star_black_48dp.xml new file mode 100644 index 0000000..810c836 --- /dev/null +++ b/app/src/main/res/drawable/ic_star_black_48dp.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_star_blue_36dp.png b/app/src/main/res/drawable/ic_star_blue_36dp.png deleted file mode 100644 index 0386ef9..0000000 Binary files a/app/src/main/res/drawable/ic_star_blue_36dp.png and /dev/null differ diff --git a/app/src/main/res/drawable/ic_star_blue_36dp.xml b/app/src/main/res/drawable/ic_star_blue_36dp.xml new file mode 100644 index 0000000..7ae4899 --- /dev/null +++ b/app/src/main/res/drawable/ic_star_blue_36dp.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_star_grey_36dp.png b/app/src/main/res/drawable/ic_star_grey_36dp.png deleted file mode 100644 index 4e4e358..0000000 Binary files a/app/src/main/res/drawable/ic_star_grey_36dp.png and /dev/null differ diff --git a/app/src/main/res/drawable/ic_star_grey_36dp.xml b/app/src/main/res/drawable/ic_star_grey_36dp.xml new file mode 100644 index 0000000..456adf1 --- /dev/null +++ b/app/src/main/res/drawable/ic_star_grey_36dp.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_star_half_black_48dp.png b/app/src/main/res/drawable/ic_star_half_black_48dp.png deleted file mode 100644 index befe521..0000000 Binary files a/app/src/main/res/drawable/ic_star_half_black_48dp.png and /dev/null differ diff --git a/app/src/main/res/drawable/ic_star_half_black_48dp.xml b/app/src/main/res/drawable/ic_star_half_black_48dp.xml new file mode 100644 index 0000000..1a29044 --- /dev/null +++ b/app/src/main/res/drawable/ic_star_half_black_48dp.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_trophy_award_black_48dp.png b/app/src/main/res/drawable/ic_trophy_award_black_48dp.png deleted file mode 100644 index e85ed53..0000000 Binary files a/app/src/main/res/drawable/ic_trophy_award_black_48dp.png and /dev/null differ diff --git a/app/src/main/res/drawable/ic_trophy_award_black_48dp.xml b/app/src/main/res/drawable/ic_trophy_award_black_48dp.xml new file mode 100644 index 0000000..8dfe2b4 --- /dev/null +++ b/app/src/main/res/drawable/ic_trophy_award_black_48dp.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_trophy_black_48dp.png b/app/src/main/res/drawable/ic_trophy_black_48dp.png deleted file mode 100644 index 7d1620f..0000000 Binary files a/app/src/main/res/drawable/ic_trophy_black_48dp.png and /dev/null differ diff --git a/app/src/main/res/drawable/ic_trophy_black_48dp.xml b/app/src/main/res/drawable/ic_trophy_black_48dp.xml new file mode 100644 index 0000000..844b657 --- /dev/null +++ b/app/src/main/res/drawable/ic_trophy_black_48dp.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_undo_black_48dp.png b/app/src/main/res/drawable/ic_undo_black_48dp.png deleted file mode 100644 index ce458bd..0000000 Binary files a/app/src/main/res/drawable/ic_undo_black_48dp.png and /dev/null differ diff --git a/app/src/main/res/drawable/ic_undo_black_48dp.xml b/app/src/main/res/drawable/ic_undo_black_48dp.xml new file mode 100644 index 0000000..cd95195 --- /dev/null +++ b/app/src/main/res/drawable/ic_undo_black_48dp.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/icon_default_12x12.png b/app/src/main/res/drawable/icon_default_12x12.png deleted file mode 100644 index 4e7d5ab..0000000 Binary files a/app/src/main/res/drawable/icon_default_12x12.png and /dev/null differ diff --git a/app/src/main/res/drawable/icon_default_12x12.xml b/app/src/main/res/drawable/icon_default_12x12.xml new file mode 100644 index 0000000..6535f6c --- /dev/null +++ b/app/src/main/res/drawable/icon_default_12x12.xml @@ -0,0 +1,375 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/icon_default_6x6.png b/app/src/main/res/drawable/icon_default_6x6.png deleted file mode 100644 index 6d665fd..0000000 Binary files a/app/src/main/res/drawable/icon_default_6x6.png and /dev/null differ diff --git a/app/src/main/res/drawable/icon_default_6x6.xml b/app/src/main/res/drawable/icon_default_6x6.xml new file mode 100644 index 0000000..89626cb --- /dev/null +++ b/app/src/main/res/drawable/icon_default_6x6.xml @@ -0,0 +1,168 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/icon_default_9x9.png b/app/src/main/res/drawable/icon_default_9x9.png deleted file mode 100644 index f066994..0000000 Binary files a/app/src/main/res/drawable/icon_default_9x9.png and /dev/null differ diff --git a/app/src/main/res/drawable/icon_default_9x9.xml b/app/src/main/res/drawable/icon_default_9x9.xml new file mode 100644 index 0000000..015c1ae --- /dev/null +++ b/app/src/main/res/drawable/icon_default_9x9.xml @@ -0,0 +1,216 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/inactive_button.xml b/app/src/main/res/drawable/inactive_button.xml index 523ab80..959e81a 100644 --- a/app/src/main/res/drawable/inactive_button.xml +++ b/app/src/main/res/drawable/inactive_button.xml @@ -4,7 +4,7 @@ + android:color="?attr/inactiveButtonColor" /> diff --git a/app/src/main/res/drawable/mnenomic_numpad_button.xml b/app/src/main/res/drawable/mnenomic_numpad_button.xml index 12c4d14..af4c22e 100644 --- a/app/src/main/res/drawable/mnenomic_numpad_button.xml +++ b/app/src/main/res/drawable/mnenomic_numpad_button.xml @@ -1,5 +1,5 @@ - + diff --git a/app/src/main/res/drawable/numfirst.png b/app/src/main/res/drawable/numfirst.png index ab44980..5e3d2e5 100644 Binary files a/app/src/main/res/drawable/numfirst.png and b/app/src/main/res/drawable/numfirst.png differ diff --git a/app/src/main/res/drawable/numfull.png b/app/src/main/res/drawable/numfull.png index a104172..9394b95 100644 Binary files a/app/src/main/res/drawable/numfull.png and b/app/src/main/res/drawable/numfull.png differ diff --git a/app/src/main/res/drawable/numpad_highlighted.xml b/app/src/main/res/drawable/numpad_highlighted.xml index 7af2c1a..2c00338 100644 --- a/app/src/main/res/drawable/numpad_highlighted.xml +++ b/app/src/main/res/drawable/numpad_highlighted.xml @@ -2,10 +2,10 @@ + android:endColor="?attr/activeButtonColor" + android:startColor="?attr/activeButtonColor" /> + android:color="?attr/colorPrimaryDark" /> \ No newline at end of file diff --git a/app/src/main/res/drawable/numpad_highlighted_four.xml b/app/src/main/res/drawable/numpad_highlighted_four.xml index 8b8e766..87ce8d9 100644 --- a/app/src/main/res/drawable/numpad_highlighted_four.xml +++ b/app/src/main/res/drawable/numpad_highlighted_four.xml @@ -2,10 +2,10 @@ + android:endColor="?attr/activeButtonColor" + android:startColor="?attr/activeButtonColor" /> + android:color="?attr/standardVectorGraphic" /> \ No newline at end of file diff --git a/app/src/main/res/drawable/numpad_highlighted_three.xml b/app/src/main/res/drawable/numpad_highlighted_three.xml index fe0f0cf..05aa808 100644 --- a/app/src/main/res/drawable/numpad_highlighted_three.xml +++ b/app/src/main/res/drawable/numpad_highlighted_three.xml @@ -2,10 +2,10 @@ + android:endColor="?attr/activeButtonColor" + android:startColor="?attr/activeButtonColor" /> + android:color="?attr/highlightedButtonBorder" /> \ No newline at end of file diff --git a/app/src/main/res/drawable/secuso_logo_blau_blau.png b/app/src/main/res/drawable/secuso_logo_blau_blau.png deleted file mode 100644 index 9c82d7c..0000000 Binary files a/app/src/main/res/drawable/secuso_logo_blau_blau.png and /dev/null differ diff --git a/app/src/main/res/drawable/secuso_logo_blau_blau.xml b/app/src/main/res/drawable/secuso_logo_blau_blau.xml new file mode 100644 index 0000000..128e26c --- /dev/null +++ b/app/src/main/res/drawable/secuso_logo_blau_blau.xml @@ -0,0 +1,214 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/standalone_button.xml b/app/src/main/res/drawable/standalone_button.xml index 8f125d3..f2d80f6 100644 --- a/app/src/main/res/drawable/standalone_button.xml +++ b/app/src/main/res/drawable/standalone_button.xml @@ -4,7 +4,7 @@ + android:color="?attr/colorPrimary" /> diff --git a/app/src/main/res/layout-land/activity_about.xml b/app/src/main/res/layout-land/activity_about.xml index 97b5716..b36a20c 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"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout-land/activity_main_menu.xml b/app/src/main/res/layout-land/activity_main_menu.xml index bc24413..5389f69 100644 --- a/app/src/main/res/layout-land/activity_main_menu.xml +++ b/app/src/main/res/layout-land/activity_main_menu.xml @@ -9,9 +9,6 @@ xmlns:app="http://schemas.android.com/apk/res-auto"> - - + android:gravity="center_vertical"> + + + + + +