├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── assets
│ │ │ ├── user.png
│ │ │ ├── flipview.png
│ │ │ └── categories
│ │ │ │ ├── ART.png
│ │ │ │ ├── BOOKS.png
│ │ │ │ ├── FILMS.png
│ │ │ │ ├── MUSIC.png
│ │ │ │ ├── ANIMALS.png
│ │ │ │ ├── COMICS.png
│ │ │ │ ├── GADGETS.png
│ │ │ │ ├── HISTORY.png
│ │ │ │ ├── SPORTS.png
│ │ │ │ ├── BOARDGAMES.png
│ │ │ │ ├── COMPUTERS.png
│ │ │ │ ├── GEOGRAPHY.png
│ │ │ │ ├── MYTHOLOGY.png
│ │ │ │ ├── POLITICS.png
│ │ │ │ ├── TELEVISION.png
│ │ │ │ ├── VEHICLES.png
│ │ │ │ ├── VIDEOGAMES.png
│ │ │ │ ├── CELEBRITIES.png
│ │ │ │ ├── MATHEMATICS.png
│ │ │ │ ├── ANIME_AND_MANGA.png
│ │ │ │ ├── GENERAL_KNOWLEDGE.png
│ │ │ │ ├── MUSICAL_AND_THEATRES.png
│ │ │ │ ├── SCIENCE_AND_NATURE.png
│ │ │ │ └── CARTOONS_AND_ANIMATIONS.png
│ │ ├── ic_launcher-web.png
│ │ ├── res
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_launcher_round.png
│ │ │ │ ├── ic_launcher_foreground.png
│ │ │ │ └── launcher_icon_custom.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_launcher_round.png
│ │ │ │ └── ic_launcher_foreground.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_launcher_round.png
│ │ │ │ └── ic_launcher_foreground.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_launcher_round.png
│ │ │ │ └── ic_launcher_foreground.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_launcher_round.png
│ │ │ │ └── ic_launcher_foreground.png
│ │ │ ├── values
│ │ │ │ ├── ic_launcher_background.xml
│ │ │ │ ├── styles.xml
│ │ │ │ ├── colors.xml
│ │ │ │ └── strings.xml
│ │ │ ├── drawable
│ │ │ │ ├── rounded_progressbar.xml
│ │ │ │ ├── plus.xml
│ │ │ │ ├── highscore.xml
│ │ │ │ ├── rounded_corners.xml
│ │ │ │ ├── image.xml
│ │ │ │ ├── email.xml
│ │ │ │ ├── delete.xml
│ │ │ │ ├── logout.xml
│ │ │ │ ├── changeusername.xml
│ │ │ │ ├── changepass.xml
│ │ │ │ └── settings.xml
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── layout
│ │ │ │ ├── spinner_item.xml
│ │ │ │ ├── activity_question.xml
│ │ │ │ ├── dialog_custom_adpt.xml
│ │ │ │ ├── app_bar.xml
│ │ │ │ ├── item_difficulty.xml
│ │ │ │ ├── activity_settings.xml
│ │ │ │ ├── dialog_loading.xml
│ │ │ │ ├── dialog_add_question.xml
│ │ │ │ ├── fragment_loading.xml
│ │ │ │ ├── dialog_confirm_pass.xml
│ │ │ │ ├── fragment_finish.xml
│ │ │ │ ├── item_highscore.xml
│ │ │ │ ├── activity_splash.xml
│ │ │ │ └── dialog_change_pass.xml
│ │ │ ├── menu
│ │ │ │ └── start_meny.xml
│ │ │ ├── drawable-v24
│ │ │ │ └── ic_launcher_foreground.xml
│ │ │ └── xml
│ │ │ │ └── preferences.xml
│ │ └── java
│ │ │ └── com
│ │ │ └── example
│ │ │ └── qwez
│ │ │ ├── util
│ │ │ ├── UrlConstant.java
│ │ │ ├── QuestionC.java
│ │ │ ├── ExtrasConstant.java
│ │ │ ├── QuestionType.java
│ │ │ ├── QuestionUtil.java
│ │ │ └── Difficulty.java
│ │ │ ├── di
│ │ │ ├── FragmentScope.java
│ │ │ ├── ActivityScope.java
│ │ │ ├── ApplicationScope.java
│ │ │ ├── ApplicationModule.java
│ │ │ ├── OpenTDBModule.java
│ │ │ ├── SharedPreferencesModule.java
│ │ │ ├── AppComponent.java
│ │ │ ├── BinderModule.java
│ │ │ ├── FirebaseModule.java
│ │ │ └── NetworkModule.java
│ │ │ ├── bus
│ │ │ └── event
│ │ │ │ ├── NullEvent.java
│ │ │ │ ├── GameEvent.java
│ │ │ │ ├── BooleanEvent.java
│ │ │ │ ├── ChangePassowordEvent.java
│ │ │ │ ├── SignupEvent.java
│ │ │ │ └── LoginEvent.java
│ │ │ ├── entity
│ │ │ ├── ErrorCarrier.java
│ │ │ ├── FinishedGame.java
│ │ │ ├── Answer.java
│ │ │ ├── Highscore.java
│ │ │ └── IntroData.java
│ │ │ ├── repository
│ │ │ ├── opentdb
│ │ │ │ ├── OpenTDBType.java
│ │ │ │ ├── OpenTDBAPI.java
│ │ │ │ ├── OpenTDB.java
│ │ │ │ └── entity
│ │ │ │ │ └── ResponseBody.java
│ │ │ ├── firebase
│ │ │ │ ├── FirebaseStorageRepositoryType.java
│ │ │ │ ├── FirebaseDatabaseType.java
│ │ │ │ ├── rxwrapper
│ │ │ │ │ ├── RxWrapperNullException.java
│ │ │ │ │ ├── FirebaseFunctionsWrapper.java
│ │ │ │ │ ├── FirebaseStorageWrapper.java
│ │ │ │ │ ├── MaybeTask.java
│ │ │ │ │ └── CompletableTask.java
│ │ │ │ ├── FirebaseStorageRepository.java
│ │ │ │ └── FirebaseAuthRepositoryType.java
│ │ │ ├── local
│ │ │ │ ├── entity
│ │ │ │ │ ├── GameQuestion.java
│ │ │ │ │ └── Game.java
│ │ │ │ ├── dao
│ │ │ │ │ ├── GameDao.java
│ │ │ │ │ ├── QuestionDao.java
│ │ │ │ │ ├── GameQuestionDao.java
│ │ │ │ │ └── BaseDao.java
│ │ │ │ └── GameDatabase.java
│ │ │ ├── sharedpref
│ │ │ │ ├── SharedPreferencesRepositoryType.java
│ │ │ │ └── SharedPreferencesRepository.java
│ │ │ └── ApiOperator.java
│ │ │ ├── ui
│ │ │ ├── login
│ │ │ │ ├── LoginFragmentModule.java
│ │ │ │ ├── LoginVMFactory.java
│ │ │ │ ├── LoginModule.java
│ │ │ │ └── LoginViewModel.java
│ │ │ ├── highscore
│ │ │ │ ├── HighscoreVMFactory.java
│ │ │ │ ├── HighscoreModule.java
│ │ │ │ ├── recyclerview
│ │ │ │ │ ├── HighscoreAdapter.java
│ │ │ │ │ └── HighscoreHolder.java
│ │ │ │ └── HighscoreViewmodel.java
│ │ │ ├── splash
│ │ │ │ ├── SplashModule.java
│ │ │ │ ├── SplashVMFactory.java
│ │ │ │ ├── SplashActivity.java
│ │ │ │ └── SplashViewModel.java
│ │ │ ├── common
│ │ │ │ └── ItemDecorator.java
│ │ │ ├── start
│ │ │ │ ├── recycler
│ │ │ │ │ ├── CustomAdapter.java
│ │ │ │ │ ├── GameAdapter.java
│ │ │ │ │ └── GameHolder.java
│ │ │ │ └── StartVMFactory.java
│ │ │ ├── dialog
│ │ │ │ └── DifficultyItem.java
│ │ │ ├── question
│ │ │ │ ├── QuestionVMFactory.java
│ │ │ │ ├── FinishFragment.java
│ │ │ │ └── QuestionModule.java
│ │ │ └── settings
│ │ │ │ └── SettingsVMFactory.java
│ │ │ ├── validator
│ │ │ ├── EmailValidate.java
│ │ │ ├── EmptyValidate.java
│ │ │ ├── PasswordValidate.java
│ │ │ ├── PatternValidate.java
│ │ │ └── Validate.java
│ │ │ ├── base
│ │ │ ├── BaseAdapter.java
│ │ │ ├── BaseViewHolder.java
│ │ │ ├── BaseViewModel.java
│ │ │ └── BaseFragment.java
│ │ │ ├── router
│ │ │ ├── HighscoreRouter.java
│ │ │ ├── QuestionRouter.java
│ │ │ ├── LoginRouter.java
│ │ │ ├── SettingsRouter.java
│ │ │ └── StartRouter.java
│ │ │ ├── interactor
│ │ │ ├── LogoutUserInteractor.java
│ │ │ ├── DeleteGameInteractor.java
│ │ │ ├── GetUserInteractor.java
│ │ │ ├── FirstTimeInteractor.java
│ │ │ ├── GetAllGamesInteractor.java
│ │ │ ├── LoginUserInteractor.java
│ │ │ ├── SignupInteractor.java
│ │ │ ├── ChangeUserNickInteractor.java
│ │ │ ├── GetAllGamesAndQuestionsInteractor.java
│ │ │ ├── UpdateQuestionAndGameInteractor.java
│ │ │ ├── DeleteAccountInteractor.java
│ │ │ ├── ChangeUserPasswordInteractor.java
│ │ │ ├── ChangeProfilePhotoInteractor.java
│ │ │ ├── GetSingleGameAndQuestionsInteractor.java
│ │ │ ├── rx
│ │ │ │ └── Operators.java
│ │ │ ├── GetHighscoreInteractor.java
│ │ │ ├── RememberUserInteractor.java
│ │ │ ├── PointsInteractor.java
│ │ │ └── FetchQuestionsInteractor.java
│ │ │ └── service
│ │ │ └── MyFirebaseMessagingService.java
│ └── test
│ │ └── java
│ │ └── com
│ │ └── example
│ │ └── qwez
│ │ ├── android
│ │ └── text
│ │ │ └── TextUtils.java
│ │ ├── ui
│ │ └── question
│ │ │ └── QuestionViewModelTest.java
│ │ ├── RxResources.java
│ │ ├── util
│ │ └── QuestionConverterTest.java
│ │ ├── repository
│ │ └── firebase
│ │ │ └── rxwrapper
│ │ │ └── FirebaseTestHelper.java
│ │ ├── validator
│ │ └── ValidateTest.java
│ │ └── interactor
│ │ ├── LogoutUserInteractorTest.java
│ │ ├── DeleteGameInteractorTest.java
│ │ ├── LoginUserInteractorTest.java
│ │ ├── GetUserInteractorTest.java
│ │ ├── GetSingleGameAndQuestionsInteractorTest.java
│ │ ├── SignupInteractorTest.java
│ │ └── GetAllGamesInteractorTest.java
├── google-services.json
└── proguard-rules.pro
├── functions
├── .gitignore
└── package.json
├── settings.gradle
├── Qwez.zip
├── .firebaserc
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── firebase.json
├── .idea
├── encodings.xml
├── vcs.xml
├── compiler.xml
├── inspectionProfiles
│ └── Project_Default.xml
├── runConfigurations.xml
├── gradle.xml
└── misc.xml
├── .gitignore
├── gradle.properties
├── .travis.yml
└── gradlew.bat
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/functions/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/Qwez.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appearance/Qwez/master/Qwez.zip
--------------------------------------------------------------------------------
/.firebaserc:
--------------------------------------------------------------------------------
1 | {
2 | "projects": {
3 | "default": "qwez-take-2"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/app/src/main/assets/user.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appearance/Qwez/master/app/src/main/assets/user.png
--------------------------------------------------------------------------------
/app/src/main/assets/flipview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appearance/Qwez/master/app/src/main/assets/flipview.png
--------------------------------------------------------------------------------
/app/src/main/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appearance/Qwez/master/app/src/main/ic_launcher-web.png
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appearance/Qwez/master/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/assets/categories/ART.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appearance/Qwez/master/app/src/main/assets/categories/ART.png
--------------------------------------------------------------------------------
/app/src/main/assets/categories/BOOKS.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appearance/Qwez/master/app/src/main/assets/categories/BOOKS.png
--------------------------------------------------------------------------------
/app/src/main/assets/categories/FILMS.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appearance/Qwez/master/app/src/main/assets/categories/FILMS.png
--------------------------------------------------------------------------------
/app/src/main/assets/categories/MUSIC.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appearance/Qwez/master/app/src/main/assets/categories/MUSIC.png
--------------------------------------------------------------------------------
/app/src/main/assets/categories/ANIMALS.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appearance/Qwez/master/app/src/main/assets/categories/ANIMALS.png
--------------------------------------------------------------------------------
/app/src/main/assets/categories/COMICS.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appearance/Qwez/master/app/src/main/assets/categories/COMICS.png
--------------------------------------------------------------------------------
/app/src/main/assets/categories/GADGETS.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appearance/Qwez/master/app/src/main/assets/categories/GADGETS.png
--------------------------------------------------------------------------------
/app/src/main/assets/categories/HISTORY.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appearance/Qwez/master/app/src/main/assets/categories/HISTORY.png
--------------------------------------------------------------------------------
/app/src/main/assets/categories/SPORTS.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appearance/Qwez/master/app/src/main/assets/categories/SPORTS.png
--------------------------------------------------------------------------------
/app/src/main/assets/categories/BOARDGAMES.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appearance/Qwez/master/app/src/main/assets/categories/BOARDGAMES.png
--------------------------------------------------------------------------------
/app/src/main/assets/categories/COMPUTERS.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appearance/Qwez/master/app/src/main/assets/categories/COMPUTERS.png
--------------------------------------------------------------------------------
/app/src/main/assets/categories/GEOGRAPHY.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appearance/Qwez/master/app/src/main/assets/categories/GEOGRAPHY.png
--------------------------------------------------------------------------------
/app/src/main/assets/categories/MYTHOLOGY.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appearance/Qwez/master/app/src/main/assets/categories/MYTHOLOGY.png
--------------------------------------------------------------------------------
/app/src/main/assets/categories/POLITICS.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appearance/Qwez/master/app/src/main/assets/categories/POLITICS.png
--------------------------------------------------------------------------------
/app/src/main/assets/categories/TELEVISION.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appearance/Qwez/master/app/src/main/assets/categories/TELEVISION.png
--------------------------------------------------------------------------------
/app/src/main/assets/categories/VEHICLES.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appearance/Qwez/master/app/src/main/assets/categories/VEHICLES.png
--------------------------------------------------------------------------------
/app/src/main/assets/categories/VIDEOGAMES.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appearance/Qwez/master/app/src/main/assets/categories/VIDEOGAMES.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appearance/Qwez/master/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appearance/Qwez/master/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appearance/Qwez/master/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/firebase.json:
--------------------------------------------------------------------------------
1 | {
2 | "functions": {
3 | "predeploy": [
4 | "npm --prefix \"$RESOURCE_DIR\" run lint"
5 | ]
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/app/src/main/assets/categories/CELEBRITIES.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appearance/Qwez/master/app/src/main/assets/categories/CELEBRITIES.png
--------------------------------------------------------------------------------
/app/src/main/assets/categories/MATHEMATICS.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appearance/Qwez/master/app/src/main/assets/categories/MATHEMATICS.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appearance/Qwez/master/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appearance/Qwez/master/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/assets/categories/ANIME_AND_MANGA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appearance/Qwez/master/app/src/main/assets/categories/ANIME_AND_MANGA.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appearance/Qwez/master/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appearance/Qwez/master/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/assets/categories/GENERAL_KNOWLEDGE.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appearance/Qwez/master/app/src/main/assets/categories/GENERAL_KNOWLEDGE.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appearance/Qwez/master/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appearance/Qwez/master/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/assets/categories/MUSICAL_AND_THEATRES.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appearance/Qwez/master/app/src/main/assets/categories/MUSICAL_AND_THEATRES.png
--------------------------------------------------------------------------------
/app/src/main/assets/categories/SCIENCE_AND_NATURE.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appearance/Qwez/master/app/src/main/assets/categories/SCIENCE_AND_NATURE.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appearance/Qwez/master/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/launcher_icon_custom.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appearance/Qwez/master/app/src/main/res/mipmap-hdpi/launcher_icon_custom.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appearance/Qwez/master/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appearance/Qwez/master/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appearance/Qwez/master/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appearance/Qwez/master/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/assets/categories/CARTOONS_AND_ANIMATIONS.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appearance/Qwez/master/app/src/main/assets/categories/CARTOONS_AND_ANIMATIONS.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appearance/Qwez/master/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/values/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #A63A35
4 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/rounded_progressbar.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/util/UrlConstant.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.util;
2 |
3 | /**
4 | * Constants class
5 | */
6 | public class UrlConstant {
7 |
8 | public static final String URL_END_POINT = "https://opentdb.com/";
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/app/src/test/java/com/example/qwez/android/text/TextUtils.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.android.text;
2 |
3 |
4 | public class TextUtils {
5 | public static boolean isEmpty(CharSequence str) {
6 | return str == null || str.length() == 0;
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Thu Apr 18 16:49:01 CEST 2019
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
7 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/test/java/com/example/qwez/ui/question/QuestionViewModelTest.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.ui.question;
2 |
3 | import org.junit.Before;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | public class QuestionViewModelTest {
8 |
9 | @Before
10 | public void setUp() throws Exception {
11 | }
12 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/di/FragmentScope.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.di;
2 |
3 | import java.lang.annotation.Retention;
4 | import java.lang.annotation.RetentionPolicy;
5 |
6 | import javax.inject.Scope;
7 |
8 | @Scope
9 | @Retention(RetentionPolicy.RUNTIME)
10 | public @interface FragmentScope {
11 | }
12 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/util/QuestionC.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.util;
2 |
3 | /**
4 | * Constants Class
5 | */
6 | public class QuestionC {
7 |
8 | public static final int AMOUNT_STANDARD = 10;
9 | public static final String TIMEOUT_ANSWER = "RandonStringAnswer5936952760765365307";
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/util/ExtrasConstant.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.util;
2 |
3 | /**
4 | * Bundle Extras Constants Class
5 | */
6 | public class ExtrasConstant {
7 |
8 | public static final String QUESTION_LIST = "question list";
9 | public static final String QUESTION_ID = "question id";
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/bus/event/NullEvent.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.bus.event;
2 |
3 | /**
4 | * Empty object to be used when no data is to be published.
5 | *
6 | * This is a requirement to be able to subscribe/publish anyting, because RxBus publishsubject cannot
7 | * accept nulls
8 | */
9 | public class NullEvent {
10 | }
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/plus.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/spinner_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/di/ActivityScope.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.di;
2 |
3 | import java.lang.annotation.Retention;
4 | import java.lang.annotation.RetentionPolicy;
5 |
6 | import javax.inject.Scope;
7 |
8 | /**
9 | * Dagger scope specifying scope for Activity
10 | */
11 | @Scope
12 | @Retention(RetentionPolicy.RUNTIME)
13 | public @interface ActivityScope {
14 | }
15 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/highscore.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/rounded_corners.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/util/QuestionType.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.util;
2 |
3 | /**
4 | * Question type ENUM
5 | */
6 | public enum QuestionType {
7 |
8 | MULTIPLE_CHOICE("multiple"),
9 | TRUE_OR_FALSE("boolean");
10 |
11 | private String type;
12 |
13 | QuestionType(String type) {
14 | this.type = type;
15 | }
16 |
17 | public String getType() {
18 | return type;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/di/ApplicationScope.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.di;
2 |
3 | import java.lang.annotation.Retention;
4 | import java.lang.annotation.RetentionPolicy;
5 |
6 | import javax.inject.Scope;
7 |
8 | /**
9 | * Dagger scope specifying scope for Application
10 | * Lifecycle as long as application is running
11 | */
12 | @Scope
13 | @Retention(RetentionPolicy.RUNTIME)
14 | public @interface ApplicationScope {
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/entity/ErrorCarrier.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.entity;
2 |
3 | /**
4 | * POJO class for carrying error(s) in application. Used in Viewmodel LiveData error
5 | */
6 | public class ErrorCarrier {
7 |
8 | private final String message;
9 |
10 | public ErrorCarrier(String message) {
11 | this.message = message;
12 | }
13 |
14 | public String getMessage() {
15 | return message;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/image.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/entity/FinishedGame.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.entity;
2 |
3 | public class FinishedGame {
4 |
5 | private final int score;
6 | private final int gameID;
7 |
8 | public FinishedGame(int score, int gameID) {
9 | this.score = score;
10 | this.gameID = gameID;
11 | }
12 |
13 | public int getScore() {
14 | return score;
15 | }
16 |
17 | public int getGameID() {
18 | return gameID;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/email.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/delete.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/repository/opentdb/OpenTDBType.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.repository.opentdb;
2 |
3 | import com.example.qwez.repository.opentdb.entity.Question;
4 |
5 | import java.util.List;
6 |
7 | import io.reactivex.Single;
8 |
9 | /**
10 | * API interface
11 | */
12 | public interface OpenTDBType {
13 |
14 | /**
15 | * Get Question from API
16 | */
17 | Single> getQuestionByCategory(int amount, int category, String difficulty, String type);
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/bus/event/GameEvent.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.bus.event;
2 |
3 | import com.example.qwez.repository.local.entity.Game;
4 |
5 | /**
6 | * POJO class for {@link com.example.qwez.bus.RxBus} event type.
7 | * final variable value and getter(s).
8 | */
9 | public class GameEvent {
10 |
11 | private final Game game;
12 |
13 | public GameEvent(Game game) {
14 | this.game = game;
15 | }
16 |
17 | public Game getGame() {
18 | return game;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_question.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/bus/event/BooleanEvent.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.bus.event;
2 |
3 | /**
4 | * POJO class for {@link com.example.qwez.bus.RxBus} event type.
5 | * final variable value and getter(s).
6 | */
7 | public class BooleanEvent {
8 |
9 | private final boolean booleanValue;
10 |
11 | public BooleanEvent(boolean booleanValue) {
12 | this.booleanValue = booleanValue;
13 | }
14 |
15 | public boolean isBooleanValue() {
16 | return booleanValue;
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/repository/firebase/FirebaseStorageRepositoryType.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.repository.firebase;
2 |
3 | import android.net.Uri;
4 |
5 | import com.google.firebase.storage.StorageReference;
6 | import com.google.firebase.storage.UploadTask;
7 |
8 | import io.reactivex.Single;
9 |
10 | public interface FirebaseStorageRepositoryType {
11 |
12 | Single uploadPhoto(String user, Uri uri);
13 |
14 | Single getDownloadUrl(StorageReference reference);
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/logout.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_custom_adpt.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/di/ApplicationModule.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.di;
2 |
3 | import android.content.Context;
4 |
5 | import com.example.qwez.App;
6 |
7 | import dagger.Module;
8 | import dagger.Provides;
9 |
10 | @Module
11 | public class ApplicationModule {
12 |
13 | /**
14 | * Get application context
15 | * @param app
16 | * @return
17 | */
18 | @Provides
19 | @ApplicationScope
20 | Context context(App app){
21 | return app.getApplicationContext();
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/bus/event/ChangePassowordEvent.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.bus.event;
2 |
3 | /**
4 | * POJO class for {@link com.example.qwez.bus.RxBus} event type.
5 | * final variable value and getter(s).
6 | */
7 | public class ChangePassowordEvent {
8 |
9 | private final String newPassword;
10 |
11 | public ChangePassowordEvent(String newPassword) {
12 | this.newPassword = newPassword;
13 | }
14 |
15 | public String getNewPassword() {
16 | return newPassword;
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/entity/Answer.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.entity;
2 |
3 | public class Answer {
4 |
5 | private String yourAnswer;
6 | private String correctAnswer;
7 |
8 | public Answer(String yourAnswer, String correctAnswer) {
9 | this.yourAnswer = yourAnswer;
10 | this.correctAnswer = correctAnswer;
11 | }
12 |
13 | public String getYourAnswer() {
14 | return yourAnswer;
15 | }
16 |
17 | public String getCorrectAnswer() {
18 | return correctAnswer;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/entity/Highscore.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.entity;
2 |
3 | import java.io.Serializable;
4 |
5 | public class Highscore implements Serializable{
6 |
7 | private String nick;
8 | private int score;
9 |
10 | public Highscore(){}
11 |
12 | public Highscore(String nick, int score) {
13 | this.nick = nick;
14 | this.score = score;
15 | }
16 |
17 | public String getNick() {
18 | return nick;
19 | }
20 |
21 | public int getScore() {
22 | return score;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/repository/local/entity/GameQuestion.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.repository.local.entity;
2 |
3 | import androidx.room.Embedded;
4 | import androidx.room.Relation;
5 |
6 | import java.util.List;
7 |
8 | /**
9 | * Nested fields Class with {@link @Entity #Game} as @Embedded object and @Relation (as list)
10 | * {@link @Entity #Question}
11 | */
12 | public class GameQuestion {
13 |
14 | @Embedded
15 | public Game game;
16 |
17 | @Relation(parentColumn = "id", entityColumn = "question_id")
18 | public List questions;
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/start_meny.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/repository/firebase/FirebaseDatabaseType.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.repository.firebase;
2 |
3 | import com.example.qwez.entity.Highscore;
4 |
5 | import java.util.List;
6 |
7 | import io.reactivex.Completable;
8 | import io.reactivex.Single;
9 |
10 | public interface FirebaseDatabaseType {
11 |
12 | Single getUserHighscore(String uId);
13 |
14 | Single> getTop50Highscores();
15 |
16 | Completable updateNick(String uid, String newNick);
17 |
18 | Completable updateHighscore(String uid, int addToHighscore);
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/ui/login/LoginFragmentModule.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.ui.login;
2 |
3 | import com.example.qwez.interactor.RememberUserInteractor;
4 | import com.example.qwez.repository.sharedpref.SharedPreferencesRepositoryType;
5 |
6 | import dagger.Module;
7 | import dagger.Provides;
8 |
9 | @Module
10 | public class LoginFragmentModule {
11 |
12 | @Provides
13 | RememberUserInteractor rememberUserInteractor(SharedPreferencesRepositoryType sharedPreferencesRepositoryType){
14 | return new RememberUserInteractor(sharedPreferencesRepositoryType);
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/repository/firebase/rxwrapper/RxWrapperNullException.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.repository.firebase.rxwrapper;
2 |
3 | /**
4 | * RxWrapper specific Exception class
5 | */
6 | public class RxWrapperNullException extends Exception {
7 |
8 | public final static String NO_CURRENT_USER = "No current user(s) found.";
9 |
10 | private final String message;
11 |
12 | public RxWrapperNullException(String message) {
13 | this.message = message;
14 | }
15 |
16 | @Override
17 | public String getMessage() {
18 | return message;
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/app_bar.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/validator/EmailValidate.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.validator;
2 |
3 | import java.util.regex.Pattern;
4 |
5 | /**
6 | * Validate Class for checking String against email Regex Pattern
7 | */
8 | public class EmailValidate extends PatternValidate {
9 |
10 | private static final String NOT_VALID_EMAIL = "Not a valid email address.";
11 | private static final String EMAIL_PATTERN = "^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,6}$";
12 |
13 | public EmailValidate() {
14 | super(NOT_VALID_EMAIL, Pattern.compile(EMAIL_PATTERN, Pattern.CASE_INSENSITIVE));
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/changeusername.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/changepass.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
13 |
14 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/bus/event/SignupEvent.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.bus.event;
2 |
3 | /**
4 | * POJO class for {@link com.example.qwez.bus.RxBus} event type.
5 | * final variable value and getter(s).
6 | */
7 | public class SignupEvent {
8 |
9 | private final String email;
10 | private final String password;
11 |
12 | public SignupEvent(String email, String password) {
13 | this.email = email;
14 | this.password = password;
15 | }
16 |
17 | public String getEmail() {
18 | return email;
19 | }
20 |
21 | public String getPassword() {
22 | return password;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/di/OpenTDBModule.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.di;
2 |
3 | import com.example.qwez.repository.opentdb.OpenTDB;
4 | import com.example.qwez.repository.opentdb.OpenTDBAPI;
5 | import com.example.qwez.repository.opentdb.OpenTDBType;
6 |
7 | import dagger.Module;
8 | import dagger.Provides;
9 |
10 | @Module
11 | public class OpenTDBModule {
12 |
13 | /**
14 | * Get OpenTDB. Singleton
15 | * @param openTDBAPI Dagger provided
16 | * @return OpenTDB
17 | */
18 | @Provides
19 | @ApplicationScope
20 | OpenTDBType openTDBType(OpenTDBAPI openTDBAPI){
21 | return new OpenTDB(openTDBAPI);
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/validator/EmptyValidate.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.validator;
2 |
3 | /**
4 | * Validate Class for checking if String is empty
5 | */
6 | public class EmptyValidate extends Validate {
7 |
8 | private static final String EMPTY_ERROR_MESSAGE = "Field cannot be empty";
9 |
10 | public EmptyValidate() {
11 | super(EMPTY_ERROR_MESSAGE);
12 | }
13 |
14 | /**
15 | * Check if String is not empty
16 | * @param textToCheck to check
17 | * @return true if valid
18 | */
19 | @Override
20 | public boolean isValid(String textToCheck) {
21 | return textToCheck.length() > 0;
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_difficulty.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/repository/sharedpref/SharedPreferencesRepositoryType.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.repository.sharedpref;
2 |
3 | import io.reactivex.Completable;
4 | import io.reactivex.Single;
5 |
6 | /**
7 | * SharedPrefences interface
8 | */
9 | public interface SharedPreferencesRepositoryType {
10 |
11 | Single getNotFirstTime();
12 |
13 | Completable setNotFirstTime(boolean setTo);
14 |
15 | Single isRemembered();
16 |
17 | Single getRemembered();
18 |
19 | Completable setRemembered(String toRemember);
20 |
21 | Completable setNotRemember();
22 |
23 | Completable setIsRemember(boolean remember);
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/test/java/com/example/qwez/RxResources.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez;
2 |
3 | import org.junit.rules.ExternalResource;
4 |
5 | import io.reactivex.android.plugins.RxAndroidPlugins;
6 | import io.reactivex.plugins.RxJavaPlugins;
7 | import io.reactivex.schedulers.Schedulers;
8 |
9 | public class RxResources extends ExternalResource {
10 |
11 | @Override
12 | protected void before() throws Throwable {
13 | RxAndroidPlugins.reset();
14 | RxJavaPlugins.reset();
15 | RxAndroidPlugins.setInitMainThreadSchedulerHandler(schedulerCallable -> Schedulers.trampoline());
16 | RxJavaPlugins.setIoSchedulerHandler(scheduler -> Schedulers.trampoline());
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/validator/PasswordValidate.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.validator;
2 |
3 | import java.util.regex.Pattern;
4 |
5 | /**
6 | * Validate Class for checking passwords
7 | */
8 | public class PasswordValidate extends PatternValidate {
9 |
10 | private static final String PASSWORD_NOT_VALID = "Password must contain at least 1 lowercase character, 1 uppercase character, 1 special character, and be between 8 to 20 characters long.";
11 | private static final String PASSWORD_REGEX = "((?=.*[a-z])(?=.*\\d)(?=.*[A-Z])(?=.*[@#$%!=]).{8,20})";
12 |
13 | public PasswordValidate() {
14 | super(PASSWORD_NOT_VALID, Pattern.compile(PASSWORD_REGEX));
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/functions/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "functions",
3 | "description": "Cloud Functions for Firebase",
4 | "scripts": {
5 | "lint": "eslint .",
6 | "serve": "firebase serve --only functions",
7 | "shell": "firebase functions:shell",
8 | "start": "npm run shell",
9 | "deploy": "firebase deploy --only functions",
10 | "logs": "firebase functions:log"
11 | },
12 | "engines": {
13 | "node": "8"
14 | },
15 | "dependencies": {
16 | "firebase-admin": "~7.0.0",
17 | "firebase-functions": "^2.3.0"
18 | },
19 | "devDependencies": {
20 | "eslint": "^5.12.0",
21 | "eslint-plugin-promise": "^4.0.1",
22 | "firebase-functions-test": "^0.1.6"
23 | },
24 | "private": true
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/base/BaseAdapter.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.base;
2 |
3 | import androidx.annotation.NonNull;
4 | import androidx.recyclerview.widget.RecyclerView;
5 |
6 | import java.util.ArrayList;
7 | import java.util.List;
8 |
9 | public abstract class BaseAdapter extends RecyclerView.Adapter {
10 |
11 | protected final List dataList = new ArrayList<>();
12 |
13 | public void setData(@NonNull List newDataList){
14 | dataList.clear();
15 | dataList.addAll(newDataList);
16 | notifyDataSetChanged();
17 | }
18 |
19 | @Override
20 | public int getItemCount() {
21 | return dataList.size();
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/router/HighscoreRouter.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.router;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 |
6 | import androidx.annotation.NonNull;
7 |
8 | import com.example.qwez.ui.highscore.HighscoreActivity;
9 |
10 | import org.jetbrains.annotations.NotNull;
11 |
12 | public class HighscoreRouter {
13 |
14 | public void open(@NonNull Context context, boolean clearStack){
15 | Intent intent = new Intent(context, HighscoreActivity.class);
16 | if (clearStack) {
17 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
18 | }
19 | context.startActivity(intent);
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/validator/PatternValidate.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.validator;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import java.util.regex.Pattern;
6 |
7 | /**
8 | * Validate extension Class that for Regex Matching
9 | */
10 | public class PatternValidate extends Validate {
11 | private final Pattern pattern;
12 |
13 | public PatternValidate(String errorMessage, @NonNull Pattern pattern) {
14 | super(errorMessage);
15 | this.pattern = pattern;
16 | }
17 |
18 | /**
19 | * Check if String is valid by matching to Regex Pattern
20 | * @param textToCheck to check
21 | * @return true if valid
22 | */
23 | public boolean isValid(String textToCheck) {
24 | return pattern.matcher(textToCheck).matches();
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/bus/event/LoginEvent.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.bus.event;
2 |
3 | /**
4 | * POJO class for {@link com.example.qwez.bus.RxBus} event type.
5 | * final variable value and getter(s).
6 | */
7 | public class LoginEvent {
8 |
9 | private final String email;
10 | private final String password;
11 | private final boolean rememberMe;
12 |
13 | public LoginEvent(String email, String password, boolean rememberMe) {
14 | this.email = email;
15 | this.password = password;
16 | this.rememberMe = rememberMe;
17 | }
18 |
19 | public String getEmail() {
20 | return email;
21 | }
22 |
23 | public String getPassword() {
24 | return password;
25 | }
26 |
27 | public boolean isRememberMe() {
28 | return rememberMe;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | android.enableJetifier=true
10 | android.useAndroidX=true
11 | org.gradle.jvmargs=-Xmx1536m
12 | # When configured, Gradle will run in incubating parallel mode.
13 | # This option should only be used with decoupled projects. More details, visit
14 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
15 | # org.gradle.parallel=true
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/router/QuestionRouter.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.router;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 |
6 | import androidx.annotation.NonNull;
7 |
8 | import com.example.qwez.ui.question.QuestionActivity;
9 | import com.example.qwez.util.ExtrasConstant;
10 |
11 | /**
12 | * Opens QuestionActivity.class
13 | */
14 | public class QuestionRouter {
15 |
16 | /**
17 | * Open QuestionActivity.class
18 | * @param context of current Activity
19 | * @param qId clear Activity stack. true clears stack
20 | */
21 | public void open(@NonNull Context context, int qId){
22 | Intent intent = new Intent(context, QuestionActivity.class);
23 | intent.putExtra(ExtrasConstant.QUESTION_ID, qId);
24 | context.startActivity(intent);
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/repository/local/dao/GameDao.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.repository.local.dao;
2 |
3 | import androidx.room.Dao;
4 | import androidx.room.Insert;
5 | import androidx.room.Query;
6 |
7 | import com.example.qwez.repository.local.entity.Game;
8 |
9 | import java.util.List;
10 |
11 | import io.reactivex.Completable;
12 | import io.reactivex.Flowable;
13 | import io.reactivex.Single;
14 |
15 | /**
16 | * Dao for @Entity Game
17 | */
18 | @Dao
19 | public interface GameDao extends BaseDao {
20 |
21 | @Insert
22 | long insertReturnId(Game game);
23 |
24 | @Query("SELECT * FROM games")
25 | Flowable> getAll();
26 |
27 | @Query("DELETE FROM games WHERE id=:id")
28 | Completable deleteById(int id);
29 |
30 | @Query("SELECT * FROM games WHERE id=:id")
31 | Single getGameById(int id);
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/router/LoginRouter.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.router;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 |
6 | import androidx.annotation.NonNull;
7 |
8 | import com.example.qwez.ui.login.LoginActivity;
9 |
10 | /**
11 | * Opens LoginActivity.class
12 | */
13 | public class LoginRouter {
14 |
15 | /**
16 | * Open LoginActivity.class
17 | * @param context of current Activity
18 | * @param clearStack clear Activity stack. true clears stack
19 | */
20 | public void open(@NonNull Context context, boolean clearStack){
21 | Intent intent = new Intent(context, LoginActivity.class);
22 | if (clearStack) {
23 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
24 | }
25 | context.startActivity(intent);
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/ui/highscore/HighscoreVMFactory.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.ui.highscore;
2 |
3 | import androidx.annotation.NonNull;
4 | import androidx.lifecycle.ViewModel;
5 | import androidx.lifecycle.ViewModelProvider;
6 |
7 | import com.example.qwez.interactor.GetHighscoreInteractor;
8 |
9 | public class HighscoreVMFactory implements ViewModelProvider.Factory {
10 |
11 | private final GetHighscoreInteractor getUserHighscoreInteractor;
12 |
13 | HighscoreVMFactory(GetHighscoreInteractor getUserHighscoreInteractor) {
14 | this.getUserHighscoreInteractor = getUserHighscoreInteractor;
15 | }
16 |
17 | @SuppressWarnings("unchecked")
18 | @NonNull
19 | @Override
20 | public T create(@NonNull Class modelClass) {
21 | return (T) new HighscoreViewmodel(getUserHighscoreInteractor);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/validator/Validate.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.validator;
2 |
3 | /**
4 | * Abstract Class for Validation
5 | */
6 | public abstract class Validate {
7 | protected String errorMessage;
8 |
9 | public Validate(String errorMessage) {
10 | this.errorMessage = errorMessage;
11 | }
12 |
13 | /**
14 | * Checks if {@code textToCheck} is valid
15 | * @param textToCheck to check
16 | * @return true if valid
17 | */
18 | public abstract boolean isValid(String textToCheck);
19 |
20 | /**
21 | * @return true is Validate has error message
22 | */
23 | public boolean hasErrorMessage() {
24 | return errorMessage != null;
25 | }
26 |
27 | /**
28 | * @return error message
29 | */
30 | public String getErrorMessage() {
31 | return errorMessage;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/router/SettingsRouter.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.router;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 |
6 | import androidx.annotation.NonNull;
7 |
8 | import com.example.qwez.ui.settings.SettingsActivity;
9 |
10 | /**
11 | * Opens SettingsActivity.class
12 | */
13 | public class SettingsRouter {
14 |
15 | /**
16 | * Open SettingsActivity.class
17 | * @param context of current Activity
18 | * @param clearStack clear Activity stack. true clears stack
19 | */
20 | public void open(@NonNull Context context, boolean clearStack){
21 | Intent intent = new Intent(context, SettingsActivity.class);
22 | if (clearStack) {
23 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
24 | }
25 | context.startActivity(intent);
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/interactor/LogoutUserInteractor.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.interactor;
2 |
3 | import com.example.qwez.repository.firebase.FirebaseAuthRepositoryType;
4 |
5 | import io.reactivex.Completable;
6 | import io.reactivex.android.schedulers.AndroidSchedulers;
7 |
8 | /**
9 | * Interactor to log out FirebaseUser
10 | */
11 | public class LogoutUserInteractor {
12 |
13 | private final FirebaseAuthRepositoryType firebaseAuthRepositoryType;
14 |
15 | public LogoutUserInteractor(FirebaseAuthRepositoryType firebaseAuthRepositoryType) {
16 | this.firebaseAuthRepositoryType = firebaseAuthRepositoryType;
17 | }
18 |
19 | /**
20 | * Log out FirebaseUser
21 | * @return Completable
22 | */
23 | public Completable logout(){
24 | return firebaseAuthRepositoryType.logoutUser()
25 | .observeOn(AndroidSchedulers.mainThread());
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/router/StartRouter.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.router;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 |
6 | import androidx.annotation.NonNull;
7 |
8 | import com.example.qwez.ui.start.StartActivity;
9 |
10 | import org.jetbrains.annotations.NotNull;
11 |
12 | /**
13 | * Opens StartActivity.class
14 | */
15 | public class StartRouter {
16 |
17 | /**
18 | * Open StartActivity.class
19 | * @param context of current Activity
20 | * @param clearStack clear Activity stack. true clears stack
21 | */
22 | public void open(@NonNull Context context, boolean clearStack){
23 | Intent intent = new Intent(context, StartActivity.class);
24 | if (clearStack) {
25 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
26 | }
27 | context.startActivity(intent);
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/repository/firebase/rxwrapper/FirebaseFunctionsWrapper.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.repository.firebase.rxwrapper;
2 |
3 | import com.google.firebase.functions.FirebaseFunctions;
4 | import com.google.firebase.functions.HttpsCallableResult;
5 |
6 | import io.reactivex.Single;
7 |
8 | public final class FirebaseFunctionsWrapper {
9 |
10 | public static Single performFunction(FirebaseFunctions functions, String function, Object data){
11 | return Single.create(emitter -> functions.getHttpsCallable(function)
12 | .call(data)
13 | .addOnSuccessListener(emitter::onSuccess)
14 | .addOnFailureListener(emitter::onError));
15 | }
16 |
17 | public static Single performFunction(FirebaseFunctions functions, String function){
18 | return performFunction(functions, function, null);
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/repository/opentdb/OpenTDBAPI.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.repository.opentdb;
2 |
3 |
4 | import com.example.qwez.repository.opentdb.entity.ResponseBody;
5 |
6 | import io.reactivex.Observable;
7 | import retrofit2.Response;
8 | import retrofit2.http.GET;
9 | import retrofit2.http.Query;
10 |
11 | public interface OpenTDBAPI {
12 |
13 | /**
14 | * Get Question from API
15 | * @param amount of questions to get
16 | * @param category Question category
17 | * @param difficulty Question difficulty
18 | * @param type Question type
19 | * @return Retrofit Response with List of Question
20 | */
21 | @GET("api.php?")
22 | Observable> getQuestions(
23 | @Query("amount") int amount,
24 | @Query("category") int category,
25 | @Query("difficulty") String difficulty,
26 | @Query("type") String type
27 | );
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/repository/local/GameDatabase.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.repository.local;
2 |
3 | import androidx.room.Database;
4 | import androidx.room.RoomDatabase;
5 |
6 | import com.example.qwez.repository.local.dao.GameDao;
7 | import com.example.qwez.repository.local.dao.GameQuestionDao;
8 | import com.example.qwez.repository.local.dao.QuestionDao;
9 | import com.example.qwez.repository.local.entity.Game;
10 | import com.example.qwez.repository.local.entity.Question;
11 |
12 | /**
13 | * Application Database Class.
14 | *
15 | * Provides Dao(s)
16 | */
17 | @Database(entities = {Game.class, Question.class}, version = 2)
18 | public abstract class GameDatabase extends RoomDatabase {
19 |
20 | public static final String DATABASE_NAME = "game_database";
21 |
22 | public abstract GameDao gameDao();
23 |
24 | public abstract QuestionDao questionDao();
25 |
26 | public abstract GameQuestionDao gameQuestionDao();
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/settings.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/ui/highscore/HighscoreModule.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.ui.highscore;
2 |
3 | import com.example.qwez.interactor.GetHighscoreInteractor;
4 | import com.example.qwez.repository.firebase.FirebaseAuthRepositoryType;
5 | import com.example.qwez.repository.firebase.FirebaseDatabaseType;
6 |
7 | import dagger.Module;
8 | import dagger.Provides;
9 |
10 | @Module
11 | public class HighscoreModule {
12 |
13 | @Provides
14 | GetHighscoreInteractor getUserHighscoreInteractor(FirebaseDatabaseType firebaseDatabaseType,
15 | FirebaseAuthRepositoryType firebaseAuthRepositoryType){
16 | return new GetHighscoreInteractor(firebaseDatabaseType, firebaseAuthRepositoryType);
17 | }
18 |
19 | @Provides
20 | HighscoreVMFactory highscoreVMFactory(GetHighscoreInteractor getUserHighscoreInteractor){
21 | return new HighscoreVMFactory(getUserHighscoreInteractor);
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/interactor/DeleteGameInteractor.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.interactor;
2 |
3 | import com.example.qwez.repository.local.GameRepositoryType;
4 | import com.example.qwez.repository.local.entity.Game;
5 |
6 | import io.reactivex.Completable;
7 | import io.reactivex.android.schedulers.AndroidSchedulers;
8 |
9 | /**
10 | * Interactor for deleting games.
11 | */
12 | public class DeleteGameInteractor {
13 |
14 | private final GameRepositoryType gameRepositoryType;
15 |
16 | public DeleteGameInteractor(GameRepositoryType gameRepositoryType){
17 | this.gameRepositoryType = gameRepositoryType;
18 | }
19 |
20 | /**
21 | * Delete {@code game} from local database
22 | * @param game @Entity object to delete
23 | * @return Completable
24 | */
25 | public Completable deleteGame(Game game){
26 | return gameRepositoryType.deleteGame(game)
27 | .observeOn(AndroidSchedulers.mainThread());
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/di/SharedPreferencesModule.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.di;
2 |
3 | import android.content.Context;
4 | import android.content.SharedPreferences;
5 |
6 | import com.example.qwez.repository.sharedpref.SharedPreferencesRepository;
7 | import com.example.qwez.repository.sharedpref.SharedPreferencesRepositoryType;
8 |
9 | import dagger.Module;
10 | import dagger.Provides;
11 |
12 | @Module
13 | public class SharedPreferencesModule {
14 |
15 | private static final String SHARED_PREFS = "shared prefs qwez";
16 |
17 | @Provides
18 | @ApplicationScope
19 | SharedPreferencesRepositoryType sharedPreferencesRepositoryType(SharedPreferences sharedPreferences){
20 | return new SharedPreferencesRepository(sharedPreferences);
21 | }
22 |
23 | @Provides
24 | @ApplicationScope
25 | SharedPreferences sharedPreferences(Context context){
26 | return context.getSharedPreferences(SHARED_PREFS, Context.MODE_PRIVATE);
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/ui/highscore/recyclerview/HighscoreAdapter.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.ui.highscore.recyclerview;
2 |
3 | import android.view.ViewGroup;
4 |
5 | import androidx.annotation.NonNull;
6 |
7 | import com.example.qwez.R;
8 | import com.example.qwez.base.BaseAdapter;
9 | import com.example.qwez.entity.Highscore;
10 |
11 | public class HighscoreAdapter extends BaseAdapter {
12 |
13 | public HighscoreAdapter() {
14 | }
15 |
16 | @NonNull
17 | @Override
18 | public HighscoreHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
19 | return new HighscoreHolder(R.layout.item_highscore, parent);
20 | }
21 |
22 | @Override
23 | public void onBindViewHolder(@NonNull HighscoreHolder holder, int position) {
24 | holder.bind(dataList.get(position));
25 | }
26 |
27 | @Override
28 | public int getItemCount() {
29 | return dataList.size();
30 | }
31 |
32 |
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/interactor/GetUserInteractor.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.interactor;
2 |
3 | import com.example.qwez.repository.firebase.FirebaseAuthRepositoryType;
4 | import com.google.firebase.auth.FirebaseUser;
5 |
6 | import io.reactivex.Observable;
7 | import io.reactivex.android.schedulers.AndroidSchedulers;
8 |
9 | /**
10 | * Interactor to get FirebaseUser
11 | */
12 | public class GetUserInteractor {
13 |
14 | private final FirebaseAuthRepositoryType firebaseAuthRepositoryType;
15 |
16 | public GetUserInteractor(FirebaseAuthRepositoryType firebaseAuthRepositoryType) {
17 | this.firebaseAuthRepositoryType = firebaseAuthRepositoryType;
18 | }
19 |
20 | /**
21 | * Get user
22 | * @return Observable which emits FirebaseUser object
23 | */
24 | public Observable getUser(){
25 | return firebaseAuthRepositoryType.getCurrentUser()
26 | .observeOn(AndroidSchedulers.mainThread());
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | #92D1C3
6 | #8BB8A8
7 | #FFD5FF
8 | #B47EB3
9 | #FDF5BF
10 |
11 |
12 | #C0FFE0
13 | #00c262
14 | #FF8400
15 | #FFFFE0
16 | #E00000
17 | #FA8072
18 |
19 |
20 | #D81B60
21 | #9F0000
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/repository/local/dao/QuestionDao.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.repository.local.dao;
2 |
3 | import androidx.room.Dao;
4 | import androidx.room.Query;
5 | import androidx.room.Update;
6 |
7 | import com.example.qwez.repository.local.entity.Question;
8 |
9 | import java.util.List;
10 |
11 | import io.reactivex.Completable;
12 | import io.reactivex.Flowable;
13 | import io.reactivex.Single;
14 |
15 | /**
16 | * Dao for Question
17 | */
18 | @Dao
19 | public interface QuestionDao extends BaseDao {
20 |
21 | @Query("SELECT * FROM questions")
22 | Single> getAll();
23 |
24 | @Query("DELETE FROM questions WHERE question_id=:id")
25 | Completable deleteById(int id);
26 |
27 | @Update
28 | Single insertAndReturnId(Question question);
29 |
30 | @Query("SELECT * FROM QUESTIONS where id=:id")
31 | Single getById(int id);
32 |
33 | @Query("SELECT * FROM QUESTIONS where question_id=:id")
34 | Flowable> getAllQuestionsByGameId(int id);
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/service/MyFirebaseMessagingService.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.service;
2 |
3 | import com.google.firebase.messaging.FirebaseMessagingService;
4 | import com.google.firebase.messaging.RemoteMessage;
5 |
6 | //preperation for new features
7 | public class MyFirebaseMessagingService extends FirebaseMessagingService {
8 |
9 | public MyFirebaseMessagingService() {
10 | super();
11 | }
12 |
13 | @Override
14 | public void onMessageReceived(RemoteMessage remoteMessage) {
15 | super.onMessageReceived(remoteMessage);
16 | }
17 |
18 | @Override
19 | public void onDeletedMessages() {
20 | super.onDeletedMessages();
21 | }
22 |
23 | @Override
24 | public void onMessageSent(String s) {
25 | super.onMessageSent(s);
26 | }
27 |
28 | @Override
29 | public void onSendError(String s, Exception e) {
30 | super.onSendError(s, e);
31 | }
32 |
33 | @Override
34 | public void onNewToken(String s) {
35 | super.onNewToken(s);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/interactor/FirstTimeInteractor.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.interactor;
2 |
3 | import com.example.qwez.repository.sharedpref.SharedPreferencesRepositoryType;
4 |
5 | import io.reactivex.Completable;
6 | import io.reactivex.Single;
7 | import io.reactivex.android.schedulers.AndroidSchedulers;
8 |
9 | public class FirstTimeInteractor {
10 |
11 | private final SharedPreferencesRepositoryType sharedPreferencesRepositoryType;
12 |
13 | public FirstTimeInteractor(SharedPreferencesRepositoryType sharedPreferencesRepositoryType) {
14 | this.sharedPreferencesRepositoryType = sharedPreferencesRepositoryType;
15 | }
16 |
17 | public Single checkNotFirstTime(){
18 | return sharedPreferencesRepositoryType.getNotFirstTime()
19 | .observeOn(AndroidSchedulers.mainThread());
20 | }
21 |
22 | public Completable setNotFirstTime(){
23 | return sharedPreferencesRepositoryType.setNotFirstTime(true)
24 | .observeOn(AndroidSchedulers.mainThread());
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/interactor/GetAllGamesInteractor.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.interactor;
2 |
3 | import com.example.qwez.repository.local.GameRepositoryType;
4 | import com.example.qwez.repository.local.entity.Game;
5 |
6 | import java.util.List;
7 |
8 | import io.reactivex.Flowable;
9 | import io.reactivex.android.schedulers.AndroidSchedulers;
10 |
11 | /**
12 | * Interactor to get all games.
13 | */
14 | public class GetAllGamesInteractor {
15 |
16 | private final GameRepositoryType gameRepositoryType;
17 |
18 | public GetAllGamesInteractor(GameRepositoryType gameRepositoryType) {
19 | this.gameRepositoryType = gameRepositoryType;
20 | }
21 |
22 | /**
23 | * Get all Games continually.
24 | * @return Flowable that emitts Game object, once on subscribe, and there after emitts
25 | * on each change in database
26 | */
27 | public Flowable> getAllGames(){
28 | return gameRepositoryType
29 | .getAllGames()
30 | .observeOn(AndroidSchedulers.mainThread());
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_loading.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_add_question.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
18 |
19 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/ui/splash/SplashModule.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.ui.splash;
2 |
3 | import com.example.qwez.interactor.GetUserInteractor;
4 | import com.example.qwez.repository.firebase.FirebaseAuthRepositoryType;
5 | import com.example.qwez.router.LoginRouter;
6 | import com.example.qwez.router.StartRouter;
7 |
8 | import dagger.Module;
9 | import dagger.Provides;
10 |
11 | @Module
12 | public class SplashModule {
13 |
14 | @Provides
15 | SplashVMFactory splashVMFactory(StartRouter startRouter, GetUserInteractor getUserInteractor, LoginRouter loginRouter){
16 | return new SplashVMFactory(startRouter, getUserInteractor, loginRouter);
17 | }
18 |
19 | @Provides
20 | GetUserInteractor getUserInteractor(FirebaseAuthRepositoryType firebaseAuthRepositoryType){
21 | return new GetUserInteractor(firebaseAuthRepositoryType);
22 | }
23 |
24 | @Provides
25 | LoginRouter loginRouter(){
26 | return new LoginRouter();
27 | }
28 |
29 | @Provides
30 | StartRouter startRouter(){
31 | return new StartRouter();
32 | }
33 |
34 | }
35 |
36 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/interactor/LoginUserInteractor.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.interactor;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import com.example.qwez.repository.firebase.FirebaseAuthRepositoryType;
6 |
7 | import io.reactivex.Completable;
8 | import io.reactivex.android.schedulers.AndroidSchedulers;
9 |
10 | /**
11 | * Interactor to log in FirebaseUser
12 | */
13 | public class LoginUserInteractor {
14 |
15 | private final FirebaseAuthRepositoryType firebaseAuthRepositoryType;
16 |
17 | public LoginUserInteractor(FirebaseAuthRepositoryType firebaseAuthRepositoryType) {
18 | this.firebaseAuthRepositoryType = firebaseAuthRepositoryType;
19 | }
20 |
21 | /**
22 | * Log in user
23 | * @param email user email address
24 | * @param password user password
25 | * @return Completable
26 | */
27 | public Completable login(@NonNull String email, @NonNull String password){
28 | return firebaseAuthRepositoryType.signInUserEmailAndPassword(email, password)
29 | .observeOn(AndroidSchedulers.mainThread());
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_loading.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
23 |
24 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/interactor/SignupInteractor.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.interactor;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import com.example.qwez.repository.firebase.FirebaseAuthRepositoryType;
6 |
7 | import io.reactivex.Completable;
8 | import io.reactivex.android.schedulers.AndroidSchedulers;
9 |
10 | /**
11 | * Interactor to sign up a new FirebaseUser
12 | */
13 | public class SignupInteractor {
14 |
15 | private final FirebaseAuthRepositoryType firebaseAuthRepositoryType;
16 |
17 | public SignupInteractor(FirebaseAuthRepositoryType firebaseAuthRepositoryType) {
18 | this.firebaseAuthRepositoryType = firebaseAuthRepositoryType;
19 | }
20 |
21 | /**
22 | * Sign up new FirebaseUser
23 | * @param email user email address
24 | * @param password user password
25 | * @return Completable
26 | */
27 | public Completable signupUser(@NonNull String email, @NonNull String password){
28 | return Completable.fromMaybe(firebaseAuthRepositoryType.createUserEmailAndPassword(email, password)
29 | .observeOn(AndroidSchedulers.mainThread()));
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/ui/splash/SplashVMFactory.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.ui.splash;
2 |
3 | import androidx.annotation.NonNull;
4 | import androidx.lifecycle.ViewModel;
5 | import androidx.lifecycle.ViewModelProvider;
6 |
7 | import com.example.qwez.interactor.GetUserInteractor;
8 | import com.example.qwez.router.LoginRouter;
9 | import com.example.qwez.router.StartRouter;
10 |
11 | public class SplashVMFactory implements ViewModelProvider.Factory {
12 |
13 | private final StartRouter startRouter;
14 | private final GetUserInteractor getUserInteractor;
15 | private final LoginRouter loginRouter;
16 |
17 | SplashVMFactory(StartRouter startRouter, GetUserInteractor getUserInteractor, LoginRouter loginRouter) {
18 | this.startRouter = startRouter;
19 | this.getUserInteractor = getUserInteractor;
20 | this.loginRouter = loginRouter;
21 | }
22 |
23 | @SuppressWarnings("unchecked")
24 | @NonNull
25 | @Override
26 | public T create(@NonNull Class modelClass) {
27 | return (T) new SplashViewModel(startRouter, loginRouter, getUserInteractor);
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_confirm_pass.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
17 |
18 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/repository/local/dao/GameQuestionDao.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.repository.local.dao;
2 |
3 | import androidx.room.Dao;
4 | import androidx.room.Insert;
5 | import androidx.room.Query;
6 | import androidx.room.Transaction;
7 |
8 | import com.example.qwez.repository.local.entity.GameQuestion;
9 | import com.example.qwez.repository.local.entity.Question;
10 |
11 | import java.util.List;
12 |
13 | import io.reactivex.Completable;
14 | import io.reactivex.Flowable;
15 | import io.reactivex.Single;
16 |
17 | /**
18 | * Dao for GameQuestion objects
19 | */
20 | @Dao
21 | public interface GameQuestionDao {
22 |
23 | @Transaction
24 | @Query("SELECT * FROM games")
25 | Flowable> getGameQuestions();
26 |
27 | @Transaction
28 | @Query("SELECT * FROM games WHERE id=:id")
29 | Flowable getGameQuestionById(int id);
30 |
31 |
32 | @Transaction
33 | @Query("SELECT * FROM questions where questions.answer_chosen=questions.correct_answer and questions.question_id=:gameId")
34 | Single> getPoints(int gameId);
35 | //can't manage to get it to return GameQuestion Object?
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/app/google-services.json:
--------------------------------------------------------------------------------
1 | {
2 | "project_info": {
3 | "project_number": "961562256854",
4 | "firebase_url": "https://qwez-take-2.firebaseio.com",
5 | "project_id": "qwez-take-2",
6 | "storage_bucket": "qwez-take-2.appspot.com"
7 | },
8 | "client": [
9 | {
10 | "client_info": {
11 | "mobilesdk_app_id": "1:961562256854:android:30245e41159ed5f1",
12 | "android_client_info": {
13 | "package_name": "com.example.qwez"
14 | }
15 | },
16 | "oauth_client": [
17 | {
18 | "client_id": "961562256854-sepn33a3fkcolh37up20i6f34tlir5q7.apps.googleusercontent.com",
19 | "client_type": 3
20 | }
21 | ],
22 | "api_key": [
23 | {
24 | "current_key": "AIzaSyBZUnUV_cUMgju6ZXl5fld2-0Q1D5eT2FU"
25 | }
26 | ],
27 | "services": {
28 | "appinvite_service": {
29 | "other_platform_oauth_client": [
30 | {
31 | "client_id": "961562256854-sepn33a3fkcolh37up20i6f34tlir5q7.apps.googleusercontent.com",
32 | "client_type": 3
33 | }
34 | ]
35 | }
36 | }
37 | }
38 | ],
39 | "configuration_version": "1"
40 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/repository/firebase/rxwrapper/FirebaseStorageWrapper.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.repository.firebase.rxwrapper;
2 |
3 | import android.net.Uri;
4 |
5 | import com.google.firebase.storage.StorageReference;
6 | import com.google.firebase.storage.StorageTask;
7 | import com.google.firebase.storage.UploadTask;
8 |
9 | import io.reactivex.Maybe;
10 | import io.reactivex.Single;
11 |
12 | public final class FirebaseStorageWrapper {
13 |
14 | public static Single putFile(StorageReference reference, Uri uri){
15 | return Single.create(emitter -> {
16 | StorageTask task = reference.putFile(uri)
17 | .addOnSuccessListener(emitter::onSuccess)
18 | .addOnFailureListener(e -> {
19 | if(!emitter.isDisposed()){
20 | emitter.onError(e);
21 | }
22 | });
23 | emitter.setCancellable(task::cancel);
24 | });
25 | }
26 |
27 | public static Maybe getDownloadUrl(StorageReference ref) {
28 | return Maybe.create(emitter -> MaybeTask.assign(emitter, ref.getDownloadUrl()));
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/ui/highscore/recyclerview/HighscoreHolder.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.ui.highscore.recyclerview;
2 |
3 | import android.view.ViewGroup;
4 | import android.widget.TextView;
5 |
6 | import androidx.annotation.LayoutRes;
7 | import androidx.annotation.Nullable;
8 |
9 | import com.example.qwez.R;
10 | import com.example.qwez.base.BaseViewHolder;
11 | import com.example.qwez.entity.Highscore;
12 |
13 | import butterknife.BindView;
14 |
15 | public class HighscoreHolder extends BaseViewHolder {
16 |
17 | @BindView(R.id.highscore_nick)
18 | TextView nick;
19 | @BindView(R.id.highscore_score)
20 | TextView score;
21 |
22 | /**
23 | * Create a BaseViewHolder with {@code layoutRes} layout
24 | *
25 | * @param layoutRes
26 | * @param parent
27 | */
28 | HighscoreHolder(@LayoutRes int layoutRes, ViewGroup parent) {
29 | super(layoutRes, parent);
30 | }
31 |
32 | @Override
33 | public void bind(@Nullable Highscore data) {
34 | if(data != null){
35 | this.data = data;
36 | nick.setText(data.getNick());
37 | score.setText(String.format("%s points", data.getScore()));
38 | }
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/app/src/test/java/com/example/qwez/util/QuestionConverterTest.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.util;
2 |
3 | import com.example.qwez.repository.opentdb.entity.Question;
4 |
5 | import org.junit.Test;
6 |
7 | import java.util.ArrayList;
8 | import java.util.List;
9 |
10 | import static org.junit.Assert.assertEquals;
11 |
12 | public class QuestionConverterTest {
13 |
14 | @Test
15 | public void toDatabase() {
16 |
17 | List questions = new ArrayList<>();
18 | List wrongAnswers = new ArrayList<>();
19 | wrongAnswers.add("2");
20 | wrongAnswers.add("3");
21 | wrongAnswers.add("4");
22 |
23 | Question question = new Question("categ", "type", "diff", "1?", "1", wrongAnswers);
24 | questions.add(question);
25 |
26 | List converted = QuestionUtil.toDatabase(questions);
27 |
28 | assertEquals(converted.size(),1 );
29 | com.example.qwez.repository.local.entity.Question questionConverted = converted.get(0);
30 | assertEquals(questionConverted.getCorrectAnswer(), question.getCorrectAnswer());
31 | assertEquals(questionConverted.getWrongAnswer1(), question.getIncorrectAnswers().get(0));
32 |
33 | }
34 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/ui/login/LoginVMFactory.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.ui.login;
2 |
3 | import androidx.annotation.NonNull;
4 | import androidx.lifecycle.ViewModel;
5 | import androidx.lifecycle.ViewModelProvider;
6 |
7 | import com.example.qwez.interactor.LoginUserInteractor;
8 | import com.example.qwez.interactor.SignupInteractor;
9 | import com.example.qwez.router.StartRouter;
10 |
11 | public class LoginVMFactory implements ViewModelProvider.Factory {
12 |
13 | private final LoginUserInteractor loginUserInteractor;
14 | private final SignupInteractor signupInteractor;
15 | private final StartRouter startRouter;
16 |
17 | LoginVMFactory(LoginUserInteractor loginUserInteractor,
18 | SignupInteractor signupInteractor,
19 | StartRouter startRouter) {
20 | this.loginUserInteractor = loginUserInteractor;
21 | this.signupInteractor = signupInteractor;
22 | this.startRouter = startRouter;
23 | }
24 |
25 | @SuppressWarnings("unchecked")
26 | @NonNull
27 | @Override
28 | public T create(@NonNull Class modelClass) {
29 | return (T) new LoginViewModel(loginUserInteractor, signupInteractor, startRouter);
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/repository/local/dao/BaseDao.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.repository.local.dao;
2 |
3 | import androidx.room.Delete;
4 | import androidx.room.Insert;
5 | import androidx.room.Query;
6 | import androidx.room.Update;
7 |
8 | import io.reactivex.Completable;
9 |
10 | /**
11 | * Base Dao Class that all Dao(s) must implement
12 | */
13 | public interface BaseDao {
14 |
15 | /**
16 | * Insert T t into database
17 | * @param t object to insert
18 | * @return Completable that emits insertion result
19 | */
20 | @Insert
21 | Completable insert(T t);
22 |
23 | /**
24 | * Insert T ...t objects into database
25 | * @param t objects to insert
26 | * @return Completable that emits insertion result
27 | */
28 | @Insert
29 | Completable insert(T ...t);
30 |
31 | /**
32 | * Update T t object in the database
33 | * @param t object to update
34 | * @return Completable that emits update result
35 | */
36 | @Update
37 | Completable update(T t);
38 |
39 | /**
40 | * Delete T t object in the database
41 | * @param t object to delete
42 | * @return Completable that emits deletion result
43 | */
44 | @Delete
45 | Completable delete(T t);
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/ui/common/ItemDecorator.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.ui.common;
2 |
3 | import android.graphics.Rect;
4 | import android.view.View;
5 |
6 | import androidx.annotation.NonNull;
7 | import androidx.recyclerview.widget.RecyclerView;
8 |
9 | /**
10 | * Custom ItemDecorator class for Recyclerview
11 | * Adds padding to bottom of items in recyclerView.
12 | */
13 | public class ItemDecorator extends RecyclerView.ItemDecoration {
14 |
15 | //amont of dp of padding
16 | private final int space;
17 | public static final int MARGIN = 5;
18 |
19 | /**
20 | *
21 | * @param space amount of space to add as padding at bottom of each recyclerView (In dp as unit).
22 | */
23 | public ItemDecorator(int space) {
24 | this.space = space;
25 | }
26 |
27 | /**
28 | * Only set the bottom space if it is not the last item
29 | */
30 | @Override
31 | public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
32 | outRect.left = space;
33 | outRect.right = space;
34 | if (parent.getChildAdapterPosition(view) != state.getItemCount()-1) {
35 | outRect.bottom = space * 2;
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/interactor/ChangeUserNickInteractor.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.interactor;
2 |
3 | import com.example.qwez.repository.firebase.FirebaseAuthRepositoryType;
4 | import com.example.qwez.repository.firebase.FirebaseDatabaseType;
5 |
6 | import io.reactivex.Completable;
7 | import io.reactivex.android.schedulers.AndroidSchedulers;
8 |
9 | public class ChangeUserNickInteractor {
10 |
11 | private final FirebaseAuthRepositoryType firebaseAuthRepositoryType;
12 | private final FirebaseDatabaseType firebaseDatabaseType;
13 |
14 | public ChangeUserNickInteractor(FirebaseAuthRepositoryType firebaseAuthRepositoryType, FirebaseDatabaseType firebaseDatabaseType) {
15 | this.firebaseAuthRepositoryType = firebaseAuthRepositoryType;
16 | this.firebaseDatabaseType = firebaseDatabaseType;
17 | }
18 |
19 | public Completable changeNick(String nick){
20 | return firebaseAuthRepositoryType.getCurrentUser()
21 | .take(1)
22 | .flatMapCompletable(firebaseUser -> firebaseAuthRepositoryType.changeUserNick(firebaseUser, nick)
23 | .doOnComplete(() -> firebaseDatabaseType.updateNick(firebaseUser.getUid(), nick)))
24 | .observeOn(AndroidSchedulers.mainThread());
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/ui/start/recycler/CustomAdapter.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.ui.start.recycler;
2 |
3 | import android.content.Context;
4 | import android.graphics.Color;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.ArrayAdapter;
8 | import android.widget.TextView;
9 |
10 | import androidx.annotation.NonNull;
11 | import androidx.annotation.Nullable;
12 |
13 | import java.util.List;
14 |
15 | public class CustomAdapter extends ArrayAdapter {
16 |
17 | public CustomAdapter(@NonNull Context context, int resource, @NonNull List objects) {
18 | super(context, resource, objects);
19 | }
20 |
21 | @Override
22 | public boolean isEnabled(int position) {
23 | return position != 0;
24 | }
25 |
26 | @Override
27 | public View getDropDownView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
28 | View view = super.getDropDownView(position, convertView, parent);
29 | TextView tv = (TextView) view;
30 | if(position == 0){
31 | // Set the hint text color gray
32 | tv.setTextColor(Color.GRAY);
33 | }
34 | else {
35 | tv.setTextColor(Color.BLACK);
36 | }
37 | return view;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/interactor/GetAllGamesAndQuestionsInteractor.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.interactor;
2 |
3 | import com.example.qwez.repository.local.GameRepositoryType;
4 | import com.example.qwez.repository.local.entity.GameQuestion;
5 |
6 | import org.reactivestreams.Publisher;
7 |
8 | import java.util.List;
9 | import java.util.Observable;
10 |
11 | import io.reactivex.Flowable;
12 | import io.reactivex.android.schedulers.AndroidSchedulers;
13 | import io.reactivex.functions.Function;
14 |
15 | /**
16 | * Interactor for getting all games and questions
17 | */
18 | public class GetAllGamesAndQuestionsInteractor {
19 |
20 | private final GameRepositoryType gameRepositoryType;
21 |
22 | public GetAllGamesAndQuestionsInteractor(GameRepositoryType gameRepositoryType) {
23 | this.gameRepositoryType = gameRepositoryType;
24 | }
25 |
26 | /**
27 | * Get all Games and Questions continually.
28 | * @return Flowable that emitts GameQuestion object, once on subscribe, and there after emitts
29 | * on each change in database
30 | */
31 | public Flowable> getAllGamesAndQuestions(){
32 | return gameRepositoryType.getAllGamesAndQuestions()
33 | .observeOn(AndroidSchedulers.mainThread());
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/interactor/UpdateQuestionAndGameInteractor.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.interactor;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import com.example.qwez.interactor.rx.Operators;
6 | import com.example.qwez.repository.local.GameRepositoryType;
7 | import com.example.qwez.repository.local.entity.Question;
8 |
9 | import io.reactivex.Completable;
10 | import io.reactivex.android.schedulers.AndroidSchedulers;
11 | import io.reactivex.schedulers.Schedulers;
12 | import timber.log.Timber;
13 |
14 | import static com.example.qwez.interactor.rx.Operators.GAME_MAPPER;
15 |
16 | public class UpdateQuestionAndGameInteractor {
17 |
18 | private final GameRepositoryType gameRepositoryType;
19 |
20 | public UpdateQuestionAndGameInteractor(GameRepositoryType gameRepositoryType) {
21 | this.gameRepositoryType = gameRepositoryType;
22 | }
23 |
24 | public Completable updateQuestion(@NonNull Question question){
25 | return gameRepositoryType.updateQuestion(question)
26 | //.andThen(gameRepositoryType.getGameById(question.getqId())
27 | //.map(GAME_MAPPER)
28 | //.flatMapCompletable(gameRepositoryType::updateGame))
29 | .observeOn(AndroidSchedulers.mainThread());
30 | }
31 |
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_finish.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
18 |
19 |
29 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/ui/login/LoginModule.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.ui.login;
2 |
3 | import com.example.qwez.interactor.LoginUserInteractor;
4 | import com.example.qwez.interactor.SignupInteractor;
5 | import com.example.qwez.repository.firebase.FirebaseAuthRepositoryType;
6 | import com.example.qwez.router.StartRouter;
7 |
8 | import javax.inject.Named;
9 |
10 | import dagger.Module;
11 | import dagger.Provides;
12 |
13 | @Module
14 | public class LoginModule {
15 |
16 | @Provides
17 | LoginVMFactory loginVMFactory(LoginUserInteractor loginUserInteractor,
18 | SignupInteractor signupInteractor,
19 | StartRouter startRouter){
20 | return new LoginVMFactory(loginUserInteractor, signupInteractor, startRouter);
21 | }
22 |
23 | @Provides
24 | @Named("login")
25 | StartRouter startRouter(){
26 | return new StartRouter();
27 | }
28 |
29 | @Provides
30 | SignupInteractor signupInteractor(FirebaseAuthRepositoryType firebaseAuthRepository){
31 | return new SignupInteractor(firebaseAuthRepository);
32 | }
33 |
34 | @Provides
35 | LoginUserInteractor loginUserInteractor(FirebaseAuthRepositoryType firebaseAuthRepositoryType){
36 | return new LoginUserInteractor(firebaseAuthRepositoryType);
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: required
2 | language: android
3 | jdk: oraclejdk8
4 | dist: trusty
5 | android:
6 | components:
7 | - tools
8 | - tools
9 | - platform-tools
10 | - build-tools-28.0.3
11 | - android-28
12 | - add-on
13 | - extra
14 | before_install:
15 | - echo yes | sdkmanager "build-tools;28.0.3"
16 | - echo yes | sdkmanager "platforms;android-28"
17 | - mkdir "$ANDROID_HOME/licenses" || true
18 | - echo -e "\n8933bad161af4178b1185d1a37fbf41ea5269c55" > "$ANDROID_HOME/licenses/android-sdk-license"
19 | - echo -e "\ " > "$ANDROID_HOME/licenses/android-sdk-preview-license"
20 | - sdkmanager "system-images;android-28;google_apis;x86"
21 | - echo no | avdmanager create avd --force -n emulatorApi28 -k "system-images;android-28;google_apis;x86"
22 | - emulator -avd test -no-audio -no-window &
23 | - ./gradlew dependencies || true
24 | before_script:
25 | - android-wait-for-emulator
26 | - adb shell input keyevent 82 &
27 | script:
28 | - ./gradlew build connectedCheck
29 | - "./gradlew clean build connectedCheck -PdisablePreDex --stacktrace"
30 | before_cache:
31 | - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
32 | - rm -fr $HOME/.gradle/caches/*/plugin-resolution/
33 | cache:
34 | directories:
35 | - $HOME/.gradle/caches/
36 | - $HOME/.gradle/wrapper/
37 | - $HOME/.android/build-cache
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/repository/firebase/FirebaseStorageRepository.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.repository.firebase;
2 |
3 | import android.net.Uri;
4 |
5 | import com.example.qwez.repository.firebase.rxwrapper.FirebaseStorageWrapper;
6 | import com.google.firebase.storage.StorageReference;
7 | import com.google.firebase.storage.UploadTask;
8 |
9 | import io.reactivex.Single;
10 | import io.reactivex.schedulers.Schedulers;
11 |
12 | public class FirebaseStorageRepository implements FirebaseStorageRepositoryType {
13 |
14 | private final StorageReference reference;
15 | private final StorageReference userPhoto;
16 |
17 | public FirebaseStorageRepository(StorageReference reference) {
18 | this.reference = reference;
19 | userPhoto = reference.child("/user_photo");
20 | }
21 |
22 | @Override
23 | public Single uploadPhoto(String user, Uri uri) {
24 | StorageReference thisRef = userPhoto.child(String.format("/%s", user));
25 | return FirebaseStorageWrapper.putFile(thisRef, uri)
26 | .subscribeOn(Schedulers.io());
27 | }
28 |
29 | @Override
30 | public Single getDownloadUrl(StorageReference reference) {
31 | return FirebaseStorageWrapper.getDownloadUrl(reference)
32 | .toSingle()
33 | .subscribeOn(Schedulers.io());
34 | }
35 |
36 |
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/ui/dialog/DifficultyItem.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.ui.dialog;
2 |
3 | import android.view.ViewGroup;
4 | import android.widget.TextView;
5 |
6 | import androidx.annotation.Nullable;
7 |
8 | import com.example.qwez.R;
9 | import com.example.qwez.base.BaseViewHolder;
10 |
11 | import butterknife.BindView;
12 |
13 | public class DifficultyItem extends BaseViewHolder {
14 |
15 | @BindView(R.id.diff_text) TextView textView;
16 |
17 | DifficultyItem(int layoutRes, ViewGroup parent) {
18 | super(layoutRes, parent);
19 | }
20 |
21 | @Override
22 | public void bind(@Nullable String data) {
23 | if(data != null){
24 | textView.setText(data);
25 | switch (data){
26 | case "Easy":
27 | textView.setBackgroundColor(itemView.getResources().getColor(R.color.colorProgressGreenLight));
28 | break;
29 | case "Medium":
30 | textView.setBackgroundColor(itemView.getResources().getColor(R.color.colorProgressYellow));
31 | break;
32 | case "Hard":
33 | textView.setBackgroundColor(itemView.getResources().getColor(R.color.colorProgressRed));
34 | break;
35 | default:
36 | break;
37 | }
38 | }
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/app/src/test/java/com/example/qwez/repository/firebase/rxwrapper/FirebaseTestHelper.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.repository.firebase.rxwrapper;
2 |
3 | import com.google.android.gms.tasks.OnCompleteListener;
4 | import com.google.android.gms.tasks.OnFailureListener;
5 | import com.google.android.gms.tasks.OnSuccessListener;
6 | import com.google.android.gms.tasks.Task;
7 |
8 | import org.mockito.ArgumentCaptor;
9 |
10 | import static org.mockito.Mockito.when;
11 |
12 | class FirebaseTestHelper {
13 |
14 | static final String EMAIL = "test@gmail.com";
15 | static final String PASSWORD = "test123";
16 | static final String NEW_PASSWORD = "newtest123";
17 | static final Exception EXCEPTION = new Exception();
18 |
19 | static ArgumentCaptor testOnCompleteListener = ArgumentCaptor.forClass(OnCompleteListener.class);
20 | static ArgumentCaptor testOnSuccessListener = ArgumentCaptor.forClass(OnSuccessListener.class);
21 | static ArgumentCaptor testOnFailureListener = ArgumentCaptor.forClass(OnFailureListener.class);
22 |
23 | static void setupTask(Task task) {
24 | when(task.addOnCompleteListener(testOnCompleteListener.capture())).thenReturn(task);
25 | when(task.addOnSuccessListener(testOnSuccessListener.capture())).thenReturn(task);
26 | when(task.addOnFailureListener(testOnFailureListener.capture())).thenReturn(task);
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/base/BaseViewHolder.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.base;
2 |
3 | import android.view.LayoutInflater;
4 | import android.view.View;
5 | import android.view.ViewGroup;
6 |
7 | import androidx.annotation.LayoutRes;
8 | import androidx.annotation.Nullable;
9 | import androidx.recyclerview.widget.RecyclerView;
10 |
11 | import butterknife.ButterKnife;
12 |
13 | public abstract class BaseViewHolder extends RecyclerView.ViewHolder {
14 |
15 | //data to bind to this viewholder
16 | protected T data;
17 |
18 | /**
19 | * Create a BaseViewHolder with {@code layoutRes} layout
20 | * @param layoutRes
21 | * @param parent
22 | */
23 | public BaseViewHolder(@LayoutRes int layoutRes, ViewGroup parent) {
24 | super(LayoutInflater.from(parent.getContext())
25 | .inflate(layoutRes, parent, false));
26 | ButterKnife.bind(this, itemView);
27 | }
28 |
29 | /**
30 | * Get bound viewholder data
31 | * @return bound data
32 | */
33 | public T getData(){
34 | return data;
35 | }
36 |
37 | /**
38 | * Returns the View associated with this viewholder
39 | * @return Viewholders View
40 | */
41 | public View getItemView(){
42 | return itemView;
43 | }
44 |
45 | /**
46 | * Binds {@code data} to this viewholder
47 | * @param data to bind
48 | */
49 | public abstract void bind(@Nullable T data);
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/interactor/DeleteAccountInteractor.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.interactor;
2 |
3 | import com.example.qwez.repository.firebase.FirebaseAuthRepositoryType;
4 | import com.example.qwez.repository.local.GameRepositoryType;
5 | import com.google.firebase.auth.AuthResult;
6 |
7 | import io.reactivex.Completable;
8 | import io.reactivex.android.schedulers.AndroidSchedulers;
9 |
10 | public class DeleteAccountInteractor {
11 |
12 | private final FirebaseAuthRepositoryType firebaseAuthRepositoryType;
13 | private final GameRepositoryType gameRepositoryType;
14 |
15 | public DeleteAccountInteractor(FirebaseAuthRepositoryType firebaseAuthRepositoryType, GameRepositoryType gameRepositoryType) {
16 | this.firebaseAuthRepositoryType = firebaseAuthRepositoryType;
17 | this.gameRepositoryType = gameRepositoryType;
18 | }
19 |
20 | public Completable delete(String password){
21 | return firebaseAuthRepositoryType
22 | .getCurrentUser()
23 | .take(1)
24 | .flatMapMaybe(firebaseUser -> firebaseAuthRepositoryType.reAuthenticateUserAndReturnUser(firebaseUser, firebaseUser.getEmail(), password))
25 | .map(AuthResult::getUser)
26 | .flatMapCompletable(firebaseAuthRepositoryType::deleteUser)
27 | .andThen(gameRepositoryType.deleteAll())
28 | .observeOn(AndroidSchedulers.mainThread());
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/interactor/ChangeUserPasswordInteractor.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.interactor;
2 |
3 | import com.example.qwez.repository.firebase.FirebaseAuthRepositoryType;
4 |
5 | import io.reactivex.Completable;
6 | import io.reactivex.android.schedulers.AndroidSchedulers;
7 |
8 | /**
9 | * Interactor to change user password
10 | */
11 | public class ChangeUserPasswordInteractor {
12 |
13 | private final FirebaseAuthRepositoryType firebaseAuthRepositoryType;
14 |
15 | public ChangeUserPasswordInteractor(FirebaseAuthRepositoryType firebaseAuthRepositoryType) {
16 | this.firebaseAuthRepositoryType = firebaseAuthRepositoryType;
17 | }
18 |
19 | /**
20 | * Get current user, then change password
21 | * @param oldPass old password of user
22 | * @param newPass new password to set
23 | * @return Completable that emits operation result
24 | */
25 | public Completable changeUserPassword(String oldPass, String newPass){
26 | //version force re auth directly. No checking
27 | return firebaseAuthRepositoryType.getCurrentUser()
28 | .take(1) //limit to 1 firebaseuser to complete stream
29 | .flatMapCompletable(fu -> firebaseAuthRepositoryType.reAuthenticateUser(fu,fu.getEmail(),oldPass)
30 | .andThen(firebaseAuthRepositoryType.changeUserPassword(fu, newPass)))
31 | .observeOn(AndroidSchedulers.mainThread());
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/interactor/ChangeProfilePhotoInteractor.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.interactor;
2 |
3 | import android.net.Uri;
4 |
5 | import androidx.annotation.NonNull;
6 |
7 | import com.example.qwez.repository.firebase.FirebaseAuthRepositoryType;
8 | import com.example.qwez.repository.firebase.FirebaseStorageRepositoryType;
9 |
10 | import io.reactivex.Completable;
11 | import io.reactivex.android.schedulers.AndroidSchedulers;
12 |
13 | public class ChangeProfilePhotoInteractor {
14 |
15 | private final FirebaseAuthRepositoryType authRepo;
16 | private final FirebaseStorageRepositoryType storageRepo;
17 |
18 | public ChangeProfilePhotoInteractor(FirebaseAuthRepositoryType authRepo,
19 | FirebaseStorageRepositoryType storageRepo) {
20 | this.authRepo = authRepo;
21 | this.storageRepo = storageRepo;
22 | }
23 |
24 |
25 | public Completable changeProfilePhoto(@NonNull Uri uri){
26 | return authRepo.getCurrentUser()
27 | .take(1)
28 | .flatMapCompletable(firebaseUser -> storageRepo.uploadPhoto(firebaseUser.getUid(), uri)
29 | .flatMap(taskSnapshot -> storageRepo.getDownloadUrl(taskSnapshot.getMetadata().getReference()))
30 | .flatMapCompletable(uploadedUri -> authRepo.changeUserPhoto(firebaseUser, uploadedUri)))
31 | .observeOn(AndroidSchedulers.mainThread());
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/di/AppComponent.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.di;
2 |
3 | import com.example.qwez.App;
4 | import com.example.qwez.ui.highscore.HighscoreModule;
5 | import com.example.qwez.ui.login.LoginFragmentModule;
6 | import com.example.qwez.ui.login.LoginModule;
7 | import com.example.qwez.ui.question.QuestionModule;
8 | import com.example.qwez.ui.settings.SettingsModule;
9 | import com.example.qwez.ui.splash.SplashModule;
10 | import com.example.qwez.ui.start.StartModule;
11 |
12 | import dagger.BindsInstance;
13 | import dagger.Component;
14 | import dagger.android.AndroidInjectionModule;
15 |
16 | /**
17 | * Dagger Component
18 | */
19 | @ApplicationScope
20 | @Component(modules = {AndroidInjectionModule.class,
21 | FirebaseModule.class,
22 | NetworkModule.class,
23 | StartModule.class,
24 | BinderModule.class,
25 | OpenTDBModule.class,
26 | ApplicationModule.class,
27 | LocalDatabaseModule.class,
28 | SplashModule.class,
29 | LoginModule.class,
30 | SettingsModule.class,
31 | QuestionModule.class,
32 | SharedPreferencesModule.class,
33 | LoginFragmentModule.class,
34 | HighscoreModule.class})
35 | public interface AppComponent {
36 |
37 | @Component.Builder
38 | interface Builder {
39 | @BindsInstance
40 | Builder application(App app);
41 | AppComponent build();
42 | }
43 |
44 | void inject(App app);
45 |
46 | }
47 |
48 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_highscore.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
11 |
12 |
21 |
22 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/interactor/GetSingleGameAndQuestionsInteractor.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.interactor;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import com.example.qwez.repository.local.GameRepositoryType;
6 | import com.example.qwez.repository.local.entity.GameQuestion;
7 | import com.example.qwez.repository.local.entity.Question;
8 |
9 | import org.jetbrains.annotations.NotNull;
10 |
11 | import java.util.List;
12 |
13 | import io.reactivex.Flowable;
14 | import io.reactivex.android.schedulers.AndroidSchedulers;
15 |
16 | public class GetSingleGameAndQuestionsInteractor {
17 |
18 | private final GameRepositoryType gameRepositoryType;
19 |
20 | public GetSingleGameAndQuestionsInteractor(GameRepositoryType gameRepositoryType) {
21 | this.gameRepositoryType = gameRepositoryType;
22 | }
23 |
24 | /**
25 | * Get GameQuestion object with Game and all associated Question(s)
26 | * @param id of game
27 | * @return Flowable that emit first on subscribe(), and on each update in database, a GameQuestion object
28 | */
29 | public Flowable getGameQuestions(int id){
30 | return gameRepositoryType.getGameQuestionBy(id)
31 | .observeOn(AndroidSchedulers.mainThread());
32 | }
33 |
34 | public Flowable> getAllGamesByQuestionID(int id){
35 | return gameRepositoryType.getAllQuestionsByGameId(id)
36 | .observeOn(AndroidSchedulers.mainThread());
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/interactor/rx/Operators.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.interactor.rx;
2 |
3 | import com.example.qwez.entity.Highscore;
4 | import com.example.qwez.repository.local.entity.Game;
5 | import com.example.qwez.repository.local.entity.GameQuestion;
6 |
7 | import java.util.ArrayList;
8 | import java.util.Collections;
9 | import java.util.List;
10 |
11 | import io.reactivex.functions.Function;
12 |
13 | /**
14 | * Rx Util Class with RxJava Operators & Transformations
15 | */
16 | public class Operators {
17 |
18 | /**
19 | * Takes a Game object and increments its Answered field
20 | */
21 | public static final Function GAME_MAPPER = game -> {
22 | int ans = game.getAnswered();
23 | ans++;
24 | game.setAnswered(ans);
25 | return game;
26 | };
27 |
28 | /**
29 | * Takes a List of Highscore Objects and sorts them in Descending order
30 | */
31 | public static final Function,List> SORT_HIGHSCORES = highscores -> {
32 | List sorted = new ArrayList<>(highscores);
33 | Collections.sort(sorted, (o1, o2) -> o2.getScore() - o1.getScore());
34 | return sorted;
35 | };
36 |
37 | public static final Function,List> SET_GAME_SCORES = gameQuestions -> {
38 | gameQuestions.forEach(gameQuestion -> gameQuestion.game.setAnswered(gameQuestion.questions.size()));
39 | return gameQuestions;
40 | };
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/repository/local/entity/Game.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.repository.local.entity;
2 |
3 | import androidx.room.ColumnInfo;
4 | import androidx.room.Entity;
5 | import androidx.room.PrimaryKey;
6 |
7 | /**
8 | * Game @Entity
9 | */
10 | @Entity(tableName = "games")
11 | public class Game {
12 |
13 | @PrimaryKey(autoGenerate = true)
14 | @ColumnInfo(name = "id")
15 | private int gameId;
16 |
17 | @ColumnInfo(name = "category")
18 | private String category;
19 |
20 | @ColumnInfo(name = "difficulty")
21 | private String difficulty;
22 |
23 | @ColumnInfo(name = "answered")
24 | private int answered;
25 |
26 | public Game(String category, String difficulty) {
27 | this.category = category;
28 | this.difficulty = difficulty;
29 | this.answered = 0;
30 | }
31 |
32 | public int getGameId() {
33 | return gameId;
34 | }
35 |
36 | public void setGameId(int gameId) {
37 | this.gameId = gameId;
38 | }
39 |
40 | public String getCategory() {
41 | return category;
42 | }
43 |
44 | public void setCategory(String category) {
45 | this.category = category;
46 | }
47 |
48 | public String getDifficulty() {
49 | return difficulty;
50 | }
51 |
52 | public void setDifficulty(String difficulty) {
53 | this.difficulty = difficulty;
54 | }
55 |
56 | public int getAnswered() {
57 | return answered;
58 | }
59 |
60 | public void setAnswered(int answered) {
61 | this.answered = answered;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/repository/opentdb/OpenTDB.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.repository.opentdb;
2 |
3 | import com.example.qwez.repository.ApiOperator;
4 | import com.example.qwez.repository.opentdb.entity.Question;
5 | import com.example.qwez.repository.opentdb.entity.ResponseBody;
6 | import com.example.qwez.util.QuestionType;
7 |
8 | import java.util.List;
9 |
10 | import io.reactivex.Single;
11 | import io.reactivex.schedulers.Schedulers;
12 |
13 | /**
14 | * OPENTDB API Repository
15 | */
16 | public class OpenTDB implements OpenTDBType {
17 |
18 | private final OpenTDBAPI api;
19 |
20 | public OpenTDB(OpenTDBAPI api) {
21 | this.api = api;
22 | }
23 |
24 |
25 | /**
26 | * Get List of Question(s)
27 | * @param amount of questions
28 | * @param category Question category
29 | * @param difficulty Question difficulty
30 | * @param type Question type
31 | * @return Single that emits List of Question
32 | */
33 | @Override
34 | public Single> getQuestionByCategory(int amount,
35 | int category,
36 | String difficulty,
37 | String type) {
38 | return Single.fromObservable(api.getQuestions(amount, category, difficulty, QuestionType.MULTIPLE_CHOICE.getType())
39 | .lift(new ApiOperator<>())
40 | .map(ResponseBody::getQuestions)
41 | .subscribeOn(Schedulers.io())
42 | );
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/interactor/GetHighscoreInteractor.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.interactor;
2 |
3 | import com.example.qwez.entity.Highscore;
4 | import com.example.qwez.interactor.rx.Operators;
5 | import com.example.qwez.repository.firebase.FirebaseAuthRepositoryType;
6 | import com.example.qwez.repository.firebase.FirebaseDatabaseType;
7 |
8 | import java.util.List;
9 |
10 | import io.reactivex.Single;
11 | import io.reactivex.android.schedulers.AndroidSchedulers;
12 |
13 | public class GetHighscoreInteractor {
14 |
15 | private final FirebaseDatabaseType firebaseDatabaseType;
16 | private final FirebaseAuthRepositoryType firebaseAuthRepositoryType;
17 |
18 | public GetHighscoreInteractor(FirebaseDatabaseType firebaseDatabaseType,
19 | FirebaseAuthRepositoryType firebaseAuthRepositoryType) {
20 | this.firebaseDatabaseType = firebaseDatabaseType;
21 | this.firebaseAuthRepositoryType = firebaseAuthRepositoryType;
22 | }
23 |
24 | public Single getUserScore(){
25 | return Single.fromObservable(firebaseAuthRepositoryType
26 | .getCurrentUser()
27 | .take(1)
28 | .flatMapSingle(firebaseUser -> firebaseDatabaseType.getUserHighscore(firebaseUser.getUid())))
29 | .observeOn(AndroidSchedulers.mainThread());
30 | }
31 |
32 | public Single> getTop50Highscore(){
33 | return firebaseDatabaseType.getTop50Highscores()
34 | .map(Operators.SORT_HIGHSCORES)
35 | .observeOn(AndroidSchedulers.mainThread());
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 | -keepattributes *Annotation*
23 | -keepnames class * implements java.io.Serializable
24 | -keepclassmembers class * implements java.io.Serializable {
25 | static final long serialVersionUID;
26 | private static final java.io.ObjectStreamField[] serialPersistentFields;
27 | !static !transient ;
28 | private void writeObject(java.io.ObjectOutputStream);
29 | private void readObject(java.io.ObjectInputStream);
30 | java.lang.Object writeReplace();
31 | java.lang.Object readResolve();
32 | }
33 |
34 | -assumenosideeffects class android.util.Log {
35 | public static boolean isLoggable(java.lang.String, int);
36 | public static int v(...);
37 | public static int i(...);
38 | public static int w(...);
39 | public static int d(...);
40 | public static int e(...);
41 | }
42 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/ui/splash/SplashActivity.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.ui.splash;
2 |
3 | import android.os.Bundle;
4 |
5 | import androidx.annotation.Nullable;
6 | import androidx.lifecycle.ViewModelProviders;
7 |
8 | import com.example.qwez.R;
9 | import com.example.qwez.base.BaseActivity;
10 | import com.example.qwez.entity.ErrorCarrier;
11 | import com.google.firebase.auth.FirebaseUser;
12 |
13 | import javax.inject.Inject;
14 |
15 | import timber.log.Timber;
16 |
17 | public class SplashActivity extends BaseActivity {
18 |
19 | @Inject
20 | SplashVMFactory factory;
21 | SplashViewModel viewModel;
22 |
23 | @Override
24 | protected void onCreate(@Nullable Bundle savedInstanceState) {
25 | super.onCreate(savedInstanceState);
26 |
27 | hideToolbar();
28 |
29 | viewModel = ViewModelProviders.of(this, factory).get(SplashViewModel.class);
30 | viewModel.progress().observe(this, this::onProgress);
31 | viewModel.error().observe(this, this::onError);
32 | viewModel.user().observe(this, this::onUser);
33 |
34 | viewModel.getUser();
35 |
36 | }
37 |
38 | private void onUser(FirebaseUser firebaseUser) {
39 | Timber.d("onUser");
40 | if(firebaseUser != null){
41 | viewModel.openStart(this);
42 | }else{
43 | viewModel.openLogin(this);
44 | }
45 | }
46 |
47 | private void onError(ErrorCarrier errorCarrier) {
48 | }
49 |
50 | private void onProgress(Boolean aBoolean) {
51 | }
52 |
53 | @Override
54 | protected int getLayout() {
55 | return R.layout.activity_splash;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/repository/opentdb/entity/ResponseBody.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.repository.opentdb.entity;
2 |
3 | import com.google.gson.annotations.Expose;
4 | import com.google.gson.annotations.SerializedName;
5 |
6 | import java.io.Serializable;
7 | import java.util.List;
8 |
9 | /**
10 | * OPENTDB API Respone wrapper
11 | */
12 | public class ResponseBody implements Serializable
13 | {
14 |
15 | @SerializedName("response_code")
16 | @Expose
17 | private int responseCode;
18 | @SerializedName("results")
19 | @Expose
20 | private List questions;
21 |
22 | /**
23 | * No args constructor for use in serialization
24 | *
25 | */
26 | public ResponseBody() {
27 | }
28 |
29 | /**
30 | *
31 | * @param responseCode
32 | * @param questions
33 | */
34 | public ResponseBody(int responseCode, List questions) {
35 | super();
36 | this.responseCode = responseCode;
37 | this.questions = questions;
38 | }
39 |
40 | public int getResponseCode() {
41 | return responseCode;
42 | }
43 |
44 | public void setResponseCode(int responseCode) {
45 | this.responseCode = responseCode;
46 | }
47 |
48 | public List getQuestions() {
49 | return questions;
50 | }
51 |
52 | public void setResults(List questions) {
53 | this.questions = questions;
54 | }
55 |
56 | @Override
57 | public String toString() {
58 | return "ResponseBody{" +
59 | "responseCode=" + responseCode +
60 | ", questions=" + questions +
61 | '}';
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/ui/start/recycler/GameAdapter.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.ui.start.recycler;
2 |
3 | import android.content.Context;
4 | import android.view.ViewGroup;
5 |
6 | import androidx.annotation.NonNull;
7 |
8 | import com.example.qwez.R;
9 | import com.example.qwez.base.BaseAdapter;
10 | import com.example.qwez.bus.RxBus;
11 | import com.example.qwez.bus.event.GameEvent;
12 | import com.example.qwez.repository.local.entity.Game;
13 |
14 | public class GameAdapter extends BaseAdapter {
15 |
16 | private final Context context;
17 | private boolean isClickable;
18 |
19 | public GameAdapter(Context context) {
20 | this.context = context;
21 | }
22 |
23 | @NonNull
24 | @Override
25 | public GameHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
26 | return new GameHolder(R.layout.item_question, parent);
27 | }
28 |
29 | @Override
30 | public void onBindViewHolder(@NonNull GameHolder holder, int position) {
31 | holder.bind(dataList.get(position));
32 | holder.getItemView().setOnClickListener(v -> {
33 | if(isClickable){
34 | Game game = holder.getData();
35 | RxBus.publish(RxBus.GAME_TOUCHED, new GameEvent(game));
36 | }
37 | });
38 | }
39 |
40 | void deleteItem(int position) {
41 | RxBus.publish(RxBus.DELETE_GAME, new GameEvent(dataList.get(position)));
42 | notifyDataSetChanged(); //restore view if not/after deleted
43 | }
44 |
45 | public Context getContext(){
46 | return context;
47 | }
48 |
49 | public void setClickable(boolean clickable) {
50 | isClickable = clickable;
51 | }
52 | }
53 |
54 |
55 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/ui/highscore/HighscoreViewmodel.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.ui.highscore;
2 |
3 | import androidx.lifecycle.MutableLiveData;
4 |
5 | import com.example.qwez.base.BaseViewModel;
6 | import com.example.qwez.entity.Highscore;
7 | import com.example.qwez.interactor.GetHighscoreInteractor;
8 |
9 | import java.util.List;
10 |
11 | class HighscoreViewmodel extends BaseViewModel {
12 |
13 | private MutableLiveData> highscore = new MutableLiveData<>();
14 | private MutableLiveData yourscore = new MutableLiveData<>();
15 |
16 | private final GetHighscoreInteractor getUserHighscoreInteractor;
17 |
18 | HighscoreViewmodel(GetHighscoreInteractor interactor) {
19 | this.getUserHighscoreInteractor = interactor;
20 | }
21 |
22 | void getUserHighscore(){
23 | progress.setValue(true);
24 | disposable = getUserHighscoreInteractor
25 | .getUserScore()
26 | .subscribe(this::onHighscore,this::onError);
27 | }
28 |
29 | void getHighscores(){
30 | progress.setValue(true);
31 | disposable = getUserHighscoreInteractor
32 | .getTop50Highscore()
33 | .subscribe(this::onScores,this::onError);
34 | }
35 |
36 | private void onScores(List scores) {
37 | progress.setValue(false);
38 | highscore.setValue(scores);
39 | }
40 |
41 | private void onHighscore(Integer integer) {
42 | progress.setValue(false);
43 | yourscore.setValue(integer);
44 | }
45 |
46 | MutableLiveData> highscore() {
47 | return highscore;
48 | }
49 |
50 | MutableLiveData yourscore() {
51 | return yourscore;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/base/BaseViewModel.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.base;
2 |
3 |
4 | import androidx.lifecycle.LiveData;
5 | import androidx.lifecycle.MutableLiveData;
6 | import androidx.lifecycle.ViewModel;
7 |
8 | import com.example.qwez.entity.ErrorCarrier;
9 |
10 | import io.reactivex.disposables.Disposable;
11 |
12 | public class BaseViewModel extends ViewModel {
13 |
14 | //LiveData for errors
15 | private final MutableLiveData error = new MutableLiveData<>();
16 | //LiveData for progress
17 | protected final MutableLiveData progress = new MutableLiveData<>();
18 | //Disposable resource
19 | protected Disposable disposable;
20 |
21 | @Override
22 | protected void onCleared() {
23 | cancel();
24 | }
25 |
26 | /**
27 | * Cancel this viewmodels disposable
28 | */
29 | public void cancel() {
30 | if (disposable != null && !disposable.isDisposed()) {
31 | disposable.dispose();
32 | }
33 | }
34 |
35 | /**
36 | * Set error to LiveData. Also sets {@code progress} to false
37 | * @param throwable to set
38 | */
39 | protected void onError(Throwable throwable) {
40 | if(progress.getValue()!=null && progress.getValue()){
41 | progress.setValue(false);
42 | }
43 | error.postValue(new ErrorCarrier(throwable.getMessage()));
44 | }
45 |
46 | /**
47 | * Get error LiveData
48 | * @return error
49 | */
50 | public LiveData error() {
51 | return error;
52 | }
53 |
54 | /**
55 | * Get progress LiveData
56 | * @return progress
57 | */
58 | public LiveData progress() {
59 | return progress;
60 | }
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/ui/question/QuestionVMFactory.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.ui.question;
2 |
3 | import androidx.annotation.NonNull;
4 | import androidx.lifecycle.ViewModel;
5 | import androidx.lifecycle.ViewModelProvider;
6 |
7 | import com.example.qwez.interactor.GetSingleGameAndQuestionsInteractor;
8 | import com.example.qwez.interactor.PointsInteractor;
9 | import com.example.qwez.interactor.UpdateQuestionAndGameInteractor;
10 | import com.example.qwez.router.StartRouter;
11 |
12 | public class QuestionVMFactory implements ViewModelProvider.Factory {
13 |
14 | private final GetSingleGameAndQuestionsInteractor getSingleGameAndQuestionsInteractor;
15 | private final UpdateQuestionAndGameInteractor updateQuestionInteractor;
16 | private final PointsInteractor pointsInteractor;
17 | private final StartRouter startRouter;
18 |
19 | QuestionVMFactory(GetSingleGameAndQuestionsInteractor getSingleGameAndQuestionsInteractor,
20 | UpdateQuestionAndGameInteractor updateQuestionInteractor,
21 | PointsInteractor pointsInteractor,
22 | StartRouter startRouter) {
23 | this.getSingleGameAndQuestionsInteractor = getSingleGameAndQuestionsInteractor;
24 | this.updateQuestionInteractor = updateQuestionInteractor;
25 | this.pointsInteractor = pointsInteractor;
26 | this.startRouter = startRouter;
27 | }
28 |
29 | @SuppressWarnings("unchecked")
30 | @NonNull
31 | @Override
32 | public T create(@NonNull Class modelClass) {
33 | return (T) new QuestionViewModel(getSingleGameAndQuestionsInteractor,
34 | updateQuestionInteractor,
35 | pointsInteractor,
36 | startRouter);
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/app/src/test/java/com/example/qwez/validator/ValidateTest.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.validator;
2 |
3 | import org.junit.Test;
4 | import org.junit.runner.RunWith;
5 | import org.junit.runners.JUnit4;
6 |
7 | import static org.junit.Assert.assertFalse;
8 | import static org.junit.Assert.assertTrue;
9 |
10 | @RunWith(JUnit4.class)
11 | public class ValidateTest {
12 |
13 | EmailValidate emailValidate = new EmailValidate();
14 | PasswordValidate passwordValidate = new PasswordValidate();
15 | EmptyValidate emptyValidate = new EmptyValidate();
16 |
17 | @Test
18 | public void emailTest() {
19 | assertTrue( emailValidate.isValid("myemail@gmail.com"));
20 | assertFalse( emailValidate.isValid("myemailgmailcom"));
21 | }
22 |
23 | @Test
24 | public void passwordTest() {
25 | assertTrue( passwordValidate.isValid("HejHejHej123#"));
26 | assertFalse( passwordValidate.isValid("hej"));
27 | assertFalse( passwordValidate.isValid("hejhejhejhejhej"));
28 | assertFalse( passwordValidate.isValid("123"));
29 | assertFalse( passwordValidate.isValid("13123123123123123"));
30 | assertFalse( passwordValidate.isValid("???"));
31 | assertFalse( passwordValidate.isValid("?????????????????"));
32 | assertFalse( passwordValidate.isValid("HHHHHHHHHHHHHHHHH"));
33 | assertFalse( passwordValidate.isValid("hhhhhhhhhhhhhhhhhh"));
34 | }
35 |
36 | @Test
37 | public void emptyTest() {
38 | assertTrue( emptyValidate.isValid("hej"));
39 | assertFalse( emptyValidate.isValid(""));
40 | }
41 |
42 | @Test
43 | public void errorMessageTest(){
44 | assertTrue(emailValidate.hasErrorMessage());
45 | assertTrue(passwordValidate.hasErrorMessage());
46 | assertTrue(emptyValidate.hasErrorMessage());
47 | }
48 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/base/BaseFragment.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.base;
2 |
3 | import android.content.Context;
4 | import android.os.Bundle;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 |
9 | import androidx.annotation.LayoutRes;
10 | import androidx.annotation.NonNull;
11 | import androidx.annotation.Nullable;
12 | import androidx.fragment.app.Fragment;
13 |
14 | import com.example.qwez.bus.RxBus;
15 |
16 | import org.jetbrains.annotations.NotNull;
17 |
18 | import butterknife.ButterKnife;
19 | import butterknife.Unbinder;
20 |
21 | public abstract class BaseFragment extends Fragment {
22 |
23 | //Keep a reference to Context.
24 | //Setting Context from onAttach makes sure that context not
25 | //running a risk of being null
26 | protected Context context;
27 | private Unbinder unbinder;
28 |
29 | protected abstract @LayoutRes int getLayout();
30 |
31 | @Nullable
32 | @Override
33 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
34 | View view = inflater.inflate(getLayout() ,container, false);
35 | unbinder = ButterKnife.bind(this, view);
36 | return view;
37 | }
38 |
39 | @Override
40 | public void onAttach(@NotNull Context context) {
41 | super.onAttach(context);
42 |
43 | //setting context here to avoid null pointers
44 | this.context = context;
45 | }
46 |
47 | @Override
48 | public void onStop() {
49 | super.onStop();
50 |
51 | //Stop this fragment from receiving events
52 | RxBus.unregister(this);
53 | }
54 |
55 | @Override
56 | public void onDestroyView() {
57 | super.onDestroyView();
58 | unbinder.unbind();
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/ui/splash/SplashViewModel.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.ui.splash;
2 |
3 | import android.content.Context;
4 |
5 | import androidx.lifecycle.MutableLiveData;
6 |
7 | import com.example.qwez.base.BaseViewModel;
8 | import com.example.qwez.interactor.GetUserInteractor;
9 | import com.example.qwez.router.LoginRouter;
10 | import com.example.qwez.router.StartRouter;
11 | import com.google.firebase.auth.FirebaseUser;
12 |
13 | public class SplashViewModel extends BaseViewModel {
14 |
15 | private final StartRouter startRouter;
16 | private final LoginRouter loginRouter;
17 | private final GetUserInteractor getUserInteractor;
18 | private final MutableLiveData user = new MutableLiveData<>();
19 |
20 | SplashViewModel(StartRouter startRouter, LoginRouter loginRouter, GetUserInteractor getUserInteractor) {
21 | this.startRouter = startRouter;
22 | this.loginRouter = loginRouter;
23 | this.getUserInteractor = getUserInteractor;
24 | }
25 |
26 | void getUser(){
27 | disposable = getUserInteractor.getUser()
28 | .subscribe(this::onUser, this::onError);
29 | }
30 |
31 | private void onUser(FirebaseUser firebaseUser) {
32 | user.setValue(firebaseUser);
33 | }
34 |
35 | void openStart(Context context){
36 | startRouter.open(context, true);
37 | }
38 |
39 | void openLogin(Context context){
40 | loginRouter.open(context, true);
41 | }
42 |
43 | public MutableLiveData user(){
44 | return user;
45 | }
46 |
47 | @Override
48 | protected void onError(Throwable throwable) {
49 | if(throwable instanceof NullPointerException){
50 | user.setValue(null);
51 | }else{
52 | super.onError(throwable);
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/interactor/RememberUserInteractor.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.interactor;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import com.example.qwez.repository.sharedpref.SharedPreferencesRepositoryType;
6 |
7 | import io.reactivex.Completable;
8 | import io.reactivex.Maybe;
9 | import io.reactivex.android.schedulers.AndroidSchedulers;
10 |
11 | public class RememberUserInteractor {
12 |
13 | private final SharedPreferencesRepositoryType sharedPreferencesRepositoryType;
14 |
15 | public RememberUserInteractor(SharedPreferencesRepositoryType sharedPreferencesRepositoryType) {
16 | this.sharedPreferencesRepositoryType = sharedPreferencesRepositoryType;
17 | }
18 |
19 | public Completable setRemembered(@NonNull String remember){
20 | return sharedPreferencesRepositoryType.setRemembered(remember)
21 | .andThen( sharedPreferencesRepositoryType.setIsRemember(true))
22 | .observeOn(AndroidSchedulers.mainThread());
23 | }
24 |
25 | public Maybe getRememberIfExists(){
26 | return sharedPreferencesRepositoryType.isRemembered()
27 | .flatMapMaybe(aBoolean -> {
28 | if(aBoolean){
29 | return sharedPreferencesRepositoryType.getRemembered()
30 | .flatMapMaybe(Maybe::just);
31 | }else{
32 | return Maybe.empty();
33 | }
34 | })
35 | .observeOn(AndroidSchedulers.mainThread());
36 | }
37 |
38 | public Completable dontRemember(){
39 | return sharedPreferencesRepositoryType.setIsRemember(false)
40 | .andThen(sharedPreferencesRepositoryType.setNotRemember())
41 | .observeOn(AndroidSchedulers.mainThread());
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/app/src/test/java/com/example/qwez/interactor/LogoutUserInteractorTest.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.interactor;
2 |
3 | import com.example.qwez.RxResources;
4 | import com.example.qwez.repository.firebase.FirebaseAuthRepositoryType;
5 |
6 | import org.junit.Before;
7 | import org.junit.ClassRule;
8 | import org.junit.Test;
9 | import org.junit.runner.RunWith;
10 | import org.junit.runners.JUnit4;
11 | import org.mockito.InjectMocks;
12 | import org.mockito.Mock;
13 | import org.mockito.MockitoAnnotations;
14 |
15 | import io.reactivex.Completable;
16 |
17 | import static org.mockito.Mockito.verify;
18 | import static org.mockito.Mockito.when;
19 |
20 | @RunWith(JUnit4.class)
21 | public class LogoutUserInteractorTest {
22 |
23 | @ClassRule
24 | public static RxResources rxres = new RxResources();
25 |
26 | @Mock
27 | FirebaseAuthRepositoryType firebaseAuthRepositoryType;
28 |
29 | @Mock
30 | Throwable error;
31 |
32 | @InjectMocks
33 | LogoutUserInteractor interactor;
34 |
35 | @Before
36 | public void setUp() throws Exception {
37 | MockitoAnnotations.initMocks(this);
38 | }
39 |
40 | @Test
41 | public void logout() {
42 | when(firebaseAuthRepositoryType.logoutUser()).thenReturn(Completable.complete());
43 |
44 | interactor.logout()
45 | .test()
46 | .assertNoErrors()
47 | .assertComplete();
48 |
49 | verify(firebaseAuthRepositoryType).logoutUser();
50 | }
51 |
52 | @Test
53 | public void logoutError() {
54 | when(firebaseAuthRepositoryType.logoutUser()).thenReturn(Completable.error(error));
55 |
56 | interactor.logout()
57 | .test()
58 | .assertNotComplete()
59 | .assertNoValues()
60 | .assertError(error);
61 |
62 | verify(firebaseAuthRepositoryType).logoutUser();
63 | }
64 | }
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Qwez
3 | Start Quiz
4 | %1$s/%1$s
5 | My Games
6 | Loading...
7 | Login/Signup
8 | Email
9 | Password
10 | Log In
11 | Sign Up
12 | Don\'t have an account yet? Sign up!
13 | Enter Email
14 | Enter Nickname
15 | Enter Password
16 | Enter Password Again
17 | Sign in to continue
18 | Sign up to continue
19 | Settings
20 | Choose a category:
21 | Choose difficulty
22 | Enter new password
23 | Repeat new password
24 | Please choose a category and a difficulty
25 | Enter old pass
26 | Welcome back!
27 | Qwez
28 | Enter password
29 | Start
30 | Answered 0/10
31 | Continue
32 |
33 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/util/QuestionUtil.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.util;
2 |
3 | import com.example.qwez.repository.local.entity.Question;
4 |
5 | import org.apache.commons.text.StringEscapeUtils;
6 |
7 | import java.util.ArrayList;
8 | import java.util.List;
9 |
10 | /**
11 | * Helper Class for converting between {@link com.example.qwez.repository.opentdb.entity.Question} and {@link Question}
12 | */
13 | public final class QuestionUtil {
14 |
15 | private QuestionUtil(){
16 | //private constructor to avoid instantiation
17 | }
18 |
19 | /**
20 | * Convert a List of {@link com.example.qwez.repository.opentdb.entity.Question} to a List of{@link Question}
21 | * @param toConvert List of OPENTBD API Question(s) to convert
22 | * @return List of Local Database Question(s)
23 | */
24 | public static List toDatabase(List toConvert) {
25 |
26 | List converted = new ArrayList<>();
27 |
28 | toConvert.forEach(question -> {
29 | String q = question.getQuestion();
30 | String qCA = question.getCorrectAnswer();
31 | String wA1 = question.getIncorrectAnswers().get(0);
32 | String wA2 = question.getIncorrectAnswers().get(1);
33 | String wA3 = question.getIncorrectAnswers().get(2);
34 | q = StringEscapeUtils.unescapeHtml4(q);
35 | qCA = StringEscapeUtils.unescapeHtml4(qCA);
36 | wA1 = StringEscapeUtils.unescapeHtml4(wA1);
37 | wA2 = StringEscapeUtils.unescapeHtml4(wA2);
38 | wA3 = StringEscapeUtils.unescapeHtml4(wA3);
39 |
40 | converted.add(new Question(
41 | q,
42 | qCA,
43 | wA1,
44 | wA2,
45 | wA3));
46 | });
47 |
48 | return converted;
49 |
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/ui/start/recycler/GameHolder.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.ui.start.recycler;
2 |
3 | import android.net.Uri;
4 | import android.view.View;
5 | import android.view.ViewGroup;
6 | import android.widget.ImageView;
7 | import android.widget.TextView;
8 |
9 | import androidx.annotation.LayoutRes;
10 | import androidx.annotation.Nullable;
11 |
12 | import com.bumptech.glide.Glide;
13 | import com.example.qwez.R;
14 | import com.example.qwez.base.BaseViewHolder;
15 | import com.example.qwez.repository.local.entity.Game;
16 | import com.example.qwez.util.Category;
17 |
18 | import butterknife.BindView;
19 |
20 | public class GameHolder extends BaseViewHolder implements View.OnLongClickListener{
21 |
22 | @BindView(R.id.tcq)
23 | TextView cat;
24 | @BindView(R.id.tdq)
25 | TextView diff;
26 | @BindView(R.id.question_icon)
27 | ImageView icon;
28 | @BindView(R.id.how_many_answered)
29 | TextView answered;
30 |
31 | GameHolder(@LayoutRes int layoutRes, ViewGroup parent) {
32 | super(layoutRes,parent);
33 | }
34 |
35 | @Override
36 | public void bind(@Nullable Game data) {
37 | if(data != null){
38 | this.data = data;
39 | cat.setText(data.getCategory());
40 | diff.setText(data.getDifficulty());
41 | answered.setText(String.format("%d/10", data.getAnswered()));
42 | Glide
43 | .with(icon.getContext())
44 | .asBitmap()
45 | .load(
46 | Uri.parse(String.format(
47 | "file:///android_asset/categories/%s.png",
48 | Category.getMap().get(data.getCategory()))))
49 | .into(icon);
50 | }
51 | }
52 |
53 | @Override
54 | public boolean onLongClick(View v) {
55 | return false;
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/test/java/com/example/qwez/interactor/DeleteGameInteractorTest.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.interactor;
2 |
3 | import com.example.qwez.RxResources;
4 | import com.example.qwez.repository.local.GameRepositoryType;
5 | import com.example.qwez.repository.local.entity.Game;
6 |
7 | import org.junit.Before;
8 | import org.junit.ClassRule;
9 | import org.junit.Test;
10 | import org.junit.runner.RunWith;
11 | import org.junit.runners.JUnit4;
12 | import org.mockito.InjectMocks;
13 | import org.mockito.Mock;
14 | import org.mockito.MockitoAnnotations;
15 |
16 | import io.reactivex.Completable;
17 |
18 | import static org.mockito.Mockito.verify;
19 | import static org.mockito.Mockito.when;
20 |
21 | @RunWith(JUnit4.class)
22 | public class DeleteGameInteractorTest {
23 |
24 | @ClassRule
25 | public static RxResources rxres = new RxResources();
26 |
27 | @Mock
28 | GameRepositoryType gameRepositoryType;
29 |
30 | @Mock
31 | Throwable error;
32 |
33 | @InjectMocks
34 | DeleteGameInteractor deleteGameInteractor;
35 |
36 | Game game;
37 |
38 | @Before
39 | public void setUp() throws Exception {
40 | MockitoAnnotations.initMocks(this);
41 |
42 | game = new Game("cat", "diff");
43 | }
44 |
45 | @Test
46 | public void deleteGame() {
47 | when(gameRepositoryType.deleteGame(game)).thenReturn(Completable.complete());
48 | deleteGameInteractor.deleteGame(game)
49 | .test()
50 | .assertNoErrors()
51 | .assertComplete();
52 |
53 | verify(gameRepositoryType).deleteGame(game);
54 | }
55 |
56 | @Test
57 | public void deleteGameError() {
58 | when(gameRepositoryType.deleteGame(game)).thenReturn(Completable.error(error));
59 |
60 | deleteGameInteractor.deleteGame(game)
61 | .test()
62 | .assertNotComplete()
63 | .assertNoValues()
64 | .assertError(error);
65 |
66 | verify(gameRepositoryType).deleteGame(game);
67 | }
68 |
69 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/util/Difficulty.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.util;
2 |
3 | import java.util.ArrayList;
4 | import java.util.HashMap;
5 | import java.util.List;
6 | import java.util.Map;
7 |
8 | /**
9 | * Constants class for quiz constants
10 | */
11 | public enum Difficulty {
12 |
13 | EASY("easy"),
14 | MEDIUM("medium"),
15 | HARD("hard");
16 |
17 | private String difficulty;
18 |
19 | Difficulty(String difficulty){
20 | this.difficulty = difficulty;
21 | }
22 |
23 | public String getDifficulty() {
24 | return difficulty;
25 | }
26 |
27 |
28 | /**
29 | * Get Difficulty ENUM(s) String value
30 | * @param difficulty of which String value is wanted
31 | * @return Difficulty String value
32 | */
33 | public static String getAsString(Difficulty difficulty){
34 | String toReturn;
35 | switch (difficulty){
36 | case EASY:
37 | toReturn = "Easy";
38 | break;
39 | case MEDIUM:
40 | toReturn = "Medium";
41 | break;
42 | case HARD:
43 | toReturn = "Hard";
44 | break;
45 | default:
46 | toReturn = "ERROR unknown difficulty";
47 | break;
48 | }
49 | return toReturn;
50 | }
51 |
52 | /**
53 | * @return Map of Difficulty String Value as key, and Difficulty ENUM as value
54 | */
55 | public static Map getMap(){
56 | HashMap map = new HashMap<>();
57 | map.put("Easy", Difficulty.EASY);
58 | map.put("Medium", Difficulty.MEDIUM);
59 | map.put("Hard", Difficulty.HARD);
60 | return map;
61 | }
62 |
63 | /**
64 | * @return List of Difficulty String Value(s)
65 | */
66 | public static List getList(){
67 | List list = new ArrayList<>();
68 | list.add("Easy");
69 | list.add("Medium");
70 | list.add("Hard");
71 | return list;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/di/BinderModule.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.di;
2 |
3 | import com.example.qwez.ui.highscore.HighscoreActivity;
4 | import com.example.qwez.ui.highscore.HighscoreModule;
5 | import com.example.qwez.ui.login.LoginActivity;
6 | import com.example.qwez.ui.login.LoginFragment;
7 | import com.example.qwez.ui.login.LoginFragmentModule;
8 | import com.example.qwez.ui.login.LoginModule;
9 | import com.example.qwez.ui.question.QuestionActivity;
10 | import com.example.qwez.ui.question.QuestionModule;
11 | import com.example.qwez.ui.settings.SettingsActivity;
12 | import com.example.qwez.ui.settings.SettingsModule;
13 | import com.example.qwez.ui.splash.SplashActivity;
14 | import com.example.qwez.ui.splash.SplashModule;
15 | import com.example.qwez.ui.start.StartActivity;
16 | import com.example.qwez.ui.start.StartModule;
17 |
18 | import dagger.Module;
19 | import dagger.android.ContributesAndroidInjector;
20 |
21 | /**
22 | * Bind all Activity package Module(s) to their Activity-
23 | */
24 | @Module
25 | public abstract class BinderModule {
26 |
27 | @ActivityScope
28 | @ContributesAndroidInjector(modules = StartModule.class)
29 | abstract StartActivity bindStartActivity();
30 |
31 | @ActivityScope
32 | @ContributesAndroidInjector(modules = SplashModule.class)
33 | abstract SplashActivity bindSplashActivity();
34 |
35 | @ActivityScope
36 | @ContributesAndroidInjector(modules = LoginModule.class)
37 | abstract LoginActivity bindLoginActivity();
38 |
39 | @ActivityScope
40 | @ContributesAndroidInjector(modules = SettingsModule.class)
41 | abstract SettingsActivity bindSettingsActivity();
42 |
43 | @ActivityScope
44 | @ContributesAndroidInjector(modules = QuestionModule.class)
45 | abstract QuestionActivity bindQuestionActivity();
46 |
47 | @FragmentScope
48 | @ContributesAndroidInjector(modules = LoginFragmentModule.class)
49 | abstract LoginFragment bindLoginFragment();
50 |
51 | @ActivityScope
52 | @ContributesAndroidInjector(modules = HighscoreModule.class)
53 | abstract HighscoreActivity bindHighscoreActivity();
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/app/src/test/java/com/example/qwez/interactor/LoginUserInteractorTest.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.interactor;
2 |
3 | import com.example.qwez.RxResources;
4 | import com.example.qwez.repository.firebase.FirebaseAuthRepositoryType;
5 |
6 | import org.junit.Before;
7 | import org.junit.ClassRule;
8 | import org.junit.Test;
9 | import org.junit.runner.RunWith;
10 | import org.junit.runners.JUnit4;
11 | import org.mockito.InjectMocks;
12 | import org.mockito.Mock;
13 | import org.mockito.MockitoAnnotations;
14 |
15 | import io.reactivex.Completable;
16 |
17 | import static org.mockito.ArgumentMatchers.anyString;
18 | import static org.mockito.Mockito.verify;
19 | import static org.mockito.Mockito.when;
20 |
21 | @RunWith(JUnit4.class)
22 | public class LoginUserInteractorTest {
23 |
24 | @ClassRule
25 | public static RxResources rexrs = new RxResources();
26 |
27 | @Mock
28 | FirebaseAuthRepositoryType firebaseAuthRepositoryType;
29 |
30 | @Mock
31 | Throwable error;
32 |
33 | @InjectMocks
34 | LoginUserInteractor interactor;
35 |
36 | @Before
37 | public void setUp() throws Exception {
38 | MockitoAnnotations.initMocks(this);
39 | }
40 |
41 | @Test
42 | public void login() {
43 | when(firebaseAuthRepositoryType.signInUserEmailAndPassword(anyString(), anyString())).thenReturn(Completable.complete());
44 |
45 | interactor.login("test", "test2")
46 | .test()
47 | .assertNoErrors()
48 | .assertComplete();
49 |
50 | verify(firebaseAuthRepositoryType).signInUserEmailAndPassword("test", "test2");
51 | }
52 |
53 | @Test
54 | public void loginError() {
55 | when(firebaseAuthRepositoryType.signInUserEmailAndPassword(anyString(), anyString())).thenReturn(Completable.error(error));
56 |
57 | interactor.login("test", "test2")
58 | .test()
59 | .assertNotComplete()
60 | .assertNoValues()
61 | .assertError(error);
62 |
63 | verify(firebaseAuthRepositoryType).signInUserEmailAndPassword("test", "test2");
64 | }
65 |
66 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/interactor/PointsInteractor.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.interactor;
2 |
3 | import com.example.qwez.entity.FinishedGame;
4 | import com.example.qwez.repository.firebase.FirebaseAuthRepositoryType;
5 | import com.example.qwez.repository.firebase.FirebaseDatabaseType;
6 | import com.example.qwez.repository.local.GameRepositoryType;
7 | import com.google.firebase.auth.FirebaseUser;
8 |
9 | import io.reactivex.Completable;
10 | import io.reactivex.Observable;
11 | import io.reactivex.Observer;
12 | import io.reactivex.Single;
13 | import io.reactivex.android.schedulers.AndroidSchedulers;
14 | import timber.log.Timber;
15 |
16 | public class PointsInteractor {
17 |
18 | private final GameRepositoryType gameRepositoryType;
19 | private final FirebaseDatabaseType firebaseDatabaseType;
20 | private final FirebaseAuthRepositoryType firebaseAuthRepositoryType;
21 |
22 | public PointsInteractor(GameRepositoryType gameRepositoryType, FirebaseDatabaseType firebaseDatabaseType, FirebaseAuthRepositoryType firebaseAuthRepositoryType) {
23 | this.gameRepositoryType = gameRepositoryType;
24 | this.firebaseDatabaseType = firebaseDatabaseType;
25 | this.firebaseAuthRepositoryType = firebaseAuthRepositoryType;
26 | }
27 |
28 | public Single getPoints(int id){
29 | return gameRepositoryType.getAllCorrectQuestions(id)
30 | .map(questions -> new FinishedGame(questions.size(), id))
31 | .observeOn(AndroidSchedulers.mainThread());
32 | }
33 |
34 | public Completable finishGameAndUploadPoints(int gameId){
35 | return Single.fromObservable(firebaseAuthRepositoryType.getCurrentUser().take(1))
36 | .zipWith(gameRepositoryType.getAllCorrectQuestions(gameId),
37 | (firebaseUser, questions) -> firebaseDatabaseType.updateHighscore(firebaseUser.getUid(),questions.size()))
38 | .flatMapCompletable(completable -> gameRepositoryType.getGameById(gameId)
39 | .flatMapCompletable(gameRepositoryType::deleteGame))
40 | .observeOn(AndroidSchedulers.mainThread());
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/app/src/test/java/com/example/qwez/interactor/GetUserInteractorTest.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.interactor;
2 |
3 | import com.example.qwez.RxResources;
4 | import com.example.qwez.repository.firebase.FirebaseAuthRepositoryType;
5 | import com.google.firebase.auth.FirebaseUser;
6 |
7 | import org.junit.Before;
8 | import org.junit.ClassRule;
9 | import org.junit.Test;
10 | import org.junit.runner.RunWith;
11 | import org.junit.runners.JUnit4;
12 | import org.mockito.InjectMocks;
13 | import org.mockito.Mock;
14 | import org.mockito.MockitoAnnotations;
15 |
16 | import io.reactivex.Observable;
17 |
18 | import static org.junit.Assert.assertEquals;
19 | import static org.mockito.Mockito.verify;
20 | import static org.mockito.Mockito.when;
21 |
22 | @RunWith(JUnit4.class)
23 | public class GetUserInteractorTest {
24 |
25 | @ClassRule
26 | public static RxResources rxres = new RxResources();
27 |
28 | @Mock
29 | FirebaseAuthRepositoryType firebaseAuthRepositoryType;
30 |
31 | @Mock
32 | FirebaseUser firebaseUser;
33 |
34 | @Mock
35 | Throwable error;
36 |
37 | @InjectMocks
38 | GetUserInteractor interactor;
39 |
40 | @Before
41 | public void setUp() throws Exception {
42 | MockitoAnnotations.initMocks(this);
43 | }
44 |
45 | @Test
46 | public void getUser() {
47 | when(firebaseAuthRepositoryType.getCurrentUser()).thenReturn(Observable.just(firebaseUser));
48 |
49 | FirebaseUser f = interactor.getUser()
50 | .test()
51 | .assertNoErrors()
52 | .assertValueCount(1)
53 | .values()
54 | .get(0);
55 |
56 | assertEquals(firebaseUser, f);
57 | verify(firebaseAuthRepositoryType).getCurrentUser();
58 | }
59 |
60 | @Test
61 | public void getUserError() {
62 | when(firebaseAuthRepositoryType.getCurrentUser()).thenReturn(Observable.error(error));
63 |
64 | interactor.getUser()
65 | .test()
66 | .assertNotComplete()
67 | .assertNoValues()
68 | .assertError(error);
69 |
70 | verify(firebaseAuthRepositoryType).getCurrentUser();
71 | }
72 |
73 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/ui/login/LoginViewModel.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.ui.login;
2 |
3 | import android.content.Context;
4 |
5 | import androidx.lifecycle.MutableLiveData;
6 |
7 | import com.example.qwez.base.BaseViewModel;
8 | import com.example.qwez.interactor.LoginUserInteractor;
9 | import com.example.qwez.interactor.SignupInteractor;
10 | import com.example.qwez.router.StartRouter;
11 |
12 | class LoginViewModel extends BaseViewModel {
13 |
14 | private final MutableLiveData logIn = new MutableLiveData<>();
15 | private final MutableLiveData signUp = new MutableLiveData<>();
16 |
17 | private final LoginUserInteractor loginUserInteractor;
18 | private final SignupInteractor signupInteractor;
19 | private final StartRouter startRouter;
20 |
21 | LoginViewModel(LoginUserInteractor loginUserInteractor,
22 | SignupInteractor signupInteractor,
23 | StartRouter startRouter) {
24 | this.loginUserInteractor = loginUserInteractor;
25 | this.signupInteractor = signupInteractor;
26 | this.startRouter = startRouter;
27 | }
28 |
29 | void logInUser(String email, String password){
30 | progress.setValue(true);
31 | disposable = loginUserInteractor.login(email, password)
32 | .subscribe(this::onLoginSuccess, this::onError);
33 | }
34 |
35 | void signupUser(String email, String password){
36 | progress.setValue(true);
37 | disposable = signupInteractor.signupUser(email, password)
38 | .subscribe(this::onSignupSuccess, this::onError);
39 | }
40 |
41 | private void onSignupSuccess() {
42 | progress.setValue(false);
43 | signUp.setValue(true);
44 | }
45 |
46 | private void onLoginSuccess() {
47 | progress.setValue(false);
48 | logIn.setValue(true);
49 | }
50 |
51 | void openStart(Context context, boolean clearStack){
52 | startRouter.open(context,clearStack);
53 | }
54 |
55 | MutableLiveData login() {
56 | return logIn;
57 | }
58 |
59 | MutableLiveData singup() {
60 | return signUp;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/app/src/test/java/com/example/qwez/interactor/GetSingleGameAndQuestionsInteractorTest.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.interactor;
2 |
3 | import com.example.qwez.RxResources;
4 | import com.example.qwez.repository.local.GameRepositoryType;
5 | import com.example.qwez.repository.local.entity.GameQuestion;
6 |
7 | import org.junit.Before;
8 | import org.junit.ClassRule;
9 | import org.junit.Test;
10 | import org.junit.runner.RunWith;
11 | import org.junit.runners.JUnit4;
12 | import org.mockito.InjectMocks;
13 | import org.mockito.Mock;
14 | import org.mockito.MockitoAnnotations;
15 |
16 | import io.reactivex.Flowable;
17 |
18 | import static org.mockito.ArgumentMatchers.anyInt;
19 | import static org.mockito.Mockito.verify;
20 | import static org.mockito.Mockito.when;
21 |
22 | @RunWith(JUnit4.class)
23 | public class GetSingleGameAndQuestionsInteractorTest {
24 |
25 | @ClassRule
26 | public static RxResources rxres = new RxResources();
27 |
28 | @Mock
29 | GameRepositoryType gameRepositoryType;
30 |
31 | @Mock
32 | Throwable error;
33 |
34 | @InjectMocks
35 | GetSingleGameAndQuestionsInteractor interactor;
36 |
37 | @Before
38 | public void setUp() throws Exception {
39 | MockitoAnnotations.initMocks(this);
40 | }
41 |
42 | @Test
43 | public void getGameQuestions() {
44 | GameQuestion gameQuestion = new GameQuestion();
45 |
46 | when(gameRepositoryType.getGameQuestionBy(5454)).thenReturn(Flowable.just(gameQuestion));
47 |
48 | int id = 5454;
49 |
50 | interactor.getGameQuestions(id)
51 | .test()
52 | .assertValueCount(1)
53 | .assertValue(gameQuestion);
54 |
55 | verify(gameRepositoryType).getGameQuestionBy(id);
56 | }
57 |
58 | @Test
59 | public void getGameQuestionsError() {
60 | when(gameRepositoryType.getGameQuestionBy(anyInt())).thenReturn(Flowable.error(error));
61 |
62 | int random = 343;
63 |
64 | interactor.getGameQuestions(random)
65 | .test()
66 | .assertNoValues()
67 | .assertError(error);
68 |
69 | verify(gameRepositoryType).getGameQuestionBy(random);
70 | }
71 | }
--------------------------------------------------------------------------------
/app/src/test/java/com/example/qwez/interactor/SignupInteractorTest.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.interactor;
2 |
3 | import com.example.qwez.RxResources;
4 | import com.example.qwez.repository.firebase.FirebaseAuthRepositoryType;
5 | import com.google.firebase.auth.AuthResult;
6 |
7 | import org.junit.Before;
8 | import org.junit.ClassRule;
9 | import org.junit.Test;
10 | import org.junit.runner.RunWith;
11 | import org.junit.runners.JUnit4;
12 | import org.mockito.InjectMocks;
13 | import org.mockito.Mock;
14 | import org.mockito.MockitoAnnotations;
15 |
16 | import io.reactivex.Maybe;
17 |
18 | import static org.mockito.ArgumentMatchers.anyString;
19 | import static org.mockito.Mockito.verify;
20 | import static org.mockito.Mockito.when;
21 |
22 | @RunWith(JUnit4.class)
23 | public class SignupInteractorTest {
24 |
25 | @ClassRule
26 | public static RxResources rxres = new RxResources();
27 |
28 | @Mock
29 | FirebaseAuthRepositoryType firebaseAuthRepositoryType;
30 |
31 | @Mock
32 | AuthResult authResult;
33 |
34 | @Mock
35 | Throwable error;
36 |
37 | @InjectMocks
38 | SignupInteractor interactor;
39 |
40 | @Before
41 | public void setUp() throws Exception {
42 | MockitoAnnotations.initMocks(this);
43 | }
44 |
45 | @Test
46 | public void signupUser() {
47 | when(firebaseAuthRepositoryType.createUserEmailAndPassword(anyString(), anyString())).thenReturn(Maybe.just(authResult));
48 |
49 | interactor.signupUser("fake", "fake3")
50 | .test()
51 | .assertNoErrors()
52 | .assertComplete();
53 |
54 | verify(firebaseAuthRepositoryType).createUserEmailAndPassword("fake", "fake3");
55 | }
56 |
57 | @Test
58 | public void signupUserError() {
59 | when(firebaseAuthRepositoryType.createUserEmailAndPassword(anyString(), anyString())).thenReturn(Maybe.error(error));
60 |
61 | interactor.signupUser("fake", "fake3")
62 | .test()
63 | .assertNotComplete()
64 | .assertNoValues()
65 | .assertError(error);
66 |
67 | verify(firebaseAuthRepositoryType).createUserEmailAndPassword("fake", "fake3");
68 | }
69 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/repository/firebase/FirebaseAuthRepositoryType.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.repository.firebase;
2 |
3 | import android.net.Uri;
4 |
5 | import com.google.firebase.auth.AuthResult;
6 | import com.google.firebase.auth.FirebaseUser;
7 |
8 | import io.reactivex.Completable;
9 | import io.reactivex.Maybe;
10 | import io.reactivex.Observable;
11 | import io.reactivex.Single;
12 |
13 | /**
14 | * FirebaseAuthRepository interface
15 | */
16 | public interface FirebaseAuthRepositoryType {
17 |
18 | /**
19 | * Sign in user with email and password, return Completable.
20 | */
21 | Completable signInUserEmailAndPassword(String email, String password);
22 |
23 | /**
24 | * Create user with email and password, return Maybe that emits AuthResult
25 | */
26 | Maybe createUserEmailAndPassword(String email, String password);
27 |
28 | /**
29 | * Get Observable that emits current signed in user. Observable emits onError if
30 | * no user currently authorized.
31 | */
32 | Observable getCurrentUser();
33 |
34 | Single getCurrentUserOnce();
35 |
36 | /**
37 | * Log out current authorized user. Returns Completable with operation result.
38 | */
39 | Completable logoutUser();
40 |
41 | /**
42 | * Change current user password. Returns Completable with operation result.
43 | */
44 | Completable changeUserPassword(FirebaseUser firebaseUser, String newPassword);
45 |
46 | /**
47 | * Change current user nickname. Return Completable with operation results
48 | */
49 | Completable changeUserNick(FirebaseUser firebaseUser, String newNick);
50 |
51 | /**
52 | * Re-authenticate current logged in user
53 | */
54 | Completable reAuthenticateUser(FirebaseUser firebaseUser, String email, String password);
55 |
56 | /**
57 | * Re-authenticate current logged in user
58 | */
59 | Maybe reAuthenticateUserAndReturnUser(FirebaseUser firebaseUser, String email, String password);
60 |
61 | /**
62 | *
63 | */
64 | Completable changeUserPhoto(FirebaseUser firebaseUser, Uri uri);
65 |
66 | Completable deleteUser(FirebaseUser firebaseUser);
67 |
68 | }
69 |
70 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/ui/question/FinishFragment.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.ui.question;
2 |
3 | import android.os.Bundle;
4 | import android.view.View;
5 | import android.widget.Button;
6 | import android.widget.TextView;
7 |
8 | import androidx.annotation.Nullable;
9 | import androidx.lifecycle.ViewModelProviders;
10 |
11 | import com.example.qwez.R;
12 | import com.example.qwez.base.BaseFragment;
13 | import com.example.qwez.entity.ErrorCarrier;
14 | import com.example.qwez.entity.FinishedGame;
15 | import com.example.qwez.repository.local.entity.GameQuestion;
16 | import com.example.qwez.repository.local.entity.Question;
17 |
18 | import butterknife.BindView;
19 | import butterknife.BindViews;
20 | import butterknife.OnClick;
21 | import timber.log.Timber;
22 |
23 | public class FinishFragment extends BaseFragment {
24 |
25 | private QuestionViewModel viewModel;
26 |
27 | @BindView(R.id.you_scored) TextView score;
28 | @BindView(R.id.finish_game) Button button;
29 |
30 | @Override
31 | public void onActivityCreated(@Nullable Bundle savedInstanceState) {
32 | super.onActivityCreated(savedInstanceState);
33 |
34 | viewModel = ViewModelProviders.of(getActivity()).get(QuestionViewModel.class);
35 | viewModel.game().observe(this, this::onGame);
36 | viewModel.finishedGame().observe(this, this::onFinishedGame);
37 | viewModel.error().observe(this, this::onError);
38 |
39 | }
40 |
41 | private void onError(ErrorCarrier errorCarrier) {
42 | Timber.d("onError: %s",errorCarrier.getMessage());
43 | }
44 |
45 | private void onFinishedGame(FinishedGame finishedGame) {
46 | Timber.d("onFinishedGame");
47 | score.setText(String.format("You scored %s points",finishedGame.getScore()));
48 | button.setVisibility(View.VISIBLE);
49 | }
50 |
51 | private void onGame(GameQuestion gameQuestion) {
52 | Timber.d("onGame");
53 | viewModel.getPoints(gameQuestion.game.getGameId());
54 | }
55 |
56 | @OnClick(R.id.finish_game)
57 | public void click(){
58 | Timber.d("click finish_game");
59 | viewModel.uploadScore();
60 | }
61 |
62 | @Override
63 | protected int getLayout() {
64 | return R.layout.fragment_finish;
65 | }
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/ui/question/QuestionModule.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.ui.question;
2 |
3 | import com.example.qwez.interactor.GetSingleGameAndQuestionsInteractor;
4 | import com.example.qwez.interactor.PointsInteractor;
5 | import com.example.qwez.interactor.UpdateQuestionAndGameInteractor;
6 | import com.example.qwez.repository.firebase.FirebaseAuthRepositoryType;
7 | import com.example.qwez.repository.firebase.FirebaseDatabaseType;
8 | import com.example.qwez.repository.local.GameRepositoryType;
9 | import com.example.qwez.router.StartRouter;
10 |
11 | import javax.inject.Named;
12 |
13 | import dagger.Module;
14 | import dagger.Provides;
15 |
16 | @Module
17 | public class QuestionModule {
18 |
19 | @Provides
20 | QuestionVMFactory questionVMFactory(GetSingleGameAndQuestionsInteractor getSingleGameAndQuestionsInteractor,
21 | UpdateQuestionAndGameInteractor updateQuestionInteractor,
22 | PointsInteractor pointsInteractor,
23 | StartRouter startRouter){
24 | return new QuestionVMFactory(getSingleGameAndQuestionsInteractor,
25 | updateQuestionInteractor,
26 | pointsInteractor,
27 | startRouter);
28 | }
29 |
30 | @Provides
31 | @Named("question")
32 | StartRouter startRouter(){
33 | return new StartRouter();
34 | }
35 |
36 | @Provides
37 | PointsInteractor pointsInteractor(GameRepositoryType gameRepositoryType,
38 | FirebaseDatabaseType firebaseDatabaseType,
39 | FirebaseAuthRepositoryType firebaseAuthRepositoryType){
40 | return new PointsInteractor(gameRepositoryType, firebaseDatabaseType, firebaseAuthRepositoryType);
41 | }
42 |
43 | @Provides
44 | UpdateQuestionAndGameInteractor updateQuestionInteractor(GameRepositoryType gameRepositoryType){
45 | return new UpdateQuestionAndGameInteractor(gameRepositoryType);
46 | }
47 |
48 | @Provides
49 | GetSingleGameAndQuestionsInteractor getSingleGameAndQuestionsInteractor(GameRepositoryType gameRepositoryType){
50 | return new GetSingleGameAndQuestionsInteractor(gameRepositoryType);
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/entity/IntroData.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.entity;
2 |
3 | import android.net.Uri;
4 | import android.os.Parcel;
5 | import android.os.Parcelable;
6 |
7 | public class IntroData implements Parcelable {
8 |
9 | private final String category;
10 | private final String difficulty;
11 | private final Uri photoUrl;
12 | private final int answered;
13 |
14 | public IntroData(String category, String difficulty, Uri photoUrl, int answered) {
15 | this.category = category;
16 | this.difficulty = difficulty;
17 | this.photoUrl = photoUrl;
18 | this.answered = answered;
19 | }
20 |
21 | protected IntroData(Parcel in) {
22 | category = in.readString();
23 | difficulty = in.readString();
24 | photoUrl = in.readParcelable(Uri.class.getClassLoader());
25 | answered = in.readInt();
26 | }
27 |
28 | @Override
29 | public void writeToParcel(Parcel dest, int flags) {
30 | dest.writeString(category);
31 | dest.writeString(difficulty);
32 | dest.writeParcelable(photoUrl, flags);
33 | dest.writeInt(answered);
34 | }
35 |
36 | @Override
37 | public int describeContents() {
38 | return 0;
39 | }
40 |
41 | public static final Creator CREATOR = new Creator() {
42 | @Override
43 | public IntroData createFromParcel(Parcel in) {
44 | return new IntroData(in);
45 | }
46 |
47 | @Override
48 | public IntroData[] newArray(int size) {
49 | return new IntroData[size];
50 | }
51 | };
52 |
53 | public String getCategory() {
54 | return category;
55 | }
56 |
57 | public String getDifficulty() {
58 | return difficulty;
59 | }
60 |
61 | public Uri getPhotoUrl() {
62 | return photoUrl;
63 | }
64 |
65 | public int getAnswered() {
66 | return answered;
67 | }
68 |
69 | @Override
70 | public String toString() {
71 | return "IntroData{" +
72 | "category='" + category + '\'' +
73 | ", difficulty='" + difficulty + '\'' +
74 | ", photoUrl=" + photoUrl +
75 | ", answered=" + answered +
76 | '}';
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/app/src/test/java/com/example/qwez/interactor/GetAllGamesInteractorTest.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.interactor;
2 |
3 | import com.example.qwez.RxResources;
4 | import com.example.qwez.repository.local.GameRepositoryType;
5 | import com.example.qwez.repository.local.entity.Game;
6 |
7 | import org.junit.Before;
8 | import org.junit.ClassRule;
9 | import org.junit.Test;
10 | import org.junit.runner.RunWith;
11 | import org.junit.runners.JUnit4;
12 | import org.mockito.InjectMocks;
13 | import org.mockito.Mock;
14 | import org.mockito.MockitoAnnotations;
15 |
16 | import java.util.ArrayList;
17 | import java.util.List;
18 |
19 | import io.reactivex.Flowable;
20 |
21 | import static org.junit.Assert.assertEquals;
22 | import static org.mockito.Mockito.verify;
23 | import static org.mockito.Mockito.when;
24 |
25 | @RunWith(JUnit4.class)
26 | public class GetAllGamesInteractorTest {
27 |
28 | @ClassRule
29 | public static RxResources rxres = new RxResources();
30 |
31 | @Mock
32 | GameRepositoryType gameRepositoryType;
33 |
34 | @Mock
35 | Throwable error;
36 |
37 | @InjectMocks
38 | GetAllGamesInteractor interactor;
39 |
40 | @Before
41 | public void setUp() throws Exception {
42 | MockitoAnnotations.initMocks(this);
43 | }
44 |
45 | @Test
46 | public void getAllGames() {
47 | Game game = new Game("1", "2");
48 | Game game2 = new Game("3", "3");
49 | List listOfGames = new ArrayList<>();
50 | listOfGames.add(game);
51 | listOfGames.add(game2);
52 |
53 | when(gameRepositoryType.getAllGames()).thenReturn(Flowable.just(listOfGames));
54 |
55 | int size = interactor.getAllGames()
56 | .test()
57 | .assertValueCount(1)
58 | .values()
59 | .get(0)
60 | .size();
61 |
62 | assertEquals(2, size);
63 | verify(gameRepositoryType).getAllGames();
64 | }
65 |
66 | @Test
67 | public void getAllGamesError() {
68 | when(gameRepositoryType.getAllGames()).thenReturn(Flowable.error(error));
69 |
70 | interactor.getAllGames()
71 | .test()
72 | .assertNoValues()
73 | .assertError(error);
74 |
75 | verify(gameRepositoryType).getAllGames();
76 | }
77 | }
--------------------------------------------------------------------------------
/app/src/main/res/xml/preferences.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 |
14 |
15 |
22 |
23 |
30 |
31 |
38 |
39 |
40 |
41 |
43 |
44 |
51 |
52 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/repository/sharedpref/SharedPreferencesRepository.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.repository.sharedpref;
2 |
3 | import android.content.SharedPreferences;
4 |
5 | import io.reactivex.Completable;
6 | import io.reactivex.Single;
7 | import io.reactivex.schedulers.Schedulers;
8 |
9 | /**
10 | * Sharedpreferences repository
11 | */
12 | public class SharedPreferencesRepository implements SharedPreferencesRepositoryType {
13 |
14 | private final SharedPreferences prefs;
15 |
16 | private static final String FIRST_TIME = "first time";
17 | private static final String REMEMBER = "remember";
18 | private static final String IS_REMEMBER = "is remember";
19 |
20 | public SharedPreferencesRepository(SharedPreferences prefs) {
21 | this.prefs = prefs;
22 | }
23 |
24 | @Override
25 | public Single getNotFirstTime() {
26 | return Single.fromCallable(() -> prefs.getBoolean(FIRST_TIME, false))
27 | .subscribeOn(Schedulers.io());
28 | }
29 |
30 | @Override
31 | public Completable setNotFirstTime(boolean setTo) {
32 | return Completable.fromAction( () -> prefs.edit().putBoolean(FIRST_TIME, setTo).apply())
33 | .subscribeOn(Schedulers.io());
34 | }
35 |
36 | @Override
37 | public Single isRemembered() {
38 | return Single.fromCallable(() -> prefs.getBoolean(IS_REMEMBER, false))
39 | .subscribeOn(Schedulers.io());
40 | }
41 |
42 | @Override
43 | public Single getRemembered() {
44 | return Single.fromCallable(() -> prefs.getString(REMEMBER, null))
45 | .subscribeOn(Schedulers.io());
46 | }
47 |
48 | @Override
49 | public Completable setRemembered(String toRemember) {
50 | return Completable.fromAction(() -> prefs.edit().putString(REMEMBER, toRemember).apply())
51 | .subscribeOn(Schedulers.io());
52 | }
53 |
54 | @Override
55 | public Completable setNotRemember() {
56 | return Completable.fromAction(() -> prefs.edit().remove(REMEMBER).apply())
57 | .subscribeOn(Schedulers.io());
58 | }
59 |
60 | @Override
61 | public Completable setIsRemember(boolean remember) {
62 | return Completable.fromAction(() -> prefs.edit().putBoolean(IS_REMEMBER, remember).apply());
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_splash.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
21 |
22 |
23 |
36 |
37 |
38 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/repository/ApiOperator.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.repository;
2 |
3 | import com.example.qwez.repository.opentdb.entity.ResponseBody;
4 |
5 | import io.reactivex.ObservableOperator;
6 | import io.reactivex.Observer;
7 | import io.reactivex.observers.DisposableObserver;
8 | import retrofit2.Response;
9 | import timber.log.Timber;
10 |
11 | /**
12 | * Custom ObservableOperator that takes in Retrofit Response.
13 | *
14 | * Disposes to not break operator chain, and subscribes observer to this.
15 | *
16 | * Parses T data from Response
17 | *
18 | * Emits onError() if Retrofit response was not successful
19 | *
20 | * @param Object type
21 | */
22 | public final class ApiOperator implements ObservableOperator> {
23 |
24 | private static final int RESPONSE_CODE_ERROR = 1;
25 |
26 | @Override
27 | public Observer super Response> apply(final Observer super T> observer) throws Exception {
28 | return new DisposableObserver>() {
29 |
30 | @Override
31 | public void onStart() {
32 | observer.onSubscribe(this);
33 | }
34 |
35 | @Override
36 | public void onNext(Response tResponse) {
37 | dispose();
38 | if(!tResponse.isSuccessful()) {
39 | observer.onError(new Exception("ApiOperator " + tResponse.message()));
40 | }else{
41 | ResponseBody responseBody = (ResponseBody) tResponse.body();
42 | if (responseBody != null && responseBody.getResponseCode()==RESPONSE_CODE_ERROR) {
43 | observer.onError(new Exception("ApiOperator empty QuestionList"));
44 | }else{
45 | observer.onNext(tResponse.body());
46 | observer.onComplete();
47 | }
48 | }
49 | }
50 |
51 | @Override
52 | public void onError(Throwable e) {
53 | if (!isDisposed()) {
54 | observer.onError(new Throwable("ApiOperator: "+e.getMessage()));
55 | }
56 | }
57 |
58 | @Override
59 | public void onComplete() {
60 | if (!isDisposed()) {
61 | observer.onComplete();
62 | }
63 | }
64 | };
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/ui/settings/SettingsVMFactory.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.ui.settings;
2 |
3 | import androidx.annotation.NonNull;
4 | import androidx.lifecycle.ViewModel;
5 | import androidx.lifecycle.ViewModelProvider;
6 |
7 | import com.example.qwez.interactor.ChangeProfilePhotoInteractor;
8 | import com.example.qwez.interactor.ChangeUserNickInteractor;
9 | import com.example.qwez.interactor.ChangeUserPasswordInteractor;
10 | import com.example.qwez.interactor.DeleteAccountInteractor;
11 | import com.example.qwez.interactor.LogoutUserInteractor;
12 | import com.example.qwez.router.LoginRouter;
13 |
14 | public class SettingsVMFactory implements ViewModelProvider.Factory {
15 |
16 | private final LogoutUserInteractor logoutUserInteractor;
17 | private final ChangeUserPasswordInteractor changeUserPasswordInteractor;
18 | private final LoginRouter loginRouter;
19 | private final ChangeUserNickInteractor changeUserNickInteractor;
20 | private final ChangeProfilePhotoInteractor changeProfilePhotoInteractor;
21 | private final DeleteAccountInteractor deleteAccountInteractor;
22 |
23 | SettingsVMFactory(LogoutUserInteractor logoutUserInteractor,
24 | ChangeUserPasswordInteractor changeUserPasswordInteractor,
25 | LoginRouter loginRouter,
26 | ChangeUserNickInteractor changeUserNickInteractor,
27 | ChangeProfilePhotoInteractor changeProfilePhotoInteractor,
28 | DeleteAccountInteractor deleteAccountInteractor) {
29 | this.logoutUserInteractor = logoutUserInteractor;
30 | this.changeUserPasswordInteractor = changeUserPasswordInteractor;
31 | this.loginRouter = loginRouter;
32 | this.changeUserNickInteractor = changeUserNickInteractor;
33 | this.changeProfilePhotoInteractor = changeProfilePhotoInteractor;
34 | this.deleteAccountInteractor = deleteAccountInteractor;
35 | }
36 |
37 | @SuppressWarnings("unchecked")
38 | @NonNull
39 | @Override
40 | public T create(@NonNull Class modelClass) {
41 | return (T) new SettingsViewModel(logoutUserInteractor,
42 | changeUserPasswordInteractor,
43 | loginRouter,
44 | changeUserNickInteractor,
45 | changeProfilePhotoInteractor,
46 | deleteAccountInteractor);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/di/FirebaseModule.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.di;
2 |
3 | import com.example.qwez.repository.firebase.FirebaseAuthRepository;
4 | import com.example.qwez.repository.firebase.FirebaseAuthRepositoryType;
5 | import com.example.qwez.repository.firebase.FirebaseDatabase;
6 | import com.example.qwez.repository.firebase.FirebaseDatabaseType;
7 | import com.example.qwez.repository.firebase.FirebaseStorageRepository;
8 | import com.example.qwez.repository.firebase.FirebaseStorageRepositoryType;
9 | import com.google.firebase.auth.FirebaseAuth;
10 | import com.google.firebase.firestore.FirebaseFirestore;
11 | import com.google.firebase.storage.FirebaseStorage;
12 | import com.google.firebase.storage.StorageReference;
13 |
14 | import dagger.Module;
15 | import dagger.Provides;
16 |
17 | /**
18 | * Provides FireBaseAuth
19 | */
20 | @Module
21 | public class FirebaseModule {
22 |
23 | @Provides
24 | @ApplicationScope
25 | FirebaseDatabaseType firebaseDatabaseType(FirebaseFirestore firebaseFirestore){
26 | return new FirebaseDatabase(firebaseFirestore);
27 | }
28 |
29 | @Provides
30 | @ApplicationScope
31 | FirebaseFirestore firebaseFirestore(){
32 | return FirebaseFirestore.getInstance();
33 | }
34 |
35 | @Provides
36 | @ApplicationScope
37 | FirebaseStorageRepositoryType firebaseStorageRepositoryType(StorageReference storageReference){
38 | return new FirebaseStorageRepository(storageReference);
39 | }
40 |
41 | @Provides
42 | @ApplicationScope
43 | StorageReference storageReference(FirebaseStorage firebaseStorage){
44 | return firebaseStorage.getReference();
45 | }
46 |
47 | @Provides
48 | @ApplicationScope
49 | FirebaseStorage firebaseStorage(){
50 | return FirebaseStorage.getInstance();
51 | }
52 |
53 | /**
54 | * Get FirebaseAuthRepository. Singleton.
55 | * @param firebaseAuth Dagger Provided
56 | * @return FirebaseAuthRepository
57 | */
58 | @Provides
59 | @ApplicationScope
60 | FirebaseAuthRepositoryType firebaseAuthRepositoryType(FirebaseAuth firebaseAuth){
61 | return new FirebaseAuthRepository(firebaseAuth);
62 | }
63 |
64 | /**
65 | * Get FirebaseAuth. Singleton
66 | * @return FirebaseAuth
67 | */
68 | @Provides
69 | @ApplicationScope
70 | FirebaseAuth firebaseAuth(){
71 | return FirebaseAuth.getInstance();
72 | }
73 |
74 | }
75 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/repository/firebase/rxwrapper/MaybeTask.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.repository.firebase.rxwrapper;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import com.google.android.gms.tasks.OnCompleteListener;
6 | import com.google.android.gms.tasks.OnFailureListener;
7 | import com.google.android.gms.tasks.OnSuccessListener;
8 | import com.google.android.gms.tasks.Task;
9 |
10 | import io.reactivex.MaybeEmitter;
11 |
12 | /**
13 | * Wraps Task into a MaybeEmitter super T>
14 | */
15 | public final class MaybeTask implements OnSuccessListener, OnFailureListener, OnCompleteListener {
16 |
17 | private final MaybeEmitter super T> emitter;
18 |
19 | private MaybeTask(MaybeEmitter super T> emitter) {
20 | this.emitter = emitter;
21 | }
22 |
23 | /**
24 | * assign Task to be wrapped, to wrap in a MaybeEmitter super T>
25 | * @param emitter MaybeEmitter super T> wrapper
26 | * @param task Task to be wrapped
27 | * @param Task type
28 | */
29 | public static void assign(MaybeEmitter super T> emitter, Task task) {
30 | MaybeTask handler = new MaybeTask<>(emitter);
31 | task.addOnSuccessListener(handler);
32 | task.addOnFailureListener(handler);
33 | task.addOnCompleteListener(handler);
34 | }
35 |
36 |
37 | /**
38 | * Calls MaybeEmitter.onComplete when Task.onComplete() is called.
39 | * Calls and checks !MaybeEmitter.isDisposed
40 | * @param task wrapped Task
41 | */
42 | @Override
43 | public void onComplete(@NonNull Task task) {
44 | if (!emitter.isDisposed()){
45 | emitter.onComplete();
46 | }
47 | }
48 |
49 | /**
50 | * Calls MaybeEmitter.onError(Throwable) when Task.onFailure(Exception) is called.
51 | * Calls and checks !MaybeEmitter.isDisposed
52 | * @param e Exception thrown
53 | */
54 | @Override
55 | public void onFailure(@NonNull Exception e) {
56 | if (!emitter.isDisposed()){
57 | emitter.onError(e);
58 | }
59 | }
60 |
61 | /**
62 | * Calls MaybeEmitter.onSuccess when Task.onSuccess() is called.
63 | * Calls and checks !MaybeEmitter.isDisposed
64 | * @param t Generic value returned by Task
65 | */
66 | @Override
67 | public void onSuccess(T t) {
68 | if (!emitter.isDisposed()){
69 | emitter.onSuccess(t);
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/di/NetworkModule.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.di;
2 |
3 | import com.example.qwez.repository.opentdb.OpenTDBAPI;
4 | import com.example.qwez.util.UrlConstant;
5 | import com.google.gson.Gson;
6 |
7 | import dagger.Module;
8 | import dagger.Provides;
9 | import okhttp3.OkHttpClient;
10 | import okhttp3.logging.HttpLoggingInterceptor;
11 | import retrofit2.Retrofit;
12 | import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
13 | import retrofit2.converter.gson.GsonConverterFactory;
14 |
15 | @Module
16 | public class NetworkModule {
17 |
18 | /**
19 | * Get OpenTDBAPI. Singleton
20 | * @param retrofit Dagger provided
21 | * @return OpenTDBAPI
22 | */
23 | @Provides
24 | @ApplicationScope
25 | OpenTDBAPI openTDBAPI(Retrofit retrofit){
26 | return retrofit.create(OpenTDBAPI.class);
27 | }
28 |
29 | /**
30 | * Get Retrofit. Singleton
31 | * @param okHttpClient Dagger provided
32 | * @param gson Dagger provided
33 | * @return Retrofit
34 | */
35 | @Provides
36 | @ApplicationScope
37 | Retrofit retrofit(OkHttpClient okHttpClient, Gson gson){
38 | return new Retrofit.Builder()
39 | .baseUrl(UrlConstant.URL_END_POINT)
40 | .client(okHttpClient)
41 | .addConverterFactory(GsonConverterFactory.create(gson))
42 | .addCallAdapterFactory(RxJava2CallAdapterFactory.createAsync())
43 | .build();
44 | }
45 |
46 | /**
47 | *Get OkHttpClient. Singleton
48 | * @param httpLoggingInterceptor Dagger provided
49 | * @return OkHttpClient
50 | */
51 | @Provides
52 | @ApplicationScope
53 | OkHttpClient okHttpClient(HttpLoggingInterceptor httpLoggingInterceptor){
54 | return new OkHttpClient()
55 | .newBuilder()
56 | .addInterceptor(httpLoggingInterceptor)
57 | .build();
58 | }
59 |
60 | /**
61 | * Get HttpLoggingInterceptor. Singleton
62 | * @return HttpLoggingInterceptor
63 | */
64 | @Provides
65 | @ApplicationScope
66 | HttpLoggingInterceptor httpLoggingInterceptor(){
67 | return new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.HEADERS);
68 | }
69 |
70 | /**
71 | * Get Gson. Singleton
72 | * @return Gson
73 | */
74 | @Provides
75 | @ApplicationScope
76 | Gson gson(){
77 | return new Gson();
78 | }
79 |
80 | }
81 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/ui/start/StartVMFactory.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.ui.start;
2 |
3 | import androidx.annotation.NonNull;
4 | import androidx.lifecycle.ViewModel;
5 | import androidx.lifecycle.ViewModelProvider;
6 |
7 | import com.example.qwez.interactor.DeleteGameInteractor;
8 | import com.example.qwez.interactor.FetchQuestionsInteractor;
9 | import com.example.qwez.interactor.GetAllGamesInteractor;
10 | import com.example.qwez.interactor.GetUserInteractor;
11 | import com.example.qwez.router.HighscoreRouter;
12 | import com.example.qwez.router.QuestionRouter;
13 | import com.example.qwez.router.SettingsRouter;
14 |
15 | public class StartVMFactory implements ViewModelProvider.Factory {
16 |
17 | private final FetchQuestionsInteractor getQuestionsInteractor;
18 | private final GetAllGamesInteractor getAllGamesInteractor;
19 | private final GetUserInteractor getUserInteractor;
20 | private final SettingsRouter settingsRouter;
21 | private final DeleteGameInteractor deleteGameInteractor;
22 | private final QuestionRouter questionRouter;
23 | private final HighscoreRouter highscoreRouter;
24 |
25 | StartVMFactory(FetchQuestionsInteractor getQuestionsInteractor,
26 | GetAllGamesInteractor getAllGamesInteractor,
27 | GetUserInteractor getUserInteractor,
28 | SettingsRouter settingsRouter,
29 | DeleteGameInteractor deleteGameInteractor,
30 | QuestionRouter questionRouter,
31 | HighscoreRouter highscoreRouter) {
32 | this.getQuestionsInteractor = getQuestionsInteractor;
33 | this.getAllGamesInteractor = getAllGamesInteractor;
34 | this.getUserInteractor = getUserInteractor;
35 | this.settingsRouter = settingsRouter;
36 | this.deleteGameInteractor = deleteGameInteractor;
37 | this.questionRouter = questionRouter;
38 | this.highscoreRouter = highscoreRouter;
39 | }
40 |
41 | @SuppressWarnings("unchecked")
42 | @NonNull
43 | @Override
44 | public T create(@NonNull Class modelClass) {
45 | return (T) new StartViewModel(getQuestionsInteractor,
46 | getAllGamesInteractor,
47 | getUserInteractor,
48 | settingsRouter,
49 | deleteGameInteractor,
50 | questionRouter,
51 | highscoreRouter);
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/repository/firebase/rxwrapper/CompletableTask.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.repository.firebase.rxwrapper;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import com.google.android.gms.tasks.OnCompleteListener;
6 | import com.google.android.gms.tasks.OnFailureListener;
7 | import com.google.android.gms.tasks.OnSuccessListener;
8 | import com.google.android.gms.tasks.Task;
9 |
10 | import io.reactivex.CompletableEmitter;
11 |
12 | /**
13 | * Wraps Task into a CompletableEmitter.
14 | */
15 | public final class CompletableTask implements OnSuccessListener, OnFailureListener, OnCompleteListener {
16 |
17 | private final CompletableEmitter emitter;
18 |
19 | private CompletableTask(CompletableEmitter emitter) {
20 | this.emitter = emitter;
21 | }
22 |
23 | /**
24 | * assign Task to be wrapped, to wrap in a CompletableEmitter
25 | * @param emitter CompletableEmitter wrapper
26 | * @param task Task to be wrapped
27 | * @param Task type
28 | */
29 | public static void assign(CompletableEmitter emitter, Task task){
30 | CompletableTask completeTask = new CompletableTask(emitter);
31 | task.addOnCompleteListener(completeTask);
32 | task.addOnSuccessListener(completeTask);
33 | task.addOnFailureListener(completeTask);
34 | }
35 |
36 | /**
37 | * Calls CompletableEmitter.onComplete when Task.onComplete() is called.
38 | * Calls and checks !CompletableEmitter.isDisposed
39 | * @param task wrapped Task
40 | */
41 | @Override
42 | public void onComplete(@NonNull Task task) {
43 | if(!emitter.isDisposed()){
44 | emitter.onComplete();
45 | }
46 | }
47 |
48 | /**
49 | * Calls CompletableEmitter.onError(Throwable) when Task.onFailure(Exception) is called.
50 | *Calls and checks !CompletableEmitter.isDisposed
51 | * @param e Exception thrown
52 | */
53 | @Override
54 | public void onFailure(@NonNull Exception e) {
55 | if(!emitter.isDisposed()){
56 | emitter.onError(e);
57 | }
58 | }
59 |
60 | /**
61 | * Calls CompletableEmitter.onComplete() when Task.onSuccess(Object) is called.
62 | * Calls and checks !CompletableEmitter.isDisposed
63 | * @param o Object returned by Task
64 | */
65 | @Override
66 | public void onSuccess(Object o) {
67 | if(!emitter.isDisposed()){
68 | emitter.onComplete();
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_change_pass.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
17 |
18 |
24 |
25 |
26 |
27 |
34 |
35 |
41 |
42 |
43 |
44 |
51 |
52 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/qwez/interactor/FetchQuestionsInteractor.java:
--------------------------------------------------------------------------------
1 | package com.example.qwez.interactor;
2 |
3 | import com.example.qwez.repository.local.GameRepositoryType;
4 | import com.example.qwez.repository.local.entity.Game;
5 | import com.example.qwez.repository.opentdb.OpenTDBType;
6 | import com.example.qwez.util.Category;
7 | import com.example.qwez.util.Difficulty;
8 | import com.example.qwez.util.QuestionC;
9 | import com.example.qwez.util.QuestionUtil;
10 | import com.example.qwez.util.QuestionType;
11 |
12 | import io.reactivex.Completable;
13 | import io.reactivex.android.schedulers.AndroidSchedulers;
14 | import timber.log.Timber;
15 |
16 | /**
17 | * Interactor to get Questions from API, and store them in local database
18 | */
19 | public class FetchQuestionsInteractor {
20 |
21 | private final OpenTDBType openTDBType;
22 | private final GameRepositoryType gameRepositoryType;
23 |
24 | public FetchQuestionsInteractor(OpenTDBType openTDBType, GameRepositoryType gameRepositoryType) {
25 | this.openTDBType = openTDBType;
26 | this.gameRepositoryType = gameRepositoryType;
27 | }
28 |
29 | /**
30 | * Get {@link com.example.qwez.repository.opentdb.entity.Question} Question from API,
31 | * convert it to an {@link com.example.qwez.repository.opentdb.entity.Question} object, and
32 | * store in the database.
33 | * @param category Question category
34 | * @param difficulty Question difficulty
35 | * @return Single that emitts a List of all Question objects in the local database, including the newly
36 | * returned Question from API
37 | */
38 | public Completable getQuestionByCategoryMultiple(Category category, Difficulty difficulty){
39 | return openTDBType.getQuestionByCategory(QuestionC.AMOUNT_STANDARD,
40 | category.getCategory(),
41 | difficulty.getDifficulty(),
42 | QuestionType.MULTIPLE_CHOICE.getType())
43 | .map(QuestionUtil::toDatabase)
44 | .flatMap(questions -> gameRepositoryType.addGameReturnId(new Game(Category.getAsString(category),Difficulty.getAsString(difficulty)))
45 | .map(aLong -> {
46 | questions.forEach(question -> question.setqId((int) (long)aLong));
47 | return questions;
48 | }))
49 | .flatMapCompletable(gameRepositoryType::addQuestions)
50 | .observeOn(AndroidSchedulers.mainThread());
51 |
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------