Android typically used XML layout files to define the user interface. Kotlin code then works behind to load the layout for each activity, fragment, etc. Essentially we are connecting the visual to the program side. All visual elements are objects and we can assign variables such as id and properties.
Activity Lifecycle
Override onCreate()
MainActivity.kt class is the first screen or activity to appear when the user launches the app and linked to activity_main.xml. We are able to automatically access the variables to the visual side as priority is given to the code during execution.
onCreate() is where you initialize your activity. Most importantly, here you will usually call setContentView(int) with a layout resource defining your UI, and using findViewById(int) to retrieve the widgets in that UI that you need to interact with programmatically.
Function or Method in Kotlin
A function is a block of code which only runs when it is called. You can pass data, known as parameters, into a function.
Functions are used to perform certain actions, and they are also known as methods. A typical method in Kotlin appears to have a fun keyword, method name, formal parameters (arguments) and return type.
MainActivity.kt
Method | Description |
onStart() | check if a user has already signed in to your app with Google. Else sign in anonymously |
savePreferenceSignIn() | Shared preferences allow you to store small amounts of primitive data as key/value pairs in a file on the device. To get a handle to a preference file, and to read, write, and manage preference data, use the SharedPreferences class. |
onCreateOptionsMenu() | called by the Android runtime when it need to create the option menu |
onActivityResult() | when you done with the subsequent activity and returns, the system calls your activity’s onActivityResult() method. This method includes three arguments: (1) The request code you passed to startActivityForResult(). (2) result code specified by the second activity. This is either RESULT_OK if the operation was successful or RESULT_CANCELED if the operation failed (3) An Intent that carries the result data. |
showDownloadDialog() | inflate a dialog box for fetching game |
showTimeDialog() | inflate a dialog for timer setting |
setTimeToMemory() | storing new timer to sharedpreferences |
setupSponsorBanner() | setup a sponsor banner and display any message or link provided by sponsor |
downloadGame() | fetch the desirable game from Firebase |
updateFirebaseCounter() | increment counter each time game is fetched from Firebase |
updateFirebaseRecord() | updates new time recorded |
showCreationDialog() | inflate a dialog box for creating new card game |
showNewSizeDialog() | inflate a dialog box for board size selection |
showReplayDialog() | inflate a dialog box for replaying game |
startCountDownTimer() | timer begin counting down upon game starts |
onFinish() | call showReplayDialog() upon game finishes |
startBlink() | animate the timer during game |
stopTimer() | cancel time counter |
setupBoard() | setup the board game upon app starts or game ends |
formatTime() | time formatter |
updateGameWithFlip() | flip card at position upon clicked |
class MainActivity : AppCompatActivity() { companion object { private const val TAG = "MainActivity" private const val CREATE_REQUEST_CODE = 248 private const val GAME_REQUEST_CODE = 173 private const val SPONSOR_CODE = 199 private const val SPONSOR_SECTION_CODE = 118 }private lateinit var clRoot: CoordinatorLayout private lateinit var rvBoard: RecyclerView private lateinit var tvNumMoves: TextView private lateinit var tvNumPairs: TextView private lateinit var tvTimer: TextView private lateinit var tvSponsor_Message: TextView private lateinit var cvSponsor_Message: CardView private lateinit var ivProfile: ImageView /* COUNT DOWN TIMER */ private lateinit var countTimer: CountDownTimer private var initTime : Long = 0 private var timeResult : String? = null private var isStarted: Boolean = false private var isTimeChange: Boolean = false // Change time in setting private var isResetTime: Boolean = false private var timeLeft = 0L var recordInFb : Int = 0 private var t1 : Int = 0 private var t2 : Int = 0 private var t3 : Int = 0 private lateinit var database : DatabaseReference private var playTimeToDisplay : String = "" private var secDisplay : Int = 0 private var milliDisplay : Int = 0 private var playSec : Int = 0 private var playMilli : Int = 0 var disableToastReady : Boolean = false var disableGame : Boolean = false var onlineGame : Boolean = false var displayRecordTime : String? = null private lateinit var auth: FirebaseAuth private val db = Firebase.firestore var gameName: String? = null private var customGameImages: List<String>? = null private lateinit var memoryGame: MemoryGame private lateinit var adapter: MemoryBoardAdapter private var boardSize: BoardSize = BoardSize.EASY lateinit var mAdView : AdView private var soundId = 1 private lateinit var soundPool: SoundPool private lateinit var soundPool2: SoundPool // change "Unit ID" in activity_main.xml before upload to playstore // Preference private var storedCustomName : String? = "" private var storedPickedName : String? = "" private var sponsorEmail : String? = "" private var profileImage : String? = "" override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) getPreference() auth = getInstance() MobileAds.initialize(this) {} mAdView = findViewById(R.id.adView) val adRequest = AdRequest.Builder().build() mAdView.loadAd(adRequest) clRoot = findViewById(R.id.clRoot) rvBoard = findViewById(R.id.rvBoard) tvNumMoves = findViewById(R.id.tvNumMoves) tvNumPairs = findViewById(R.id.tvNumPairs) tvTimer = findViewById(R.id.tvTimer) //** For sponsorship ** tvSponsor_Message = findViewById(R.id.tvSponsor_Message) cvSponsor_Message = findViewById(R.id.cvLogoAbout) cvSponsor_Message.setOnClickListener { val intent = Intent( this, GamelistActivity::class.java) intent.putExtra(EXTRA_GAME_NAME, "") intent.putExtra(EXTRA_CMD, "sponsor select game") startActivityForResult(intent, SPONSOR_CODE) } PaymentConfiguration.init( applicationContext, "pk_live_51JiugAJfHDlzUJLDVcKktrXBlSD8i2wZyjp2EuIJITFSGXQgZw0bNNzYqSuSUnNI2dfeJM90Pnp3WvGUh4zQak8500gFo0Yo2Z" ) soundPool = SoundPool(6, AudioManager.STREAM_MUSIC, 0) soundPool.load(baseContext, R.raw.match, 1) soundPool2 = SoundPool(6, AudioManager.STREAM_MUSIC, 0) soundPool2.load(baseContext, R.raw.cheer, 1) /********************** * Game selected from GamelistActivity */ val bundle : Bundle?= intent.extras val selectedGameName = bundle?.getString("gameSelected") val bestrecord = bundle?.getString("bestrecord") displayRecordTime = convertRecordTime(bestrecord) when { selectedGameName != null -> { gameName = selectedGameName downloadGame(selectedGameName) } } setupBoard() } private fun convertRecordTime(bestrecord: String?): String? { Log.i(TAG,"convertRecordTime>bestrecord = $bestrecord") return bestrecord } public override fun onStart() { super.onStart() // Check if user is signed in google sign-in. Else anonymous val currentUser = auth.currentUser if (!isGoogleSignedIn()) { savePreferenceSignIn("anonymous") signInAnonymously() }else{ savePreferenceSignIn("google") } } private fun savePreferenceSignIn(name: String) { val sharedPreferences = getSharedPreferences("sharedPrefs", Context.MODE_PRIVATE) val editor = sharedPreferences.edit() editor.apply { putString("SIGN_IN", name) }.apply() Toast.makeText( this, "Game name>preference Saved", Toast.LENGTH_SHORT).cancel() } private fun isGoogleSignedIn(): Boolean { return GoogleSignIn.getLastSignedInAccount(this) != null } private fun signInAnonymously() { // [START signin_anonymously] auth.signInAnonymously() .addOnCompleteListener(this) { task -> if (task.isSuccessful) { // Sign in success, update UI with the signed-in user's information Log.d(TAG, "signInAnonymously:success") val user = auth.currentUser //updateUI(user) } else { // If sign in fails, display a message to the user. Log.w(TAG, "MainActivity:failure", task.exception) Toast.makeText(baseContext, "Authentication failed.", Toast.LENGTH_SHORT).show() //updateUI(null) } } // [END signin_anonymously] } override fun onCreateOptionsMenu(menu: Menu?): Boolean { menuInflater.inflate(R.menu.menu_main, menu) return true } @SuppressLint("ShowToast") override fun onOptionsItemSelected(item: MenuItem): Boolean { var selectedGameName = "" // Register a listener to menu option whenever it is tapped when (item.itemId) { R.id.mi_refresh -> { if (isStarted) { stopTimer() disableGame = true } if (memoryGame.getNumMovers() > 0 && !memoryGame.haveWonGame()) { showAlertDialog(getString(R.string.quitYourCurrentGame), null, View.OnClickListener { setupBoard() }) } else { setupBoard() } } R.id.mi_new_size -> { if (isStarted) { stopTimer() disableGame = true } showNewSizeDialog() return true } R.id.mi_custom -> { if (isStarted) { stopTimer() disableGame = true } showCreationDialog() return true } R.id.mi_download -> { if (isStarted) { stopTimer() disableGame = true } showDownloadDialog() return true } R.id.mi_topgame -> { if (isStarted) { stopTimer() disableGame = true } val intent = Intent( this, GamelistActivity::class.java) intent.putExtra(EXTRA_MENU, "topgame") startActivityForResult(intent, GAME_REQUEST_CODE) return true } R.id.mi_sponsor_game -> { if (isStarted) { stopTimer() disableGame = true } if (onlineGame && sponsorEmail!!.isEmpty() ) { val intent = Intent( this, CheckoutActivity::class.java) intent.putExtra(EXTRA_GAME_NAME, gameName) intent.putExtra(EXTRA_CMD, "check login status") intent.putExtra(EXTRA_MENU, "sponsor") startActivityForResult(intent, SPONSOR_CODE) } else { val intent = Intent( this, GamelistActivity::class.java) intent.putExtra(EXTRA_GAME_NAME, "") intent.putExtra(EXTRA_CMD, "sponsor select game") startActivityForResult(intent, SPONSOR_CODE) } return true } R.id.mi_sponsor_section -> { if (isStarted) { stopTimer() disableGame = true } if (isGoogleSignedIn() && sponsorEmail != "") { val intent = Intent(this, GamelistActivity::class.java) intent.putExtra(EXTRA_CMD, "my sponsor") startActivityForResult(intent, SPONSOR_SECTION_CODE) } else { var toast = Toast.makeText(this, getString(R.string.sponsor_pg_msg), Toast.LENGTH_SHORT) toast.setGravity(Gravity.CENTER_HORIZONTAL, 0, 0) toast.show() } return true } } return super.onOptionsItemSelected(item) } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { if (requestCode == CREATE_REQUEST_CODE && resultCode == Activity.RESULT_OK) { val customGameName = data?.getStringExtra(EXTRA_GAME_NAME) if (customGameName == null) { Log.e(TAG, "Got null custom game from CreateActivity") return } downloadGame(customGameName) } super.onActivityResult(requestCode, resultCode, data) } private fun getPreference() { val sharePreferences: SharedPreferences = getSharedPreferences("sharedPrefs", Context.MODE_PRIVATE) storedCustomName = sharePreferences.getString("STRING_KEY", null) storedPickedName = sharePreferences.getString("PICKED_KEY", null) sponsorEmail = sharePreferences.getString("SPONSOR_EMAIL", null) } private fun showDownloadDialog() { var savedString : String = "" if (storedPickedName != "") { savedString = storedPickedName.toString() } else { if (storedCustomName != "") { savedString = storedCustomName.toString() } } var gameToDownload: String val boardDownloadView = LayoutInflater.from( this).inflate(R.layout.dialog_download_board, null) val etDownGame = boardDownloadView.findViewById<EditText>(R.id.etDownloadGame) etDownGame.setText(savedString) showAlertDialog(getString(R.string.fetchMemoryGame), boardDownloadView, View.OnClickListener { // Grab the text of the game name that the user wants to download if (isStarted) { stopTimer() } if (etDownGame.text.toString().isEmpty()) { Toast.makeText(this, getString(R.string.emptyField), Toast.LENGTH_SHORT).show() } else { gameToDownload = etDownGame.text.toString().trim() downloadGame(gameToDownload) Toast.makeText(this, "main Act > download game $gameToDownload", Toast.LENGTH_SHORT).cancel() } }) } private fun showTimeDialog() { val timeView = LayoutInflater.from( this).inflate(R.layout.dialog_set_time, null) val etTime: EditText = timeView.findViewById(R.id.etTime) showAlertDialog(getString(R.string.dialog_setNewTime_title), timeView, View.OnClickListener { if (etTime.text.toString().isEmpty()) { Toast.makeText(this, getString(R.string.emptyField), Toast.LENGTH_SHORT).show() } else { var newTime = etTime.text.toString().trim() setTimeToMemory(newTime) isTimeChange = true Toast.makeText(this, "new time is $newTime", Toast.LENGTH_SHORT).cancel() initTime = newTime.toLong() tvNumPairs.text = getString(R.string.btn_setting) tvTimer.text = "$initTime:00" } }) setupBoard() } private fun setTimeToMemory(preferTime: String) { val sharedPreferences = getSharedPreferences("sharedPrefs", Context.MODE_PRIVATE) val editor = sharedPreferences.edit() editor.apply { when (boardSize){ BoardSize.EASY -> { t1 = preferTime.toInt() putString("level1", preferTime) } BoardSize.MEDIUM -> { t2 = preferTime.toInt() putString("level2", preferTime) } BoardSize.HARD -> { t3 = preferTime.toInt() putString("level3", preferTime) } } }.apply() Toast.makeText( this, "$boardSize> preferTime $preferTime", Toast.LENGTH_SHORT).cancel() } private fun getFbRecord(customGameName: String) : Int { db.collection("games").document(customGameName).get().addOnSuccessListener { document -> recordInFb = document.get("bestrecord").toString().toInt() } return recordInFb } private fun setupSponsorBanner(customGameName: String) { db.collection("games").document(customGameName).get().addOnSuccessListener { document -> //var rec = document.get("bestrecord").toString().toInt() val email = document.get("sponsor").toString() var message = document.get("sponsormessage").toString() var link = document.get("sponsorlink").toString() var text = "" if (message.isBlank()) { message = getString(R.string.sponsor_bannerMsg_default) tvSponsor_Message.setOnClickListener{ val intent = Intent( this, GamelistActivity::class.java) intent.putExtra(EXTRA_GAME_NAME, "") intent.putExtra(EXTRA_CMD, "sponsor select game") startActivityForResult(intent, SPONSOR_CODE) } } tvSponsor_Message.isClickable = true tvSponsor_Message.movementMethod = LinkMovementMethod.getInstance() if (link.isNotBlank()) { text = "<a href='http://" + link +"'>" + message + "</a>" tvSponsor_Message.text = Html.fromHtml(text) } else { text = message } tvSponsor_Message.setTextColor("#000000".toColorInt()) val animation = AnimationUtils.loadAnimation(this, R.anim.flash_leave_now) tvSponsor_Message.startAnimation(animation) } } private fun downloadGame(customGameName: String) { onlineGame = true Log.i(TAG, "online game > true. game name $customGameName") db.collection("games").document(customGameName).get().addOnSuccessListener { document -> Log.i(TAG, "Main > before get userImageList" ) val userImageList = document.toObject(UserImageList::class.java) Log.i(TAG, "Main > after get userImageList size ${userImageList?.images?.size}" ) if (userImageList?.images?.size == null) { val intent = Intent( this, GamelistActivity::class.java) intent.putExtra(EXTRA_MENU, "topgame") intent.putExtra(EXTRA_CMD, "search error") startActivityForResult(intent, GAME_REQUEST_CODE) } var counterNew: Int = document.get("counter").toString().toInt() Log.i(TAG, "Main > counterNew = $counterNew" ) recordInFb = document.get("bestrecord").toString().toInt() Log.i(TAG, "Main > recordInFb $recordInFb" ) counterNew++ updateFirebaseCounter(customGameName, counterNew) Log.i(TAG, "MainAct > download game > counterNew : $counterNew") if (userImageList?.images == null) { Log.e(TAG, "Invalid custom game data from Firestore") //Snackbar.make(clRoot, getString(R.string.sorryCannotFindSuchGame) + "'$customGameName'", Snackbar.LENGTH_SHORT).show() var toast = Toast.makeText(this, getString(R.string.sorryCannotFindSuchGame) + " $customGameName", Toast.LENGTH_SHORT) toast.setGravity(Gravity.CENTER_HORIZONTAL, 0, 0) toast.show() return@addOnSuccessListener } val numCards: Int = userImageList.images.size * 2 boardSize = BoardSize.getByValue(numCards) gameName = customGameName customGameImages = userImageList.images // Once we found the game name, pre fetch all the images with Picasso for (imageUrl :String in userImageList.images) { Picasso.get().load(imageUrl).fetch() } Snackbar.make(clRoot, getString(R.string.youAreNowPlaying, customGameName), Snackbar.LENGTH_SHORT).show() gameName = customGameName setupBoard() }.addOnFailureListener { exception -> Log.e(TAG, "Exception when retrieving game", exception) } } private fun updateFirebaseCounter(gid: String, counterNew: Int) { val db = FirebaseFirestore.getInstance() val noteRef = db.collection("games") .document(gid) noteRef.update( "counter", counterNew ).addOnCompleteListener { task -> if (task.isSuccessful) { Toast.makeText( this, "Updated Note $counterNew", Toast.LENGTH_LONG).cancel() } else { Toast.makeText( this, "Failed. Check Log", Toast.LENGTH_LONG).cancel() } } } private fun updateFirebaseRecord(gid: String, recordNew: Int) { val db = FirebaseFirestore.getInstance() val noteRef = db.collection("games") .document(gid) noteRef.update( "bestrecord", recordNew ).addOnCompleteListener { task -> if (task.isSuccessful) { Toast.makeText( this, "Updated recordNew $recordNew", Toast.LENGTH_SHORT).cancel() } else { Toast.makeText( this, "Failed update recordNew. Check Log", Toast.LENGTH_SHORT).cancel() } } } private fun showCreationDialog() { val boardSizeView = LayoutInflater.from( this).inflate(R.layout.dialog_board_size, null) val radioGroupSize = boardSizeView.findViewById<RadioGroup>(R.id.radioGroup) //showAlertDialog( getString(R.string.createYourOwnMemoryBoard, DAYS_ALIVE), boardSizeView, View.OnClickListener { showAlertDialog( getString(R.string.createYourOwnMemoryBoard), boardSizeView, View.OnClickListener { // Set a new value for the board size val desiredBoardSize: BoardSize = when (radioGroupSize.checkedRadioButtonId) { R.id.rbEasy -> BoardSize.EASY R.id.rbMedium -> BoardSize.MEDIUM else -> BoardSize.HARD } // Navigate the user to a new activity // choose photo val intent = Intent( this, CreateActivity::class.java) intent.putExtra(EXTRA_BOARD_SIZE, desiredBoardSize) startActivityForResult(intent, CREATE_REQUEST_CODE) }) } private fun showNewSizeDialog() { val boardSizeView = LayoutInflater.from( this).inflate(R.layout.dialog_board_size, null) val radioGroupSize = boardSizeView.findViewById<RadioGroup>(R.id.radioGroup) when (boardSize) { BoardSize.EASY -> radioGroupSize.check(R.id.rbEasy) BoardSize.MEDIUM -> radioGroupSize.check(R.id.rbMedium) BoardSize.HARD -> radioGroupSize.check(R.id.rbHard) } showAlertDialog( getString(R.string.choose_new_size), boardSizeView, View.OnClickListener { // Set a new value for the board size boardSize = when (radioGroupSize.checkedRadioButtonId) { R.id.rbEasy -> BoardSize.EASY R.id.rbMedium -> BoardSize.MEDIUM else -> BoardSize.HARD } gameName = null customGameImages = null onlineGame = false tvSponsor_Message.text = getString(R.string.sponsor_bannerMsg_default) setupBoard() }) } private fun showAlertDialog( title:String, view: View?, positiveClickListener: View.OnClickListener) { AlertDialog.Builder( this) .setTitle(title) .setView(view) .setCancelable(false) .setNeutralButton(getString(R.string.btn_topgames)) {_, _ -> Toast.makeText(applicationContext, "New screen", Toast.LENGTH_LONG).cancel() val intent = Intent( this, GamelistActivity::class.java) startActivityForResult(intent, GAME_REQUEST_CODE) } .setPositiveButton("OK") {_, _ -> positiveClickListener.onClick( null) disableToastReady = false }.show() } private fun showReplayDialog( title:String, view: View?, positiveClickListener: View.OnClickListener) { AlertDialog.Builder( this) .setTitle(title) .setView(view) .setCancelable(false) .setNeutralButton(getString(R.string.btn_topgames)) {_, _ -> Toast.makeText(applicationContext, "New screen", Toast.LENGTH_LONG).cancel() val intent = Intent( this, GamelistActivity::class.java) startActivityForResult(intent, GAME_REQUEST_CODE) } //.setNegativeButton( getString(R.string.cancel), null) .setPositiveButton("OK") {_, _ -> positiveClickListener.onClick( null) disableToastReady = false }.show() } private fun startCountDownTimer(initTime : Long) { //Log.i(TAG, "startTimer()>initTime = $initTime, boardSize=$boardSize") countTimer = object: CountDownTimer(initTime*1000, 100) { override fun onTick(millisUntilFinished: Long) { timeLeft = millisUntilFinished timeResult = "${(millisUntilFinished / 1000).toString().padStart(2, '0')}:" + "${(millisUntilFinished / 100 % 60).toString().padStart(2, '0')} " tvTimer.text = "$timeResult" if (timeLeft in 0..5000) { startBlink(tvTimer, 5) } else { stopBlink(tvTimer) } } override fun onFinish() { Log.d(TAG, "onFinish: called. haveWon = ${memoryGame.haveWonGame()}") disableGame = true showReplayDialog(getString(R.string.dial_game_over), null, View.OnClickListener { disableGame = false stopTimer() setupBoard() }) } }.start() } @SuppressLint("WrongConstant") private fun startBlink(tv: TextView, dura: Long) { val animator = ObjectAnimator.ofInt( tv, "backgroundColor", Color.RED ) animator.duration = dura animator.setEvaluator(ArgbEvaluator()) animator.start() } private fun stopBlink(tv: TextView) { //tv.animation.cancel() tv.clearAnimation() } private fun stopTimer() { countTimer.cancel() isStarted = false } private fun saveGameNameToPreference(name: String) { val sharedPreferences = getSharedPreferences("sharedPrefs", Context.MODE_PRIVATE) val editor = sharedPreferences.edit() editor.apply { putString("STRING_KEY", name) putString("PICKED_KEY", "") }.apply() Toast.makeText( this, "Game name>preference Saved", Toast.LENGTH_SHORT).cancel() } private fun convertRecord(record: Int): String { var sec = record / 1000 var milli = (record % 1000) / 10 Log.i(TAG, "record=$record, fb sec=$sec milli=$milli ") return "$sec.$milli" } @SuppressLint("SetTextI18n") private fun setupBoard() { tvTimer.setBackgroundColor(Color.WHITE) disableGame = false supportActionBar?.title = gameName ?: getString(R.string.app_name) if (onlineGame) { val fbLastRecord = getFbRecord(gameName.toString()) setupSponsorBanner(gameName.toString()) if ( fbLastRecord.toString().toInt() == 0) { supportActionBar?.subtitle = getString(R.string.actbar_main_norecord) } else { var recordPrevious = convertRecord(recordInFb) supportActionBar?.subtitle = getString(R.string.actbar_main_bestrecord, recordPrevious) } Log.i(TAG, "subtitle convert. Record passed. fbLastRecord=$recordInFb gameName=$gameName") } else { supportActionBar?.subtitle = getString(R.string.abar_subtitle) } when (boardSize){ BoardSize.EASY -> { tvNumMoves.text = getString(R.string.Easy4x2) if (isTimeChange) { tvTimer.text = "$initTime" + ":00" } else { tvTimer.text = boardSize.getTime().toString() + ":00" initTime = boardSize.getTime().toLong() } } BoardSize.MEDIUM -> { tvNumMoves.text = getString(R.string.Medium6x3) if (isTimeChange) { tvTimer.text = "$initTime" + ":00" } else { tvTimer.text = boardSize.getTime().toString() + ":00" initTime = boardSize.getTime().toLong() } } BoardSize.HARD -> { tvNumMoves.text = getString(R.string.Hard6x4) if (isTimeChange) { tvTimer.text = "$initTime" + ":00" } else { tvTimer.text = boardSize.getTime().toString() + ":00" initTime = boardSize.getTime().toLong() } } } if (!disableToastReady) { var toastReady = Toast.makeText(this, getString(R.string.toast_get_ready), Toast.LENGTH_SHORT) toastReady.setGravity(Gravity.CENTER, 0, 0) toastReady.show() } tvNumPairs.setOnClickListener { disableToastReady = true if (isStarted) { stopTimer() } showTimeDialog() } tvNumMoves.setOnClickListener { disableToastReady = true if (isStarted) { stopTimer() } showNewSizeDialog() } memoryGame = MemoryGame(boardSize, customGameImages) adapter = MemoryBoardAdapter( this, boardSize, memoryGame.cards, object : MemoryBoardAdapter.CardClickListener { override fun onCardClicked(position: Int) { if (!disableGame) { updateGameWithFlip(position) } if (adapter.numCardFlip == 1) { startCountDownTimer(initTime) isStarted = true } else { if (memoryGame.haveWonGame() && timeLeft > 0) { stopTimer() val timeUse : Int = getUseTime() var timeFormatted: String = formatTime(timeUse) } } } }) rvBoard.adapter = adapter rvBoard.setHasFixedSize(true) rvBoard.layoutManager = GridLayoutManager( this, boardSize.getWidth()) } private fun formatTime(myTime : Int): String { var timeToString : String = "${(myTime / 1000).toString().padStart(2, '0')}:" + "${(myTime / 100 % 60).toString().padStart(2, '0')} " Log.i(TAG,"given time = $myTime, formatted $timeToString") return timeToString } private fun getUseTime() : Int { var startTime = initTime.toInt() * 1000 var stopTime = timeLeft.toInt() Log.i(TAG,"startTime = $startTime, stopTime = $stopTime") return startTime - stopTime } private fun updateGameWithFlip(position: Int) { // Error handling if (memoryGame.haveWonGame()) { Snackbar.make(clRoot, getString(R.string.sb_alreadywon), Snackbar.LENGTH_LONG).show() return } if (memoryGame.isCardFaceUp(position)) { Snackbar.make(clRoot, getString(R.string.sb_invalidmove), Snackbar.LENGTH_SHORT).show() return } // Actually flip over the card if (memoryGame.flipCard(position)) { soundPool.play(soundId, 1F, 1F, 0, 0, 1F) Log.i(TAG, "Found a match! Num pairs found: ${memoryGame.numPairsFound}") val color = ArgbEvaluator().evaluate( memoryGame.numPairsFound.toFloat() / boardSize.getNumPairs(), ContextCompat.getColor(this, R.color.color_progress_none), ContextCompat.getColor(this, R.color.color_progress_full) ) as Int if (memoryGame.haveWonGame()) { Log.i(TAG, "Won Game. initTime = $initTime time Left: $timeLeft, recordinfb: $recordInFb gameName: $gameName") secDisplay = tvTimer.text.take(2).toString().toInt() milliDisplay = tvTimer.text.substring(3,5).toString().toInt() playSec = (initTime - secDisplay).toInt() playMilli = 60 - milliDisplay playTimeToDisplay = "$playSec.$playMilli" Log.i(TAG, "online game? $onlineGame") if (onlineGame) { // Todo: Check if new record. alert box for best time val gameRecordMilli = (playSec * 1000) + (playMilli * 10) val bestRecordTime = convertRecord(gameRecordMilli.toString().toInt()) if (gameRecordMilli < recordInFb || recordInFb == 0) { supportActionBar?.subtitle = getString(R.string.subtitle_best_record, bestRecordTime) recordInFb = gameRecordMilli updateFirebaseRecord(gameName.toString(), gameRecordMilli.toInt()) showAlertDialog( getString(R.string.dial_new_record, playTimeToDisplay) , null, View.OnClickListener { setupBoard() }) } else { showAlertDialog(getString(R.string.dial_great_job, playTimeToDisplay), null, View.OnClickListener { setupBoard() }) } } if (!onlineGame) { showAlertDialog(getString(R.string.dial_great_job, playTimeToDisplay), null, View.OnClickListener { setupBoard() }) } Snackbar.make(clRoot, getString(R.string.sb_congratulation), Snackbar.LENGTH_LONG).show() soundPool2.play(soundId, 1F, 1F, 0, 0, 1F) CommonConfetti.rainingConfetti(clRoot, intArrayOf(Color.YELLOW, Color.GREEN, Color.MAGENTA)).oneShot() } } adapter.notifyDataSetChanged() } }