diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..948b738 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,56 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 28 + buildToolsVersion '28.0.3' + + defaultConfig { + applicationId "io.github.trytonvanmeer.libretrivia" + minSdkVersion 19 + targetSdkVersion 28 + versionCode 3 + versionName "0.3" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + + compileOptions { + encoding = 'UTF-8' + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +ext { + butterknife = '9.0.0-rc2' +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + + implementation 'androidx.annotation:annotation:1.0.0' + implementation 'androidx.appcompat:appcompat:1.0.2' + implementation 'androidx.cardview:cardview:1.0.0' + implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha2' + implementation 'androidx.legacy:legacy-preference-v14:1.0.0' + implementation 'androidx.legacy:legacy-support-v4:1.0.0' + implementation 'androidx.recyclerview:recyclerview:1.0.0' + + implementation "com.jakewharton:butterknife:${butterknife}" + annotationProcessor "com.jakewharton:butterknife-compiler:${butterknife}" + + implementation 'com.google.code.gson:gson:2.8.5' + implementation 'com.mikepenz:aboutlibraries:6.2.0' + + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.3.2' + androidTestImplementation 'org.junit.jupiter:junit-jupiter-api:5.3.2' + androidTestImplementation 'androidx.test:core:1.0.0' + androidTestImplementation 'androidx.test:runner:1.1.0' + androidTestImplementation 'androidx.test.ext:junit:1.0.0' +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..f1b4245 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/app/src/androidTest/java/io/github/trytonvanmeer/libretrivia/ApplicationTest.java b/app/src/androidTest/java/io/github/trytonvanmeer/libretrivia/ApplicationTest.java new file mode 100644 index 0000000..fe1f95f --- /dev/null +++ b/app/src/androidTest/java/io/github/trytonvanmeer/libretrivia/ApplicationTest.java @@ -0,0 +1,27 @@ +package io.github.trytonvanmeer.libretrivia; + +import android.content.Context; + +import org.junit.jupiter.api.Test; +import org.junit.runner.RunWith; + +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ApplicationTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = ApplicationProvider.getApplicationContext(); + + assertEquals("io.github.trytonvanmeer.libretrivia", appContext.getPackageName()); + } +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..6bfe14d --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/ic_launcher-web.png b/app/src/main/ic_launcher-web.png new file mode 100644 index 0000000..c4487c4 Binary files /dev/null and b/app/src/main/ic_launcher-web.png differ diff --git a/app/src/main/java/io/github/trytonvanmeer/libretrivia/LibreTriviaApplication.java b/app/src/main/java/io/github/trytonvanmeer/libretrivia/LibreTriviaApplication.java new file mode 100644 index 0000000..02f6085 --- /dev/null +++ b/app/src/main/java/io/github/trytonvanmeer/libretrivia/LibreTriviaApplication.java @@ -0,0 +1,21 @@ +package io.github.trytonvanmeer.libretrivia; + +import android.annotation.SuppressLint; +import android.app.Application; +import android.content.Context; + + +public class LibreTriviaApplication extends Application { + @SuppressLint("StaticFieldLeak") + private static Context context; + + @Override + public void onCreate() { + super.onCreate(); + LibreTriviaApplication.context = getApplicationContext(); + } + + public static Context getAppContext() { + return LibreTriviaApplication.context; + } +} diff --git a/app/src/main/java/io/github/trytonvanmeer/libretrivia/activities/BaseActivity.java b/app/src/main/java/io/github/trytonvanmeer/libretrivia/activities/BaseActivity.java new file mode 100644 index 0000000..23a88e3 --- /dev/null +++ b/app/src/main/java/io/github/trytonvanmeer/libretrivia/activities/BaseActivity.java @@ -0,0 +1,59 @@ +package io.github.trytonvanmeer.libretrivia.activities; + +import android.annotation.SuppressLint; +import android.content.Intent; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; + +import com.mikepenz.aboutlibraries.Libs; +import com.mikepenz.aboutlibraries.LibsBuilder; + +import androidx.appcompat.app.AppCompatActivity; +import io.github.trytonvanmeer.libretrivia.R; +import io.github.trytonvanmeer.libretrivia.settings.SettingsActivity; + +@SuppressLint("Registered") +public class BaseActivity extends AppCompatActivity { + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.app_menu, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.settings: + onSettings(); + return true; + case R.id.about: + onAbout(); + return true; + case android.R.id.home: + onBackPressed(); + default: + return super.onOptionsItemSelected(item); + } + } + + private void onSettings() { + Intent intent = new Intent(this, SettingsActivity.class); + startActivity(intent); + } + + private void onAbout() { + String appName = getResources().getString(R.string.app_name); + String appDescription = getResources().getString(R.string.app_description); + + new LibsBuilder() + .withActivityStyle(Libs.ActivityStyle.LIGHT_DARK_TOOLBAR) + .withAboutIconShown(true) + .withAboutAppName(appName) + .withAboutVersionShownName(true) + .withAboutDescription(appDescription) + .start(this); + } +} diff --git a/app/src/main/java/io/github/trytonvanmeer/libretrivia/activities/MainActivity.java b/app/src/main/java/io/github/trytonvanmeer/libretrivia/activities/MainActivity.java new file mode 100644 index 0000000..b93a71b --- /dev/null +++ b/app/src/main/java/io/github/trytonvanmeer/libretrivia/activities/MainActivity.java @@ -0,0 +1,67 @@ +package io.github.trytonvanmeer.libretrivia.activities; + +import android.content.Intent; +import android.os.Bundle; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.Spinner; + +import butterknife.BindView; +import butterknife.ButterKnife; +import io.github.trytonvanmeer.libretrivia.R; +import io.github.trytonvanmeer.libretrivia.trivia.TriviaCategory; +import io.github.trytonvanmeer.libretrivia.trivia.TriviaDifficulty; +import io.github.trytonvanmeer.libretrivia.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; + + @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(); + + 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); + }); + + + 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())); + + spinnerDifficulty.setAdapter( + new ArrayAdapter<>( + this, android.R.layout.simple_list_item_1, TriviaDifficulty.values())); + } +} diff --git a/app/src/main/java/io/github/trytonvanmeer/libretrivia/activities/TriviaGameActivity.java b/app/src/main/java/io/github/trytonvanmeer/libretrivia/activities/TriviaGameActivity.java new file mode 100644 index 0000000..779b792 --- /dev/null +++ b/app/src/main/java/io/github/trytonvanmeer/libretrivia/activities/TriviaGameActivity.java @@ -0,0 +1,227 @@ +package io.github.trytonvanmeer.libretrivia.activities; + +import android.app.AlertDialog; +import android.content.Intent; +import android.content.res.ColorStateList; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.os.AsyncTask; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.ProgressBar; +import android.widget.TextView; + +import java.io.IOException; + +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentTransaction; +import butterknife.BindView; +import butterknife.ButterKnife; +import io.github.trytonvanmeer.libretrivia.R; +import io.github.trytonvanmeer.libretrivia.exceptions.NoTriviaResultsException; +import io.github.trytonvanmeer.libretrivia.fragments.TriviaGameErrorFragment; +import io.github.trytonvanmeer.libretrivia.fragments.TriviaQuestionFragment; +import io.github.trytonvanmeer.libretrivia.interfaces.IDownloadTriviaQuestionReceiver; +import io.github.trytonvanmeer.libretrivia.trivia.TriviaGame; +import io.github.trytonvanmeer.libretrivia.trivia.TriviaQuery; +import io.github.trytonvanmeer.libretrivia.trivia.TriviaQuestion; +import io.github.trytonvanmeer.libretrivia.util.ApiUtil; +import io.github.trytonvanmeer.libretrivia.util.SoundUtil; + +public class TriviaGameActivity extends BaseActivity + implements IDownloadTriviaQuestionReceiver { + static final String EXTRA_TRIVIA_QUERY = "extra_trivia_query"; + private final String STATE_TRIVIA_GAME = "state_trivia_game"; + + private TriviaGame game; + + @BindView(R.id.progress_bar) + ProgressBar progressBar; + @BindView(R.id.trivia_status_bar) + LinearLayout triviaStatusBar; + @BindView(R.id.text_question_category) + TextView textViewQuestionCategory; + @BindView(R.id.text_question_difficulty) + TextView textViewQuestionDifficulty; + @BindView(R.id.text_question_progress) + TextView textViewQuestionProgress; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_trivia_game); + ButterKnife.bind(this); + + if (savedInstanceState != null) { + this.game = (TriviaGame) savedInstanceState.getSerializable(STATE_TRIVIA_GAME); + } else { + Bundle bundle = getIntent().getExtras(); + assert bundle != null; + TriviaQuery query = (TriviaQuery) bundle.get(EXTRA_TRIVIA_QUERY); + + progressBar.setVisibility(View.VISIBLE); + + DownloadTriviaQuestionsTask task = new DownloadTriviaQuestionsTask(); + task.setReceiver(this); + task.execute(query); + } + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putSerializable(STATE_TRIVIA_GAME, this.game); + } + + @Override + public void onBackPressed() { + Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.frame_trivia_game); + + if (fragment instanceof TriviaGameErrorFragment) { + super.onBackPressed(); + } else { + new AlertDialog.Builder(this) + .setTitle(R.string.ui_quit_game) + .setMessage(R.string.ui_quit_game_msg) + .setPositiveButton(android.R.string.yes, (dialog, which) -> + TriviaGameActivity.super.onBackPressed()) + .setNegativeButton(android.R.string.no, (dialog, which) -> { + }) + .show(); + } + } + + public void onTriviaQuestionsDownloaded(String json) { + if (json == null) { + onNetworkError(); + return; + } else { + try { + this.game = new TriviaGame(ApiUtil.jsonToQuestionArray(json)); + } catch (NoTriviaResultsException e) { + onNoTriviaResults(); + return; + } + } + + // Setup game layout + progressBar.setVisibility(View.GONE); + triviaStatusBar.setVisibility(View.VISIBLE); + updateStatusBar(); + updateTriviaQuestion(); + } + + private void updateStatusBar() { + String progress = getResources().getString(R.string.ui_question_progress, + game.getQuestionProgress(), game.getQuestionsCount()); + + String category = (game.getCurrentQuestion().getCategory() != null) + ? game.getCurrentQuestion().getCategory().toString() : ""; + + String difficulty = game.getCurrentQuestion().getDifficulty().toString(); + + textViewQuestionProgress.setText(progress); + textViewQuestionCategory.setText(category); + textViewQuestionDifficulty.setText(difficulty); + } + + private void updateTriviaQuestion() { + Fragment fragment = TriviaQuestionFragment.newInstance(); + getSupportFragmentManager().beginTransaction() + .replace(R.id.frame_trivia_game, fragment) + .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN) + .commit(); + } + + private void onNetworkError() { + String msg = getResources().getString(R.string.error_network); + Fragment errorFragment = TriviaGameErrorFragment.newInstance(msg); + + FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); + ft.replace(R.id.frame_trivia_game, errorFragment); + ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN); + ft.commit(); + } + + private void onNoTriviaResults() { + String msg = getResources().getString(R.string.error_no_trivia_results); + Fragment errorFragment = TriviaGameErrorFragment.newInstance(msg); + + FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); + ft.replace(R.id.frame_trivia_game, errorFragment); + ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN); + ft.commit(); + } + + public TriviaQuestion getCurrentQuestion() { + return this.game.getCurrentQuestion(); + } + + public void onAnswerClick(Button answer, Button correctAnswer) { + boolean guess = game.nextQuestion(answer.getText().toString()); + + final int green = getResources().getColor(R.color.colorAccentGreen); + int color = guess ? green + : getResources().getColor(R.color.colorAccentRed); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + ColorStateList stateList = ColorStateList.valueOf(color); + answer.setBackgroundTintList(stateList); + + if (!guess) { + final ColorStateList greenStateList = ColorStateList.valueOf(green); + correctAnswer.setBackgroundTintList(greenStateList); + } + } else { + answer.getBackground().getCurrent().setColorFilter( + new PorterDuffColorFilter(color, PorterDuff.Mode.MULTIPLY)); + + if (!guess) + correctAnswer.getBackground().getCurrent().setColorFilter( + new PorterDuffColorFilter(green, PorterDuff.Mode.MULTIPLY)); + } + + SoundUtil.playSound(this, guess ? + SoundUtil.SOUND_ANSWER_CORRECT : SoundUtil.SOUND_ANSWER_WRONG); + + new Handler().postDelayed(() -> { + if (game.isDone()) { + Intent intent = new Intent(getApplicationContext(), TriviaGameResultsActivity.class); + intent.putExtra(TriviaGameResultsActivity.EXTRA_TRIVIA_GAME, game); + startActivity(intent); + finish(); + } else { + updateStatusBar(); + updateTriviaQuestion(); + } + }, 500); + } + + private static class DownloadTriviaQuestionsTask extends AsyncTask { + private IDownloadTriviaQuestionReceiver receiver; + + @Override + protected String doInBackground(TriviaQuery... query) { + String json; + try { + json = ApiUtil.GET(query[0]); + } catch (IOException e) { + return null; + } + return json; + } + + @Override + protected void onPostExecute(String json) { + receiver.onTriviaQuestionsDownloaded(json); + } + + private void setReceiver(IDownloadTriviaQuestionReceiver receiver) { + this.receiver = receiver; + } + } +} diff --git a/app/src/main/java/io/github/trytonvanmeer/libretrivia/activities/TriviaGameResultsActivity.java b/app/src/main/java/io/github/trytonvanmeer/libretrivia/activities/TriviaGameResultsActivity.java new file mode 100644 index 0000000..6f33c18 --- /dev/null +++ b/app/src/main/java/io/github/trytonvanmeer/libretrivia/activities/TriviaGameResultsActivity.java @@ -0,0 +1,47 @@ +package io.github.trytonvanmeer.libretrivia.activities; + +import android.os.Bundle; +import android.widget.Button; +import android.widget.TextView; + +import butterknife.BindView; +import butterknife.ButterKnife; +import io.github.trytonvanmeer.libretrivia.R; +import io.github.trytonvanmeer.libretrivia.trivia.TriviaGame; + +public class TriviaGameResultsActivity extends BaseActivity { + static final String EXTRA_TRIVIA_GAME = "extra_trivia_game"; + + @BindView(R.id.text_results_correct) + TextView textResultsCorrect; + @BindView(R.id.text_results_wrong) + TextView textResultsWrong; + @BindView(R.id.text_results_total) + TextView textResultsTotal; + @BindView(R.id.button_return_to_menu) + Button buttonReturnToMenu; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_trivia_game_results); + ButterKnife.bind(this); + + Bundle bundle = getIntent().getExtras(); + TriviaGame game = (TriviaGame) bundle.get(EXTRA_TRIVIA_GAME); + + int correctTotal = 0; + + for (boolean result : game.getResults()) { + if (result) { + correctTotal++; + } + } + + textResultsCorrect.setText(String.valueOf(correctTotal)); + textResultsWrong.setText(String.valueOf(game.getQuestionsCount() - correctTotal)); + textResultsTotal.setText(String.valueOf(game.getQuestionsCount())); + + buttonReturnToMenu.setOnClickListener(v -> finish()); + } +} diff --git a/app/src/main/java/io/github/trytonvanmeer/libretrivia/exceptions/NoTriviaResultsException.java b/app/src/main/java/io/github/trytonvanmeer/libretrivia/exceptions/NoTriviaResultsException.java new file mode 100644 index 0000000..58dafff --- /dev/null +++ b/app/src/main/java/io/github/trytonvanmeer/libretrivia/exceptions/NoTriviaResultsException.java @@ -0,0 +1,4 @@ +package io.github.trytonvanmeer.libretrivia.exceptions; + +public class NoTriviaResultsException extends Exception { +} diff --git a/app/src/main/java/io/github/trytonvanmeer/libretrivia/fragments/TriviaGameErrorFragment.java b/app/src/main/java/io/github/trytonvanmeer/libretrivia/fragments/TriviaGameErrorFragment.java new file mode 100644 index 0000000..036c9ec --- /dev/null +++ b/app/src/main/java/io/github/trytonvanmeer/libretrivia/fragments/TriviaGameErrorFragment.java @@ -0,0 +1,46 @@ +package io.github.trytonvanmeer.libretrivia.fragments; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; +import butterknife.BindView; +import butterknife.ButterKnife; +import io.github.trytonvanmeer.libretrivia.R; + +public class TriviaGameErrorFragment extends Fragment { + private final static String ARG_ERROR_MSG = "arg_error_msg"; + + @BindView(R.id.text_error_msg) + TextView textView; + + public TriviaGameErrorFragment() { + } + + public static TriviaGameErrorFragment newInstance(String msg) { + Bundle args = new Bundle(); + args.putString(ARG_ERROR_MSG, msg); + + TriviaGameErrorFragment fragment = new TriviaGameErrorFragment(); + fragment.setArguments(args); + return fragment; + } + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_trivia_game_error, container, false); + ButterKnife.bind(this, view); + + Bundle args; + if ((args = getArguments()) != null) { + textView.setText(args.getString(ARG_ERROR_MSG)); + } + + return view; + } +} diff --git a/app/src/main/java/io/github/trytonvanmeer/libretrivia/fragments/TriviaQuestionFragment.java b/app/src/main/java/io/github/trytonvanmeer/libretrivia/fragments/TriviaQuestionFragment.java new file mode 100644 index 0000000..470c364 --- /dev/null +++ b/app/src/main/java/io/github/trytonvanmeer/libretrivia/fragments/TriviaQuestionFragment.java @@ -0,0 +1,117 @@ +package io.github.trytonvanmeer.libretrivia.fragments; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.TextView; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import butterknife.BindViews; +import butterknife.ButterKnife; +import io.github.trytonvanmeer.libretrivia.R; +import io.github.trytonvanmeer.libretrivia.activities.TriviaGameActivity; +import io.github.trytonvanmeer.libretrivia.trivia.TriviaQuestion; +import io.github.trytonvanmeer.libretrivia.trivia.TriviaQuestionMultiple; + + +public class TriviaQuestionFragment extends Fragment { + + private static final int buttonAnswerOneID = R.id.button_answer_one; + private static final int buttonAnswerTwoID = R.id.button_answer_two; + private static final int buttonAnswerThreeID = R.id.button_answer_three; + private static final int buttonAnswerFourID = R.id.button_answer_four; + + @BindViews({ + buttonAnswerOneID, + buttonAnswerTwoID, + buttonAnswerThreeID, + buttonAnswerFourID + }) + Button[] buttonAnswers; + + Button buttonAnswerCorrect; + + Button buttonAnswerTrue; + Button buttonAnswerFalse; + + public TriviaQuestionFragment() { + } + + public static TriviaQuestionFragment newInstance() { + return new TriviaQuestionFragment(); + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + TriviaQuestion question = ((TriviaGameActivity) getActivity()).getCurrentQuestion(); + View view; + + if (question instanceof TriviaQuestionMultiple) { + view = inflater.inflate(R.layout.fragment_trivia_question_multiple, container, false); + ButterKnife.bind(this, view); + } else { + view = inflater.inflate(R.layout.fragment_trivia_question_boolean, container, false); + this.buttonAnswerTrue = view.findViewById(R.id.button_answer_true); + this.buttonAnswerFalse = view.findViewById(R.id.button_answer_false); + } + + TextView textViewQuestion = view.findViewById(R.id.text_trivia_question); + textViewQuestion.setText(question.getQuestion()); + setupButtons(); + + return view; + } + + private void setupButtons() { + AnswerButtonListener listener = new AnswerButtonListener(); + TriviaQuestion question = ((TriviaGameActivity) getActivity()).getCurrentQuestion(); + + if (question instanceof TriviaQuestionMultiple) { + List answers = Arrays.asList(( + (TriviaQuestionMultiple) question).getAnswerList()); + Collections.shuffle(answers); + + for (int i = 0; i < buttonAnswers.length; i++) { + buttonAnswers[i].setText(answers.get(i)); + buttonAnswers[i].setOnClickListener(listener); + if (question.checkAnswer(answers.get(i))) { + buttonAnswerCorrect = buttonAnswers[i]; + } + } + } else { + buttonAnswerTrue.setOnClickListener(listener); + buttonAnswerFalse.setOnClickListener(listener); + } + } + + private void disableButtons() { + TriviaQuestion question = ((TriviaGameActivity) getActivity()).getCurrentQuestion(); + if (question instanceof TriviaQuestionMultiple) { + buttonAnswers[0].setEnabled(false); + buttonAnswers[1].setEnabled(false); + buttonAnswers[2].setEnabled(false); + buttonAnswers[3].setEnabled(false); + } else { + buttonAnswerTrue.setEnabled(false); + buttonAnswerFalse.setEnabled(false); + } + } + + private class AnswerButtonListener implements View.OnClickListener { + @Override + public void onClick(View v) { + disableButtons(); + ((TriviaGameActivity) getActivity()).onAnswerClick((Button) v, buttonAnswerCorrect); + } + } +} diff --git a/app/src/main/java/io/github/trytonvanmeer/libretrivia/interfaces/IDownloadTriviaQuestionReceiver.java b/app/src/main/java/io/github/trytonvanmeer/libretrivia/interfaces/IDownloadTriviaQuestionReceiver.java new file mode 100644 index 0000000..4be0ee9 --- /dev/null +++ b/app/src/main/java/io/github/trytonvanmeer/libretrivia/interfaces/IDownloadTriviaQuestionReceiver.java @@ -0,0 +1,5 @@ +package io.github.trytonvanmeer.libretrivia.interfaces; + +public interface IDownloadTriviaQuestionReceiver { + void onTriviaQuestionsDownloaded(String json); +} diff --git a/app/src/main/java/io/github/trytonvanmeer/libretrivia/settings/SettingsActivity.java b/app/src/main/java/io/github/trytonvanmeer/libretrivia/settings/SettingsActivity.java new file mode 100644 index 0000000..fff1083 --- /dev/null +++ b/app/src/main/java/io/github/trytonvanmeer/libretrivia/settings/SettingsActivity.java @@ -0,0 +1,37 @@ +package io.github.trytonvanmeer.libretrivia.settings; + +import android.os.Bundle; +import android.view.MenuItem; + +import androidx.annotation.Nullable; +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AppCompatActivity; + +public class SettingsActivity extends AppCompatActivity { + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + actionBar.setDisplayHomeAsUpEnabled(true); + actionBar.setTitle(null); + } + + getFragmentManager() + .beginTransaction() + .replace(android.R.id.content, new SettingsFragment()) + .commit(); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + finish(); + return true; + default: + return super.onOptionsItemSelected(item); + } + } +} diff --git a/app/src/main/java/io/github/trytonvanmeer/libretrivia/settings/SettingsFragment.java b/app/src/main/java/io/github/trytonvanmeer/libretrivia/settings/SettingsFragment.java new file mode 100644 index 0000000..fa04ca5 --- /dev/null +++ b/app/src/main/java/io/github/trytonvanmeer/libretrivia/settings/SettingsFragment.java @@ -0,0 +1,15 @@ +package io.github.trytonvanmeer.libretrivia.settings; + +import android.os.Bundle; +import android.preference.PreferenceFragment; + +import androidx.annotation.Nullable; +import io.github.trytonvanmeer.libretrivia.R; + +public class SettingsFragment extends PreferenceFragment { + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + addPreferencesFromResource(R.xml.preferences); + } +} diff --git a/app/src/main/java/io/github/trytonvanmeer/libretrivia/trivia/TriviaCategory.java b/app/src/main/java/io/github/trytonvanmeer/libretrivia/trivia/TriviaCategory.java new file mode 100644 index 0000000..65003de --- /dev/null +++ b/app/src/main/java/io/github/trytonvanmeer/libretrivia/trivia/TriviaCategory.java @@ -0,0 +1,102 @@ +package io.github.trytonvanmeer.libretrivia.trivia; + +import java.util.HashMap; +import java.util.Map; + +import io.github.trytonvanmeer.libretrivia.LibreTriviaApplication; +import io.github.trytonvanmeer.libretrivia.R; + +/* + Categories that a Trivia Question can fall into + */ +public enum TriviaCategory { + ANY(-1, "Any", R.string.ui_any), + + GENERAL_KNOWLEDGE(9, "General Knowledge", + R.string.category_general_knowledge), + ENTERTAINMENT_BOOKS(10, "Entertainment: Books", + R.string.category_entertainment_books), + ENTERTAINMENT_FILM(11, "Entertainment: Film", + R.string.category_entertainment_film), + ENTERTAINMENT_MUSIC(12, "Entertainment: Music", + R.string.category_entertainment_music), + ENTERTAINMENT_MUSICALS_THEATRES(13, "Entertainment: Musicals & Theatres", + R.string.category_entertainment_musicals_theatres), + ENTERTAINMENT_TELEVISION(14, "Entertainment: Television", + R.string.category_entertainment_television), + ENTERTAINMENT_VIDEO_GAMES(15, "Entertainment: Video Games", + R.string.category_entertainment_video_games), + ENTERTAINMENT_BOARD_GAMES(16, "Entertainment: Board Games", + R.string.category_entertainment_board_games), + ENTERTAINMENT_JAPANESE_ANIME_MANGA(31, "Entertainment: Japanese Anime & Manga", + R.string.category_entertainment_japanese_anime_manga), + ENTERTAINMENT_CARTOON_ANIMATIONS(32, "Entertainment: Cartoons & Animation", + R.string.category_entertainment_cartoon_animations), + ENTERTAINMENT_COMICS(29, "Entertainment: Comics", + R.string.category_entertainment_comics), + SCIENCE_NATURE(17, "Science & Nature", + R.string.category_science_nature), + SCIENCE_COMPUTERS(18, "Science: Computers", + R.string.category_science_computers), + SCIENCE_MATHEMATICS(19, "Science: Mathematics", + R.string.category_science_mathematics), + SCIENCE_GADGETS(30, "Science: Gadgets", + R.string.category_science_gadgets), + MYTHOLOGY(20, "Mythology", + R.string.category_mythology), + SPORTS(21, "Sports", + R.string.category_sports), + GEOGRAPHY(22, "Geography", + R.string.category_geography), + HISTORY(23, "History", + R.string.category_history), + POLITICS(24, "Politics", + R.string.category_politics), + ART(25, "Art", + R.string.category_art), + CELEBRITIES(26, "Celebrities", + R.string.category_celebrities), + ANIMALS(27, "Animals", + R.string.category_animals), + VEHICLES(28, "Vehicles", + R.string.category_vehicles); + + // The id of the category in the opentdb api + // see + private final int ID; + // The name of the category in the JSON response + private final String name; + // The display name of the category + private final int displayName; + + private static final Map lookup = new HashMap<>(); + + static { + for (TriviaCategory category : TriviaCategory.values()) { + lookup.put(category.getName(), category); + } + } + + TriviaCategory(int ID, String name, int displayName) { + this.ID = ID; + this.name = name; + this.displayName = displayName; + } + + public int getID() { + return this.ID; + } + + private String getName() { + return this.name; + } + + public static TriviaCategory get(String name) { + return lookup.get(name); + } + + @Override + public String toString() { + return LibreTriviaApplication.getAppContext().getResources().getString(this.displayName); + } +} diff --git a/app/src/main/java/io/github/trytonvanmeer/libretrivia/trivia/TriviaDifficulty.java b/app/src/main/java/io/github/trytonvanmeer/libretrivia/trivia/TriviaDifficulty.java new file mode 100644 index 0000000..b2c4084 --- /dev/null +++ b/app/src/main/java/io/github/trytonvanmeer/libretrivia/trivia/TriviaDifficulty.java @@ -0,0 +1,46 @@ +package io.github.trytonvanmeer.libretrivia.trivia; + +import java.util.HashMap; +import java.util.Map; + +import io.github.trytonvanmeer.libretrivia.LibreTriviaApplication; +import io.github.trytonvanmeer.libretrivia.R; + +public enum TriviaDifficulty { + ANY("any", R.string.ui_any), + + EASY("easy", R.string.difficulty_easy), + MEDIUM("medium", R.string.difficulty_medium), + HARD("hard", R.string.difficulty_hard); + + // Name of difficulty used in queries + private final String name; + // Display name of the difficulty + private final int displayName; + + private static final Map lookup = new HashMap<>(); + + static { + for (TriviaDifficulty difficulty : TriviaDifficulty.values()) { + lookup.put(difficulty.getName(), difficulty); + } + } + + TriviaDifficulty(String name, int displayName) { + this.name = name; + this.displayName = displayName; + } + + public String getName() { + return this.name; + } + + public static TriviaDifficulty get(String name) { + return lookup.get(name); + } + + @Override + public String toString() { + return LibreTriviaApplication.getAppContext().getResources().getString(this.displayName); + } +} diff --git a/app/src/main/java/io/github/trytonvanmeer/libretrivia/trivia/TriviaGame.java b/app/src/main/java/io/github/trytonvanmeer/libretrivia/trivia/TriviaGame.java new file mode 100644 index 0000000..f69f78b --- /dev/null +++ b/app/src/main/java/io/github/trytonvanmeer/libretrivia/trivia/TriviaGame.java @@ -0,0 +1,46 @@ +package io.github.trytonvanmeer.libretrivia.trivia; + +import java.io.Serializable; +import java.util.List; + +public class TriviaGame implements Serializable { + private int currentQuestion; + private final boolean[] results; + private final List questions; + + public TriviaGame(List questions) { + this.currentQuestion = 0; + this.questions = questions; + this.results = new boolean[questions.size()]; + } + + public TriviaQuestion getCurrentQuestion() { + return this.questions.get(currentQuestion); + } + + public int getQuestionProgress() { + return this.currentQuestion + 1; + } + + public int getQuestionsCount() { + return this.questions.size(); + } + + public boolean[] getResults() { + return this.results; + } + + public boolean nextQuestion(String guess) { + TriviaQuestion question = getCurrentQuestion(); + boolean answer = question.checkAnswer(guess); + + results[currentQuestion] = answer; + currentQuestion++; + + return answer; + } + + public boolean isDone() { + return (this.currentQuestion == questions.size()); + } +} diff --git a/app/src/main/java/io/github/trytonvanmeer/libretrivia/trivia/TriviaQuery.java b/app/src/main/java/io/github/trytonvanmeer/libretrivia/trivia/TriviaQuery.java new file mode 100644 index 0000000..fed6828 --- /dev/null +++ b/app/src/main/java/io/github/trytonvanmeer/libretrivia/trivia/TriviaQuery.java @@ -0,0 +1,78 @@ +package io.github.trytonvanmeer.libretrivia.trivia; + +import java.io.Serializable; + +public class TriviaQuery implements Serializable { + private static final String BASE = "https://opentdb.com/api.php?"; + private static final int DEFAULT_AMOUNT = 10; + + private final int amount; + private final TriviaCategory category; + private final TriviaDifficulty difficulty; + private final TriviaType type; + + private TriviaQuery(Builder builder) { + this.amount = builder.amount; + this.category = builder.category; + this.difficulty = builder.difficulty; + this.type = builder.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 TriviaQuery build() { + return new TriviaQuery(this); + } + } + + @Override + public String toString() { + StringBuilder url = new StringBuilder(); + + url.append(BASE); + 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/io/github/trytonvanmeer/libretrivia/trivia/TriviaQuestion.java b/app/src/main/java/io/github/trytonvanmeer/libretrivia/trivia/TriviaQuestion.java new file mode 100644 index 0000000..b708006 --- /dev/null +++ b/app/src/main/java/io/github/trytonvanmeer/libretrivia/trivia/TriviaQuestion.java @@ -0,0 +1,31 @@ +package io.github.trytonvanmeer.libretrivia.trivia; + +import java.io.Serializable; + +public abstract class TriviaQuestion implements Serializable { + private final TriviaCategory category; + private final TriviaDifficulty difficulty; + + private final String question; + + TriviaQuestion(TriviaCategory category, TriviaDifficulty difficulty, String question) { + this.category = category; + this.difficulty = difficulty; + + this.question = question; + } + + public TriviaCategory getCategory() { + return this.category; + } + + public TriviaDifficulty getDifficulty() { + return this.difficulty; + } + + public String getQuestion() { + return this.question; + } + + public abstract boolean checkAnswer(String guess); +} diff --git a/app/src/main/java/io/github/trytonvanmeer/libretrivia/trivia/TriviaQuestionBoolean.java b/app/src/main/java/io/github/trytonvanmeer/libretrivia/trivia/TriviaQuestionBoolean.java new file mode 100644 index 0000000..6a70516 --- /dev/null +++ b/app/src/main/java/io/github/trytonvanmeer/libretrivia/trivia/TriviaQuestionBoolean.java @@ -0,0 +1,34 @@ +package io.github.trytonvanmeer.libretrivia.trivia; + +import android.text.Html; + +import com.google.gson.JsonObject; + +public class TriviaQuestionBoolean extends TriviaQuestion { + + private final Boolean correctAnswer; + + public TriviaQuestionBoolean(TriviaCategory category, TriviaDifficulty difficulty, + String question, boolean correctAnswer) { + super(category, difficulty, question); + this.correctAnswer = correctAnswer; + } + + @Override + public boolean checkAnswer(String guess) { + return this.correctAnswer.equals(Boolean.valueOf(guess)); + } + + public boolean checkAnswer(Boolean guess) { + return checkAnswer(guess.toString()); + } + + public static TriviaQuestionBoolean fromJson(JsonObject json) { + TriviaCategory category = TriviaCategory.get(json.get("category").getAsString()); + TriviaDifficulty difficulty = TriviaDifficulty.get(json.get("difficulty").getAsString()); + String question = Html.fromHtml(json.get("question").getAsString()).toString(); + Boolean correctAnswer = json.get("correct_answer").getAsBoolean(); + + return new TriviaQuestionBoolean(category, difficulty, question, correctAnswer); + } +} diff --git a/app/src/main/java/io/github/trytonvanmeer/libretrivia/trivia/TriviaQuestionMultiple.java b/app/src/main/java/io/github/trytonvanmeer/libretrivia/trivia/TriviaQuestionMultiple.java new file mode 100644 index 0000000..7674790 --- /dev/null +++ b/app/src/main/java/io/github/trytonvanmeer/libretrivia/trivia/TriviaQuestionMultiple.java @@ -0,0 +1,48 @@ +package io.github.trytonvanmeer.libretrivia.trivia; + +import android.text.Html; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; + +public class TriviaQuestionMultiple extends TriviaQuestion { + private final String correctAnswer; + private final String[] incorrectAnswers; + + public TriviaQuestionMultiple(TriviaCategory category, TriviaDifficulty difficulty, + String question, String correctAnswer, String[] incorrectAnswers) { + super(category, difficulty, question); + + this.correctAnswer = correctAnswer; + this.incorrectAnswers = incorrectAnswers; + } + + public String[] getAnswerList() { + return new String[]{correctAnswer, + incorrectAnswers[0], + incorrectAnswers[1], + incorrectAnswers[2]}; + } + + @Override + public boolean checkAnswer(String guess) { + return this.correctAnswer.equals(guess); + } + + public static TriviaQuestionMultiple fromJson(JsonObject json) { + TriviaCategory category = TriviaCategory.get(json.get("category").getAsString()); + TriviaDifficulty difficulty = TriviaDifficulty.get(json.get("difficulty").getAsString()); + String question = Html.fromHtml(json.get("question").getAsString()).toString(); + String correctAnswer = Html.fromHtml(json.get("correct_answer").getAsString()).toString(); + + JsonArray incorrectAnswersJson = json.get("incorrect_answers").getAsJsonArray(); + String[] incorrectAnswers = new String[]{ + Html.fromHtml(incorrectAnswersJson.get(0).getAsString()).toString(), + Html.fromHtml(incorrectAnswersJson.get(1).getAsString()).toString(), + Html.fromHtml(incorrectAnswersJson.get(2).getAsString()).toString() + }; + + return new TriviaQuestionMultiple( + category, difficulty, question, correctAnswer, incorrectAnswers); + } +} diff --git a/app/src/main/java/io/github/trytonvanmeer/libretrivia/trivia/TriviaType.java b/app/src/main/java/io/github/trytonvanmeer/libretrivia/trivia/TriviaType.java new file mode 100644 index 0000000..8f8f76f --- /dev/null +++ b/app/src/main/java/io/github/trytonvanmeer/libretrivia/trivia/TriviaType.java @@ -0,0 +1,45 @@ +package io.github.trytonvanmeer.libretrivia.trivia; + +import java.util.HashMap; +import java.util.Map; + +import io.github.trytonvanmeer.libretrivia.LibreTriviaApplication; +import io.github.trytonvanmeer.libretrivia.R; + +public enum TriviaType { + ANY("any", R.string.ui_any), + + MULTIPLE("multiple", R.string.question_type_multiple), + BOOLEAN("boolean", R.string.question_type_boolean); + + // Name of type used in queries + private final String name; + // Display name of the type + private final int displayName; + + private static final Map lookup = new HashMap<>(); + + static { + for (TriviaType type : TriviaType.values()) { + lookup.put(type.getName(), type); + } + } + + TriviaType(String name, int displayName) { + this.name = name; + this.displayName = displayName; + } + + public String getName() { + return this.name; + } + + public static TriviaType get(String name) { + return lookup.get(name); + } + + @Override + public String toString() { + return LibreTriviaApplication.getAppContext().getResources().getString(this.displayName); + } +} diff --git a/app/src/main/java/io/github/trytonvanmeer/libretrivia/util/ApiUtil.java b/app/src/main/java/io/github/trytonvanmeer/libretrivia/util/ApiUtil.java new file mode 100644 index 0000000..a6e4374 --- /dev/null +++ b/app/src/main/java/io/github/trytonvanmeer/libretrivia/util/ApiUtil.java @@ -0,0 +1,82 @@ +package io.github.trytonvanmeer.libretrivia.util; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.ArrayList; + +import io.github.trytonvanmeer.libretrivia.exceptions.NoTriviaResultsException; +import io.github.trytonvanmeer.libretrivia.trivia.TriviaQuery; +import io.github.trytonvanmeer.libretrivia.trivia.TriviaQuestion; +import io.github.trytonvanmeer.libretrivia.trivia.TriviaQuestionBoolean; +import io.github.trytonvanmeer.libretrivia.trivia.TriviaQuestionMultiple; +import io.github.trytonvanmeer.libretrivia.trivia.TriviaType; + +public class ApiUtil { + + private static String readStream(InputStream in) throws IOException { + StringBuilder builder = new StringBuilder(); + BufferedReader reader = new BufferedReader(new InputStreamReader(in), 1000); + + for (String line = reader.readLine(); line != null; line = reader.readLine()) { + builder.append(line); + } + + in.close(); + return builder.toString(); + } + + private static String GET(String query) throws IOException { + String response; + + URL url = new URL(query); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + + try { + InputStream in = new BufferedInputStream(connection.getInputStream()); + response = readStream(in); + } finally { + connection.disconnect(); + } + + return response; + } + + public static String GET(TriviaQuery query) throws IOException { + return GET(query.toString()); + } + + public static ArrayList jsonToQuestionArray(String json) throws NoTriviaResultsException { + JsonObject jsonObject = new JsonParser().parse(json).getAsJsonObject(); + + if (jsonObject.get("response_code").getAsInt() == 1) { + throw new NoTriviaResultsException(); + } + + JsonArray jsonArray = jsonObject.getAsJsonArray("results"); + + ArrayList questions = new ArrayList<>(); + + for (JsonElement element : jsonArray) { + JsonObject object = element.getAsJsonObject(); + TriviaType type = TriviaType.get(object.get("type").getAsString()); + + if (type == TriviaType.MULTIPLE) { + questions.add(TriviaQuestionMultiple.fromJson(object)); + } else { + questions.add(TriviaQuestionBoolean.fromJson(object)); + } + } + + return questions; + } +} diff --git a/app/src/main/java/io/github/trytonvanmeer/libretrivia/util/SoundUtil.java b/app/src/main/java/io/github/trytonvanmeer/libretrivia/util/SoundUtil.java new file mode 100644 index 0000000..07e393f --- /dev/null +++ b/app/src/main/java/io/github/trytonvanmeer/libretrivia/util/SoundUtil.java @@ -0,0 +1,25 @@ +package io.github.trytonvanmeer.libretrivia.util; + +import android.content.Context; +import android.content.SharedPreferences; +import android.media.MediaPlayer; +import android.preference.PreferenceManager; + +import io.github.trytonvanmeer.libretrivia.R; + +public class SoundUtil { + public static final int SOUND_ANSWER_CORRECT = R.raw.sound_answer_correct; + public static final int SOUND_ANSWER_WRONG = R.raw.sound_answer_wrong; + + public static void playSound(Context context, int sound) { + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); + String key_sound = context.getResources().getString(R.string.pref_sound_answer); + boolean pref_sound = preferences.getBoolean(key_sound, true); + + if (pref_sound) { + final MediaPlayer player = MediaPlayer.create(context, sound); + player.setVolume(0.25f, 0.25f); + player.start(); + } + } +} diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..6b5d817 --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_clipboard_text.xml b/app/src/main/res/drawable/ic_clipboard_text.xml new file mode 100644 index 0000000..14e9928 --- /dev/null +++ b/app/src/main/res/drawable/ic_clipboard_text.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_emoticon_sad.xml b/app/src/main/res/drawable/ic_emoticon_sad.xml new file mode 100644 index 0000000..b269c0b --- /dev/null +++ b/app/src/main/res/drawable/ic_emoticon_sad.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..20db57f --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + +