From 081291c4d77e8734dcdb560716dcfe3a64ff76c0 Mon Sep 17 00:00:00 2001 From: "luca0N\\!" Date: Sun, 14 Mar 2021 15:10:52 -0300 Subject: [PATCH 1/6] Prepared trivia file support implementation Based on #10. --- .../pluck/activities/MainActivity.java | 109 +++++++++++------- .../pluck/activities/TriviaGameActivity.java | 20 +++- .../joguitos/pluck/trivia/TriviaSource.java | 66 +++++++++++ app/src/main/res/values/strings.xml | 16 ++- 4 files changed, 162 insertions(+), 49 deletions(-) create mode 100644 app/src/main/java/com/luca0n/joguitos/pluck/trivia/TriviaSource.java diff --git a/app/src/main/java/com/luca0n/joguitos/pluck/activities/MainActivity.java b/app/src/main/java/com/luca0n/joguitos/pluck/activities/MainActivity.java index a29015a..0adeb1f 100644 --- a/app/src/main/java/com/luca0n/joguitos/pluck/activities/MainActivity.java +++ b/app/src/main/java/com/luca0n/joguitos/pluck/activities/MainActivity.java @@ -24,67 +24,92 @@ Contact us at . package com.luca0n.joguitos.pluck.activities; import android.content.Intent; +import android.os.Build; import android.os.Bundle; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.Spinner; +import androidx.appcompat.app.AlertDialog; import butterknife.BindView; import butterknife.ButterKnife; import com.luca0n.joguitos.pluck.R; +import com.luca0n.joguitos.pluck.trivia.TriviaSource; import com.luca0n.joguitos.pluck.trivia.TriviaCategory; import com.luca0n.joguitos.pluck.trivia.TriviaDifficulty; import com.luca0n.joguitos.pluck.trivia.TriviaQuery; public class MainActivity extends BaseActivity { - @BindView(R.id.button_play) - Button buttonPlay; - @BindView(R.id.spinner_number) - Spinner spinnerNumber; - @BindView(R.id.spinner_category) - Spinner spinnerCategory; - @BindView(R.id.spinner_difficulty) - Spinner spinnerDifficulty; + @BindView(R.id.button_play) + Button buttonPlay; + @BindView(R.id.spinner_source) + Spinner spinnerSource; + @BindView(R.id.spinner_number) + Spinner spinnerNumber; + @BindView(R.id.spinner_category) + Spinner spinnerCategory; + @BindView(R.id.spinner_difficulty) + Spinner spinnerDifficulty; - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - ButterKnife.bind(this); + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + ButterKnife.bind(this); - buttonPlay.setOnClickListener(v -> { - int amount = (int) spinnerNumber.getSelectedItem(); - TriviaCategory category = (TriviaCategory) spinnerCategory.getSelectedItem(); - TriviaDifficulty difficulty = (TriviaDifficulty) spinnerDifficulty.getSelectedItem(); + buttonPlay.setOnClickListener(v -> { + int sourcePos = spinnerSource.getSelectedItemPosition(); + if (TriviaSource.values()[sourcePos] == TriviaSource.FILE){ + // Check the Android version. + if (Build.VERSION.SDK_INT >= 19){ + // TODO: Show the open document dialog and read the trivia file. + return; + } else { + // Unsupported version. + new AlertDialog.Builder(this) + .setIcon(android.R.drawable.ic_dialog_alert) + .setTitle(R.string.error) + .setMessage(R.string.error_source_file_android_version_unsupported) + .setPositiveButton(android.R.string.ok, null) + .show(); + } + return; + } + int amount = (int) spinnerNumber.getSelectedItem(); + TriviaCategory category = (TriviaCategory) spinnerCategory.getSelectedItem(); + TriviaDifficulty difficulty = (TriviaDifficulty) spinnerDifficulty.getSelectedItem(); - TriviaQuery query = new TriviaQuery.Builder(amount) - .category(category) - .difficulty(difficulty) - .build(); + TriviaQuery query = new TriviaQuery.Builder(amount) + .category(category) + .difficulty(difficulty) + .build(); - Intent intent = new Intent(getApplicationContext(), TriviaGameActivity.class); - intent.putExtra(TriviaGameActivity.EXTRA_TRIVIA_QUERY, query); - startActivity(intent); - }); + Intent intent = new Intent(getApplicationContext(), TriviaGameActivity.class); + intent.putExtra(TriviaGameActivity.EXTRA_TRIVIA_QUERY, query); + startActivity(intent); + }); + Integer[] numbers = new Integer[50]; + for (int i = 0; i < 50; ) { + numbers[i] = ++i; + } + spinnerSource.setAdapter( + new ArrayAdapter<>( + this, android.R.layout.simple_list_item_1, TriviaSource.values()) + ); + spinnerNumber.setAdapter( + new ArrayAdapter<>( + this, android.R.layout.simple_list_item_1, numbers) + ); + spinnerNumber.setSelection(9); - Integer[] numbers = new Integer[50]; - for (int i = 0; i < 50; ) { - numbers[i] = ++i; - } - spinnerNumber.setAdapter( - new ArrayAdapter<>( - this, android.R.layout.simple_list_item_1, numbers) - ); - spinnerNumber.setSelection(9); + spinnerCategory.setAdapter( + new ArrayAdapter<>( + this, android.R.layout.simple_list_item_1, TriviaCategory.values())); - spinnerCategory.setAdapter( - new ArrayAdapter<>( - this, android.R.layout.simple_list_item_1, TriviaCategory.values())); - - spinnerDifficulty.setAdapter( - new ArrayAdapter<>( - this, android.R.layout.simple_list_item_1, TriviaDifficulty.values())); - } + spinnerDifficulty.setAdapter( + new ArrayAdapter<>( + this, android.R.layout.simple_list_item_1, TriviaDifficulty.values())); + } } diff --git a/app/src/main/java/com/luca0n/joguitos/pluck/activities/TriviaGameActivity.java b/app/src/main/java/com/luca0n/joguitos/pluck/activities/TriviaGameActivity.java index cece721..0167303 100644 --- a/app/src/main/java/com/luca0n/joguitos/pluck/activities/TriviaGameActivity.java +++ b/app/src/main/java/com/luca0n/joguitos/pluck/activities/TriviaGameActivity.java @@ -95,9 +95,23 @@ public class TriviaGameActivity extends BaseActivity progressBar.setVisibility(View.VISIBLE); - DownloadTriviaQuestionsTask task = new DownloadTriviaQuestionsTask(); - task.setReceiver(this); - task.execute(query); + // Gather information about the trivia location. + /*String triviaLocation; + + switch (triviaLocation){ + case "GET_FROM_URL":*/ + DownloadTriviaQuestionsTask task = new DownloadTriviaQuestionsTask(); + task.setReceiver(this); + task.execute(query);/* + break; + case "GET_FROM_FILE": + + break; + default: + // Unknown specified trivia location. + // TODO: Show error message to the user about this internal error. + break; + }*/ } } diff --git a/app/src/main/java/com/luca0n/joguitos/pluck/trivia/TriviaSource.java b/app/src/main/java/com/luca0n/joguitos/pluck/trivia/TriviaSource.java new file mode 100644 index 0000000..7a09cbb --- /dev/null +++ b/app/src/main/java/com/luca0n/joguitos/pluck/trivia/TriviaSource.java @@ -0,0 +1,66 @@ +/* +Pluck: an open source trivia game for Android + +Copyright (C) 2021 Joguitos do luca0N! + +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 3 of the License, or (at your option) any later +version. + +This program 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 +this program. If not, see . + +This game is a fork of LibreTrivia, and its source code is available at +. + +Contact us at . +*/ + +package com.luca0n.joguitos.pluck.trivia; + +import java.util.HashMap; +import java.util.Map; + +import com.luca0n.joguitos.pluck.PluckApplication; +import com.luca0n.joguitos.pluck.R; + +public enum TriviaSource { + SERVER("server", R.string.source_server), + FILE("file", R.string.source_file); + + // Name of source + private final String name; + // Display name of the source + private final int displayName; + + private static final Map lookup = new HashMap<>(); + + static { + for (TriviaSource source : TriviaSource.values()) { + lookup.put(source.getName(), source); + } + } + + TriviaSource(String name, int displayName) { + this.name = name; + this.displayName = displayName; + } + + public String getName() { + return this.name; + } + + public static TriviaSource get(String name) { + return lookup.get(name); + } + + @Override + public String toString() { + return PluckApplication.getAppContext().getResources().getString(this.displayName); + } +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f726a49..3ac5954 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -61,6 +61,10 @@ Contact us at . Multiple Choice True / False + + Server + File + Settings About @@ -80,13 +84,17 @@ Contact us at . Correct Answers Wrong Answers Total Questions - Return To Menu + Return To Menu + Source + This value specifies where the game should retrieve trivia data. By default, the game gathers trivia data from a server, but you can load trivia files with the "File" source option. - + + Error Network error!\n\nCould not connect to a network. Check if your custom server address is correct. If Tor is enabled in the game settings, check if Orbot is running and then try again. No trivia results!\n\nWas not able to find trivia questions that satisfied all options. Server response error!\n\nCould not parse the server response. The server response may be in a unsupported version. If you are using a custom server, make sure the server address points to a valid address and then try again. An unknown error occurred! + Trivia files are not supported in this version of Android. Settings @@ -98,6 +106,6 @@ Contact us at . Network Connect Using Tor Retrieves trivia data via the Tor network. Requires Orbot (recommended) or a Tor daemon with a SOCKS5 proxy listening on port 9050. - Server address - Specifies the server location used by the game to fetch questions. Leave empty to use the default server. + Server Address + Specifies the server location used by the game to fetch questions when using a server as the trivia source. Leave this setting empty to use the default server. From 35ade89e34bf6fe310ffeab0c407f3c5b0be7a69 Mon Sep 17 00:00:00 2001 From: "luca0N\\!" Date: Mon, 15 Mar 2021 14:47:08 -0300 Subject: [PATCH 2/6] Added main logic for trivia file support Added main logic for trivia file support and renamed exception class `InvalidTriviaFormatException`. --- .../pluck/activities/MainActivity.java | 54 ++++++++++++++++ .../pluck/activities/TriviaGameActivity.java | 63 ++++++++++++++----- .../InvalidTriviaFormatException.java | 35 +++++++++++ .../joguitos/pluck/trivia/TriviaQuery.java | 29 +++------ .../luca0n/joguitos/pluck/util/ApiUtil.java | 6 +- app/src/main/res/layout/activity_main.xml | 10 +++ app/src/main/res/values/strings.xml | 2 + 7 files changed, 159 insertions(+), 40 deletions(-) create mode 100644 app/src/main/java/com/luca0n/joguitos/pluck/exceptions/InvalidTriviaFormatException.java diff --git a/app/src/main/java/com/luca0n/joguitos/pluck/activities/MainActivity.java b/app/src/main/java/com/luca0n/joguitos/pluck/activities/MainActivity.java index 0adeb1f..2bda23f 100644 --- a/app/src/main/java/com/luca0n/joguitos/pluck/activities/MainActivity.java +++ b/app/src/main/java/com/luca0n/joguitos/pluck/activities/MainActivity.java @@ -23,9 +23,12 @@ Contact us at . package com.luca0n.joguitos.pluck.activities; +import android.app.Activity; import android.content.Intent; +import android.content.SharedPreferences; import android.os.Build; import android.os.Bundle; +import android.preference.PreferenceManager; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.Spinner; @@ -33,6 +36,7 @@ import androidx.appcompat.app.AlertDialog; import butterknife.BindView; import butterknife.ButterKnife; + import com.luca0n.joguitos.pluck.R; import com.luca0n.joguitos.pluck.trivia.TriviaSource; import com.luca0n.joguitos.pluck.trivia.TriviaCategory; @@ -52,6 +56,8 @@ public class MainActivity extends BaseActivity { @BindView(R.id.spinner_difficulty) Spinner spinnerDifficulty; + private static final int REQUEST_OPEN_DOCUMENT = 0; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -64,6 +70,9 @@ public class MainActivity extends BaseActivity { // Check the Android version. if (Build.VERSION.SDK_INT >= 19){ // TODO: Show the open document dialog and read the trivia file. + Intent i = new Intent(Intent.ACTION_OPEN_DOCUMENT); + i.setType("*/*"); + startActivityForResult(i, REQUEST_OPEN_DOCUMENT); return; } else { // Unsupported version. @@ -80,12 +89,28 @@ public class MainActivity extends BaseActivity { TriviaCategory category = (TriviaCategory) spinnerCategory.getSelectedItem(); TriviaDifficulty difficulty = (TriviaDifficulty) spinnerDifficulty.getSelectedItem(); + // Put the server address in the TriviaQuery object. + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); + String serverKey = getResources().getString(R.string.pref_network_server); + // Load default server URL in case the player did not specify a custom server URL. + String defaultServer = getResources().getString(R.string.pref_network_server_default); + String base = sp.getString(serverKey, defaultServer) + "?"; // append "?" to the end of the URL so we can specify GET parameters. + + // Check if the custom server URL preference was empty. If it was, change it to the default URL. + if (base.equals("?")){ + base = defaultServer + "?"; + sp.edit().putString(serverKey, defaultServer).commit(); + } + TriviaQuery query = new TriviaQuery.Builder(amount) .category(category) .difficulty(difficulty) + .base(base) .build(); + // Launch the trivia game activity. Intent intent = new Intent(getApplicationContext(), TriviaGameActivity.class); + intent.putExtra(TriviaGameActivity.EXTRA_TRIVIA_SOURCE, TriviaSource.SERVER.getName()); // Specify the trivia source. intent.putExtra(TriviaGameActivity.EXTRA_TRIVIA_QUERY, query); startActivity(intent); }); @@ -112,4 +137,33 @@ public class MainActivity extends BaseActivity { new ArrayAdapter<>( this, android.R.layout.simple_list_item_1, TriviaDifficulty.values())); } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent extras){ + switch (requestCode){ + case REQUEST_OPEN_DOCUMENT: + // Was the request executed successfully? + if (resultCode == Activity.RESULT_OK){ + // Check if the user canceled the operation. + if (extras == null) + break; + + Intent intent = new Intent(getApplicationContext(), TriviaGameActivity.class); + intent.putExtra(TriviaGameActivity.EXTRA_TRIVIA_SOURCE, TriviaSource.FILE.getName()); // Specify the trivia source. + intent.putExtra(TriviaGameActivity.EXTRA_TRIVIA_SOURCE_DATA, extras.getData()); // Specify the trivia source data. In this case, since the user chose the "File" source option, we should put the file Uri object here. + startActivity(intent); + } else { + new AlertDialog.Builder(this) + .setIcon(android.R.drawable.ic_dialog_alert) + .setTitle(R.string.error) + .setMessage(R.string.error_trivia_file_fetch_unknown) + .setPositiveButton(android.R.string.ok, null) + .show(); + } + break; + default: + super.onActivityResult(requestCode, resultCode, extras); + break; + } + } } diff --git a/app/src/main/java/com/luca0n/joguitos/pluck/activities/TriviaGameActivity.java b/app/src/main/java/com/luca0n/joguitos/pluck/activities/TriviaGameActivity.java index 0167303..e298853 100644 --- a/app/src/main/java/com/luca0n/joguitos/pluck/activities/TriviaGameActivity.java +++ b/app/src/main/java/com/luca0n/joguitos/pluck/activities/TriviaGameActivity.java @@ -23,7 +23,6 @@ Contact us at . package com.luca0n.joguitos.pluck.activities; -import android.app.AlertDialog; import android.content.Intent; import android.content.res.ColorStateList; import android.graphics.PorterDuff; @@ -32,6 +31,7 @@ import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.os.Handler; +import android.net.Uri; import android.view.View; import android.view.MenuItem; import android.widget.Button; @@ -40,30 +40,40 @@ import android.widget.ProgressBar; import android.widget.TextView; import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.io.Reader; +import java.io.InputStreamReader; +import java.io.InputStream; +import java.io.BufferedReader; import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentTransaction; import butterknife.BindView; import butterknife.ButterKnife; import com.luca0n.joguitos.pluck.R; import com.luca0n.joguitos.pluck.exceptions.NoTriviaResultsException; -import com.luca0n.joguitos.pluck.exceptions.InvalidServerResponseException; +import com.luca0n.joguitos.pluck.exceptions.InvalidTriviaFormatException; import com.luca0n.joguitos.pluck.fragments.TriviaGameErrorFragment; import com.luca0n.joguitos.pluck.fragments.TriviaQuestionFragment; import com.luca0n.joguitos.pluck.interfaces.IDownloadTriviaQuestionReceiver; import com.luca0n.joguitos.pluck.trivia.TriviaGame; import com.luca0n.joguitos.pluck.trivia.TriviaQuery; import com.luca0n.joguitos.pluck.trivia.TriviaQuestion; +import com.luca0n.joguitos.pluck.trivia.TriviaSource; import com.luca0n.joguitos.pluck.util.ApiUtil; import com.luca0n.joguitos.pluck.util.SoundUtil; public class TriviaGameActivity extends BaseActivity implements IDownloadTriviaQuestionReceiver { static final String EXTRA_TRIVIA_QUERY = "extra_trivia_query"; + static final String EXTRA_TRIVIA_SOURCE = "extra_trivia_source"; + static final String EXTRA_TRIVIA_SOURCE_DATA = "extra_trivia_source_data"; private final String STATE_TRIVIA_GAME = "state_trivia_game"; private TriviaGame game; + private TriviaSource source; @BindView(R.id.progress_bar) ProgressBar progressBar; @@ -96,22 +106,34 @@ public class TriviaGameActivity extends BaseActivity progressBar.setVisibility(View.VISIBLE); // Gather information about the trivia location. - /*String triviaLocation; - - switch (triviaLocation){ - case "GET_FROM_URL":*/ + String sourceData; + source = TriviaSource.get(bundle.getString(EXTRA_TRIVIA_SOURCE)); + switch (source){ + case SERVER: DownloadTriviaQuestionsTask task = new DownloadTriviaQuestionsTask(); task.setReceiver(this); - task.execute(query);/* + task.execute(query); break; - case "GET_FROM_FILE": - + case FILE: + // Attempt to open the trivia file specified by the player. + // Read the file as a plain text JSON trivia file. + StringBuilder sb = new StringBuilder(); + try { + InputStream is = getContentResolver().openInputStream((Uri) bundle.get(EXTRA_TRIVIA_SOURCE_DATA)); + Reader in = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8)); + for (int c; (c = in.read()) >= 0;) + sb.append((char) c); + } catch (IOException e){ + onException(e); + return; + } + onTriviaQuestionsDownloaded(sb.toString()); break; default: - // Unknown specified trivia location. + // Unknown trivia source. // TODO: Show error message to the user about this internal error. break; - }*/ + } } } @@ -159,6 +181,8 @@ public class TriviaGameActivity extends BaseActivity this.game = new TriviaGame(ApiUtil.jsonToQuestionArray(json)); } catch (Exception e) { //onNoTriviaResults(); + new AlertDialog.Builder(this) + .setMessage(e.toString()).show(); onException(e); return; } @@ -214,12 +238,19 @@ public class TriviaGameActivity extends BaseActivity // Hide the progress bar. progressBar.setVisibility(View.GONE); String msg; + int id; if (e instanceof NoTriviaResultsException) - msg = getResources().getString(R.string.error_no_trivia_results); - else if (e instanceof InvalidServerResponseException) - msg = getResources().getString(R.string.error_server_response_invalid); - else - msg = getResources().getString(R.string.error_unknown); + id = R.string.error_no_trivia_results; + else if (e instanceof InvalidTriviaFormatException) + // Check the trivia source type. + if (source == TriviaSource.SERVER) + id = R.string.error_server_response_invalid; + else + id = R.string.error_trivia_file_invalid; + else + id = R.string.error_unknown; + + msg = getResources().getString(id); Fragment errorFragment = TriviaGameErrorFragment.newInstance(msg); diff --git a/app/src/main/java/com/luca0n/joguitos/pluck/exceptions/InvalidTriviaFormatException.java b/app/src/main/java/com/luca0n/joguitos/pluck/exceptions/InvalidTriviaFormatException.java new file mode 100644 index 0000000..3832b49 --- /dev/null +++ b/app/src/main/java/com/luca0n/joguitos/pluck/exceptions/InvalidTriviaFormatException.java @@ -0,0 +1,35 @@ +/* +Pluck: an open source trivia game for Android + +Copyright (C) 2021 Joguitos do luca0N! + +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 3 of the License, or (at your option) any later +version. + +This program 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 +this program. If not, see . + +This game is a fork of LibreTrivia, and its source code is available at +. + +Contact us at . +*/ + +package com.luca0n.joguitos.pluck.exceptions; + +import java.lang.Throwable; + +public class InvalidTriviaFormatException extends Exception { + public InvalidTriviaFormatException(){ + super(); + } + public InvalidTriviaFormatException(Throwable cause){ + super(cause); + } +} diff --git a/app/src/main/java/com/luca0n/joguitos/pluck/trivia/TriviaQuery.java b/app/src/main/java/com/luca0n/joguitos/pluck/trivia/TriviaQuery.java index f9a0767..59edc52 100644 --- a/app/src/main/java/com/luca0n/joguitos/pluck/trivia/TriviaQuery.java +++ b/app/src/main/java/com/luca0n/joguitos/pluck/trivia/TriviaQuery.java @@ -23,15 +23,8 @@ Contact us at . package com.luca0n.joguitos.pluck.trivia; -import com.luca0n.joguitos.pluck.PluckApplication; -import com.luca0n.joguitos.pluck.R; - import java.io.Serializable; -import android.content.Context; -import android.content.SharedPreferences; -import android.preference.PreferenceManager; - public class TriviaQuery implements Serializable { private static final int DEFAULT_AMOUNT = 10; @@ -39,11 +32,13 @@ public class TriviaQuery implements Serializable { private final TriviaCategory category; private final TriviaDifficulty difficulty; private final TriviaType type; + private final String base; private TriviaQuery(Builder builder) { this.amount = builder.amount; this.category = builder.category; this.difficulty = builder.difficulty; + this.base = builder.base; this.type = builder.type; } @@ -52,6 +47,7 @@ public class TriviaQuery implements Serializable { private TriviaCategory category; private TriviaDifficulty difficulty; private TriviaType type; + private String base; public Builder() { this.amount = DEFAULT_AMOUNT; @@ -80,6 +76,11 @@ public class TriviaQuery implements Serializable { return this; } + public Builder base(String base){ + this.base = base; + return this; + } + public TriviaQuery build() { return new TriviaQuery(this); } @@ -89,20 +90,6 @@ public class TriviaQuery implements Serializable { public String toString() { StringBuilder url = new StringBuilder(); - // Load custom server URL. - Context c = PluckApplication.getAppContext(); - SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(c); - String serverKey = c.getResources().getString(R.string.pref_network_server); - // Load default server URL in case the player did not specify a custom server URL. - String defaultServer = c.getResources().getString(R.string.pref_network_server_default); - String base = sp.getString(serverKey, defaultServer) + "?"; // append "?" to the end of the URL so we can specify GET parameters. - - // Check if the custom server URL preference was empty. If it was, change it to the default URL. - if (base.equals("?")){ - base = defaultServer + "?"; - sp.edit().putString(serverKey, defaultServer).commit(); - } - url.append(base); url.append("amount=").append(this.amount); diff --git a/app/src/main/java/com/luca0n/joguitos/pluck/util/ApiUtil.java b/app/src/main/java/com/luca0n/joguitos/pluck/util/ApiUtil.java index 5a77fb5..610ae15 100644 --- a/app/src/main/java/com/luca0n/joguitos/pluck/util/ApiUtil.java +++ b/app/src/main/java/com/luca0n/joguitos/pluck/util/ApiUtil.java @@ -47,7 +47,7 @@ import java.util.ArrayList; import com.luca0n.joguitos.pluck.R; import com.luca0n.joguitos.pluck.PluckApplication; import com.luca0n.joguitos.pluck.exceptions.NoTriviaResultsException; -import com.luca0n.joguitos.pluck.exceptions.InvalidServerResponseException; +import com.luca0n.joguitos.pluck.exceptions.InvalidTriviaFormatException; import com.luca0n.joguitos.pluck.trivia.TriviaQuery; import com.luca0n.joguitos.pluck.trivia.TriviaQuestion; import com.luca0n.joguitos.pluck.trivia.TriviaQuestionBoolean; @@ -105,13 +105,13 @@ public class ApiUtil { } public static ArrayList jsonToQuestionArray(String json) - throws NoTriviaResultsException, InvalidServerResponseException { + throws NoTriviaResultsException, InvalidTriviaFormatException { JsonObject jsonObject; try{ jsonObject = new JsonParser().parse(json).getAsJsonObject(); } catch (JsonSyntaxException e){ // Unknown server response. - throw new InvalidServerResponseException(); + throw new InvalidTriviaFormatException(e); } if (jsonObject.get("response_code").getAsInt() == 1) { diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 9870330..a4d4044 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -44,6 +44,16 @@ Contact us at . app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintTop_toTopOf="parent"> + + + + + . Server response error!\n\nCould not parse the server response. The server response may be in a unsupported version. If you are using a custom server, make sure the server address points to a valid address and then try again. An unknown error occurred! Trivia files are not supported in this version of Android. + Invalid trivia file!\n\nTrivia files should be in a plain text JSON format. + An unknown error occurred while attempting to read the opened file. Settings From 7d92099a9598a0ed5e2adc3380ff160d2bd2f477 Mon Sep 17 00:00:00 2001 From: "luca0N\\!" Date: Tue, 16 Mar 2021 00:48:01 -0300 Subject: [PATCH 3/6] Added initial filter support for trivia files This allows the game to filter trivia files using the category and difficulty filters. Question amount is currently ignored --- .../pluck/activities/MainActivity.java | 16 +++ .../pluck/activities/TriviaGameActivity.java | 14 +- .../joguitos/pluck/trivia/TriviaCategory.java | 2 +- .../joguitos/pluck/trivia/TriviaFilters.java | 128 ++++++++++++++++++ .../joguitos/pluck/trivia/TriviaQuery.java | 2 +- .../luca0n/joguitos/pluck/util/ApiUtil.java | 25 +++- 6 files changed, 178 insertions(+), 9 deletions(-) create mode 100644 app/src/main/java/com/luca0n/joguitos/pluck/trivia/TriviaFilters.java diff --git a/app/src/main/java/com/luca0n/joguitos/pluck/activities/MainActivity.java b/app/src/main/java/com/luca0n/joguitos/pluck/activities/MainActivity.java index 2bda23f..ea48e47 100644 --- a/app/src/main/java/com/luca0n/joguitos/pluck/activities/MainActivity.java +++ b/app/src/main/java/com/luca0n/joguitos/pluck/activities/MainActivity.java @@ -42,6 +42,7 @@ import com.luca0n.joguitos.pluck.trivia.TriviaSource; import com.luca0n.joguitos.pluck.trivia.TriviaCategory; import com.luca0n.joguitos.pluck.trivia.TriviaDifficulty; import com.luca0n.joguitos.pluck.trivia.TriviaQuery; +import com.luca0n.joguitos.pluck.trivia.TriviaFilters; public class MainActivity extends BaseActivity { @@ -112,6 +113,11 @@ public class MainActivity extends BaseActivity { Intent intent = new Intent(getApplicationContext(), TriviaGameActivity.class); intent.putExtra(TriviaGameActivity.EXTRA_TRIVIA_SOURCE, TriviaSource.SERVER.getName()); // Specify the trivia source. intent.putExtra(TriviaGameActivity.EXTRA_TRIVIA_QUERY, query); + intent.putExtra(TriviaGameActivity.EXTRA_TRIVIA_FILTERS, + new TriviaFilters.Builder(amount) + .category(category) + .difficulty(difficulty) + .build()); startActivity(intent); }); @@ -147,8 +153,18 @@ public class MainActivity extends BaseActivity { // Check if the user canceled the operation. if (extras == null) break; + + int amount = (int) spinnerNumber.getSelectedItem(); + TriviaCategory category = (TriviaCategory) spinnerCategory.getSelectedItem(); + TriviaDifficulty difficulty = (TriviaDifficulty) spinnerDifficulty.getSelectedItem(); Intent intent = new Intent(getApplicationContext(), TriviaGameActivity.class); + // Put filters on the extras object. + intent.putExtra(TriviaGameActivity.EXTRA_TRIVIA_FILTERS, + new TriviaFilters.Builder(amount) + .category(category) + .difficulty(difficulty) + .build()); intent.putExtra(TriviaGameActivity.EXTRA_TRIVIA_SOURCE, TriviaSource.FILE.getName()); // Specify the trivia source. intent.putExtra(TriviaGameActivity.EXTRA_TRIVIA_SOURCE_DATA, extras.getData()); // Specify the trivia source data. In this case, since the user chose the "File" source option, we should put the file Uri object here. startActivity(intent); diff --git a/app/src/main/java/com/luca0n/joguitos/pluck/activities/TriviaGameActivity.java b/app/src/main/java/com/luca0n/joguitos/pluck/activities/TriviaGameActivity.java index e298853..7349b44 100644 --- a/app/src/main/java/com/luca0n/joguitos/pluck/activities/TriviaGameActivity.java +++ b/app/src/main/java/com/luca0n/joguitos/pluck/activities/TriviaGameActivity.java @@ -62,6 +62,7 @@ import com.luca0n.joguitos.pluck.trivia.TriviaGame; import com.luca0n.joguitos.pluck.trivia.TriviaQuery; import com.luca0n.joguitos.pluck.trivia.TriviaQuestion; import com.luca0n.joguitos.pluck.trivia.TriviaSource; +import com.luca0n.joguitos.pluck.trivia.TriviaFilters; import com.luca0n.joguitos.pluck.util.ApiUtil; import com.luca0n.joguitos.pluck.util.SoundUtil; @@ -70,10 +71,12 @@ public class TriviaGameActivity extends BaseActivity static final String EXTRA_TRIVIA_QUERY = "extra_trivia_query"; static final String EXTRA_TRIVIA_SOURCE = "extra_trivia_source"; static final String EXTRA_TRIVIA_SOURCE_DATA = "extra_trivia_source_data"; + static final String EXTRA_TRIVIA_FILTERS = "extra_trivia_filters"; private final String STATE_TRIVIA_GAME = "state_trivia_game"; private TriviaGame game; private TriviaSource source; + private TriviaFilters filters; @BindView(R.id.progress_bar) ProgressBar progressBar; @@ -102,6 +105,8 @@ public class TriviaGameActivity extends BaseActivity Bundle bundle = getIntent().getExtras(); assert bundle != null; TriviaQuery query = (TriviaQuery) bundle.get(EXTRA_TRIVIA_QUERY); + + filters = (TriviaFilters) bundle.get(EXTRA_TRIVIA_FILTERS); progressBar.setVisibility(View.VISIBLE); @@ -178,11 +183,8 @@ public class TriviaGameActivity extends BaseActivity return; } else { try { - this.game = new TriviaGame(ApiUtil.jsonToQuestionArray(json)); + this.game = new TriviaGame(ApiUtil.jsonToQuestionArray(json, filters)); } catch (Exception e) { - //onNoTriviaResults(); - new AlertDialog.Builder(this) - .setMessage(e.toString()).show(); onException(e); return; } @@ -250,6 +252,10 @@ public class TriviaGameActivity extends BaseActivity else id = R.string.error_unknown; + new AlertDialog.Builder(this) + .setMessage(e.toString()) + .show(); + msg = getResources().getString(id); Fragment errorFragment = TriviaGameErrorFragment.newInstance(msg); diff --git a/app/src/main/java/com/luca0n/joguitos/pluck/trivia/TriviaCategory.java b/app/src/main/java/com/luca0n/joguitos/pluck/trivia/TriviaCategory.java index c1b2ce3..6e55097 100644 --- a/app/src/main/java/com/luca0n/joguitos/pluck/trivia/TriviaCategory.java +++ b/app/src/main/java/com/luca0n/joguitos/pluck/trivia/TriviaCategory.java @@ -110,7 +110,7 @@ public enum TriviaCategory { return this.ID; } - private String getName() { + public String getName() { return this.name; } diff --git a/app/src/main/java/com/luca0n/joguitos/pluck/trivia/TriviaFilters.java b/app/src/main/java/com/luca0n/joguitos/pluck/trivia/TriviaFilters.java new file mode 100644 index 0000000..9e817df --- /dev/null +++ b/app/src/main/java/com/luca0n/joguitos/pluck/trivia/TriviaFilters.java @@ -0,0 +1,128 @@ +/* +Pluck: an open source trivia game for Android + +Copyright (C) 2021 Joguitos do luca0N! + +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 3 of the License, or (at your option) any later +version. + +This program 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 +this program. If not, see . + +This game is a fork of LibreTrivia, and its source code is available at +. + +Contact us at . +*/ + +package com.luca0n.joguitos.pluck.trivia; + +import java.io.Serializable; + +public class TriviaFilters implements Serializable { + private static final int DEFAULT_AMOUNT = 10; + + private final int amount; + private final TriviaCategory category; + private final TriviaDifficulty difficulty; + private final TriviaType type; + + private TriviaFilters(Builder builder) { + this.amount = builder.amount; + this.category = builder.category; + this.difficulty = builder.difficulty; + this.type = builder.type; + } + + /** + * @return The desired amount of questions selected by the player. + * @since 2021-03-16 + */ + public int getAmount(){ + return amount; + } + /** + * @return The category selected by the player. + * @since 2021-03-16 + */ + public TriviaCategory getCategory(){ + return category; + } + /** + * @return The difficulty selected by the player. + * @since 2021-03-16 + */ + public TriviaDifficulty getDifficulty(){ + return difficulty; + } + /** + * @return The type selected by the player. + * @since 2021-03-16 + */ + public TriviaType getType(){ + return type; + } + + public static class Builder { + private final int amount; + private TriviaCategory category; + private TriviaDifficulty difficulty; + private TriviaType type; + + public Builder() { + this.amount = DEFAULT_AMOUNT; + } + + public Builder(int amount) { + if (amount > 50) { + this.amount = 50; + } else { + this.amount = amount; + } + } + + public Builder category(TriviaCategory category) { + this.category = category; + return this; + } + + public Builder difficulty(TriviaDifficulty difficulty) { + this.difficulty = difficulty; + return this; + } + + public Builder type(TriviaType type) { + this.type = type; + return this; + } + + public TriviaFilters build() { + return new TriviaFilters(this); + } + } + + @Override + public String toString() { + StringBuilder url = new StringBuilder(); + + url.append("amount=").append(this.amount); + + if (this.category != null & this.category != TriviaCategory.ANY) { + url.append("&category=").append(this.category.getID()); + } + if (this.difficulty != null & this.difficulty != TriviaDifficulty.ANY) { + url.append("&difficulty=").append(this.difficulty.getName()); + } + if (this.type != null & this.type != TriviaType.ANY) { + url.append("&type=").append(this.type.getName()); + } + + return url.toString(); + } +} diff --git a/app/src/main/java/com/luca0n/joguitos/pluck/trivia/TriviaQuery.java b/app/src/main/java/com/luca0n/joguitos/pluck/trivia/TriviaQuery.java index 59edc52..8e16fb9 100644 --- a/app/src/main/java/com/luca0n/joguitos/pluck/trivia/TriviaQuery.java +++ b/app/src/main/java/com/luca0n/joguitos/pluck/trivia/TriviaQuery.java @@ -38,8 +38,8 @@ public class TriviaQuery implements Serializable { this.amount = builder.amount; this.category = builder.category; this.difficulty = builder.difficulty; - this.base = builder.base; this.type = builder.type; + this.base = builder.base; } public static class Builder { diff --git a/app/src/main/java/com/luca0n/joguitos/pluck/util/ApiUtil.java b/app/src/main/java/com/luca0n/joguitos/pluck/util/ApiUtil.java index 610ae15..de8d6b3 100644 --- a/app/src/main/java/com/luca0n/joguitos/pluck/util/ApiUtil.java +++ b/app/src/main/java/com/luca0n/joguitos/pluck/util/ApiUtil.java @@ -50,9 +50,12 @@ import com.luca0n.joguitos.pluck.exceptions.NoTriviaResultsException; import com.luca0n.joguitos.pluck.exceptions.InvalidTriviaFormatException; import com.luca0n.joguitos.pluck.trivia.TriviaQuery; import com.luca0n.joguitos.pluck.trivia.TriviaQuestion; +import com.luca0n.joguitos.pluck.trivia.TriviaCategory; +import com.luca0n.joguitos.pluck.trivia.TriviaDifficulty; import com.luca0n.joguitos.pluck.trivia.TriviaQuestionBoolean; import com.luca0n.joguitos.pluck.trivia.TriviaQuestionMultiple; import com.luca0n.joguitos.pluck.trivia.TriviaType; +import com.luca0n.joguitos.pluck.trivia.TriviaFilters; public class ApiUtil { @@ -100,12 +103,14 @@ public class ApiUtil { return response; } - public static String GET(TriviaQuery query) throws IOException { + public static String GET(TriviaQuery query) + throws IOException { return GET(query.toString()); } - public static ArrayList jsonToQuestionArray(String json) - throws NoTriviaResultsException, InvalidTriviaFormatException { + public static ArrayList jsonToQuestionArray(String json, + TriviaFilters filters) throws NoTriviaResultsException, + InvalidTriviaFormatException { JsonObject jsonObject; try{ jsonObject = new JsonParser().parse(json).getAsJsonObject(); @@ -126,6 +131,16 @@ public class ApiUtil { JsonObject object = element.getAsJsonObject(); TriviaType type = TriviaType.get(object.get("type").getAsString()); + TriviaCategory fc = filters.getCategory(); + + // Check if this question matches the user specified criteria. + if (fc != null && + fc != TriviaCategory.ANY && !object.get("category").getAsString().equals(fc.getName())) + continue; + + if (filters.getDifficulty() != TriviaDifficulty.ANY && !object.get("difficulty").getAsString().equals(filters.getDifficulty().getName())) + continue; + if (type == TriviaType.MULTIPLE) { questions.add(TriviaQuestionMultiple.fromJson(object)); } else { @@ -133,6 +148,10 @@ public class ApiUtil { } } + // Check if there are no questions that match the specified user criteria. + if (questions.size() == 0) + throw new NoTriviaResultsException(); + return questions; } } From c3c820715b6a2364c7da24d6d3cc185ec43437cd Mon Sep 17 00:00:00 2001 From: "luca0N\\!" Date: Tue, 16 Mar 2021 00:57:42 -0300 Subject: [PATCH 4/6] Randomized question order --- app/src/main/java/com/luca0n/joguitos/pluck/util/ApiUtil.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/java/com/luca0n/joguitos/pluck/util/ApiUtil.java b/app/src/main/java/com/luca0n/joguitos/pluck/util/ApiUtil.java index de8d6b3..144d56d 100644 --- a/app/src/main/java/com/luca0n/joguitos/pluck/util/ApiUtil.java +++ b/app/src/main/java/com/luca0n/joguitos/pluck/util/ApiUtil.java @@ -43,6 +43,7 @@ import java.net.Proxy; import java.net.HttpURLConnection; import java.net.URL; import java.util.ArrayList; +import java.util.Collections; import com.luca0n.joguitos.pluck.R; import com.luca0n.joguitos.pluck.PluckApplication; @@ -152,6 +153,9 @@ public class ApiUtil { if (questions.size() == 0) throw new NoTriviaResultsException(); + // Shuffle the question array. + Collections.shuffle(questions); + return questions; } } From 8e84fe2ff53b91af67df00fa4a902afad7f6c48e Mon Sep 17 00:00:00 2001 From: "luca0N\\!" Date: Tue, 16 Mar 2021 01:00:19 -0300 Subject: [PATCH 5/6] Finished filter support The game now limits trivia file questions based on the question amount picked by the player on `MainActivity`. --- app/src/main/java/com/luca0n/joguitos/pluck/util/ApiUtil.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/java/com/luca0n/joguitos/pluck/util/ApiUtil.java b/app/src/main/java/com/luca0n/joguitos/pluck/util/ApiUtil.java index 144d56d..cc3883b 100644 --- a/app/src/main/java/com/luca0n/joguitos/pluck/util/ApiUtil.java +++ b/app/src/main/java/com/luca0n/joguitos/pluck/util/ApiUtil.java @@ -128,7 +128,11 @@ public class ApiUtil { ArrayList questions = new ArrayList<>(); + int elements = 0; + for (JsonElement element : jsonArray) { + if (elements++ == filters.getAmount()) + break; JsonObject object = element.getAsJsonObject(); TriviaType type = TriviaType.get(object.get("type").getAsString()); From 4045f0207ce0c9b2555de123362123813c13a100 Mon Sep 17 00:00:00 2001 From: "luca0N\\!" Date: Tue, 16 Mar 2021 01:23:17 -0300 Subject: [PATCH 6/6] Added "All" option on the question amount spinner Added a new option on the question amount spinner that allows players to view all questions on a trivia file. --- .../pluck/activities/MainActivity.java | 32 +++++++++++++------ .../luca0n/joguitos/pluck/util/ApiUtil.java | 3 +- app/src/main/res/values/strings.xml | 4 ++- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/luca0n/joguitos/pluck/activities/MainActivity.java b/app/src/main/java/com/luca0n/joguitos/pluck/activities/MainActivity.java index ea48e47..3cb9f2e 100644 --- a/app/src/main/java/com/luca0n/joguitos/pluck/activities/MainActivity.java +++ b/app/src/main/java/com/luca0n/joguitos/pluck/activities/MainActivity.java @@ -86,10 +86,21 @@ public class MainActivity extends BaseActivity { } return; } - int amount = (int) spinnerNumber.getSelectedItem(); + String amount = (String) spinnerNumber.getSelectedItem(); TriviaCategory category = (TriviaCategory) spinnerCategory.getSelectedItem(); TriviaDifficulty difficulty = (TriviaDifficulty) spinnerDifficulty.getSelectedItem(); + if (amount.equals(getString(R.string.ui_all))){ + // The "All" option is only used for trivia files. + new AlertDialog.Builder(this) + .setIcon(android.R.drawable.ic_dialog_alert) + .setTitle(R.string.error) + .setMessage(R.string.error_option_all_unavailable) + .setPositiveButton(android.R.string.ok, null) + .show(); + return; + } + // Put the server address in the TriviaQuery object. SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); String serverKey = getResources().getString(R.string.pref_network_server); @@ -103,7 +114,7 @@ public class MainActivity extends BaseActivity { sp.edit().putString(serverKey, defaultServer).commit(); } - TriviaQuery query = new TriviaQuery.Builder(amount) + TriviaQuery query = new TriviaQuery.Builder(Integer.valueOf(amount)) .category(category) .difficulty(difficulty) .base(base) @@ -114,16 +125,19 @@ public class MainActivity extends BaseActivity { intent.putExtra(TriviaGameActivity.EXTRA_TRIVIA_SOURCE, TriviaSource.SERVER.getName()); // Specify the trivia source. intent.putExtra(TriviaGameActivity.EXTRA_TRIVIA_QUERY, query); intent.putExtra(TriviaGameActivity.EXTRA_TRIVIA_FILTERS, - new TriviaFilters.Builder(amount) + new TriviaFilters.Builder(Integer.valueOf(amount)) .category(category) .difficulty(difficulty) .build()); startActivity(intent); }); - Integer[] numbers = new Integer[50]; - for (int i = 0; i < 50; ) { - numbers[i] = ++i; + // Create a String array that holds all options for spinnerNumber. + String[] numbers = new String[51]; + for (int i = 0; i < 51; i++) { + // The first entry on the spinner should be the value "All", which is used with the + // file trivia source. + numbers[i] = i == 0 ? getString(R.string.ui_all) : String.valueOf(i); } spinnerSource.setAdapter( new ArrayAdapter<>( @@ -133,7 +147,7 @@ public class MainActivity extends BaseActivity { new ArrayAdapter<>( this, android.R.layout.simple_list_item_1, numbers) ); - spinnerNumber.setSelection(9); + spinnerNumber.setSelection(10); spinnerCategory.setAdapter( new ArrayAdapter<>( @@ -154,14 +168,14 @@ public class MainActivity extends BaseActivity { if (extras == null) break; - int amount = (int) spinnerNumber.getSelectedItem(); + String amount = (String) spinnerNumber.getSelectedItem(); TriviaCategory category = (TriviaCategory) spinnerCategory.getSelectedItem(); TriviaDifficulty difficulty = (TriviaDifficulty) spinnerDifficulty.getSelectedItem(); Intent intent = new Intent(getApplicationContext(), TriviaGameActivity.class); // Put filters on the extras object. intent.putExtra(TriviaGameActivity.EXTRA_TRIVIA_FILTERS, - new TriviaFilters.Builder(amount) + new TriviaFilters.Builder(amount.equals(getString(R.string.ui_all)) ? -1 : Integer.valueOf(amount)) .category(category) .difficulty(difficulty) .build()); diff --git a/app/src/main/java/com/luca0n/joguitos/pluck/util/ApiUtil.java b/app/src/main/java/com/luca0n/joguitos/pluck/util/ApiUtil.java index cc3883b..997865f 100644 --- a/app/src/main/java/com/luca0n/joguitos/pluck/util/ApiUtil.java +++ b/app/src/main/java/com/luca0n/joguitos/pluck/util/ApiUtil.java @@ -131,7 +131,8 @@ public class ApiUtil { int elements = 0; for (JsonElement element : jsonArray) { - if (elements++ == filters.getAmount()) + int amount = filters.getAmount(); + if (amount > 0 && elements++ == amount) break; JsonObject object = element.getAsJsonObject(); TriviaType type = TriviaType.get(object.get("type").getAsString()); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 075fcc1..ed58d52 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -70,7 +70,8 @@ Contact us at . About Play Start Game - Any + Any + All True False Correct @@ -97,6 +98,7 @@ Contact us at . Trivia files are not supported in this version of Android. Invalid trivia file!\n\nTrivia files should be in a plain text JSON format. An unknown error occurred while attempting to read the opened file. + The selected question amount is only available for the "File" trivia game source. Settings