├── .github └── FUNDING.yml ├── .gitignore ├── .idea ├── .gitignore ├── .name ├── codeStyles ├── compiler.xml ├── gradle.xml ├── kotlinc.xml ├── misc.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── coding │ │ └── meet │ │ └── mathquizapp │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── assets │ │ ├── encryptedQuestion.json │ │ └── question.json │ ├── cpp │ │ ├── CMakeLists.txt │ │ └── native-lib.cpp │ ├── java │ │ └── com │ │ │ └── coding │ │ │ └── meet │ │ │ └── mathquizapp │ │ │ ├── LevelActivity.kt │ │ │ ├── QuestionActivity.kt │ │ │ ├── WelcomeActivity.kt │ │ │ ├── adapters │ │ │ └── LevelAdapter.kt │ │ │ ├── models │ │ │ ├── QuestionItem.kt │ │ │ └── QuestionSplit.kt │ │ │ └── util │ │ │ ├── CustomCountdownTimer.kt │ │ │ ├── SecurityManger.kt │ │ │ ├── SharedPreferenceManger.kt │ │ │ └── utils.kt │ └── res │ │ ├── anim │ │ └── zoom_in_cut.xml │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── app_banner_with_background.xml │ │ ├── app_icon_with_an_icon_background.xml │ │ ├── circular_progress_bar.xml │ │ ├── ic_back.xml │ │ ├── ic_done.xml │ │ ├── ic_launcher_background.xml │ │ ├── ic_lock.xml │ │ └── logo.png │ │ ├── font │ │ └── ubuntu_mono_regular.ttf │ │ ├── layout │ │ ├── activity_level.xml │ │ ├── activity_question.xml │ │ ├── activity_welcome.xml │ │ ├── game_exit_dialog.xml │ │ ├── game_score_next_round_dialog.xml │ │ ├── level_layout.xml │ │ └── toolbar_layout.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── raw │ │ ├── gameover.mp3 │ │ ├── right.mp3 │ │ └── tick.mp3 │ │ ├── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── themes.xml │ │ └── xml │ │ ├── backup_rules.xml │ │ └── data_extraction_rules.xml │ └── test │ └── java │ └── com │ └── coding │ └── meet │ └── mathquizapp │ └── ExampleUnitTest.kt ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── screenshot ├── img1.png ├── img2.png ├── img3.png └── img4.png └── settings.gradle /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | buy_me_a_coffee: codingmeet 2 | # # These are supported funding model platforms 3 | 4 | # github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 5 | # patreon: # Replace with a single Patreon username 6 | # open_collective: # Replace with a single Open Collective username 7 | # ko_fi: # Replace with a single Ko-fi username 8 | # tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 9 | # community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 10 | # liberapay: # Replace with a single Liberapay username 11 | # issuehunt: # Replace with a single IssueHunt username 12 | # lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | # polar: # Replace with a single Polar username 14 | # buy_me_a_coffee: # Replace with a single Buy Me a Coffee username 15 | # thanks_dev: # Replace with a single thanks.dev username 16 | # custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 17 | -------------------------------------------------------------------------------- /.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 | .cxx 15 | local.properties 16 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | Math Quiz App -------------------------------------------------------------------------------- /.idea/codeStyles: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 22 | 23 | 24 | 25 | 27 | 28 | 29 |
30 | 31 | 32 | 33 | xmlns:android 34 | 35 | ^$ 36 | 37 | 38 | 39 |
40 |
41 | 42 | 43 | 44 | xmlns:.* 45 | 46 | ^$ 47 | 48 | 49 | BY_NAME 50 | 51 |
52 |
53 | 54 | 55 | 56 | .*:id 57 | 58 | http://schemas.android.com/apk/res/android 59 | 60 | 61 | 62 |
63 |
64 | 65 | 66 | 67 | .*:name 68 | 69 | http://schemas.android.com/apk/res/android 70 | 71 | 72 | 73 |
74 |
75 | 76 | 77 | 78 | name 79 | 80 | ^$ 81 | 82 | 83 | 84 |
85 |
86 | 87 | 88 | 89 | style 90 | 91 | ^$ 92 | 93 | 94 | 95 |
96 |
97 | 98 | 99 | 100 | .* 101 | 102 | ^$ 103 | 104 | 105 | BY_NAME 106 | 107 |
108 |
109 | 110 | 111 | 112 | .* 113 | 114 | http://schemas.android.com/apk/res/android 115 | 116 | 117 | ANDROID_ATTRIBUTE_ORDER 118 | 119 |
120 |
121 | 122 | 123 | 124 | .* 125 | 126 | .* 127 | 128 | 129 | BY_NAME 130 | 131 |
132 |
133 |
134 |
135 |
136 |
137 |
-------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 13 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Math Quiz App Android Studio Kotlin - Video Tutorial Series 2 | 3 | Welcome to the Math Quiz App development tutorial series! In this series, we'll guide you through the process of creating a Math Quiz App using Android Studio and Kotlin. Each part of the series covers a specific aspect of app development. 4 | 5 | ## Overview 6 | 7 | Building a Math Quiz App can be both fun and educational. This series is designed to help you grasp fundamental concepts in Android development while creating an engaging and interactive application. The videos are structured to take you through the entire development lifecycle, from designing screens to implementing advanced features like encryption and API key security. 8 | 9 | Whether you're a beginner or an experienced developer, this tutorial series will provide valuable insights and hands-on experience. By the end of the series, you'll have a fully functional Math Quiz App with enhanced security features. 10 | 11 | ## Youtube Playlist [Link Here](https://youtube.com/playlist?list=PLlSuJy9SfzvHpc1-IcBTbZnyeBoVCynPE) 12 | 13 | 1. **Part 1: Design Welcome or Splash Screen** 14 | Learn to design an engaging welcome or splash screen for your app. 15 | 16 | 3. **Part 2: Design Toolbar and Level Screen** 17 | Dive into designing the toolbar and level screen elements for your app. 18 | 19 | 4. **Part 3: Design Question Screen** 20 | Explore the process of designing the question screen in your Math Quiz App. 21 | 22 | 5. **Part 4: Design Score and Exit Dialog** 23 | Create intuitive score and exit dialogs to enhance user experience. 24 | 25 | 6. **Part 5: Read JSON File and Countdown Timer** 26 | Implement reading data from a JSON file and adding a countdown timer. 27 | 28 | 7. **Part 6: Choose Answer and Calculate Points** 29 | Learn to allow users to choose answers and calculate points dynamically. 30 | 31 | 8. **How to Encrypt and Decrypt a File in Android Studio Kotlin** 32 | Understand the essentials of file encryption and decryption in Android Studio using Kotlin. 33 | 34 | 9. **Part 7: Question.json Encrypt & Decrypt File** 35 | Implement encryption and decryption for the Question.json file in your app. 36 | 37 | 10. **Part 8: Two Option Remove** 38 | Explore how to implement a feature to remove two options for a question. 39 | 40 | 11. **How to Secure API Key or String Using Library Android Studio Kotlin** 41 | Securely manage API keys and strings using libraries in Android Studio with Kotlin. 42 | 43 | 12. **How to Secure API Key or String Using NDK Android Studio Kotlin** 44 | Enhance security by utilizing the Native Development Kit (NDK) for API key and string protection. 45 | 46 | 13. **How to Secure API Key or Text Using NDK and Encryption Android Studio Kotlin** 47 | Implement NDK and encryption for added security in managing API keys and text. 48 | 49 | 14. **Part 9: All Password Encryption using NDK and Library** 50 | Securely handle all password-related operations through a combination of NDK and library usage. 51 | 52 | ## Getting Started 53 | 54 | Make sure to follow the videos in order for a seamless learning experience. Each video builds on the previous one, helping you understand the complete app development process. 55 | 56 | ## Screenshots 57 | 58 | ![Math Quiz App](screenshot/img1.png) 59 | ![Math Quiz App](screenshot/img2.png) 60 | ![Math Quiz App](screenshot/img3.png) 61 | ![Math Quiz App](screenshot/img4.png) 62 | 63 | ## How to Use This Repository 64 | 65 | - Clone the repository to your local machine. 66 | - Follow along with the video tutorials. 67 | - For videos involving encryption, find the relevant code and explanations in the corresponding folders. 68 | 69 | 70 | ## Support the Project 71 | 72 | If you find this tutorial series helpful and would like to support the development of more content, consider buying me a coffee! Your support helps in creating high-quality tutorials. 73 | 74 | [![Buy Me a Coffee](https://img.shields.io/badge/Buy%20Me%20a%20Coffee-Donate-orange?style=for-the-badge&logo=buy-me-a-coffee)](https://www.buymeacoffee.com/codingmeet) 75 | 76 | Your generosity is greatly appreciated! Thank you for supporting this project. 77 | 78 | Happy coding! 79 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'org.jetbrains.kotlin.android' 4 | } 5 | 6 | android { 7 | namespace 'com.coding.meet.mathquizapp' 8 | compileSdk 33 9 | 10 | defaultConfig { 11 | applicationId "com.coding.meet.mathquizapp" 12 | minSdk 21 13 | targetSdk 33 14 | versionCode 1 15 | versionName "1.0" 16 | 17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 18 | 19 | // buildConfigField("String","KEYPASSWORD",KEYPASSWORD) 20 | // buildConfigField("String","IVPASSWORD",IVPASSWORD) 21 | // buildConfigField("String","ALGORITHM",ALGORITHM) 22 | // buildConfigField("String","TRANSFORMATION",TRANSFORMATION) 23 | } 24 | 25 | buildTypes { 26 | release { 27 | minifyEnabled false 28 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 29 | } 30 | } 31 | compileOptions { 32 | sourceCompatibility JavaVersion.VERSION_1_8 33 | targetCompatibility JavaVersion.VERSION_1_8 34 | } 35 | kotlinOptions { 36 | jvmTarget = '1.8' 37 | } 38 | buildFeatures{ 39 | viewBinding true 40 | // buildConfig = true 41 | } 42 | 43 | externalNativeBuild { 44 | cmake{ 45 | path "src/main/cpp/CMakeLists.txt" 46 | } 47 | } 48 | } 49 | 50 | dependencies { 51 | 52 | implementation 'androidx.core:core-ktx:1.9.0' 53 | implementation 'androidx.appcompat:appcompat:1.6.1' 54 | implementation 'com.google.android.material:material:1.9.0' 55 | implementation 'androidx.constraintlayout:constraintlayout:2.1.4' 56 | implementation "androidx.core:core-splashscreen:1.0.1" 57 | implementation 'com.scwang.wave:MultiWaveHeader:1.0.0-andx' 58 | 59 | implementation 'com.google.code.gson:gson:2.10.1' 60 | implementation 'com.cossacklabs.com:themis:0.13.1' 61 | 62 | testImplementation 'junit:junit:4.13.2' 63 | androidTestImplementation 'androidx.test.ext:junit:1.1.5' 64 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' 65 | } -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /app/src/androidTest/java/com/coding/meet/mathquizapp/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.coding.meet.mathquizapp 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.coding.meet.mathquizapp", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 15 | 18 | 21 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /app/src/main/assets/encryptedQuestion.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coding-Meet/Math-Quiz-App/26397a83f72a23958eb86a0a7919d87e05ff8aba/app/src/main/assets/encryptedQuestion.json -------------------------------------------------------------------------------- /app/src/main/cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10.2) 2 | add_library( 3 | native-lib 4 | SHARED 5 | native-lib.cpp 6 | ) 7 | target_link_libraries( 8 | native-lib 9 | ${log-lib} 10 | ) -------------------------------------------------------------------------------- /app/src/main/cpp/native-lib.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | 5 | 6 | extern "C" 7 | JNIEXPORT jstring JNICALL 8 | Java_com_coding_meet_mathquizapp_util_SecurityManger_00024Keys_Secretkey(JNIEnv *env, 9 | jobject thiz) { 10 | return env ->NewStringUTF("750GdiCY1GWXzRjH2W9esuGDGAzNNvbVbxfAAAN65mt5WrZwlz49KgpYzFhLQejd"); 11 | } 12 | extern "C" 13 | JNIEXPORT jstring JNICALL 14 | Java_com_coding_meet_mathquizapp_util_SecurityManger_00024Keys_ALGORITHM(JNIEnv *env, 15 | jobject thiz) { 16 | return env ->NewStringUTF("AAEBQAwAAAAQAAAAAwAAALvnpx7c0XDQrRduHM7PiEqcMD/GsEc/9x8iSXfYibg="); 17 | 18 | } 19 | extern "C" 20 | JNIEXPORT jstring JNICALL 21 | Java_com_coding_meet_mathquizapp_util_SecurityManger_00024Keys_TRANSFORMATION(JNIEnv *env, 22 | jobject thiz) { 23 | return env ->NewStringUTF("AAEBQAwAAAAQAAAAFAAAAMcSLgMkvQiCuBcuEdPNm6xoz2MQKFePFqxNIaNJqT966t6XjQ1kk7c/mXpGRXmQQA=="); 24 | 25 | } 26 | extern "C" 27 | JNIEXPORT jstring JNICALL 28 | Java_com_coding_meet_mathquizapp_util_SecurityManger_00024Keys_KEYPASSWORD(JNIEnv *env, 29 | jobject thiz) { 30 | return env ->NewStringUTF("AAEBQAwAAAAQAAAAEAAAAFsL9Ue1xFVrtHAiL+HWDMKDVEiLNbWSuJHiNSu9Dx66lTU/v3nrF8R+tgDH"); 31 | 32 | } 33 | extern "C" 34 | JNIEXPORT jstring JNICALL 35 | Java_com_coding_meet_mathquizapp_util_SecurityManger_00024Keys_IVPASSWORD(JNIEnv *env, 36 | jobject thiz) { 37 | return env ->NewStringUTF("AAEBQAwAAAAQAAAAEAAAAPoXst4aNssK95ZMZ/+sxlJH2fMAD5gvlPpVAr1IEuC4IaW5maom1jg1/LmH"); 38 | 39 | } -------------------------------------------------------------------------------- /app/src/main/java/com/coding/meet/mathquizapp/LevelActivity.kt: -------------------------------------------------------------------------------- 1 | package com.coding.meet.mathquizapp 2 | 3 | import android.media.MediaPlayer 4 | import androidx.appcompat.app.AppCompatActivity 5 | import android.os.Bundle 6 | import com.coding.meet.mathquizapp.adapters.LevelAdapter 7 | import com.coding.meet.mathquizapp.databinding.ActivityLevelBinding 8 | import com.coding.meet.mathquizapp.util.SharedPreferenceManger 9 | 10 | class LevelActivity : AppCompatActivity() { 11 | private val levelBinding: ActivityLevelBinding by lazy { 12 | ActivityLevelBinding.inflate(layoutInflater) 13 | } 14 | private val tickMusic: MediaPlayer by lazy { 15 | MediaPlayer.create(this, R.raw.tick) 16 | } 17 | private val sharedPreferenceManger: SharedPreferenceManger by lazy { 18 | SharedPreferenceManger(this) 19 | } 20 | 21 | override fun onCreate(savedInstanceState: Bundle?) { 22 | super.onCreate(savedInstanceState) 23 | setContentView(levelBinding.root) 24 | 25 | levelBinding.toolbarLayout.titleTxt.text = "Levels" 26 | levelBinding.toolbarLayout.backImg.setOnClickListener { 27 | tickMusic.start() 28 | finish() } 29 | levelBinding.levelRV.adapter = LevelAdapter(this, sharedPreferenceManger, tickMusic) 30 | } 31 | 32 | override fun onResume() { 33 | super.onResume() 34 | levelBinding.levelRV.adapter!!.notifyDataSetChanged() 35 | } 36 | } -------------------------------------------------------------------------------- /app/src/main/java/com/coding/meet/mathquizapp/QuestionActivity.kt: -------------------------------------------------------------------------------- 1 | package com.coding.meet.mathquizapp 2 | 3 | import android.app.Dialog 4 | import android.graphics.Color 5 | import android.media.MediaPlayer 6 | import androidx.appcompat.app.AppCompatActivity 7 | import android.os.Bundle 8 | import android.os.Handler 9 | import android.os.Looper 10 | import android.os.SystemClock 11 | import android.util.Log 12 | import android.view.View 13 | import android.widget.Button 14 | import android.widget.TextView 15 | import androidx.activity.OnBackPressedCallback 16 | import com.coding.meet.mathquizapp.databinding.ActivityQuestionBinding 17 | import com.coding.meet.mathquizapp.models.QuestionItem 18 | import com.coding.meet.mathquizapp.models.QuestionSplit 19 | import com.coding.meet.mathquizapp.util.CustomCountdownTimer 20 | import com.coding.meet.mathquizapp.util.SecurityManger 21 | import com.coding.meet.mathquizapp.util.SharedPreferenceManger 22 | import com.coding.meet.mathquizapp.util.invisible 23 | import com.coding.meet.mathquizapp.util.loadJsonFromAssets 24 | import com.coding.meet.mathquizapp.util.setupDialog 25 | import com.coding.meet.mathquizapp.util.visible 26 | import com.google.android.material.dialog.MaterialAlertDialogBuilder 27 | import com.google.gson.Gson 28 | import com.google.gson.reflect.TypeToken 29 | import org.json.JSONArray 30 | import java.text.DecimalFormat 31 | import kotlin.math.roundToInt 32 | 33 | class QuestionActivity : AppCompatActivity() { 34 | private val gameScoreNextRoundDialog: Dialog by lazy { 35 | Dialog(this,R.style.DialogCustomTheme).apply { 36 | setupDialog(R.layout.game_score_next_round_dialog) 37 | } 38 | } 39 | private val onBackPressedCallback = object : OnBackPressedCallback(true) { 40 | override fun handleOnBackPressed() { 41 | onBackPressedMethod() 42 | } 43 | } 44 | 45 | private val countdownTime = 60 // 1 hour , 3600 second, 60 min 46 | private val clockTime = (countdownTime * 1000).toLong() 47 | private val progressTime = (clockTime / 1000).toFloat() 48 | 49 | private lateinit var questionTimer: CustomCountdownTimer 50 | private var levelSize = 100 51 | private var level = 0 52 | private var correctQuestionPos = 0 53 | private var correctAnswerStr = "" 54 | 55 | private lateinit var correctScoreTxt : TextView 56 | private lateinit var wrongScoreTxt : TextView 57 | private lateinit var nextResumeOrRestartBtn : Button 58 | 59 | private val tickMusic: MediaPlayer by lazy { 60 | MediaPlayer.create(this, R.raw.tick) 61 | } 62 | private val rightMusic: MediaPlayer by lazy { 63 | MediaPlayer.create(this, R.raw.right) 64 | } 65 | private val gameOverMusic: MediaPlayer by lazy { 66 | MediaPlayer.create(this, R.raw.gameover) 67 | } 68 | private val questionBinding : ActivityQuestionBinding by lazy { 69 | ActivityQuestionBinding.inflate(layoutInflater) 70 | } 71 | private val sharedPreferenceManger: SharedPreferenceManger by lazy { 72 | SharedPreferenceManger(this) 73 | } 74 | 75 | 76 | private lateinit var questionList : ArrayList> 77 | 78 | override fun onCreate(savedInstanceState: Bundle?) { 79 | super.onCreate(savedInstanceState) 80 | setContentView(questionBinding.root) 81 | onBackPressedDispatcher.addCallback(this,onBackPressedCallback) 82 | 83 | questionBinding.toolbarLayout.backImg.setOnClickListener { 84 | tickMusic.start() 85 | onBackPressedMethod() 86 | } 87 | 88 | level = intent.getIntExtra("level",0) 89 | // val jsonStr = loadJsonFromAssets("question.json") 90 | val securityManager = SecurityManger(this) 91 | val jsonStr = securityManager.decryptFile( 92 | "encryptedQuestion.json" 93 | ) 94 | Log.d("jsonStr",jsonStr) 95 | 96 | // Manually json to Data class convert 97 | val manuallyQuestionList = manuallyJsonConvertDataClass(jsonStr) 98 | Log.d("manuallyQuestionList",manuallyQuestionList.toString()) 99 | 100 | 101 | // Automatic json to data class convert 102 | val questionItemList : ArrayList = Gson().fromJson( 103 | jsonStr, object : TypeToken>() {}.type 104 | ) 105 | Log.d("questionItemList",questionItemList.toString()) 106 | 107 | // Splits this collection into a list of lists each not exceeding the given [size = 10]. 108 | questionList = questionItemList.chunked(10) as ArrayList> 109 | 110 | setBtnOnClick() 111 | 112 | var secondsLeft = 0 113 | questionTimer = object : CustomCountdownTimer(clockTime, 1000) {} 114 | questionTimer.onTick = { millisUntilFinished -> 115 | val second = (millisUntilFinished / 1000.0f).roundToInt() 116 | if (second != secondsLeft) { 117 | secondsLeft = second 118 | timerFormat( 119 | secondsLeft 120 | ) 121 | } 122 | } 123 | questionTimer.onFinish = { 124 | gameScoreNextRoundShow() 125 | } 126 | 127 | questionBinding.toolbarLayout.titleTxt.text = "Level:${level + 1}" 128 | gameScoreNextRound() 129 | questionBinding.circularProgressBar.max = progressTime.toInt() 130 | questionBinding.circularProgressBar.progress = progressTime.toInt() 131 | question() 132 | questionTimer.startTimer() 133 | 134 | 135 | } 136 | 137 | private fun setBtnOnClick() { 138 | questionBinding.firstOptionBtn.setOnClickListener { setAnswerTxt(it) } 139 | questionBinding.secondOptionBtn.setOnClickListener { setAnswerTxt(it) } 140 | questionBinding.thirdOptionBtn.setOnClickListener { setAnswerTxt(it) } 141 | questionBinding.fourthOptionBtn.setOnClickListener { setAnswerTxt(it) } 142 | 143 | questionBinding.twoOptionRemoveBtn.setOnClickListener { 144 | when { 145 | correctAnswerStr.equals(questionBinding.firstOptionBtn.text.toString(),true)->{ 146 | optionHide( 147 | questionBinding.secondOptionBtn, 148 | questionBinding.thirdOptionBtn, 149 | questionBinding.fourthOptionBtn, 150 | ) 151 | } 152 | correctAnswerStr.equals(questionBinding.secondOptionBtn.text.toString(),true)->{ 153 | optionHide( 154 | questionBinding.firstOptionBtn, 155 | questionBinding.thirdOptionBtn, 156 | questionBinding.fourthOptionBtn 157 | ) 158 | } 159 | correctAnswerStr.equals(questionBinding.thirdOptionBtn.text.toString(),true)->{ 160 | optionHide( 161 | questionBinding.secondOptionBtn, 162 | questionBinding.fourthOptionBtn, 163 | questionBinding.firstOptionBtn, 164 | ) 165 | } 166 | correctAnswerStr.equals(questionBinding.fourthOptionBtn.text.toString(),true)->{ 167 | optionHide( 168 | questionBinding.firstOptionBtn, 169 | questionBinding.secondOptionBtn, 170 | questionBinding.thirdOptionBtn 171 | ) 172 | } 173 | } 174 | questionBinding.twoOptionRemoveBtn.isEnabled = false 175 | } 176 | } 177 | 178 | private fun optionHide( 179 | option1:Button, 180 | option2:Button, 181 | option3:Button 182 | ){ 183 | when((1..3).random()){ 184 | 1 -> { 185 | option1.invisible() 186 | option2.invisible() 187 | } 188 | 2 -> { 189 | option2.invisible() 190 | option3.invisible() 191 | } 192 | else -> { 193 | option1.invisible() 194 | option3.invisible() 195 | } 196 | } 197 | } 198 | 199 | private var mLastClickTime : Long = 0 200 | private fun setAnswerTxt(view: View) { 201 | if (SystemClock.elapsedRealtime() - mLastClickTime < 1000){ 202 | return 203 | } 204 | mLastClickTime = SystemClock.elapsedRealtime() 205 | 206 | val answerTxt = (view as TextView).text.toString() 207 | Log.d("answerTxt",answerTxt) 208 | Log.d("correctAnswerStr",correctAnswerStr) 209 | if (correctAnswerStr.equals(answerTxt,true)){ 210 | view.setBackgroundColor(Color.GREEN) 211 | correctQuestionPos += 1 212 | rightMusic.start() 213 | questionBinding.noOfQuestionTxt.text = "Question ${correctQuestionPos+1} / 10" 214 | if (correctQuestionPos > 9){ 215 | correctScoreTxt.text = correctQuestionPos.toString() 216 | wrongScoreTxt.text = (10 - correctQuestionPos).toString() 217 | sharedPreferenceManger.setLevelState("level$level",true) 218 | nextResumeOrRestartBtn.text = "Next" 219 | questionTimer.pauseTimer() 220 | gameScoreNextRoundDialog.show() 221 | }else{ 222 | Handler(Looper.getMainLooper()).postDelayed({ 223 | nextQuestion() 224 | },1000) 225 | } 226 | }else{ 227 | view.setBackgroundColor(Color.RED) 228 | gameScoreNextRoundShow() 229 | } 230 | 231 | 232 | } 233 | 234 | private fun nextQuestion() { 235 | question() 236 | questionBinding.noOfQuestionTxt.text = "Question ${correctQuestionPos+1} / 10" 237 | } 238 | 239 | private fun gameScoreNextRound() { 240 | correctScoreTxt = gameScoreNextRoundDialog.findViewById(R.id.correctScoreTxt) 241 | wrongScoreTxt = gameScoreNextRoundDialog.findViewById(R.id.wrongScoreTxt) 242 | nextResumeOrRestartBtn = gameScoreNextRoundDialog.findViewById(R.id.nextResumeOrRestartBtn) 243 | 244 | nextResumeOrRestartBtn.setOnClickListener { 245 | tickMusic.start() 246 | if (nextResumeOrRestartBtn.text.toString().equals("Resume",true)){ 247 | gameScoreNextRoundDialog.dismiss() 248 | onResume() 249 | }else{ 250 | correctQuestionPos = 0 251 | if (nextResumeOrRestartBtn.text.toString().equals("Next",true)){ 252 | level += 1 253 | } 254 | gameScoreNextRoundDialog.dismiss() 255 | questionBinding.noOfQuestionTxt.text = "Question ${correctQuestionPos+1} / 10" 256 | if (level < levelSize){ 257 | questionBinding.toolbarLayout.titleTxt.text = "Level:${level + 1}" 258 | nextQuestion() 259 | questionBinding.circularProgressBar.max = progressTime.toInt() 260 | questionBinding.circularProgressBar.progress = progressTime.toInt() 261 | questionTimer.restartTimer() 262 | }else{ 263 | MaterialAlertDialogBuilder(this) 264 | .setTitle("All Level Completed") 265 | .setPositiveButton("Ok"){_,_ -> 266 | finish() 267 | } 268 | .setCancelable(false) 269 | .show() 270 | } 271 | } 272 | } 273 | 274 | val levelsBtn = gameScoreNextRoundDialog.findViewById