diff --git a/legacy/.editorconfig b/legacy/.editorconfig
deleted file mode 100644
index 6e152ba4c..000000000
--- a/legacy/.editorconfig
+++ /dev/null
@@ -1,3 +0,0 @@
-[*.{kt,kts}]
-insert_final_newline=true
-max_line_length=off
diff --git a/legacy/.gitignore b/legacy/.gitignore
deleted file mode 100644
index 05c5a69d7..000000000
--- a/legacy/.gitignore
+++ /dev/null
@@ -1,48 +0,0 @@
-###########################
-# Android Studio Defaults #
-###########################
-
-*.iml
-.idea
-.gradle
-/local.properties
-/.idea/workspace.xml
-/.idea/libraries
-/build
-/captures
-.externalNativeBuild
-
-##############
-# OS X Files #
-##############
-
-.DS_Store
-.AppleDouble
-.LSOverride
-
-# Icon must end with two \r
-Icon
-
-
-# Thumbnails
-._*
-
-# Files that might appear in the root of a volume
-.DocumentRevisions-V100
-.fseventsd
-.Spotlight-V100
-.TemporaryItems
-.Trashes
-.VolumeIcon.icns
-
-# Directories potentially created on remote AFP share
-.AppleDB
-.AppleDesktop
-Network Trash Folder
-Temporary Items
-.apdisk
-
-######################
-# Additional ignores #
-######################
-
diff --git a/legacy/README.md b/legacy/README.md
deleted file mode 100644
index f88e8dd03..000000000
--- a/legacy/README.md
+++ /dev/null
@@ -1,16 +0,0 @@
-# Bowling Companion (5 Pin Bowling Statistics)
-
-An Android app to track 5-pin bowling statistics. Allows users to track multiple bowlers and leagues, and provides in-depth statistics on their game. Useful for determining areas in which the user's performance can be improved.
-
-[View it on the Play Store](https://play.google.com/store/apps/details?id=ca.josephroque.bowlingcompanion)
-
-## How to Run
-
-1. Clone project `git clone https://github.com/autoreleasefool/approach.git`
-2. Import the project into Android Studio.
-
-## Screenshots
-
-| List of Bowlers | Editor | Statistics |
-| --------------------------------------------------- | -------------------------------------------------- | ----------------------------------------- |
-| ![List of bowlers](../media/android_v3_bowlers.png) | ![Editing a game](../media/android_v3_editing.png) | ![Graphs](../media/android_v3_graphs.png) |
diff --git a/legacy/app/.gitignore b/legacy/app/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/legacy/app/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/legacy/app/build.gradle b/legacy/app/build.gradle
deleted file mode 100644
index e691ae01d..000000000
--- a/legacy/app/build.gradle
+++ /dev/null
@@ -1,94 +0,0 @@
-apply plugin: 'com.android.application'
-
-apply plugin: 'kotlin-android'
-
-apply plugin: 'kotlin-android-extensions'
-
-def versionMajor = 3
-def versionMinor = 2
-def versionPatch = 0
-
-android {
- compileSdkVersion 28
- buildToolsVersion '28.0.3'
-
- defaultConfig {
- applicationId "ca.josephroque.bowlingcompanion"
- minSdkVersion 21
- targetSdkVersion 28
- versionCode versionMajor * 100 + versionMinor * 10 + versionPatch
- versionName "${versionMajor}.${versionMinor}.${versionPatch}"
- testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
- vectorDrawables.useSupportLibrary = true
- }
- buildTypes {
- debug {
- debuggable true
- minifyEnabled false
- applicationIdSuffix ".debug"
- }
- release {
- minifyEnabled false
- proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
- }
- }
-}
-
-dependencies {
- implementation fileTree(dir: 'libs', include: ['*.jar'])
- implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
- implementation 'com.android.support:appcompat-v7:28.0.0'
- implementation 'com.android.support:design:28.0.0'
- implementation 'com.android.support:recyclerview-v7:28.0.0'
- implementation 'com.android.support:preference-v7:28.0.0'
- implementation 'com.android.support:preference-v14:28.0.0'
- implementation 'com.android.support:support-v4:28.0.0'
- implementation 'com.android.support:support-vector-drawable:28.0.0'
- implementation 'com.android.support:customtabs:28.0.0'
- implementation 'com.android.support.constraint:constraint-layout:1.1.3'
- implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.22.2'
- implementation 'android.arch.lifecycle:extensions:1.1.1'
-
- implementation 'com.ncapdevi:frag-nav:2.4.0' // https://github.com/ncapdevi/FragNav
- implementation 'com.github.PhilJay:MPAndroidChart:v3.0.3' // https://github.com/PhilJay/MPAndroidChart
-
- testImplementation 'junit:junit:4.12'
-
- androidTestImplementation 'com.android.support.test:runner:1.0.2'
- androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
-}
-
-kotlin {
- experimental {
- coroutines "enable"
- }
-}
-
-configurations {
- ktlint
-}
-
-dependencies {
- ktlint "com.github.shyiko:ktlint:0.24.0"
- // additional 3rd party ruleset(s) can be specified here
- // just add them to the classpath (e.g. ktlint 'groupId:artifactId:version') and
- // ktlint will pick them up
-}
-
-task ktlint(type: JavaExec, group: "verification") {
- description = "Check Kotlin code style."
- classpath = configurations.ktlint
- main = "com.github.shyiko.ktlint.Main"
- args "src/**/*.kt"
- // to generate report in checkstyle format prepend following args:
- // "--reporter=plain", "--reporter=checkstyle,output=${buildDir}/ktlint.xml"
- // see https://github.com/shyiko/ktlint#usage for more
-}
-check.dependsOn ktlint
-
-task ktlintFormat(type: JavaExec, group: "formatting") {
- description = "Fix Kotlin code style deviations."
- classpath = configurations.ktlint
- main = "com.github.shyiko.ktlint.Main"
- args "-F", "src/**/*.kt"
-}
diff --git a/legacy/app/proguard-rules.pro b/legacy/app/proguard-rules.pro
deleted file mode 100644
index f1b424510..000000000
--- a/legacy/app/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/legacy/app/src/androidTest/java/ca/josephroque/bowlingcompanion/ExampleInstrumentedTest.kt b/legacy/app/src/androidTest/java/ca/josephroque/bowlingcompanion/ExampleInstrumentedTest.kt
deleted file mode 100644
index 1ab75716b..000000000
--- a/legacy/app/src/androidTest/java/ca/josephroque/bowlingcompanion/ExampleInstrumentedTest.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-package ca.josephroque.bowlingcompanion
-
-import android.support.test.InstrumentationRegistry
-import android.support.test.runner.AndroidJUnit4
-import org.junit.Assert.assertEquals
-import org.junit.Test
-import org.junit.runner.RunWith
-
-/**
- * Instrumented test, which will execute on an Android device.
- *
- * See [testing documentation](http://d.android.com/tools/testing).
- */
-@RunWith(AndroidJUnit4::class)
-class ExampleInstrumentedTest {
- @Test
- fun useAppContext() {
- // Context of the app under test.
- val appContext = InstrumentationRegistry.getTargetContext()
- assertEquals("ca.josephroque.bowlingcompanion", appContext.packageName)
- }
-}
diff --git a/legacy/app/src/main/AndroidManifest.xml b/legacy/app/src/main/AndroidManifest.xml
deleted file mode 100644
index fcf669f9e..000000000
--- a/legacy/app/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,60 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/legacy/app/src/main/assets/changelog.txt b/legacy/app/src/main/assets/changelog.txt
deleted file mode 100644
index 571213db4..000000000
--- a/legacy/app/src/main/assets/changelog.txt
+++ /dev/null
@@ -1,167 +0,0 @@
-v3.2.0
-A lot of users have been having issues with the current transfer functionality, so I've created a new process which will let you export and save the data wherever you'd like, and then import it on a new device. Hopefully this helps those with longstanding issues of being unable to move to new devices.
-
-This update also comes with some quality of life fixes:
-- No more ads
-- No more analytics
-
-v3.1.2
-- Fixed a crash some users experienced when trying to open a game
-
-v3.1.1
-This version contains a bunch of bug fixes! Here's just a few:
-- Creating or opening an event should no longer crash the app
-- Fixed sharing games through the game overview
-- Prevented scores from dropping below zero when there are too many fouls
-
-v3.1.0
-Thank you for using the app while some of the kinks in the new version are worked out!
-- See an overview of your games by tapping the list icon in the top right, or the overview menu item
-- Share games and series from the game overview (saving to external storage required)
-
-Bug fixes
-- Lefts and rights are now reported correctly
-- Return to the first incomplete game when editing a series (not just the first game)
-- Fix some more rotation crashes and returning to the app from the background
-
-v3.0.2
-This update contains a tonne of stability fixes for the new version.
-
-- Fix pins not appearing for some users
-- Fix crash when rotating the device
-- "Middle hits which were strikes" now counts denominator accurately
-- Some other stability bug fixes
-
-v3.0.0
-Welcome to the new 5 Pin Bowling Companion. We completely rebuilt the app from scratch with all your favourite features and plenty of new ones.
-
-- Teams
- - Create a team of bowlers to track together
- - Pick your team and select a league for each bowler
- - Change their order
- - Pressing the next ball button will take you to the next bowler! Keep an eye on whose name is at the top of the screen
-- Highlight settings are now per league — long press to edit
-- New statistics
-
-v2.1.9
-A long awaited update.
-
-- Stability fixes for Android Oreo
-- Fix bugs with "Combine Series"
-- Better font scaling for accessibility
-- Auto lock games after final frame is complete
-- List rows are larger for easier clicking
-- H+2 is now counted as a Headpin, and Split + 2 is a split
-- Allow more characters in bowler and league names
-- Redesign series screen to avoid overlapping scores
-
-v2.1.8
-- See your average with up to 1 decimal place by going to Settings > Statistics.
-
-v2.1.7
-- Transfer your data to a new device! By accessing the "Transfer data" option in the menu, you can transfer your data to a new device by uploading it to our servers and downloading it elsewhere with your unique code.
-
-v2.1.6
-- Added an option in the settings under "Editing Games" that allows users to disable the new change that moved the pins above the floating buttons.
-
-v2.1.5
-- Fixed a bug that prevented new events from being made.
-
-v2.1.4
-
-What's new
-- Added THIS popup, to inform you of the newest changes each release.
-- Leagues now have a "base average". Start recording a new league in the middle of a season? Long click a league, select "Change properties" and you can set your average so far and the number of games you've played, so your average in the app is more accurate.
-- You can now duplicate a series. Duplicating a series will create a new series for the current date, with the same number of games and scores as the series you duplicate. This new series will use manually set games, and will affect your average, but no other stats.
-- New statistics: percentage of time that your first ball is left or right of the headpin. Find them under "General"
-
-Other changes
-- Added an option to move the pins above the "next" button, instead of behind it, to prevent misclicks. This option is enabled by default.
-- Fixed an issue where changing the name of a league or event sometimes added an "L" or "E" to the start. If this happened to you, you'll receive a pop-up asking if you'd like the names to be fixed.
-
-v2.1.3
-- Fixed a bug introduced in v2.1.2 that caused some bowlers to not appear.
-
-v2.1.2
-- H2 and H32 (not chop off) are no longer counted as "spare chances".
-
-v2.1.1
-- Fixed a crash when checking the hi score possible
-
-v2.1
-- Games with a score of 0 are now excluded from your average and statistics
-- Strikes and spares are now highlighted in a game to be spotted more easily (you can disable this in the settings)
-- Series now show their total and are highlighted over a certain value (can be changed/disable in settings)
-- Bug fixes
-
-v2.0.5
-- Bug fixes
-
-v2.0.4
-- Bug fixes
-
-v2.0.3
-- Long click a bowler or league to edit its name
-- Improved match play recording (add your opponent's name and their score)
-- Match play results now appear in the series list (you can disable this in the settings under "Interface")
-- Bug fixes
-
-v2.0
-
-Features
-- Added a new tutorial on first startup
-- Adding graphs for stats
-- Support for tablets and landscape
-- Swipe to knock over pins
-- Series in the "Open" league can have 1-5 games
-- You can now combine series in the "Open" league
-- Swipe to delete bowlers/leagues/series
-
-Design
-- Material design
-- New app icon
-- Improved navigation
-
-v1.2.1
-- Bug fixes
-
-v1.2
-- Larger button to move to next frame
-- Option to have frame auto advance after an interval of inactivity
-- Bug fixes
-
-v1.1.3
-- Added option to change header/stat fonts to black, for better readability
-- Bug fixes
-
-v1.1.2
-- Now returns to same game when phone comes off standby mode
-- Changed display method of future frames in a game
-- Bug fixes
-
-v1.1.1
-- Added link in menu to settings
-- Simplified settings screen
-- Bug fixes
-
-v1.1
-- Added new option to set results of match play
-- Added match play statistics to the list of stats
-- Added averages by game to the statistics
-- Changed appearance of stats, now in expandable groups
-- Fixed bug where "Grey" theme was not working
-- Bug fixes
-
-v1.0.4
-- Decreased memory when saving images, should reduce crashes when trying to save or share a series.
-
-v1.0.3
-- Bug fixing
-- Decreased memory used by background images.
-- Decreased overall size of the app.
-
-v1.0.2
-- Fixing issues with user authentication
-
-v1.0.1
-- Fixing issues with user authentication
diff --git a/legacy/app/src/main/assets/licenses.txt b/legacy/app/src/main/assets/licenses.txt
deleted file mode 100644
index afdc52cf7..000000000
--- a/legacy/app/src/main/assets/licenses.txt
+++ /dev/null
@@ -1,51 +0,0 @@
-FragNav
-
-FragNav Android Fragment Navigation Library
-Copyright (c) 2016 Nic Capdevila (http://github.com/ncapdevi).
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
-Material design icons
-Material design icons
-Copyright 2018 Google
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
-MPAndroidChart
-
-Copyright 2018 Philipp Jahoda
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
-
-
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/App.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/App.kt
deleted file mode 100644
index 8433370d8..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/App.kt
+++ /dev/null
@@ -1,58 +0,0 @@
-package ca.josephroque.bowlingcompanion
-
-import android.app.Activity
-import android.app.Application
-import android.arch.lifecycle.Lifecycle
-import android.arch.lifecycle.LifecycleObserver
-import android.arch.lifecycle.OnLifecycleEvent
-import android.content.Context
-import android.support.v7.preference.PreferenceManager
-import android.view.inputmethod.InputMethodManager
-import java.util.concurrent.atomic.AtomicBoolean
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Application for custom methods and initialization.
- * @constructor creates a new [Application]
- */
-class App : Application(), LifecycleObserver {
-
- companion object {
- @Suppress("unused")
- private const val TAG = "BowlingCompanionApp"
-
- val isRunning: AtomicBoolean = AtomicBoolean(false)
-
- fun hideSoftKeyBoard(activity: Activity) {
- activity.currentFocus?.let {
- val imm = activity.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
- imm.hideSoftInputFromWindow(it.windowToken, 0)
- }
- }
-
- fun showSoftKeyBoard(activity: Activity) {
- activity.currentFocus?.let {
- val imm = activity.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
- imm.showSoftInput(it, 0)
- }
- }
- }
-
- // MARK: Lifecycle functions
-
- override fun onCreate() {
- super.onCreate()
- PreferenceManager.setDefaultValues(this, R.xml.pref_app, false)
- }
-
- @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
- fun onAppBackgrounded() {
- isRunning.set(false)
- }
-
- @OnLifecycleEvent(Lifecycle.Event.ON_START)
- fun onAppForegrounded() {
- isRunning.set(true)
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/BowlerTeamTabbedFragment.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/BowlerTeamTabbedFragment.kt
deleted file mode 100644
index 8ea27923c..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/BowlerTeamTabbedFragment.kt
+++ /dev/null
@@ -1,231 +0,0 @@
-package ca.josephroque.bowlingcompanion
-
-import android.os.Bundle
-import android.support.design.widget.TabLayout
-import android.support.v4.app.Fragment
-import android.support.v4.app.FragmentManager
-import android.support.v4.app.FragmentPagerAdapter
-import android.view.Menu
-import android.view.MenuInflater
-import android.view.MenuItem
-import ca.josephroque.bowlingcompanion.bowlers.Bowler
-import ca.josephroque.bowlingcompanion.bowlers.BowlerDialog
-import ca.josephroque.bowlingcompanion.bowlers.BowlerListFragment
-import ca.josephroque.bowlingcompanion.common.interfaces.IIdentifiable
-import ca.josephroque.bowlingcompanion.common.fragments.ListFragment
-import ca.josephroque.bowlingcompanion.common.fragments.TabbedFragment
-import ca.josephroque.bowlingcompanion.teams.Team
-import ca.josephroque.bowlingcompanion.teams.details.TeamDetailsFragment
-import ca.josephroque.bowlingcompanion.teams.list.TeamDialog
-import ca.josephroque.bowlingcompanion.teams.list.TeamListFragment
-import ca.josephroque.bowlingcompanion.transfer.BaseTransferDialogFragment
-import ca.josephroque.bowlingcompanion.utils.Analytics
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * A fragment with tabs to switch between a [BowlerListFragment] and [TeamListFragment]
- */
-class BowlerTeamTabbedFragment : TabbedFragment(),
- ListFragment.ListFragmentDelegate,
- BowlerDialog.BowlerDialogDelegate,
- TeamDialog.TeamDialogDelegate {
-
- companion object {
- @Suppress("unused")
- private const val TAG = "BowlerTeamTabFragment"
-
- enum class Tab {
- Bowlers, Teams;
-
- companion object {
- private val map = Tab.values().associateBy(Tab::ordinal)
- fun fromInt(type: Int) = map[type]
- }
-
- val title: Int
- get() = when (this) {
- Bowlers -> R.string.bowlers
- Teams -> R.string.teams
- }
- }
-
- fun newInstance(): BowlerTeamTabbedFragment {
- return BowlerTeamTabbedFragment()
- }
- }
-
- private val teamFragment: TeamListFragment?
- get() = findFragmentByPosition(Tab.Teams.ordinal) as? TeamListFragment
-
- private val bowlerFragment: BowlerListFragment?
- get() = findFragmentByPosition(Tab.Bowlers.ordinal) as? BowlerListFragment
-
- // MARK: Lifecycle functions
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setHasOptionsMenu(true)
- }
-
- override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
- super.onCreateOptionsMenu(menu, inflater)
- inflater.inflate(R.menu.fragment_bowlers_teams, menu)
- }
-
- override fun onOptionsItemSelected(item: MenuItem): Boolean {
- return when (item.itemId) {
- R.id.action_transfer -> {
- showTransferFragment()
- Analytics.trackViewTransferMenu()
- true
- }
- else -> super.onOptionsItemSelected(item)
- }
- }
-
- // MARK: BaseFragment
-
- override fun updateToolbarTitle() {
- navigationActivity?.setToolbarTitle(resources.getString(R.string.app_name))
- }
-
- // MARK: TabbedFragment
-
- override fun buildPagerAdapter(tabCount: Int): FragmentPagerAdapter {
- return BowlerTeamPagerAdapter(childFragmentManager, tabCount)
- }
-
- override fun addTabs(tabLayout: TabLayout) {
- for (tab in Tab.values()) {
- tabLayout.addTab(tabLayout.newTab().setText(tab.title))
- }
- }
-
- override fun handleTabSwitch(newTab: Int) {}
-
- // MARK: IFloatingActionButtonHandler
-
- override fun getFabImage(): Int? {
- return when (Tab.fromInt(currentTab)) {
- Tab.Bowlers -> R.drawable.ic_person_add
- Tab.Teams -> R.drawable.ic_group_add
- else -> throw RuntimeException("$currentTab is not a valid tab for BowlerTeamTabbedFragment")
- }
- }
-
- override fun onFabClick() {
- when (Tab.fromInt(currentTab)) {
- Tab.Bowlers -> promptAddOrEditBowler()
- Tab.Teams -> promptAddOrEditTeam()
- else -> throw RuntimeException("$currentTab is not a valid tab for BowlerTeamTabbedFragment")
- }
- }
-
- // MARK: ListFragmentDelegate
-
- override fun onItemSelected(item: IIdentifiable, longPress: Boolean) {
- when (item) {
- is Bowler -> {
- if (longPress) {
- promptAddOrEditBowler(item)
- } else {
- showLeaguesAndEvents(item)
- }
- }
- is Team -> {
- if (longPress) {
- promptAddOrEditTeam(item)
- } else {
- showTeamDetails(item)
- }
- }
- else -> throw RuntimeException("BowlerTeamTabbedFragment can only handle Bowler or Team and item is $item")
- }
- }
-
- override fun onItemDeleted(item: IIdentifiable) {
- when (item) {
- is Bowler -> {
- teamFragment?.refresh()
- Analytics.trackDeleteBowler()
- }
- is Team -> {
- bowlerFragment?.refresh()
- Analytics.trackDeleteTeam()
- }
- else -> throw RuntimeException("BowlerTeamTabbedFragment can only handle Bowler or Team and item is $item")
- }
- }
-
- // MARK: Private functions
-
- private fun showTransferFragment() {
- val newFragment = BaseTransferDialogFragment.newInstance()
- fragmentNavigation?.pushDialogFragment(newFragment)
- }
-
- private fun promptAddOrEditBowler(bowler: Bowler? = null) {
- val newFragment = BowlerDialog.newInstance(bowler)
- fragmentNavigation?.pushDialogFragment(newFragment)
- }
-
- private fun promptAddOrEditTeam(team: Team? = null) {
- val newFragment = TeamDialog.newInstance(team)
- fragmentNavigation?.pushDialogFragment(newFragment)
- }
-
- private fun showLeaguesAndEvents(bowler: Bowler) {
- val newFragment = LeagueEventTabbedFragment.newInstance(bowler)
- fragmentNavigation?.pushFragment(newFragment)
-
- Analytics.trackSelectBowler()
- }
-
- private fun showTeamDetails(team: Team) {
- val newFragment = TeamDetailsFragment.newInstance(team)
- fragmentNavigation?.pushFragment(newFragment)
-
- Analytics.trackSelectTeam()
- }
-
- // MARK: BowlerDialogDelegate
-
- override fun onFinishBowler(bowler: Bowler) {
- val bowlerFragment = bowlerFragment
- bowlerFragment?.refreshList(bowler)
- teamFragment?.refreshList()
- }
-
- override fun onDeleteBowler(bowler: Bowler) {
- bowlerFragment?.onItemDelete(bowler)
- teamFragment?.refreshList()
- }
-
- // MARK: TeamDialogDelegate
-
- override fun onFinishTeam(team: Team) {
- teamFragment?.refreshList(team)
- }
-
- override fun onDeleteTeam(team: Team) {
- teamFragment?.onItemDelete(team)
- }
-
- // MARK: BowlerTeamPagerAdapter
-
- class BowlerTeamPagerAdapter(
- fragmentManager: FragmentManager,
- private val tabCount: Int
- ) : FragmentPagerAdapter(fragmentManager) {
- override fun getCount() = tabCount
-
- override fun getItem(position: Int): Fragment? {
- return when (Tab.fromInt(position)) {
- Tab.Bowlers -> BowlerListFragment.newInstance()
- Tab.Teams -> TeamListFragment.newInstance()
- else -> null
- }
- }
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/LeagueEventTabbedFragment.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/LeagueEventTabbedFragment.kt
deleted file mode 100644
index 771d49828..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/LeagueEventTabbedFragment.kt
+++ /dev/null
@@ -1,214 +0,0 @@
-package ca.josephroque.bowlingcompanion
-
-import android.os.Bundle
-import android.support.design.widget.TabLayout
-import android.support.v4.app.FragmentManager
-import android.support.v4.app.FragmentPagerAdapter
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import ca.josephroque.bowlingcompanion.bowlers.Bowler
-import ca.josephroque.bowlingcompanion.common.Android
-import ca.josephroque.bowlingcompanion.common.fragments.BaseFragment
-import ca.josephroque.bowlingcompanion.common.interfaces.IIdentifiable
-import ca.josephroque.bowlingcompanion.common.fragments.ListFragment
-import ca.josephroque.bowlingcompanion.common.fragments.TabbedFragment
-import ca.josephroque.bowlingcompanion.games.GameControllerFragment
-import ca.josephroque.bowlingcompanion.games.SeriesProvider
-import ca.josephroque.bowlingcompanion.leagues.League
-import ca.josephroque.bowlingcompanion.leagues.LeagueDialog
-import ca.josephroque.bowlingcompanion.leagues.LeagueListFragment
-import ca.josephroque.bowlingcompanion.series.Series
-import ca.josephroque.bowlingcompanion.series.SeriesListFragment
-import ca.josephroque.bowlingcompanion.statistics.interfaces.IStatisticsContext
-import ca.josephroque.bowlingcompanion.statistics.provider.StatisticsProvider
-import ca.josephroque.bowlingcompanion.utils.Analytics
-import kotlinx.coroutines.experimental.launch
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * A fragment with tabs to switch between a [LeagueListFragment] showing leagues, and a
- * [LeagueListFragment] showing events.
- */
-class LeagueEventTabbedFragment : TabbedFragment(),
- ListFragment.ListFragmentDelegate,
- LeagueDialog.LeagueDialogDelegate,
- IStatisticsContext {
-
- companion object {
- @Suppress("unused")
- private const val TAG = "LeagueEventTabFragment"
-
- private const val ARG_BOWLER = "${TAG}_bowler"
-
- enum class Tab {
- Leagues, Events;
-
- companion object {
- private val map = Tab.values().associateBy(Tab::ordinal)
- fun fromInt(type: Int) = map[type]
- }
-
- fun getTitle(): Int {
- return when (this) {
- Leagues -> R.string.leagues
- Events -> R.string.events
- }
- }
- }
-
- fun newInstance(bowler: Bowler): LeagueEventTabbedFragment {
- val fragment = LeagueEventTabbedFragment()
- fragment.arguments = Bundle().apply { putParcelable(ARG_BOWLER, bowler) }
- return fragment
- }
- }
-
- override val statisticsProviders: List by lazy {
- val bowler = bowler
- return@lazy if (bowler != null) {
- arrayListOf(StatisticsProvider.BowlerStatistics(bowler))
- } else {
- emptyList()
- }
- }
-
- private var bowler: Bowler? = null
-
- private val leagueFragment: LeagueListFragment?
- get() = findFragmentByPosition(Tab.Leagues.ordinal) as? LeagueListFragment
-
- private val eventFragment: LeagueListFragment?
- get() = findFragmentByPosition(Tab.Events.ordinal) as? LeagueListFragment
-
- // MARK: LeagueEventTabbedFragment
-
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
- bowler = arguments?.getParcelable(ARG_BOWLER)
- return super.onCreateView(inflater, container, savedInstanceState)
- }
-
- // MARK: BaseFragment
-
- override fun updateToolbarTitle() {
- bowler?.let { navigationActivity?.setToolbarTitle(it.name) }
- }
-
- // MARK: TabbedFragment
-
- override fun buildPagerAdapter(tabCount: Int): FragmentPagerAdapter {
- return LeagueEventPagerAdapter(childFragmentManager, tabCount, bowler)
- }
-
- override fun addTabs(tabLayout: TabLayout) {
- for (tab in Tab.values()) {
- tabLayout.addTab(tabLayout.newTab().setText(tab.getTitle()))
- }
- }
-
- override fun handleTabSwitch(newTab: Int) {}
-
- // MARK: IFloatingActionButtonHandler
-
- override fun getFabImage(): Int? {
- return R.drawable.ic_add
- }
-
- override fun onFabClick() {
- when (Tab.fromInt(currentTab)) {
- Tab.Leagues -> promptAddOrEditLeague(false)
- Tab.Events -> promptAddOrEditLeague(true)
- else -> throw RuntimeException("$currentTab is not a valid tab for LeagueEventTabbedFragment")
- }
- }
-
- // MARK: ListFragmentDelegate
-
- override fun onItemSelected(item: IIdentifiable, longPress: Boolean) {
- if (item is League) {
- if (longPress) {
- promptAddOrEditLeague(item.isEvent, item)
- } else {
- showSeries(item)
- Analytics.trackSelectLeague(item.isPractice, item.isEvent)
- }
- } else {
- throw RuntimeException("LeagueEventTabbedFragment can only handle League and item is $item")
- }
- }
-
- override fun onItemDeleted(item: IIdentifiable) {
- Analytics.trackDeleteLeague()
- }
-
- // MARK: LeagueDialogDelegate
-
- override fun onFinishLeague(league: League) {
- val fragment = if (league.isEvent) {
- eventFragment
- } else {
- leagueFragment
- }
- fragment?.refreshList(league)
- }
-
- override fun onDeleteLeague(league: League) {
- val fragment = if (league.isEvent) {
- eventFragment
- } else {
- leagueFragment
- }
- fragment?.refreshList(league)
- }
-
- // MARK: Private functions
-
- private fun promptAddOrEditLeague(isEvent: Boolean, league: League? = null) {
- val bowler = bowler ?: return
- val newFragment = LeagueDialog.newInstance(bowler, league, isEvent)
- fragmentNavigation?.pushDialogFragment(newFragment)
- }
-
- private fun showSeries(league: League) {
- if (league.isEvent) {
- launchEvent(league)
- } else {
- val newFragment = SeriesListFragment.newInstance(league)
- fragmentNavigation?.pushFragment(newFragment)
- }
- }
-
- private fun launchEvent(league: League) {
- assert(league.isEvent) { "You can only call launchEvent with... events." }
- val context = context ?: return
- launch(Android) {
- val eventSeries = Series.fetchAll(context, league).await().first()
- val newFragment = GameControllerFragment.newInstance(SeriesProvider.BowlerSeries(eventSeries, true))
- fragmentNavigation?.pushFragment(newFragment)
- }
- }
-
- // MARK: LeagueEventPagerAdapter
-
- class LeagueEventPagerAdapter(
- fragmentManager: FragmentManager,
- private val tabCount: Int,
- private val bowler: Bowler?
- ) : FragmentPagerAdapter(fragmentManager) {
-
- override fun getCount() = tabCount
-
- override fun getItem(position: Int): BaseFragment? {
- bowler?.let {
- return when (Tab.fromInt(position)) {
- Tab.Leagues -> LeagueListFragment.newInstance(bowler, LeagueListFragment.Companion.Show.Leagues)
- Tab.Events -> LeagueListFragment.newInstance(bowler, LeagueListFragment.Companion.Show.Events)
- else -> null
- }
- }
-
- return null
- }
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/NavigationActivity.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/NavigationActivity.kt
deleted file mode 100644
index 8f182b297..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/NavigationActivity.kt
+++ /dev/null
@@ -1,460 +0,0 @@
-package ca.josephroque.bowlingcompanion
-
-import android.content.pm.PackageManager
-import android.os.Bundle
-import android.support.annotation.IdRes
-import android.support.v4.app.Fragment
-import android.support.v4.app.FragmentTransaction
-import android.support.v4.view.GravityCompat
-import android.support.v4.widget.DrawerLayout
-import android.view.MenuItem
-import android.view.View
-import ca.josephroque.bowlingcompanion.bowlers.BowlerListFragment
-import ca.josephroque.bowlingcompanion.common.FabController
-import ca.josephroque.bowlingcompanion.common.NavigationDrawerController
-import ca.josephroque.bowlingcompanion.common.interfaces.IFloatingActionButtonHandler
-import ca.josephroque.bowlingcompanion.common.activities.BaseActivity
-import ca.josephroque.bowlingcompanion.common.fragments.BaseBottomSheetDialogFragment
-import ca.josephroque.bowlingcompanion.common.fragments.BaseDialogFragment
-import ca.josephroque.bowlingcompanion.common.fragments.BaseFragment
-import ca.josephroque.bowlingcompanion.common.fragments.TabbedFragment
-import ca.josephroque.bowlingcompanion.common.interfaces.IRefreshable
-import ca.josephroque.bowlingcompanion.games.GameControllerFragment
-import ca.josephroque.bowlingcompanion.series.SeriesListFragment
-import ca.josephroque.bowlingcompanion.statistics.interfaces.IStatisticsContext
-import ca.josephroque.bowlingcompanion.statistics.provider.StatisticsProviderListFragment
-import ca.josephroque.bowlingcompanion.teams.details.TeamDetailsFragment
-import ca.josephroque.bowlingcompanion.utils.Analytics
-import ca.josephroque.bowlingcompanion.utils.Permission
-import ca.josephroque.bowlingcompanion.utils.StartupManager
-import ca.josephroque.bowlingcompanion.utils.isVisible
-import com.ncapdevi.fragnav.FragNavController
-import com.ncapdevi.fragnav.FragNavTransactionOptions
-import kotlinx.android.synthetic.main.activity_navigation.bottom_navigation as bottomNavigation
-import kotlinx.android.synthetic.main.activity_navigation.drawer_layout as drawerLayout
-import kotlinx.android.synthetic.main.activity_navigation.fab as fab
-import kotlinx.android.synthetic.main.activity_navigation.nav_drawer as navDrawer
-import kotlinx.android.synthetic.main.activity_navigation.toolbar as toolbar
-import java.lang.ref.WeakReference
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Activity to handle navigation across the app and through sub-fragments.
- */
-class NavigationActivity : BaseActivity(),
- FragNavController.TransactionListener,
- FragNavController.RootFragmentListener,
- BaseFragment.FragmentNavigation,
- BaseFragment.FabProvider,
- NavigationDrawerController.NavigationDrawerProvider,
- TabbedFragment.TabbedFragmentDelegate,
- BaseDialogFragment.OnDismissListener,
- BaseBottomSheetDialogFragment.BaseBottomSheetDialogFragmentDelegate {
-
- companion object {
- @Suppress("unused")
- private const val TAG = "NavigationActivity"
-
- enum class BottomTab {
- Record, Statistics, Equipment;
-
- companion object {
- private val map = BottomTab.values().associateBy(BottomTab::ordinal)
- fun fromInt(type: Int) = available[type]
- fun toInt(type: BottomTab) = available.indexOf(type)
- fun fromId(@IdRes id: Int): BottomTab {
- return when (id) {
- R.id.action_record -> Record
- R.id.action_statistics -> Statistics
- R.id.action_equipment -> Equipment
- else -> throw RuntimeException("$id is not valid BottomTab id")
- }
- }
- fun toId(tab: BottomTab): Int {
- return when (tab) {
- Record -> R.id.action_record
- Statistics -> R.id.action_statistics
- Equipment -> R.id.action_equipment
- }
- }
-
- val available: List by lazy {
- map.entries.asSequence().filter { it.value.isAvailable }.map { it.value }.toList()
- }
- }
-
- val isAvailable: Boolean
- get() {
- return when (this) {
- Record -> true
- Statistics -> true
- Equipment -> false // FIXME: enable equipments tab when ready
- }
- }
- }
- }
-
- private var fragNavController: FragNavController? = null
- override lateinit var navigationDrawerController: NavigationDrawerController
- private lateinit var fabController: FabController
-
- private var poppedBack = false
-
- override val stackSize: Int
- get() = fragNavController?.currentStack?.size ?: 0
-
- private val currentFragment: BaseFragment?
- get() {
- for (fragment in supportFragmentManager.fragments) {
- if (fragment != null && fragment.isVisible) {
- return fragment as? BaseFragment ?: throw java.lang.RuntimeException("$fragment is not a BaseFragment")
- }
- }
- return null
- }
-
- val isFullscreen: Boolean
- get() = !bottomNavigation.isVisible
-
- private var currentBottomSheet: BaseBottomSheetDialogFragment.Companion.BottomSheetType? = null
-
- // MARK: Lifecycle functions
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_navigation)
-
- // If you don't have an analytics token available, comment out the following line and uncomment
- // the one after it to disable analytics altogether.
- // This is only available for debug builds, and a token must be provided for release builds
- Analytics.initialize(this)
- // Analytics.disableTracking()
-
- setupToolbar()
- setupNavigationDrawer() // Must be called after setupToolbar so that `handleNavigationDrawer` can set home indicator properly
- setupBottomNavigation()
- setupFab()
- setupFragNavController(savedInstanceState)
- StartupManager.start(this)
- }
-
- override fun onStop() {
- super.onStop()
- if (isFullscreen) {
- toggleFullscreen()
- }
- }
-
- override fun onDestroy() {
- super.onDestroy()
- Analytics.flush()
- }
-
- override fun onBackPressed() {
- if (fragNavController?.isStateSaved == true) {
- super.onBackPressed()
- return
- }
-
- val fragNavController = fragNavController
- if (fragNavController != null) {
- if (currentFragment?.popChildFragment() == true) {
- return
- }
-
- if (BottomTab.fromInt(fragNavController.currentStackIndex) == BottomTab.Statistics && fragNavController.isRootFragment) {
- fragNavController.switchTab(BottomTab.Record.ordinal)
- bottomNavigation.selectedItemId = BottomTab.toId(BottomTab.Record)
- return
- }
-
- if (fragNavController.isRootFragment || fragNavController.popFragment().not()) {
- super.onBackPressed()
- }
- } else {
- super.onBackPressed()
- }
- }
-
- override fun onSupportNavigateUp(): Boolean {
- onBackPressed()
- return true
- }
-
- override fun onOptionsItemSelected(item: MenuItem): Boolean {
- currentFragment?.let {
- if (item.itemId == android.R.id.home && currentFragment is NavigationDrawerController.NavigationDrawerHandler) {
- drawerLayout.openDrawer(GravityCompat.START)
- return true
- }
- }
-
- return super.onOptionsItemSelected(item)
- }
-
- override fun onSaveInstanceState(outState: Bundle?) {
- super.onSaveInstanceState(outState)
- fragNavController?.onSaveInstanceState(outState!!)
- }
-
- // MARK: ActivityCompat
-
- override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) {
- if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
- currentFragment?.permissionGranted(Permission.fromRequestCode(requestCode))
- }
- }
-
- // MARK: FragmentNavigation
-
- override fun pushFragment(fragment: BaseFragment) {
- val transactionOptions = FragNavTransactionOptions.newBuilder()
- .transition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
- .build()
- fragNavController?.pushFragment(fragment, transactionOptions)
- }
-
- override fun pushDialogFragment(fragment: BaseDialogFragment) {
- fragment.onDismissListener = this
- fragNavController?.showDialogFragment(fragment)
- }
-
- override fun showBottomSheet(fragment: BaseBottomSheetDialogFragment, tag: String) {
- currentBottomSheet = BaseBottomSheetDialogFragment.getBottomSheetType(fragment)
- fragment.show(supportFragmentManager, tag)
- }
-
- // MARK: BaseBottomSheetDialogFragmentDelegate
-
- @Suppress("UNCHECKED_CAST")
- override fun getBottomSheetDelegate(): Delegate? {
- return when (currentBottomSheet) {
- BaseBottomSheetDialogFragment.Companion.BottomSheetType.MatchPlay -> currentFragment as? Delegate
- else -> null
- }
- }
-
- override fun bottomSheetDetached() {
- currentBottomSheet = null
- }
-
- // MARK: FabProvider
-
- override fun invalidateFab() {
- val fragment = currentFragment
- fabController.image = if (fragment is IFloatingActionButtonHandler) {
- fragment.getFabImage()
- } else {
- null
- }
- }
-
- // MARK: RootFragmentListener
-
- override fun getRootFragment(index: Int): Fragment {
- val tab = BottomTab.fromInt(index)
- val fragmentName: String
- fragmentName = when (tab) {
- BottomTab.Record -> BowlerTeamTabbedFragment::class.java.name
- BottomTab.Equipment -> BowlerListFragment::class.java.name // FIXME: enable equipment tab
- BottomTab.Statistics -> StatisticsProviderListFragment::class.java.name
- }
-
- return BaseFragment.newInstance(fragmentName)
- }
-
- // MARK: TransactionListener
-
- override fun onFragmentTransaction(fragment: Fragment?, transactionType: FragNavController.TransactionType?) {
- handleFragmentChange(fragment, transactionType)
- }
-
- override fun onTabTransaction(fragment: Fragment?, index: Int) {
- handleFragmentChange(fragment)
- }
-
- // MARK: TabbedFragmentDelegate
-
- override fun onTabSwitched() {
- invalidateFab()
- }
-
- // MARK: NavigationActivity
-
- fun setToolbarTitle(title: String? = null, subtitle: String? = null) {
- supportActionBar?.title = title
- supportActionBar?.subtitle = subtitle
- }
-
- fun toggleFullscreen() {
- if (!isFullscreen) {
- supportActionBar?.hide()
- bottomNavigation.visibility = View.GONE
- } else {
- supportActionBar?.show()
- bottomNavigation.visibility = View.VISIBLE
- }
- }
-
- // MARK: Private functions
-
- private fun handleFragmentChange(fragment: Fragment?, transactionType: FragNavController.TransactionType? = null) {
- fragNavController?.let {
- val showBackButton = it.isRootFragment.not() || BottomTab.fromInt(it.currentStackIndex) == BottomTab.Statistics
- supportActionBar?.setDisplayHomeAsUpEnabled(showBackButton)
- }
-
- fabController.image = if (fragment is IFloatingActionButtonHandler) {
- fragment.getFabImage()
- } else {
- null
- }
-
- handleNavigationDrawer(fragment)
-
- toolbar.elevation = if (fragment is TabbedFragment) {
- 0F
- } else {
- resources.getDimension(R.dimen.base_elevation)
- }
-
- if (isFullscreen) {
- toggleFullscreen()
- }
-
- if (poppedBack) {
- poppedBack = false
- refreshCurrentFragment()
- }
-
- if (fragment is StatisticsProviderListFragment && transactionType != FragNavController.TransactionType.POP) {
- val statisticsContext = fragNavController?.getStack(BottomTab.toInt(BottomTab.Record))?.peek() as? IStatisticsContext
- fragment.arguments = StatisticsProviderListFragment.buildArguments(statisticsContext?.statisticsProviders ?: emptyList())
- }
- }
-
- private fun handleNavigationDrawer(fragment: Fragment?) {
- if (fragment is NavigationDrawerController.NavigationDrawerHandler) {
- supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_menu)
- drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
- } else {
- navigationDrawerController.apply {
- isTeamMember = false
- isEvent = false
- gameNumber = 0
- numberOfGames = 0
- bowlerName = null
- leagueName = null
- }
- supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_arrow_back)
- drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
- }
- }
-
- private fun setupToolbar() {
- setSupportActionBar(toolbar)
- supportActionBar?.setDisplayHomeAsUpEnabled(true)
- supportActionBar?.setDisplayShowHomeEnabled(true)
- }
-
- private fun setupBottomNavigation() {
- val unavailableTabs: Set = BottomTab.values().toSet() - BottomTab.available.toSet()
- if (unavailableTabs.isNotEmpty()) {
- unavailableTabs.forEach { bottomNavigation.menu.removeItem(BottomTab.toId(it)) }
- bottomNavigation.invalidate()
- }
-
- bottomNavigation.setOnNavigationItemSelectedListener {
- fragNavController?.switchTab(BottomTab.fromId(it.itemId).ordinal)
- return@setOnNavigationItemSelectedListener true
- }
- }
-
- private fun setupNavigationDrawer() {
- navigationDrawerController = NavigationDrawerController(WeakReference(navDrawer))
- navDrawer.setNavigationItemSelectedListener { menuItem ->
- if (menuItem.isCheckable) {
- menuItem.isChecked = true
- }
- drawerLayout.closeDrawers()
-
- when (menuItem.itemId) {
- R.id.nav_bowlers_teams -> popBackTo(BowlerTeamTabbedFragment::class.java.name)
- R.id.nav_leagues_events -> {
- popBackTo(TeamDetailsFragment::class.java.name)
- popBackTo(LeagueEventTabbedFragment::class.java.name)
- }
- R.id.nav_series -> popBackTo(SeriesListFragment::class.java.name)
- R.id.nav_feedback -> prepareFeedbackEmail()
- R.id.nav_settings -> openSettings()
- else -> {
- currentFragment?.let {
- if (it is NavigationDrawerController.NavigationDrawerHandler) {
- it.onNavDrawerItemSelected(menuItem.itemId)
- }
- }
- }
- }
-
- return@setNavigationItemSelectedListener true
- }
-
- supportFragmentManager.fragments.forEach {
- if (it is NavigationDrawerController.NavigationDrawerHandler) {
- handleNavigationDrawer(it)
- }
- }
- }
-
- private fun setupFab() {
- fabController = FabController(fab, View.OnClickListener {
- val currentFragment = currentFragment ?: return@OnClickListener
- if (currentFragment is IFloatingActionButtonHandler) {
- currentFragment.onFabClick()
- }
- })
- }
-
- private fun setupFragNavController(savedInstanceState: Bundle?) {
- val builder = FragNavController.newBuilder(savedInstanceState, supportFragmentManager, R.id.fragment_container)
- .rootFragmentListener(this@NavigationActivity, BottomTab.available.size)
- .transactionListener(this@NavigationActivity)
- // FIXME: look into .fragmentHideStrategy(FragNavController.HIDE), .eager(true)
- fragNavController = builder.build()
- }
-
- private fun popBackTo(fragmentName: String) {
- val fragments = fragNavController?.currentStack ?: return
- var popTarget: Int? = null
-
- for (i in 0 until fragments.size) {
- if (fragments[i]::class.java.name == fragmentName) {
- popTarget = fragments.size - i - 1
- break
- }
- }
-
- popTarget?.let {
- poppedBack = true
- val currentFragment = this@NavigationActivity.currentFragment
- if (currentFragment is GameControllerFragment) {
- currentFragment.prepareToPop()
- }
- fragNavController?.popFragments(it)
- }
- }
-
- private fun refreshCurrentFragment() {
- val currentFragment = currentFragment ?: return
- if (currentFragment is IRefreshable) {
- currentFragment.refresh()
- }
- }
-
- // MARK: OnDismissListener
-
- override fun onDismiss(dismissedFragment: BaseDialogFragment) {
- refreshCurrentFragment()
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/bowlers/Bowler.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/bowlers/Bowler.kt
deleted file mode 100644
index d9a6ebf15..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/bowlers/Bowler.kt
+++ /dev/null
@@ -1,368 +0,0 @@
-package ca.josephroque.bowlingcompanion.bowlers
-
-import android.content.ContentValues
-import android.content.Context
-import android.database.Cursor
-import android.os.Parcel
-import android.support.v7.preference.PreferenceManager
-import android.util.Log
-import ca.josephroque.bowlingcompanion.R
-import ca.josephroque.bowlingcompanion.common.interfaces.INameAverage
-import ca.josephroque.bowlingcompanion.common.interfaces.KParcelable
-import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator
-import ca.josephroque.bowlingcompanion.database.Annihilator
-import ca.josephroque.bowlingcompanion.database.Contract.GameEntry
-import ca.josephroque.bowlingcompanion.database.Contract.LeagueEntry
-import ca.josephroque.bowlingcompanion.database.Contract.SeriesEntry
-import ca.josephroque.bowlingcompanion.leagues.League
-import ca.josephroque.bowlingcompanion.settings.Settings
-import kotlinx.coroutines.experimental.CommonPool
-import kotlinx.coroutines.experimental.Deferred
-import kotlinx.coroutines.experimental.async
-import java.text.SimpleDateFormat
-import java.util.Date
-import java.util.Locale
-import ca.josephroque.bowlingcompanion.database.Contract.BowlerEntry
-import ca.josephroque.bowlingcompanion.database.DatabaseManager
-import ca.josephroque.bowlingcompanion.scoring.Average
-import ca.josephroque.bowlingcompanion.utils.BCError
-import ca.josephroque.bowlingcompanion.utils.Preferences
-import java.lang.ref.WeakReference
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * A single Bowler, who has leagues, events, series, games, and stats.
- */
-class Bowler(
- override val id: Long,
- override val name: String,
- override val average: Double
-) : INameAverage, KParcelable {
-
- private var _isDeleted: Boolean = false
- override val isDeleted: Boolean
- get() = _isDeleted
-
- // MARK: Constructors
-
- private constructor(p: Parcel): this(
- id = p.readLong(),
- name = p.readString()!!,
- average = p.readDouble()
- )
-
- constructor(bowler: Bowler): this(
- id = bowler.id,
- name = bowler.name,
- average = bowler.average
- )
-
- // MARK: KParcelable
-
- override fun writeToParcel(dest: Parcel, flags: Int) = with(dest) {
- writeLong(id)
- writeString(name)
- writeDouble(average)
- }
-
- // MARK: Bowler
-
- fun fetchLeagues(context: Context): Deferred> {
- return League.fetchAll(context, this)
- }
-
- fun fetchEvents(context: Context): Deferred> {
- return League.fetchAll(context, this, false, true)
- }
-
- fun fetchLeaguesAndEvents(context: Context): Deferred> {
- return League.fetchAll(context, this, true, true)
- }
-
- // MARK: IDeletable
-
- override fun markForDeletion(): Bowler {
- val newInstance = Bowler(this)
- newInstance._isDeleted = true
- return newInstance
- }
-
- override fun cleanDeletion(): Bowler {
- val newInstance = Bowler(this)
- newInstance._isDeleted = false
- return newInstance
- }
-
- override fun delete(context: Context): Deferred {
- return async(CommonPool) {
- if (id < 0) {
- return@async
- }
-
- Annihilator.instance.delete(
- weakContext = WeakReference(context),
- tableName = BowlerEntry.TABLE_NAME,
- whereClause = "${BowlerEntry._ID}=?",
- whereArgs = arrayOf(id.toString())
- )
- }
- }
-
- companion object {
- @Suppress("unused")
- private const val TAG = "Bowler"
-
- @Suppress("unused")
- @JvmField val CREATOR = parcelableCreator(::Bowler)
-
- val REGEX_NAME = "^[A-Za-z0-9]+[ A-Za-z0-9'!@#$%^&*()_+:\"?/~-]*[A-Za-z0-9'!@#$%^&*()_+:\"?/~-]*$".toRegex()
-
- enum class Sort {
- Alphabetically,
- LastModified;
-
- companion object {
- private val map = Sort.values().associateBy(Sort::ordinal)
- fun fromInt(type: Int) = map[type]
- }
- }
-
- private fun isBowlerNameValid(name: String): Boolean = REGEX_NAME.matches(name)
-
- private fun isBowlerNameUnique(context: Context, name: String, id: Long = -1): Deferred {
- return async(CommonPool) {
- val database = DatabaseManager.getReadableDatabase(context).await()
-
- var cursor: Cursor? = null
- try {
- cursor = database.query(
- BowlerEntry.TABLE_NAME,
- arrayOf(BowlerEntry.COLUMN_BOWLER_NAME),
- "${BowlerEntry.COLUMN_BOWLER_NAME}=? AND ${BowlerEntry._ID}!=?",
- arrayOf(name, id.toString()),
- "",
- "",
- ""
- )
-
- if ((cursor?.count ?: 0) > 0) {
- return@async false
- }
- } finally {
- if (cursor != null && !cursor.isClosed) {
- cursor.close()
- }
- }
-
- true
- }
- }
-
- private fun validateSavePreconditions(context: Context, id: Long, name: String): Deferred {
- return async(CommonPool) {
- val errorTitle = R.string.issue_saving_bowler
- val errorMessage: Int? = if (!isBowlerNameValid(name)) {
- R.string.error_bowler_name_invalid
- } else if (!isBowlerNameUnique(context, name, id).await()) {
- R.string.error_bowler_name_in_use
- } else {
- null
- }
-
- return@async if (errorMessage != null) {
- BCError(errorTitle, errorMessage, BCError.Severity.Warning)
- } else {
- null
- }
- }
- }
-
- fun save(context: Context, id: Long, name: String, average: Double = 0.0): Deferred> {
- return if (id < 0) {
- createNewAndSave(context, name)
- } else {
- update(context, id, name, average)
- }
- }
-
- private fun createNewAndSave(context: Context, name: String): Deferred> {
- return async(CommonPool) {
- val error = validateSavePreconditions(context, -1, name).await()
- if (error != null) {
- return@async Pair(null, error)
- }
-
- val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CANADA)
- val currentDate = dateFormat.format(Date())
-
- var values = ContentValues().apply {
- put(BowlerEntry.COLUMN_BOWLER_NAME, name)
- put(BowlerEntry.COLUMN_DATE_MODIFIED, currentDate)
- }
-
- val bowlerId: Long
- val database = DatabaseManager.getWritableDatabase(context).await()
- database.beginTransaction()
- try {
- bowlerId = database.insert(BowlerEntry.TABLE_NAME, null, values)
-
- if (bowlerId != -1L) {
- values = ContentValues().apply {
- put(LeagueEntry.COLUMN_LEAGUE_NAME, League.PRACTICE_LEAGUE_NAME)
- put(LeagueEntry.COLUMN_DATE_MODIFIED, currentDate)
- put(LeagueEntry.COLUMN_BOWLER_ID, bowlerId)
- put(LeagueEntry.COLUMN_NUMBER_OF_GAMES, League.DEFAULT_NUMBER_OF_GAMES)
- }
- database.insert(LeagueEntry.TABLE_NAME, null, values)
- }
-
- database.setTransactionSuccessful()
- } catch (ex: Exception) {
- Log.e(TAG, "Could not create a new bowler")
- return@async Pair(
- null,
- BCError(R.string.error_saving_bowler, R.string.error_bowler_not_saved)
- )
- } finally {
- database.endTransaction()
- }
-
- Pair(Bowler(bowlerId, name, 0.0), null)
- }
- }
-
- private fun update(context: Context, id: Long, name: String, average: Double): Deferred> {
- return async(CommonPool) {
- val error = validateSavePreconditions(context, id, name).await()
- if (error != null) {
- return@async Pair(null, error)
- }
-
- val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CANADA)
- val currentDate = dateFormat.format(Date())
-
- val values = ContentValues().apply {
- put(BowlerEntry.COLUMN_BOWLER_NAME, name)
- put(BowlerEntry.COLUMN_DATE_MODIFIED, currentDate)
- }
-
- val database = DatabaseManager.getWritableDatabase(context).await()
- database.beginTransaction()
- try {
- database.update(BowlerEntry.TABLE_NAME, values, "${BowlerEntry._ID}=?", arrayOf(id.toString()))
- database.setTransactionSuccessful()
- } catch (ex: Exception) {
- Log.e(TAG, "Could not update bowler")
- return@async Pair(
- null,
- BCError(R.string.error_saving_bowler, R.string.error_bowler_not_saved)
- )
- } finally {
- database.endTransaction()
- }
-
- Pair(Bowler(id, name, average), null)
- }
- }
-
- fun fetch(context: Context, id: Long): Deferred {
- return async(CommonPool) {
- val bowlerList = fetchAll(context, id).await()
- if (bowlerList.size == 1) {
- bowlerList[0]
- } else {
- null
- }
- }
- }
-
- fun fetchAll(context: Context, filterId: Long = -1): Deferred> {
- return async(CommonPool) {
- val bowlers: MutableList = ArrayList()
- val database = DatabaseManager.getReadableDatabase(context).await()
-
- val preferences = PreferenceManager.getDefaultSharedPreferences(context)
- val includeEvents = Settings.BooleanSetting.IncludeEvents.getValue(preferences)
- val includeOpen = Settings.BooleanSetting.IncludeOpen.getValue(preferences)
- val sortBy = Sort.fromInt(preferences.getInt(Preferences.BOWLER_SORT_ORDER, Sort.Alphabetically.ordinal))
-
- val gameSumAndCountQuery = ("SELECT " +
- "league2.${LeagueEntry._ID} AS lid2, " +
- "SUM(game2.${GameEntry.COLUMN_SCORE}) AS gameSum, " +
- "COUNT(game2.${GameEntry._ID}) AS gameCount " +
- "FROM ${LeagueEntry.TABLE_NAME} AS league2 " +
- "INNER JOIN ${SeriesEntry.TABLE_NAME} AS series2 " +
- "ON lid2=${SeriesEntry.COLUMN_LEAGUE_ID} " +
- "INNER JOIN ${GameEntry.TABLE_NAME} AS game2 " +
- "ON series2.${SeriesEntry._ID}=${GameEntry.COLUMN_SERIES_ID} " +
- "WHERE game2.${GameEntry.COLUMN_SCORE}>? " +
- "AND " + (if (!includeEvents) LeagueEntry.COLUMN_IS_EVENT else "'0'") + "=? " +
- "AND " + (if (!includeOpen) LeagueEntry.COLUMN_LEAGUE_NAME + "!" else "'0'") + "=? " +
- "GROUP BY league2.${LeagueEntry._ID}")
-
- val additionalAverageQuery = ("SELECT " +
- "league3.${LeagueEntry._ID} AS lid3, " +
- "${LeagueEntry.COLUMN_ADDITIONAL_PINFALL} AS additionalPinfall, " +
- "${LeagueEntry.COLUMN_ADDITIONAL_GAMES} AS additionalGames " +
- "FROM ${LeagueEntry.TABLE_NAME} AS league3 " +
- "WHERE league3.${LeagueEntry.COLUMN_ADDITIONAL_PINFALL}>?")
-
- val orderQueryBy = if (sortBy == Sort.Alphabetically) {
- "ORDER BY bowler.${BowlerEntry.COLUMN_BOWLER_NAME} "
- } else {
- "ORDER BY bowler.${BowlerEntry.COLUMN_DATE_MODIFIED} DESC "
- }
-
- // Query to retrieve bowler names and averages from database
- val rawBowlerQuery = ("SELECT " +
- "bowler.${BowlerEntry.COLUMN_BOWLER_NAME}, " +
- "bowler.${BowlerEntry._ID} AS bid, " +
- "SUM(t.gameSum) AS totalPinfall, " +
- "SUM(t.gameCount) AS totalGames, " +
- "SUM(u.additionalPinfall) AS totalAdditionalPinfall, " +
- "SUM(u.additionalGames) AS totalAdditionalGames " +
- "FROM ${BowlerEntry.TABLE_NAME} AS bowler " +
- "LEFT JOIN ${LeagueEntry.TABLE_NAME} AS league " +
- "ON bowler.${BowlerEntry._ID}=${LeagueEntry.COLUMN_BOWLER_ID} " +
- "LEFT JOIN ($gameSumAndCountQuery) AS t " +
- "ON t.lid2=league.${LeagueEntry._ID} " +
- "LEFT JOIN ($additionalAverageQuery) AS u " +
- "ON u.lid3=league.${LeagueEntry._ID} " +
- "WHERE " + (if (filterId != -1L) "bid" else "'0'") + "=? " +
- "GROUP BY bowler.${BowlerEntry._ID} " +
- orderQueryBy)
-
- val rawBowlerArgs = arrayOf(
- /* game2.SCORE > */ 0.toString(),
- /* COLUMN_IS_EVENT || 0 = */ 0.toString(),
- /* COLUMN_LEAGUE_NAME || 0 = */ if (!includeOpen) League.PRACTICE_LEAGUE_NAME else 0.toString(),
- /* COLUMN_ADDITIONAL_PINFALL > */ 0.toString(),
- /* bid || 0 */ if (filterId != -1L) filterId.toString() else 0.toString())
-
- // Adds loaded bowler names and averages to lists to display
- var cursor: Cursor? = null
- try {
- cursor = database.rawQuery(rawBowlerQuery, rawBowlerArgs)
- while (cursor.moveToNext()) {
- val totalPinfall = cursor.getInt(cursor.getColumnIndex("totalPinfall"))
- val totalGames = cursor.getInt(cursor.getColumnIndex("totalGames"))
- val totalAdditionalPinfall = cursor.getInt(cursor.getColumnIndex("totalAdditionalPinfall"))
- val totalAdditionalGames = cursor.getInt(cursor.getColumnIndex("totalAdditionalGames"))
- val bowlerAverage = Average.getAdjustedAverage(totalPinfall, totalGames, totalAdditionalPinfall, totalAdditionalGames)
-
- val bowler = Bowler(
- cursor.getLong(cursor.getColumnIndex("bid")),
- cursor.getString(cursor.getColumnIndex(BowlerEntry.COLUMN_BOWLER_NAME)),
- bowlerAverage)
- bowlers.add(bowler)
- }
- } finally {
- cursor?.close()
- }
-
- bowlers
- }
- }
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/bowlers/BowlerDialog.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/bowlers/BowlerDialog.kt
deleted file mode 100644
index c1326497b..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/bowlers/BowlerDialog.kt
+++ /dev/null
@@ -1,212 +0,0 @@
-package ca.josephroque.bowlingcompanion.bowlers
-
-import android.app.Dialog
-import android.content.Context
-import android.os.Bundle
-import android.support.v4.app.DialogFragment
-import android.support.v7.app.AlertDialog
-import android.text.Editable
-import android.text.TextWatcher
-import android.view.LayoutInflater
-import android.view.Window
-import android.view.View
-import android.view.ViewGroup
-import android.view.inputmethod.InputMethodManager
-import ca.josephroque.bowlingcompanion.App
-import ca.josephroque.bowlingcompanion.R
-import ca.josephroque.bowlingcompanion.common.Android
-import ca.josephroque.bowlingcompanion.common.fragments.BaseDialogFragment
-import ca.josephroque.bowlingcompanion.utils.Analytics
-import ca.josephroque.bowlingcompanion.utils.Color
-import ca.josephroque.bowlingcompanion.utils.safeLet
-import kotlinx.android.synthetic.main.dialog_bowler.input_name as nameInput
-import kotlinx.android.synthetic.main.dialog_bowler.btn_delete as deleteButton
-import kotlinx.android.synthetic.main.dialog_bowler.view.*
-import kotlinx.coroutines.experimental.launch
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Dialog to create a new bowler.
- */
-class BowlerDialog : BaseDialogFragment() {
-
- companion object {
- @Suppress("unused")
- private const val TAG = "BowlerDialog"
-
- private const val ARG_BOWLER = "${TAG}_BOWLER"
-
- fun newInstance(bowler: Bowler?): BowlerDialog {
- val dialog = BowlerDialog()
- dialog.arguments = Bundle().apply { bowler?.let { putParcelable(ARG_BOWLER, bowler) } }
- return dialog
- }
- }
-
- private var bowler: Bowler? = null
- private var delegate: BowlerDialogDelegate? = null
-
- private val deleteListener = View.OnClickListener {
- safeLet(context, bowler) { context, bowler ->
- AlertDialog.Builder(context)
- .setTitle(String.format(context.resources.getString(R.string.query_delete_item), bowler.name))
- .setMessage(R.string.dialog_delete_item_message)
- .setPositiveButton(R.string.delete) { _, _ ->
- delegate?.onDeleteBowler(bowler)
- dismiss()
- }
- .setNegativeButton(R.string.cancel, null)
- .show()
- }
- }
-
- // MARK: Lifecycle functions
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setStyle(DialogFragment.STYLE_NORMAL, R.style.Dialog)
- }
-
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
- bowler = arguments?.getParcelable(ARG_BOWLER)
-
- val rootView = inflater.inflate(R.layout.dialog_bowler, container, false)
- bowler?.let { resetInputs(it, rootView) }
- setupToolbar(rootView)
- setupInput(rootView)
- return rootView
- }
-
- override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
- val dialog = super.onCreateDialog(savedInstanceState)
- dialog.requestWindowFeature(Window.FEATURE_NO_TITLE)
- return dialog
- }
-
- override fun onAttach(context: Context?) {
- super.onAttach(context)
- val parentFragment = parentFragment as? BowlerDialogDelegate ?: throw RuntimeException("${parentFragment!!} must implement BowlerDialogDelegate")
- delegate = parentFragment
- }
-
- override fun onDetach() {
- super.onDetach()
- delegate = null
- }
-
- override fun onStart() {
- super.onStart()
- dialog.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
-
- // Requesting input focus and showing keyboard
- nameInput.requestFocus()
- val imm = activity?.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
- imm.showSoftInput(nameInput, InputMethodManager.SHOW_IMPLICIT)
-
- bowler?.let { deleteButton.visibility = View.VISIBLE }
- nameInput.text?.let { nameInput.setSelection(it.length) }
- updateSaveButton()
- }
-
- override fun dismiss() {
- App.hideSoftKeyBoard(activity!!)
- activity?.supportFragmentManager?.popBackStack()
- super.dismiss()
- }
-
- // MARK: Private functions
-
- private fun setupToolbar(rootView: View) {
- if (bowler == null) {
- rootView.toolbar_bowler.setTitle(R.string.new_bowler)
- } else {
- rootView.toolbar_bowler.setTitle(R.string.edit_bowler)
- }
-
- rootView.toolbar_bowler.apply {
- inflateMenu(R.menu.dialog_bowler)
- menu.findItem(R.id.action_save).isEnabled = bowler?.name?.isNotEmpty() == true
- setNavigationIcon(R.drawable.ic_dismiss)
- setNavigationOnClickListener {
- dismiss()
- }
- setOnMenuItemClickListener {
- when (it.itemId) {
- R.id.action_save -> {
- saveBowler()
- true
- }
- else -> super.onOptionsItemSelected(it)
- }
- }
- }
- }
-
- private fun setupInput(rootView: View) {
- rootView.btn_delete.setOnClickListener(deleteListener)
- rootView.input_name.addTextChangedListener(object : TextWatcher {
- override fun afterTextChanged(s: Editable?) {}
- override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
- override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
- updateSaveButton()
- }
- })
- }
-
- private fun canSave() = nameInput.text?.isNotEmpty() ?: false
-
- private fun updateSaveButton() {
- val saveButton = view?.toolbar_bowler?.menu?.findItem(R.id.action_save)
- if (canSave()) {
- saveButton?.isEnabled = true
- saveButton?.icon?.alpha = Color.ALPHA_ENABLED
- } else {
- saveButton?.isEnabled = false
- saveButton?.icon?.alpha = Color.ALPHA_DISABLED
- }
- }
-
- private fun saveBowler() {
- launch(Android) {
- this@BowlerDialog.context?.let { context ->
- val name = nameInput.text.toString()
-
- if (canSave()) {
- val oldBowler = bowler
- val (newBowler, error) = if (oldBowler != null) {
- Bowler.save(context, oldBowler.id, name, oldBowler.average).await()
- } else {
- Bowler.save(context, -1, name).await()
- }
-
- if (error != null) {
- error.show(context)
- bowler?.let { resetInputs(it) }
- } else if (newBowler != null) {
- dismiss()
- delegate?.onFinishBowler(newBowler)
-
- if (oldBowler == null) {
- Analytics.trackCreateBowler()
- } else {
- Analytics.trackEditBowler()
- }
- }
- }
- }
- }
- }
-
- private fun resetInputs(bowler: Bowler, rootView: View? = null) {
- val nameInput = rootView?.input_name ?: this.nameInput
- nameInput.setText(bowler.name)
- }
-
- // MARK: BowlerDialogDelegate
-
- interface BowlerDialogDelegate {
- fun onFinishBowler(bowler: Bowler)
- fun onDeleteBowler(bowler: Bowler)
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/bowlers/BowlerListFragment.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/bowlers/BowlerListFragment.kt
deleted file mode 100644
index 7b1c53b10..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/bowlers/BowlerListFragment.kt
+++ /dev/null
@@ -1,114 +0,0 @@
-package ca.josephroque.bowlingcompanion.bowlers
-
-import android.graphics.Color
-import android.os.Bundle
-import android.preference.PreferenceManager
-import android.support.v7.app.AlertDialog
-import android.view.LayoutInflater
-import android.view.Menu
-import android.view.MenuInflater
-import android.view.MenuItem
-import android.view.View
-import android.view.ViewGroup
-import ca.josephroque.bowlingcompanion.BowlerTeamTabbedFragment
-import ca.josephroque.bowlingcompanion.R
-import ca.josephroque.bowlingcompanion.common.adapters.NameAverageRecyclerViewAdapter
-import ca.josephroque.bowlingcompanion.common.fragments.ListFragment
-import ca.josephroque.bowlingcompanion.utils.Analytics
-import ca.josephroque.bowlingcompanion.utils.Preferences
-import kotlinx.coroutines.experimental.CommonPool
-import kotlinx.coroutines.experimental.Deferred
-import kotlinx.coroutines.experimental.async
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * A fragment representing a list of Bowlers.
- */
-class BowlerListFragment : ListFragment>() {
-
- companion object {
- @Suppress("unused")
- private const val TAG = "BowlerListFragment"
-
- fun newInstance(): BowlerListFragment {
- return BowlerListFragment()
- }
- }
-
- override val emptyViewImage = R.drawable.empty_view_bowlers
- override val emptyViewText = R.string.empty_view_bowlers
-
- // MARK Lifecycle functions
-
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
- setHasOptionsMenu(true)
- return super.onCreateView(inflater, container, savedInstanceState)
- }
-
- override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
- super.onCreateOptionsMenu(menu, inflater)
- inflater.inflate(R.menu.fragment_bowlers, menu)
- }
-
- override fun onOptionsItemSelected(item: MenuItem): Boolean {
- return when (item.itemId) {
- R.id.action_sort_by -> {
- showSortByDialog()
- true
- }
- else -> super.onOptionsItemSelected(item)
- }
- }
-
- // MARK: ListFragment
-
- override fun buildAdapter(): NameAverageRecyclerViewAdapter {
- val adapter = NameAverageRecyclerViewAdapter(emptyList(), this)
- adapter.swipeable = true
- adapter.longPressable = true
- adapter.buildImageResource = { _, _ -> Pair(R.drawable.ic_person, Color.BLACK) }
- return adapter
- }
-
- override fun fetchItems(): Deferred> {
- context?.let {
- return Bowler.fetchAll(it)
- }
-
- return async(CommonPool) {
- mutableListOf()
- }
- }
-
- // MARK: BaseFragment
-
- override fun updateToolbarTitle() {
- // Intentionally left blank
- }
-
- // MARK: Private functions
-
- private fun showSortByDialog() {
- context?.let {
- AlertDialog.Builder(it)
- .setTitle(it.resources.getString(R.string.sort_items))
- .setItems(R.array.bowler_sort_options) { _, which: Int ->
- val order = Bowler.Companion.Sort.fromInt(which)
- order?.let { sort ->
- PreferenceManager.getDefaultSharedPreferences(context)
- .edit()
- .putInt(Preferences.BOWLER_SORT_ORDER, sort.ordinal)
- .apply()
-
- val ignoredSet: MutableSet = HashSet()
- ignoredSet.add(BowlerTeamTabbedFragment.Companion.Tab.Bowlers.ordinal)
- (parentFragment as? BowlerTeamTabbedFragment)?.refreshTabs(ignoredSet)
-
- Analytics.trackSortedBowlers(order)
- }
- }
- .show()
- }
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/Android.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/Android.kt
deleted file mode 100644
index 70abc5c17..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/Android.kt
+++ /dev/null
@@ -1,47 +0,0 @@
-package ca.josephroque.bowlingcompanion.common
-
-/*
- * Copyright 2017 Miguel Castiblanco
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import android.os.Handler
-import android.os.Looper
-import kotlin.coroutines.experimental.AbstractCoroutineContextElement
-import kotlin.coroutines.experimental.Continuation
-import kotlin.coroutines.experimental.ContinuationInterceptor
-
-/**
- * Android Continuation, guarantees that, when resumed, is on the UI Thread
- * Created by macastiblancot on 2/13/17.
- */
-private class AndroidContinuation(val cont: Continuation) : Continuation by cont {
- override fun resume(value: T) {
- if (Looper.myLooper() == Looper.getMainLooper()) cont.resume(value)
- else Handler(Looper.getMainLooper()).post { cont.resume(value) }
- }
-
- override fun resumeWithException(exception: Throwable) {
- if (Looper.myLooper() == Looper.getMainLooper()) cont.resumeWithException(exception)
- else Handler(Looper.getMainLooper()).post { cont.resumeWithException(exception) }
- }
-}
-
-/**
- * Android context, provides an AndroidContinuation, executes everything on the UI Thread
- */
-object Android : AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor {
- override fun interceptContinuation(continuation: Continuation): Continuation =
- AndroidContinuation(continuation)
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/CustomScrollingViewPager.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/CustomScrollingViewPager.kt
deleted file mode 100644
index 36170f0fe..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/CustomScrollingViewPager.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-package ca.josephroque.bowlingcompanion.common
-
-import android.annotation.SuppressLint
-import android.content.Context
-import android.support.v4.view.ViewPager
-import android.util.AttributeSet
-import android.view.MotionEvent
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Enable or disable scrolling in a ViewPager.
- */
-class CustomScrollingViewPager : ViewPager {
-
- constructor(context: Context) : this(context, null)
- constructor(context: Context, attrs: AttributeSet?): super(context, attrs)
-
- var scrollingEnabled = true
-
- @SuppressLint("ClickableViewAccessibility")
- override fun onTouchEvent(ev: MotionEvent?): Boolean {
- return scrollingEnabled && super.onTouchEvent(ev)
- }
-
- override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
- return scrollingEnabled && super.onInterceptTouchEvent(ev)
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/FabController.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/FabController.kt
deleted file mode 100644
index bfa5ce3bd..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/FabController.kt
+++ /dev/null
@@ -1,38 +0,0 @@
-package ca.josephroque.bowlingcompanion.common
-
-import android.graphics.Color
-import android.support.design.widget.FloatingActionButton
-import android.view.View
-import ca.josephroque.bowlingcompanion.utils.isVisible
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Control the floating action button with common actions.
- */
-class FabController(private val floatingActionButton: FloatingActionButton, listener: View.OnClickListener) {
- init {
- floatingActionButton.setOnClickListener(listener)
- }
-
- var image: Int? = null
- set(value) {
- field = value
- if (floatingActionButton.isVisible) {
- floatingActionButton.hide(fabVisibilityChangeListener)
- } else {
- fabVisibilityChangeListener.onHidden(floatingActionButton)
- }
- }
-
- private val fabVisibilityChangeListener = object : FloatingActionButton.OnVisibilityChangedListener() {
- override fun onHidden(fab: FloatingActionButton?) {
- fab?.let {
- it.setColorFilter(Color.BLACK)
- val image = image ?: return
- it.setImageResource(image)
- it.show()
- }
- }
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/NavigationDrawerController.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/NavigationDrawerController.kt
deleted file mode 100644
index 8595623aa..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/NavigationDrawerController.kt
+++ /dev/null
@@ -1,103 +0,0 @@
-package ca.josephroque.bowlingcompanion.common
-
-import android.support.annotation.IdRes
-import android.support.design.widget.NavigationView
-import android.widget.TextView
-import ca.josephroque.bowlingcompanion.R
-import ca.josephroque.bowlingcompanion.leagues.League
-import java.lang.ref.WeakReference
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Provides limited access to the navigation drawer so fragments can adjust display values,
- * but not alter the contents otherwise.
- */
-class NavigationDrawerController(private val navigationView: WeakReference) {
-
- companion object {
- @Suppress("unused")
- private const val TAG = "GameControllerFragment"
-
- val navGameItemIds = intArrayOf(R.id.nav_game_1, R.id.nav_game_2, R.id.nav_game_3,
- R.id.nav_game_4, R.id.nav_game_5, R.id.nav_game_6, R.id.nav_game_7, R.id.nav_game_8,
- R.id.nav_game_9, R.id.nav_game_10, R.id.nav_game_11, R.id.nav_game_12,
- R.id.nav_game_13, R.id.nav_game_14, R.id.nav_game_15, R.id.nav_game_16,
- R.id.nav_game_17, R.id.nav_game_18, R.id.nav_game_19, R.id.nav_game_20)
- }
-
- var isTeamMember: Boolean = false
- set(value) {
- field = value
- navigationView.get()?.post {
- navigationView.get()?.menu?.findItem(R.id.nav_series)?.isVisible = !value && !isEvent
- }
- }
-
- var isEvent: Boolean = false
- set(value) {
- field = value
- navigationView.get()?.post {
- navigationView.get()?.menu?.findItem(R.id.nav_series)?.isVisible = !value && !isTeamMember
- }
- }
-
- var numberOfGames: Int = League.MAX_NUMBER_OF_GAMES
- set(value) {
- field = value
- navigationView.get()?.post {
- navGameItemIds.forEachIndexed { index, id ->
- navigationView.get()?.menu?.findItem(id)?.isVisible = index < value
- }
- }
- }
-
- var bowlerName: CharSequence? = null
- set(value) {
- field = value
- navigationView.get()?.post {
- navigationView.get()?.getHeaderView(0)?.findViewById(R.id.nav_bowler_name)?.text = value
- }
- }
-
- var leagueName: CharSequence? = null
- set(value) {
- field = value
- navigationView.get()?.post {
- navigationView.get()?.getHeaderView(0)?.findViewById(R.id.nav_league_name)?.text = value
- }
- }
-
- var gameNumber: Int = 0
- set(value) {
- field = value
- navigationView.get()?.post {
- navGameItemIds.forEachIndexed { index, id ->
- navigationView.get()?.menu?.findItem(id)?.isChecked = value == index
- }
- }
- }
-
- fun updateGameScore(gameIdx: Int, score: Int) {
- navigationView.get()?.let {
- it.menu.findItem(navGameItemIds[gameIdx]).title = if (score > 0) {
- it.resources.getString(R.string.game_with_score).format(gameIdx + 1, score)
- } else {
- it.resources.getString(R.string.game_without_score).format(gameIdx + 1)
- }
- }
- }
-
- // MARK: NavigationDrawerProvider
-
- interface NavigationDrawerProvider {
- var navigationDrawerController: NavigationDrawerController
- }
-
- // MARK: NavigationDrawerHandler
-
- interface NavigationDrawerHandler {
- var navigationDrawerProvider: NavigationDrawerProvider?
- fun onNavDrawerItemSelected(@IdRes itemId: Int)
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/ScrollableTextDialog.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/ScrollableTextDialog.kt
deleted file mode 100644
index 4776a31de..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/ScrollableTextDialog.kt
+++ /dev/null
@@ -1,88 +0,0 @@
-package ca.josephroque.bowlingcompanion.common
-
-import android.app.Dialog
-import android.os.Bundle
-import android.support.annotation.StringRes
-import android.support.v4.app.DialogFragment
-import android.support.v7.app.AppCompatActivity
-import android.view.LayoutInflater
-import android.view.MenuItem
-import android.view.Window
-import android.view.View
-import android.view.ViewGroup
-import ca.josephroque.bowlingcompanion.R
-import kotlinx.android.synthetic.main.dialog_scrollable_text.toolbar_scrollable as scrollableToolbar
-import kotlinx.android.synthetic.main.dialog_scrollable_text.tv_scrollable as scrollableTextView
-import kotlinx.android.synthetic.main.dialog_scrollable_text.view.*
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Presents scrollable text.
- */
-class ScrollableTextDialog : DialogFragment() {
-
- companion object {
- @Suppress("unused")
- private const val TAG = "ScrollableTextDialog"
-
- private const val ARG_TITLE = "${TAG}_title"
- private const val ARG_TEXT = "${TAG}_text"
-
- fun newInstance(@StringRes title: Int, text: CharSequence): ScrollableTextDialog {
- val fragment = ScrollableTextDialog()
- val args = Bundle().apply {
- putInt(ARG_TITLE, title)
- putCharSequence(ARG_TEXT, text)
- }
-
- fragment.arguments = args
- return fragment
- }
- }
-
- private var title: Int = 0
- private var text: CharSequence? = null
-
- // MARK: Lifecycle functions
-
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
- arguments?.let {
- title = it.getInt(ARG_TITLE)
- text = it.getCharSequence(ARG_TEXT)
- }
-
- val rootView = inflater.inflate(R.layout.dialog_scrollable_text, container, false)
-
- val activity = activity as? AppCompatActivity
- activity?.setSupportActionBar(rootView.toolbar_scrollable)
-
- activity?.supportActionBar?.apply {
- setDisplayHomeAsUpEnabled(true)
- setDisplayShowHomeEnabled(true)
- setHomeAsUpIndicator(R.drawable.ic_dismiss)
- }
-
- setHasOptionsMenu(true)
- return rootView
- }
-
- override fun onStart() {
- super.onStart()
-
- scrollableToolbar.setTitle(title)
- scrollableTextView.text = text
- }
-
- override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
- val dialog = super.onCreateDialog(savedInstanceState)
- dialog.requestWindowFeature(Window.FEATURE_NO_TITLE)
- return dialog
- }
-
- override fun onOptionsItemSelected(item: MenuItem): Boolean {
- activity?.supportFragmentManager?.popBackStack()
- dismiss()
- return true
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/ThousandsTextWatcher.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/ThousandsTextWatcher.kt
deleted file mode 100644
index 497c4681e..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/ThousandsTextWatcher.kt
+++ /dev/null
@@ -1,52 +0,0 @@
-package ca.josephroque.bowlingcompanion.common
-
-import android.text.Editable
-import android.text.TextWatcher
-import android.widget.EditText
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Adds thousands separators to [EditText].
- */
-open class ThousandsTextWatcher(
- private val groupingSeparator: String,
- private val decimalSeparator: String
-) : TextWatcher {
-
- private var isBusy = false
-
- override fun afterTextChanged(s: Editable?) {
- if (s != null && !isBusy) {
- isBusy = true
-
- var place = 0
-
- val decimalPointIndex = s.indexOf(decimalSeparator)
- var i = if (decimalPointIndex == -1) {
- s.length - 1
- } else {
- decimalPointIndex - 1
- }
- while (i >= 0) {
- val c = s[i]
- if (c == ',') {
- s.delete(i, i + 1)
- } else {
- if (place % 3 == 0 && place != 0) {
- // insert a comma to the left of every 3rd digit (counting from right to
- // left) unless it's the leftmost digit
- s.insert(i + 1, groupingSeparator)
- }
- place++
- }
- i--
- }
-
- isBusy = false
- }
- }
-
- override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
- override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/activities/BaseActivity.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/activities/BaseActivity.kt
deleted file mode 100644
index 17d59fd4a..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/activities/BaseActivity.kt
+++ /dev/null
@@ -1,73 +0,0 @@
-package ca.josephroque.bowlingcompanion.common.activities
-
-import android.content.Intent
-import android.support.v7.app.AppCompatActivity
-import android.view.Menu
-import android.view.MenuItem
-import ca.josephroque.bowlingcompanion.BuildConfig
-import ca.josephroque.bowlingcompanion.R
-import ca.josephroque.bowlingcompanion.settings.SettingsActivity
-import ca.josephroque.bowlingcompanion.utils.Analytics
-import ca.josephroque.bowlingcompanion.utils.Email
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * A base [AppCompatActivity] implementation.
- */
-abstract class BaseActivity : AppCompatActivity() {
-
- companion object {
- @Suppress("unused")
- private const val TAG = "BaseActivity"
- }
-
- // MARK: Lifecycle functions
-
- override fun onCreateOptionsMenu(menu: Menu): Boolean {
- menuInflater.inflate(R.menu.base_activity, menu)
- return true
- }
-
- override fun onOptionsItemSelected(item: MenuItem): Boolean {
- return when (item.itemId) {
- R.id.home, R.id.homeAsUp -> {
- onBackPressed()
- true
- }
- R.id.action_settings -> {
- openSettings()
- true
- }
- R.id.action_feedback -> {
- prepareFeedbackEmail()
- true
- }
- else -> super.onOptionsItemSelected(item)
- }
- }
-
- // MARK: BaseActivity
-
- fun prepareFeedbackEmail() {
- val emailName = resources.getString(R.string.feedback_email_recipient_name);
- val emailDomain = resources.getString(R.string.feedback_email_recipient_domain);
- val emailTld = resources.getString(R.string.feedback_email_recipient_tld);
-
- Email.sendEmail(
- this,
- "$emailName@$emailDomain.$emailTld",
- String.format(resources.getString(R.string.feedback_email_subject), BuildConfig.VERSION_CODE),
- null
- )
-
- Analytics.trackSendFeedback()
- }
-
- fun openSettings() {
- val settingsIntent = Intent(this, SettingsActivity::class.java)
- startActivity(settingsIntent)
-
- Analytics.trackViewSettings()
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/adapters/BaseRecyclerViewAdapter.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/adapters/BaseRecyclerViewAdapter.kt
deleted file mode 100644
index 41a59129b..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/adapters/BaseRecyclerViewAdapter.kt
+++ /dev/null
@@ -1,198 +0,0 @@
-package ca.josephroque.bowlingcompanion.common.adapters
-
-import android.content.Context
-import android.support.v4.content.ContextCompat
-import android.support.v7.widget.DividerItemDecoration
-import android.support.v7.widget.RecyclerView
-import android.support.v7.widget.helper.ItemTouchHelper
-import android.view.View
-import ca.josephroque.bowlingcompanion.R
-import ca.josephroque.bowlingcompanion.common.interfaces.IIdentifiable
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * A basic [RecyclerView.Adapter] to handle common operations.
- */
-abstract class BaseRecyclerViewAdapter- (
- values: List
- ,
- protected var delegate: AdapterDelegate
- ?
-) : RecyclerView.Adapter.ViewHolder>(),
- View.OnClickListener,
- View.OnLongClickListener {
-
- companion object {
- @Suppress("unused")
- private const val TAG = "BaseRecyclerViewAdapter"
-
- fun applyDefaultDivider(recyclerView: RecyclerView, context: Context) {
- val itemDecorator = DividerItemDecoration(context, DividerItemDecoration.VERTICAL)
- itemDecorator.setDrawable(ContextCompat.getDrawable(context, R.drawable.divider)!!)
- recyclerView.addItemDecoration(itemDecorator)
- }
- }
-
- var swipeable: Boolean = false
- set(value) {
- if (multiSelect) {
- throw AssertionError("Cannot be multiSelect and swipeable simultaneously")
- }
- field = value
- notifyDataSetChanged()
- }
-
- var multiSelect: Boolean = false
- set(value) {
- if (swipeable) {
- throw AssertionError("Cannot be multiSelect and swipeable simultaneously")
- }
- field = value
- _selectedItems.clear()
- notifyDataSetChanged()
- }
-
- var longPressable: Boolean = false
- set(value) {
- field = value
- notifyDataSetChanged()
- }
-
- private var itemTouchHelper: ItemTouchHelper? = null
- private var recyclerView: RecyclerView? = null
-
- var items: List
- = values
- set(value) {
- field = value
- notifyDataSetChanged()
- }
-
- open fun getItemAt(position: Int): Item {
- return items[position]
- }
-
- open fun getPositionOfItem(item: Item): Int {
- return item.indexInList(items)
- }
-
- private var _selectedItems: MutableSet
- = HashSet()
- val selectedItems: Set
-
- get() = _selectedItems
-
- val selectedItemsInOrder: List
-
- get() = items.filter { _selectedItems.contains(it) }
-
- // MARK: Lifecycle functions
-
- override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
- super.onAttachedToRecyclerView(recyclerView)
- this.recyclerView = recyclerView
- itemTouchHelper = ItemTouchHelper(buildItemTouchHelper())
- itemTouchHelper?.attachToRecyclerView(this.recyclerView)
- }
-
- override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) {
- super.onDetachedFromRecyclerView(recyclerView)
- this.recyclerView = null
- delegate = null
- itemTouchHelper = null
- }
-
- override fun getItemCount(): Int {
- return items.size
- }
-
- // MARK: BaseRecyclerViewAdapter
-
- fun setSelectedElementsWithIds(ids: Set) {
- if (multiSelect) {
- items.forEachIndexed { index: Int, it: Item ->
- if (ids.contains(it.id)) {
- if (_selectedItems.add(it)) {
- notifyItemChanged(index)
- }
- } else {
- if (_selectedItems.remove(it)) {
- notifyItemChanged(index)
- }
- }
- }
- }
- }
-
- // MARK: OnClickListener
-
- override fun onClick(v: View) {
- recyclerView?.let {
- val position = it.getChildAdapterPosition(v)
- val item = getItemAt(position)
- if (multiSelect) {
- if (!_selectedItems.remove(item)) {
- _selectedItems.add(item)
- }
-
- notifyItemChanged(position)
- }
- delegate?.onItemClick(item)
- }
- }
-
- // MARK: OnLongClickListener
-
- override fun onLongClick(v: View): Boolean {
- if (!longPressable) {
- return false
- }
-
- recyclerView?.let {
- delegate?.onItemLongClick(getItemAt(it.getChildAdapterPosition(v)))
- return true
- }
-
- return false
- }
-
- open fun buildItemTouchHelper(): ItemTouchHelper.Callback {
- return SwipeCallback()
- }
-
- // MARK:: ViewHolder
-
- abstract inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
- abstract fun bind(item: Item)
- }
-
- // MARK: SwipeCallback
-
- inner class SwipeCallback : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) {
- override fun onMove(
- recyclerView: RecyclerView,
- viewHolder: RecyclerView.ViewHolder,
- target: RecyclerView.ViewHolder
- ): Boolean {
- return false
- }
-
- override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
- if (swipeable) {
- delegate?.onItemSwipe(getItemAt(viewHolder.adapterPosition))
- }
- }
-
- override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int {
- return if (swipeable) {
- ItemTouchHelper.Callback.makeMovementFlags(0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT)
- } else {
- 0
- }
- }
- }
-
- // MARK: AdapterDelegate
-
- interface AdapterDelegate {
- fun onItemClick(item: T)
- fun onItemLongClick(item: T)
- fun onItemSwipe(item: T)
- fun onItemDelete(item: T)
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/adapters/NameAverageRecyclerViewAdapter.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/adapters/NameAverageRecyclerViewAdapter.kt
deleted file mode 100644
index 9010b8ff9..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/adapters/NameAverageRecyclerViewAdapter.kt
+++ /dev/null
@@ -1,141 +0,0 @@
-package ca.josephroque.bowlingcompanion.common.adapters
-
-import android.support.v7.widget.RecyclerView
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.CheckBox
-import android.widget.ImageView
-import android.widget.TextView
-import ca.josephroque.bowlingcompanion.R
-import ca.josephroque.bowlingcompanion.common.interfaces.INameAverage
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * [RecyclerView.Adapter] that can display a [INameAverage] and makes a call to the
- * specified delegate.
- */
-class NameAverageRecyclerViewAdapter(
- items: List,
- delegate: AdapterDelegate?
-) : BaseRecyclerViewAdapter(items, delegate) {
-
- companion object {
- @Suppress("unused")
- private const val TAG = "NARecyclerViewAdapter"
-
- private enum class ViewType {
- Active,
- Selectable,
- Deleted;
-
- companion object {
- private val map = ViewType.values().associateBy(ViewType::ordinal)
- fun fromInt(type: Int) = map[type]
- }
- }
- }
-
- /**
- * Optional function to build image resource for an item.
- * First Int is the image resource ID, second is the color filter to apply.
- */
- var buildImageResource: ((item: T, position: Int) -> Pair)? = null
-
- // MARK: BaseRecyclerViewAdapter
-
- override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) {
- super.onDetachedFromRecyclerView(recyclerView)
- buildImageResource = null
- }
-
- override fun getItemViewType(position: Int): Int {
- return when {
- swipeable && getItemAt(position).isDeleted -> ViewType.Deleted.ordinal
- multiSelect -> ViewType.Selectable.ordinal
- else -> ViewType.Active.ordinal
- }
- }
-
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseRecyclerViewAdapter.ViewHolder {
- return when (ViewType.fromInt(viewType)) {
- ViewType.Active, ViewType.Selectable -> ViewHolderActive(
- LayoutInflater
- .from(parent.context)
- .inflate(R.layout.list_item_name_average, parent, false),
- viewType == ViewType.Selectable.ordinal
- )
- ViewType.Deleted -> ViewHolderDeleted(LayoutInflater
- .from(parent.context)
- .inflate(R.layout.list_item_deleted, parent, false))
- else -> throw IllegalArgumentException("View Type `$viewType` is invalid")
- }
- }
-
- override fun onBindViewHolder(holder: BaseRecyclerViewAdapter.ViewHolder, position: Int) {
- holder.bind(getItemAt(position))
- }
-
- // MARK: ViewHolderActive
-
- inner class ViewHolderActive(view: View, private val selectable: Boolean) : BaseRecyclerViewAdapter.ViewHolder(view) {
- private val tvName: TextView? = view.findViewById(R.id.tv_name)
- private val tvAverage: TextView? = view.findViewById(R.id.tv_average)
- private val ivIcon: ImageView? = view.findViewById(R.id.iv_name_average)
- private val checkBox: CheckBox? = view.findViewById(R.id.check_name_average)
-
- override fun bind(item: T) {
- val context = itemView.context
-
- tvName?.text = item.name
- tvAverage?.text = item.getDisplayAverage(context)
- checkBox?.isChecked = selectable && selectedItems.contains(item)
-
- if (selectable) {
- ivIcon?.visibility = View.GONE
- checkBox?.visibility = View.VISIBLE
- checkBox?.isChecked = selectedItems.contains(item)
- } else {
- ivIcon?.visibility = View.VISIBLE
- checkBox?.visibility = View.GONE
-
- val imageResource = buildImageResource?.invoke(item, adapterPosition)
- imageResource?.let {
- ivIcon?.setImageResource(it.first)
- ivIcon?.setColorFilter(it.second)
- }
- }
-
- itemView.setOnClickListener(this@NameAverageRecyclerViewAdapter)
- itemView.setOnLongClickListener(this@NameAverageRecyclerViewAdapter)
- }
- }
-
- // MARK: ViewHolderDeleted
-
- inner class ViewHolderDeleted(view: View) : BaseRecyclerViewAdapter.ViewHolder(view) {
- private val tvDeleted: TextView? = view.findViewById(R.id.tv_deleted)
- private val tvUndo: TextView? = view.findViewById(R.id.tv_undo)
-
- override fun bind(item: T) {
- val context = itemView.context
-
- tvDeleted?.text = String.format(
- context.resources.getString(R.string.query_delete_item),
- getItemAt(adapterPosition).name
- )
-
- val deletedItemListener = View.OnClickListener {
- if (it.id == R.id.tv_undo) {
- delegate?.onItemSwipe(getItemAt(adapterPosition))
- } else {
- delegate?.onItemDelete(getItemAt(adapterPosition))
- }
- }
- itemView.setOnClickListener(deletedItemListener)
- itemView.setOnLongClickListener(null)
- tvUndo?.setOnClickListener(deletedItemListener)
- }
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/fragments/BaseBottomSheetDialogFragment.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/fragments/BaseBottomSheetDialogFragment.kt
deleted file mode 100644
index fd0e15fe7..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/fragments/BaseBottomSheetDialogFragment.kt
+++ /dev/null
@@ -1,54 +0,0 @@
-package ca.josephroque.bowlingcompanion.common.fragments
-
-import android.content.Context
-import android.support.design.widget.BottomSheetDialogFragment
-import ca.josephroque.bowlingcompanion.matchplay.MatchPlaySheet
-import java.lang.RuntimeException
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Base implementation for BottomSheetDialogFragment
- */
-abstract class BaseBottomSheetDialogFragment : BottomSheetDialogFragment() {
-
- companion object {
- @Suppress("unused")
- private const val TAG = "BaseBottomSheetDialogFragment"
-
- enum class BottomSheetType {
- MatchPlay
- }
-
- fun getBottomSheetType(fragment: BaseBottomSheetDialogFragment): BottomSheetType? {
- return when (fragment) {
- is MatchPlaySheet -> BottomSheetType.MatchPlay
- else -> null
- }
- }
- }
-
- var delegate: BaseBottomSheetDialogFragmentDelegate? = null
-
- // MARK: Lifecycle functions
-
- @Suppress("UNCHECKED_CAST")
- override fun onAttach(context: Context?) {
- super.onAttach(context)
- context as? BaseBottomSheetDialogFragmentDelegate ?: throw RuntimeException("${context!!} must implement BaseBottomSheetDialogFragmentDelegate")
- delegate = context
- }
-
- override fun onDetach() {
- super.onDetach()
- delegate?.bottomSheetDetached()
- delegate = null
- }
-
- // MARK: BaseBottomSheetDialogFragmentDelegate
-
- interface BaseBottomSheetDialogFragmentDelegate {
- fun getBottomSheetDelegate(): Delegate?
- fun bottomSheetDetached()
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/fragments/BaseDialogFragment.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/fragments/BaseDialogFragment.kt
deleted file mode 100644
index d28498839..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/fragments/BaseDialogFragment.kt
+++ /dev/null
@@ -1,61 +0,0 @@
-package ca.josephroque.bowlingcompanion.common.fragments
-
-import android.content.Context
-import android.os.Bundle
-import android.support.v4.app.DialogFragment
-import ca.josephroque.bowlingcompanion.NavigationActivity
-import ca.josephroque.bowlingcompanion.R
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * BaseDialogFragment for all dialog fragments in the application.
- */
-abstract class BaseDialogFragment : DialogFragment() {
-
- companion object {
- @Suppress("unused")
- private const val TAG = "BaseDialogFragment"
- }
-
- protected var fragmentNavigation: BaseFragment.FragmentNavigation? = null
-
- protected var fabProvider: BaseFragment.FabProvider? = null
-
- var onDismissListener: OnDismissListener? = null
-
- protected val navigationActivity: NavigationActivity?
- get() = activity as? NavigationActivity
-
- // MARK: Lifecycle functions
-
- override fun onActivityCreated(savedInstanceState: Bundle?) {
- super.onActivityCreated(savedInstanceState)
- dialog.window?.attributes?.windowAnimations = R.style.DialogAnimation
- }
-
- override fun onAttach(context: Context?) {
- super.onAttach(context)
- fragmentNavigation = context as? BaseFragment.FragmentNavigation
- fabProvider = context as? BaseFragment.FabProvider
- onDismissListener = context as? OnDismissListener
- }
-
- override fun onDetach() {
- super.onDetach()
- fragmentNavigation = null
- fabProvider = null
- onDismissListener = null
- }
-
- override fun dismiss() {
- onDismissListener?.onDismiss(this)
- super.dismiss()
- }
-
- // MARK: OnDismissListener
-
- interface OnDismissListener {
- fun onDismiss(dismissedFragment: BaseDialogFragment)
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/fragments/BaseFragment.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/fragments/BaseFragment.kt
deleted file mode 100644
index dc4383d68..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/fragments/BaseFragment.kt
+++ /dev/null
@@ -1,78 +0,0 @@
-package ca.josephroque.bowlingcompanion.common.fragments
-
-import android.content.Context
-import android.support.v4.app.Fragment
-import ca.josephroque.bowlingcompanion.NavigationActivity
-import ca.josephroque.bowlingcompanion.utils.Permission
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * BaseFragment for all fragments in the application.
- */
-abstract class BaseFragment : Fragment() {
-
- companion object {
- @Suppress("unused")
- private const val TAG = "BaseFragment"
-
- fun newInstance(name: String): BaseFragment {
- try {
- val fragmentClass = Class.forName(name)
- return fragmentClass.newInstance() as BaseFragment
- } catch (ex: Exception) {
- throw RuntimeException("All fragments must be a subclass of BaseFragment")
- }
- }
- }
-
- protected var fragmentNavigation: FragmentNavigation? = null
- protected var fabProvider: FabProvider? = null
-
- protected val navigationActivity: NavigationActivity?
- get() = activity as? NavigationActivity
-
- // MARK: BaseFragment
-
- abstract fun updateToolbarTitle()
-
- open fun popChildFragment(): Boolean = false
-
- open fun permissionGranted(permission: Permission) {}
-
- // MARK: Lifecycle functions
-
- override fun onAttach(context: Context?) {
- super.onAttach(context)
- fragmentNavigation = context as? BaseFragment.FragmentNavigation
- fabProvider = context as? BaseFragment.FabProvider
- }
-
- override fun onDetach() {
- super.onDetach()
- fragmentNavigation = null
- fabProvider = null
- }
-
- override fun onStart() {
- super.onStart()
- updateToolbarTitle()
- }
-
- // MARK: FragmentNavigation
-
- interface FragmentNavigation {
-
- val stackSize: Int
-
- fun pushFragment(fragment: BaseFragment)
- fun pushDialogFragment(fragment: BaseDialogFragment)
- fun showBottomSheet(fragment: BaseBottomSheetDialogFragment, tag: String)
- }
-
- // MARK: FabProvider
-
- interface FabProvider {
- fun invalidateFab()
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/fragments/DatePickerFragment.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/fragments/DatePickerFragment.kt
deleted file mode 100644
index 8b26025b7..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/fragments/DatePickerFragment.kt
+++ /dev/null
@@ -1,71 +0,0 @@
-package ca.josephroque.bowlingcompanion.common.fragments
-
-import android.app.DatePickerDialog
-import android.app.Dialog
-import android.os.Bundle
-import android.widget.DatePicker
-import java.util.Calendar
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Pick a date.
- */
-class DatePickerFragment : BaseDialogFragment(),
- DatePickerDialog.OnDateSetListener {
-
- companion object {
- /** Logging identifier. */
- @Suppress("unused")
- private const val TAG = "DatePickerDialog"
-
- /** Year to show in the calendar. */
- private const val ARG_YEAR = "${TAG}_year"
-
- /** Month to show in the calendar. */
- private const val ARG_MONTH = "${TAG}_month"
-
- /** Day to show in the calendar. */
- private const val ARG_DAY = "${TAG}_day"
-
- fun newInstance(calendar: Calendar): DatePickerFragment {
- val fragment = DatePickerFragment()
- fragment.arguments = Bundle().apply {
- putInt(ARG_DAY, calendar.get(Calendar.DAY_OF_MONTH))
- putInt(ARG_MONTH, calendar.get(Calendar.MONTH))
- putInt(ARG_YEAR, calendar.get(Calendar.YEAR))
- }
- return fragment
- }
- }
-
- /** Listener for DatePickerDialog events. */
- var listener: DatePickerDialog.OnDateSetListener? = null
-
- /** @Override */
- override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
- val calendar = Calendar.getInstance()
- var year = calendar.get(Calendar.YEAR)
- var month = calendar.get(Calendar.MONTH)
- var day = calendar.get(Calendar.DAY_OF_MONTH)
-
- arguments?.let {
- year = it.getInt(ARG_YEAR, year)
- month = it.getInt(ARG_MONTH, month)
- day = it.getInt(ARG_DAY, day)
- }
-
- return DatePickerDialog(activity!!, this, year, month, day)
- }
-
- /** @Override */
- override fun onDetach() {
- super.onDetach()
- listener = null
- }
-
- /** @Override */
- override fun onDateSet(view: DatePicker, year: Int, month: Int, day: Int) {
- listener?.onDateSet(view, year, month, day)
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/fragments/ListFragment.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/fragments/ListFragment.kt
deleted file mode 100644
index 942c3dc1d..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/fragments/ListFragment.kt
+++ /dev/null
@@ -1,200 +0,0 @@
-package ca.josephroque.bowlingcompanion.common.fragments
-
-import android.content.Context
-import android.os.Bundle
-import android.support.v4.app.Fragment
-import android.support.v7.widget.LinearLayoutManager
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import ca.josephroque.bowlingcompanion.R
-import ca.josephroque.bowlingcompanion.common.Android
-import ca.josephroque.bowlingcompanion.common.interfaces.IDeletable
-import ca.josephroque.bowlingcompanion.common.interfaces.IIdentifiable
-import ca.josephroque.bowlingcompanion.common.interfaces.IRefreshable
-import ca.josephroque.bowlingcompanion.common.adapters.BaseRecyclerViewAdapter
-import kotlinx.android.synthetic.main.fragment_common_list.view.*
-import kotlinx.android.synthetic.main.view_screen_header.tv_header_caption as tvHeaderSubtitle
-import kotlinx.android.synthetic.main.view_screen_header.tv_header_title as tvHeaderTitle
-import kotlinx.android.synthetic.main.fragment_common_list.list as list
-import kotlinx.android.synthetic.main.fragment_common_list.list_header as listHeader
-import kotlinx.android.synthetic.main.fragment_common_list.list_empty_view as emptyView
-import kotlinx.coroutines.experimental.Deferred
-import kotlinx.coroutines.experimental.launch
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Basic [Fragment] implementation with a list.
- */
-abstract class ListFragment
- > :
- BaseFragment(),
- BaseRecyclerViewAdapter.AdapterDelegate
- ,
- IRefreshable {
-
- companion object {
- @Suppress("unused")
- private const val TAG = "ListFragment"
- }
-
- protected var adapter: Adapter? = null
- private var items: MutableList
- = ArrayList()
-
- protected var delegate: ListFragmentDelegate? = null
- protected var canIgnoreDelegate = false
-
- abstract val emptyViewImage: Int?
- abstract val emptyViewText: Int?
-
- protected var headerTitle: Int? = null
- set(value) {
- field = value
- if (value == null) {
- tvHeaderTitle.text = ""
- } else {
- tvHeaderTitle.setText(value)
- }
- listHeader.visibility = if (hasTitleOrSubtitle) View.VISIBLE else View.GONE
- }
-
- protected var headerSubtitle: Int? = null
- set(value) {
- field = value
- if (value == null) {
- tvHeaderSubtitle.text = ""
- } else {
- tvHeaderSubtitle.setText(value)
- }
- listHeader.visibility = if (hasTitleOrSubtitle) View.VISIBLE else View.GONE
- }
-
- private val hasTitleOrSubtitle: Boolean
- get() = headerTitle != null || headerSubtitle != null
-
- // MARK: Lifecycle functions
-
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
- val rootView = inflater.inflate(R.layout.fragment_common_list, container, false)
- setupRecyclerView(rootView)
- return rootView
- }
-
- override fun onAttach(context: Context?) {
- super.onAttach(context)
- if (canIgnoreDelegate) {
- return
- }
-
- val parent = parentFragment as? ListFragmentDelegate ?: throw RuntimeException("${parentFragment!!} must implement ListFragmentDelegate")
- delegate = parent
- }
-
- override fun onDetach() {
- super.onDetach()
- delegate = null
- }
-
- override fun onStart() {
- super.onStart()
- refreshList()
- }
-
- override fun refresh() {
- refreshList()
- }
-
- // MARK: AdapterDelegate
-
- override fun onItemClick(item: Item) {
- delegate?.onItemSelected(item, false)
- }
-
- override fun onItemDelete(item: Item) {
- val context = context ?: return
- val index = item.indexInList(items)
- if (index != -1 && item is IDeletable) {
- items.removeAt(index)
- adapter?.notifyItemRemoved(index)
- item.delete(context)
- delegate?.onItemDeleted(item)
- }
- }
-
- override fun onItemLongClick(item: Item) {
- delegate?.onItemSelected(item, true)
- }
-
- override fun onItemSwipe(item: Item) {
- val index = item.indexInList(items)
- if (index != -1 && item is IDeletable) {
- @Suppress("UNCHECKED_CAST")
- val updatedItem: Item = if (item.isDeleted) {
- item.cleanDeletion() as Item
- } else {
- item.markForDeletion() as Item
- }
-
- items[index] = updatedItem
- adapter?.notifyItemChanged(adapter?.getPositionOfItem(item) ?: index)
- }
- }
-
- // MARK: ListFragment
-
- abstract fun fetchItems(): Deferred>
-
- abstract fun buildAdapter(): Adapter
-
- open fun listWasRefreshed() {}
-
- fun refreshList(item: Item? = null) {
- launch(Android) {
- val index = item?.indexInList(this@ListFragment.items) ?: -1
- if (item == null || index == -1) {
- val items = fetchItems().await()
- this@ListFragment.items = items
- adapter?.items = items
- } else {
- this@ListFragment.items[index] = item
- adapter?.notifyItemChanged(adapter?.getPositionOfItem(item) ?: index)
- }
-
- updateEmptyView()
- listWasRefreshed()
- }
- }
-
- // MARK: Private functions
-
- private fun setupRecyclerView(rootView: View) {
- val context = context ?: return
- adapter = buildAdapter()
- rootView.list.layoutManager = LinearLayoutManager(context)
- rootView.list.adapter = adapter
- rootView.list.setHasFixedSize(true)
- BaseRecyclerViewAdapter.applyDefaultDivider(rootView.list, context)
- }
-
- private fun updateEmptyView() {
- if (view == null) { return }
-
- if (items.isEmpty()) {
- list.visibility = View.GONE
- emptyView.apply {
- emptyImageId = this@ListFragment.emptyViewImage
- emptyTextId = this@ListFragment.emptyViewText
- visibility = View.VISIBLE
- }
- } else {
- list.visibility = View.VISIBLE
- emptyView.visibility = View.GONE
- }
- }
-
- // MARK: ListFragmentDelegate
-
- interface ListFragmentDelegate {
- fun onItemSelected(item: IIdentifiable, longPress: Boolean)
- fun onItemDeleted(item: IIdentifiable)
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/fragments/TabbedFragment.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/fragments/TabbedFragment.kt
deleted file mode 100644
index d69f956df..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/fragments/TabbedFragment.kt
+++ /dev/null
@@ -1,128 +0,0 @@
-package ca.josephroque.bowlingcompanion.common.fragments
-
-import android.content.Context
-import android.os.Bundle
-import android.support.design.widget.TabLayout
-import android.support.v4.app.FragmentPagerAdapter
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import ca.josephroque.bowlingcompanion.R
-import ca.josephroque.bowlingcompanion.common.interfaces.IFloatingActionButtonHandler
-import ca.josephroque.bowlingcompanion.common.interfaces.IRefreshable
-import kotlinx.android.synthetic.main.fragment_common_tabs.tabbed_fragment_pager as fragmentPager
-import kotlinx.android.synthetic.main.fragment_common_tabs.view.*
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Base implementation for a fragment with tabs.
- */
-abstract class TabbedFragment : BaseFragment(),
- IFloatingActionButtonHandler,
- IRefreshable {
-
- companion object {
- @Suppress("unused")
- private const val TAG = "TabbedFragment"
- }
-
- protected var currentTab: Int
- get() = fragmentPager?.currentItem ?: 0
- set(value) { fragmentPager?.currentItem = value }
-
- private var delegate: TabbedFragmentDelegate? = null
-
- // MARK: Lifecycle functions
-
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
- val rootView = inflater.inflate(R.layout.fragment_common_tabs, container, false)
- configureTabLayout(rootView)
- return rootView
- }
-
- override fun onAttach(context: Context?) {
- super.onAttach(context)
- context as? TabbedFragmentDelegate ?: throw RuntimeException("${context!!} must implement TabbedFragmentDelegate")
- delegate = context
- }
-
- override fun onDetach() {
- super.onDetach()
- delegate = null
- }
-
- // MARK: TabbedFragment
-
- fun resetTabLayout() {
- configureTabLayout(view!!)
- }
-
- fun refreshTabs(ignored: Set = HashSet()) {
- val adapter = fragmentPager.adapter as? FragmentPagerAdapter
- adapter?.let {
- for (i in 0 until it.count) {
- if (ignored.contains(i)) {
- continue
- }
-
- val fragment = findFragmentByPosition(i) as? IRefreshable
- fragment?.refresh()
- }
- }
- }
-
- abstract fun addTabs(tabLayout: TabLayout)
-
- abstract fun buildPagerAdapter(tabCount: Int): FragmentPagerAdapter
-
- abstract fun handleTabSwitch(newTab: Int)
-
- fun findFragmentByPosition(position: Int): BaseFragment? {
- val fragmentPagerAdapter = fragmentPager.adapter as? FragmentPagerAdapter ?: return null
- val tag = "android:switcher:${fragmentPager.id}:${fragmentPagerAdapter.getItemId(position)}"
- return childFragmentManager.findFragmentByTag(tag) as? BaseFragment
-}
-
- // MARK: Private functions
-
- private fun configureTabLayout(rootView: View) {
- rootView.tabbed_fragment_tabs.removeAllTabs()
- addTabs(rootView.tabbed_fragment_tabs)
- rootView.tabbed_fragment_pager.scrollingEnabled = false
-
- val adapter = buildPagerAdapter(rootView.tabbed_fragment_tabs.tabCount)
- rootView.tabbed_fragment_pager.adapter = adapter
-
- rootView.tabbed_fragment_pager.addOnPageChangeListener(TabLayout.TabLayoutOnPageChangeListener(rootView.tabbed_fragment_tabs))
- rootView.tabbed_fragment_tabs.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
- override fun onTabSelected(tab: TabLayout.Tab) {
- rootView.tabbed_fragment_pager.currentItem = tab.position
- handleTabSwitch(tab.position)
- delegate?.onTabSwitched()
- }
-
- override fun onTabUnselected(tab: TabLayout.Tab) {}
- override fun onTabReselected(tab: TabLayout.Tab) {}
- })
- }
-
- // MARK: IRefreshable
-
- override fun refresh() {
- refreshTabs()
- }
-
- // MARK: BaseFragment
-
- override fun popChildFragment(): Boolean {
- val fragment = findFragmentByPosition(currentTab)
- return fragment?.popChildFragment() ?: super.popChildFragment()
- }
-
- // MARK: TabbedFragmentDelegate
-
- interface TabbedFragmentDelegate {
- fun onTabSwitched()
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/interfaces/IDeletable.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/interfaces/IDeletable.kt
deleted file mode 100644
index 5df679cea..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/interfaces/IDeletable.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-package ca.josephroque.bowlingcompanion.common.interfaces
-
-import android.content.Context
-import kotlinx.coroutines.experimental.Deferred
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Indicate an item is deletable.
- */
-interface IDeletable {
- val isDeleted: Boolean
-
- fun markForDeletion(): IDeletable
- fun cleanDeletion(): IDeletable
- fun delete(context: Context): Deferred
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/interfaces/IFloatingActionButtonHandler.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/interfaces/IFloatingActionButtonHandler.kt
deleted file mode 100644
index 71dd98aa1..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/interfaces/IFloatingActionButtonHandler.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package ca.josephroque.bowlingcompanion.common.interfaces
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Indicate an object can handle a Floating Action Button event.
- */
-interface IFloatingActionButtonHandler {
- fun onFabClick()
- fun getFabImage(): Int?
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/interfaces/IIdentifiable.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/interfaces/IIdentifiable.kt
deleted file mode 100644
index f506b2513..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/interfaces/IIdentifiable.kt
+++ /dev/null
@@ -1,12 +0,0 @@
-package ca.josephroque.bowlingcompanion.common.interfaces
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Enforces objects to have an ID.
- */
-interface IIdentifiable {
- val id: Long
-
- fun indexInList(list: List): Int = (0 until list.size).firstOrNull { list[it]::class == this::class && list[it].id == id } ?: -1
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/interfaces/INameAverage.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/interfaces/INameAverage.kt
deleted file mode 100644
index 4e096852f..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/interfaces/INameAverage.kt
+++ /dev/null
@@ -1,34 +0,0 @@
-package ca.josephroque.bowlingcompanion.common.interfaces
-
-import android.content.Context
-import android.support.v7.preference.PreferenceManager
-import ca.josephroque.bowlingcompanion.settings.Settings
-import java.text.DecimalFormat
-import kotlin.math.roundToInt
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Enforces objects to have a name and average.
- */
-interface INameAverage : IDeletable, IIdentifiable {
-
- companion object {
- @Suppress("unused")
- private const val TAG = "INameAverage"
- }
-
- val name: String
- val average: Double
-
- fun getDisplayAverage(context: Context): String {
- val formatter = DecimalFormat("0.#")
- val preferences = PreferenceManager.getDefaultSharedPreferences(context)
- val averageAsDecimal = Settings.BooleanSetting.AverageAsDecimal.getValue(preferences)
- return if (averageAsDecimal) {
- formatter.format(average)
- } else {
- average.roundToInt().toString()
- }
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/interfaces/IRefreshable.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/interfaces/IRefreshable.kt
deleted file mode 100644
index ecb8e1ae8..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/interfaces/IRefreshable.kt
+++ /dev/null
@@ -1,10 +0,0 @@
-package ca.josephroque.bowlingcompanion.common.interfaces
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Denote an item which can be refreshed.
- */
-interface IRefreshable {
- fun refresh()
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/interfaces/KParcelable.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/interfaces/KParcelable.kt
deleted file mode 100644
index 7fbcc115f..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/interfaces/KParcelable.kt
+++ /dev/null
@@ -1,85 +0,0 @@
-@file:Suppress("NOTHING_TO_INLINE", "unused")
-
-package ca.josephroque.bowlingcompanion.common.interfaces
-
-import android.os.Parcel
-import android.os.Parcelable
-import java.math.BigDecimal
-import java.math.BigInteger
-import java.util.Date
-
-/**
- * Source: https://medium.com/@BladeCoder/reducing-parcelable-boilerplate-code-using-kotlin-741c3124a49a
- */
-interface KParcelable : Parcelable {
- override fun describeContents() = 0
- override fun writeToParcel(dest: Parcel, flags: Int)
-}
-
-// Creator factory functions
-
-inline fun parcelableCreator(
- crossinline create: (Parcel) -> T
-) =
- object : Parcelable.Creator {
- override fun createFromParcel(source: Parcel) = create(source)
- override fun newArray(size: Int) = arrayOfNulls(size)
- }
-
-inline fun parcelableClassLoaderCreator(
- crossinline create: (Parcel, ClassLoader) -> T
-) =
- object : Parcelable.ClassLoaderCreator {
- override fun createFromParcel(source: Parcel, loader: ClassLoader) =
- create(source, loader)
-
- override fun createFromParcel(source: Parcel) =
- createFromParcel(source, T::class.java.classLoader!!)
-
- override fun newArray(size: Int) = arrayOfNulls(size)
- }
-
-// Parcel extensions
-
-inline fun Parcel.readBoolean() = readInt() != 0
-
-inline fun Parcel.writeBoolean(value: Boolean) = writeInt(if (value) 1 else 0)
-
-inline fun > Parcel.readEnum() =
- readInt().let { if (it >= 0) enumValues()[it] else null }
-
-inline fun > Parcel.writeEnum(value: T?) =
- writeInt(value?.ordinal ?: -1)
-
-inline fun Parcel.readNullable(reader: () -> T) =
- if (readInt() != 0) reader() else null
-
-inline fun Parcel.writeNullable(value: T?, writer: (T) -> Unit) {
- if (value != null) {
- writeInt(1)
- writer(value)
- } else {
- writeInt(0)
- }
-}
-
-fun Parcel.readDate() = readNullable { Date(readLong()) }
-
-fun Parcel.writeDate(value: Date?) = writeNullable(value) { writeLong(it.time) }
-
-fun Parcel.readBigInteger() = readNullable { BigInteger(createByteArray()) }
-
-fun Parcel.writeBigInteger(value: BigInteger?) = writeNullable(value) { writeByteArray(it.toByteArray()) }
-
-fun Parcel.readBigDecimal() = readNullable { BigDecimal(BigInteger(createByteArray()), readInt()) }
-
-fun Parcel.writeBigDecimal(value: BigDecimal?) = writeNullable(value) {
- writeByteArray(it.unscaledValue().toByteArray())
- writeInt(it.scale())
-}
-
-fun Parcel.readTypedObjectCompat(c: Parcelable.Creator) =
- readNullable { c.createFromParcel(this) }
-
-fun Parcel.writeTypedObjectCompat(value: T?, parcelableFlags: Int) =
- writeNullable(value) { it.writeToParcel(this, parcelableFlags) }
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/views/EmptyView.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/views/EmptyView.kt
deleted file mode 100644
index 6a5db03c0..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/common/views/EmptyView.kt
+++ /dev/null
@@ -1,50 +0,0 @@
-package ca.josephroque.bowlingcompanion.common.views
-
-import android.content.Context
-import android.support.constraint.ConstraintLayout
-import android.util.AttributeSet
-import android.view.LayoutInflater
-import android.view.View
-import ca.josephroque.bowlingcompanion.R
-import kotlinx.android.synthetic.main.view_empty.view.empty_image as emptyImage
-import kotlinx.android.synthetic.main.view_empty.view.empty_text as emptyText
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Displays an image and a notice describing the current empty state of a fragment.
- */
-class EmptyView : ConstraintLayout {
-
- companion object {
- @Suppress("unused")
- private const val TAG = "EmptyView"
- }
-
- // MARK: Constructors
-
- constructor(context: Context) : this(context, null)
- constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
- constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
- LayoutInflater.from(context).inflate(R.layout.view_empty, this, true)
- }
-
- var emptyImageId: Int? = null
- set(value) {
- if (value != null) {
- emptyImage.setImageResource(value)
- emptyImage.visibility = View.VISIBLE
- } else {
- emptyImage.visibility = View.INVISIBLE
- }
- }
-
- var emptyTextId: Int? = null
- set(value) {
- if (value != null) {
- emptyText.setText(value)
- } else {
- emptyText.text = null
- }
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/database/Annihilator.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/database/Annihilator.kt
deleted file mode 100644
index 2d5bf6475..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/database/Annihilator.kt
+++ /dev/null
@@ -1,97 +0,0 @@
-package ca.josephroque.bowlingcompanion.database
-
-import android.content.Context
-import android.util.Log
-import ca.josephroque.bowlingcompanion.App
-import ca.josephroque.bowlingcompanion.common.Android
-import kotlinx.coroutines.experimental.CommonPool
-import kotlinx.coroutines.experimental.CoroutineStart
-import kotlinx.coroutines.experimental.Deferred
-import kotlinx.coroutines.experimental.Job
-import kotlinx.coroutines.experimental.async
-import kotlinx.coroutines.experimental.delay
-import kotlinx.coroutines.experimental.launch
-import java.lang.ref.WeakReference
-import java.util.LinkedList
-import java.util.Queue
-import java.util.concurrent.atomic.AtomicBoolean
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Managing deleting from the database synchronously and waiting for deletion tasks
- * to finish before other database tasks can process.
- */
-class Annihilator private constructor() {
-
- companion object {
- @Suppress("unused")
- private const val TAG = "Saviour"
-
- val instance: Annihilator by lazy { Holder.INSTANCE }
- }
-
- private object Holder { val INSTANCE = Annihilator() }
-
- private var annihilatorIsRunning = AtomicBoolean(false)
-
- private val deletionQueue: Queue = LinkedList()
-
- init {
- launchAnnihilatorProcessor()
- }
-
- // MARK: Annihilator
-
- fun wait(): Deferred {
- return async(CommonPool) {
- launchAnnihilatorProcessor()
- while (deletionQueue.isNotEmpty()) {
- delay(50)
- }
- }
- }
-
- fun delete(weakContext: WeakReference, tableName: String, whereClause: String, whereArgs: Array) {
- val job = launch(context = Android, start = CoroutineStart.LAZY) {
- val strongContext = weakContext.get() ?: return@launch
- val database = DatabaseHelper.getInstance(strongContext).writableDatabase
- database.beginTransaction()
- try {
- database.delete(tableName, whereClause, whereArgs)
- database.setTransactionSuccessful()
- } catch (e: Exception) {
- // Does nothing
- // If there's an error deleting this item, then they just remain in the
- // user's data and no harm is done.
- } finally {
- database.endTransaction()
- }
- }
-
- deletionQueue.offer(job)
- launchAnnihilatorProcessor()
- }
-
- // MARK: Private functions
-
- private fun launchAnnihilatorProcessor() {
- if (annihilatorIsRunning.get()) {
- return
- }
-
- annihilatorIsRunning.set(true)
- launch(CommonPool) {
- while (App.isRunning.get() || deletionQueue.isNotEmpty()) {
- val deletionRoutine = deletionQueue.poll()
- if (!deletionRoutine.isCompleted && !deletionRoutine.isCancelled) {
- deletionRoutine.join()
- } else {
- Log.e(TAG, "Annihilator thread already processed - $deletionRoutine")
- }
- }
-
- annihilatorIsRunning.set(false)
- }
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/database/Contract.java b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/database/Contract.java
deleted file mode 100644
index d70179205..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/database/Contract.java
+++ /dev/null
@@ -1,230 +0,0 @@
-package ca.josephroque.bowlingcompanion.database;
-
-import android.provider.BaseColumns;
-
-/**
- * Copyright (c) 2015 Joseph Roque
- *
- * Defines various objects which represent tables in the schema for the
- * application's database. Member variables represent identifiers for attributes of the tables.
- */
-public final class Contract {
-
- /**
- * Table and column names for SQLite table relevant to bowlers.
- */
- public static final class BowlerEntry
- implements BaseColumns {
-
- /** Name of the table for bowler data. */
- public static final String TABLE_NAME = "bowlers";
- /** Name of the column for bowler names. */
- public static final String COLUMN_BOWLER_NAME = "bowler_name";
- /** Name of the column for bowlers' most recent date modified. */
- public static final String COLUMN_DATE_MODIFIED = "bowler_date_modified";
-
- /**
- * Private constructor, class cannot be instantiated.
- */
- private BowlerEntry() {
- // does nothing
- }
- }
-
- /**
- * Table and column names for SQLite table relevant to leagues.
- */
- public static final class LeagueEntry
- implements BaseColumns {
-
- /** Name of the table for league data. */
- public static final String TABLE_NAME = "leagues";
- /** Name of the column for league names. */
- public static final String COLUMN_LEAGUE_NAME = "league_name";
- /** Name of the column for the number of games in a league. */
- public static final String COLUMN_NUMBER_OF_GAMES = "league_number_of_games";
- /**
- * Name of the column for the additional pinfall of the league.
- *
- * @deprecated replaced by combination of COLUMN_ADDITIONAL_PINFALL and
- * COLUMN_ADDITIONAL_GAMES
- */
- @SuppressWarnings("DeprecatedIsStillUsed")
- @Deprecated
- public static final String COLUMN_BASE_AVERAGE = "league_base_avg";
- /**
- * Name of the column for the base number of games of the league.
- *
- * @deprecated replaced by combination of COLUMN_ADDITIONAL_PINFALL and
- * COLUMN_ADDITIONAL_GAMES
- */
- @SuppressWarnings("DeprecatedIsStillUsed")
- @Deprecated
- public static final String COLUMN_BASE_GAMES = "league_base_games";
- /** Name of the column for the additional pinfall of the league. */
- public static final String COLUMN_ADDITIONAL_PINFALL = "league_additional_pinfall";
- /** Name of the column for the additional games of the league. */
- public static final String COLUMN_ADDITIONAL_GAMES = "league_additional_games";
- /** Name of the column for the minimum score in the league to highlight. */
- public static final String COLUMN_GAME_HIGHLIGHT = "league_game_highlight";
- /** Name of the column for the minimum series total in the league to highlight. */
- public static final String COLUMN_SERIES_HIGHLIGHT = "league_series_highlight";
- /** Name of the column for the leagues' most recent date modified. */
- public static final String COLUMN_DATE_MODIFIED = "league_date_modified";
- /** Name of the column to indicate if the row is an event. */
- public static final String COLUMN_IS_EVENT = "league_is_event";
- /** Name of the column for foreign key to a bowler id. */
- public static final String COLUMN_BOWLER_ID = "league_bowler_id_fk";
-
- /**
- * Private constructor, class cannot be instantiated.
- */
- private LeagueEntry() {
- // does nothing
- }
- }
-
- /**
- * Table and column names for SQLite table relevant to series.
- */
- public static final class SeriesEntry
- implements BaseColumns {
-
- /** Name of the table for series data. */
- public static final String TABLE_NAME = "series";
- /** Name of the table for the date of the series. */
- public static final String COLUMN_SERIES_DATE = "series_date";
- /** Name of the table for foreign key to a league id. */
- public static final String COLUMN_LEAGUE_ID = "series_league_id_fk";
-
- /**
- * Private constructor, class cannot be instantiated.
- */
- private SeriesEntry() {
- // does nothing
- }
- }
-
- /**
- * Table and column names for SQLite table relevant to games.
- */
- public static final class GameEntry
- implements BaseColumns {
-
- /** Name of the table for game data. */
- public static final String TABLE_NAME = "games";
- /** Name of the column for game number in a series. */
- public static final String COLUMN_GAME_NUMBER = "game_number";
- /** Name of the column for the game's score. */
- public static final String COLUMN_SCORE = "game_score";
- /** Name of the column to indicate if a game is locked from editing. */
- public static final String COLUMN_IS_LOCKED = "game_is_locked";
- /** Name of the column to indicate if a game's score was manually set. */
- public static final String COLUMN_IS_MANUAL = "game_is_manual";
- /** Name of the column to indicate the match play result. */
- public static final String COLUMN_MATCH_PLAY = "game_match_play";
- /** Name of the column for foreign key to a series id. */
- public static final String COLUMN_SERIES_ID = "game_series_id_fk";
-
- /**
- * Private constructor, class cannot be instantiated.
- */
- private GameEntry() {
- // does nothing
- }
- }
-
- /**
- * Table and column names for SQLite table relevant to frames.
- */
- public static final class FrameEntry
- implements BaseColumns {
-
- /** Name of the table for frame data. */
- public static final String TABLE_NAME = "frames";
- /** Name of the column for the frame number in a game. */
- public static final String COLUMN_FRAME_NUMBER = "frame_number";
- /** Name of the column to indicate if a frame has been accessed. */
- public static final String COLUMN_IS_ACCESSED = "frame_accessed";
- /** Name of the column to indicate the state of the pins in a frame. */
- public static final String[] COLUMN_PIN_STATE =
- {"frame_pins_1", "frame_pins_2", "frame_pins_3"};
- /** Name of the column to indicate if fouls were invoked in a frame. */
- public static final String COLUMN_FOULS = "frame_fouls";
- /** Name of the column for foreign key to a game id. */
- public static final String COLUMN_GAME_ID = "frame_game_id_fk";
-
- /**
- * Private constructor, class cannot be instantiated.
- */
- private FrameEntry() {
- // does nothing
- }
- }
-
- /**
- * Table and column names for SQLite table relevant to match play results.
- */
- public static final class MatchPlayEntry
- implements BaseColumns {
-
- /** Name of the table for match play data. */
- public static final String TABLE_NAME = "match_play";
- /** Name of the column for the opponent's name. */
- public static final String COLUMN_OPPONENT_NAME = "match_opp_name";
- /** Name of the column for the opponent's score. */
- public static final String COLUMN_OPPONENT_SCORE = "match_opp_score";
- /** Name of the column for foreign key to a game id. */
- public static final String COLUMN_GAME_ID = "match_game_id_fk";
-
- /**
- * Private constructor, class cannot be instantiated.
- */
- private MatchPlayEntry() {
- // does nothing
- }
- }
-
- public static final class TeamEntry
- implements BaseColumns {
-
- /** Name of the table for team data. */
- public static final String TABLE_NAME = "teams";
- /** Name of the column for the team name. */
- public static final String COLUMN_TEAM_NAME = "team_name";
- /** Name of the column for teams' most recent date modified. */
- public static final String COLUMN_DATE_MODIFIED = "team_date_modified";
-
- /**
- * Private constructor, class cannot be instantiated.
- */
- private TeamEntry() {
- // does nothing
- }
- }
-
- public static final class TeamBowlerEntry
- implements BaseColumns {
-
- /** Name of the table for team bowlers data. */
- public static final String TABLE_NAME = "teams_bowlers";
- /** Name of the column for the team id. */
- public static final String COLUMN_TEAM_ID = "team_bowler_team_id_fk";
- /** Name of the column for the bowler id. */
- public static final String COLUMN_BOWLER_ID = "team_bowler_bowler_id_fk";
-
- /**
- * Private constructor, class cannot be instantiated.
- */
- private TeamBowlerEntry() {
- // does nothing
- }
- }
-
- /**
- * Private constructor, class cannot be instantiated.
- */
- private Contract() {
- // does nothing
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/database/DatabaseHelper.java b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/database/DatabaseHelper.java
deleted file mode 100644
index dadda07fc..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/database/DatabaseHelper.java
+++ /dev/null
@@ -1,674 +0,0 @@
-package ca.josephroque.bowlingcompanion.database;
-
-import android.content.ContentValues;
-import android.content.Context;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteOpenHelper;
-import android.util.Log;
-
-import java.util.Locale;
-
-import ca.josephroque.bowlingcompanion.database.Contract.BowlerEntry;
-import ca.josephroque.bowlingcompanion.database.Contract.FrameEntry;
-import ca.josephroque.bowlingcompanion.database.Contract.GameEntry;
-import ca.josephroque.bowlingcompanion.database.Contract.LeagueEntry;
-import ca.josephroque.bowlingcompanion.database.Contract.MatchPlayEntry;
-import ca.josephroque.bowlingcompanion.database.Contract.SeriesEntry;
-import ca.josephroque.bowlingcompanion.database.Contract.TeamBowlerEntry;
-import ca.josephroque.bowlingcompanion.database.Contract.TeamEntry;
-import ca.josephroque.bowlingcompanion.leagues.League;
-import ca.josephroque.bowlingcompanion.scoring.Fouls;
-
-/**
- * Copyright (C) 2015 Joseph Roque
- *
- * Manages interactions with the application's database, including the creation,
- * updates and deletion.
- */
-public final class DatabaseHelper
- extends SQLiteOpenHelper {
-
- /** Identifies output from this class in Logcat. */
- private static final String TAG = "DBHelper";
-
- /** Name of the database. */
- public static final String DATABASE_NAME = "bowlingdata";
-
- /** Version of the database, incremented with changes. */
- private static final int DATABASE_VERSION = 7;
-
- /** Singleton instance of the DatabaseHelper. */
- private static DatabaseHelper sDatabaseHelperInstance = null;
-
- /**
- * Returns a singleton instance of DatabaseHelper.
- *
- * @param context the current activity
- * @return static instance of DatabaseHelper
- */
- public static DatabaseHelper getInstance(Context context) {
- if (sDatabaseHelperInstance == null) {
- sDatabaseHelperInstance = new DatabaseHelper(context);
- }
- return sDatabaseHelperInstance;
- }
-
- /**
- * Close the current instance of the database helper.
- */
- public static void closeInstance() {
- if (sDatabaseHelperInstance != null) {
- sDatabaseHelperInstance.close();
- sDatabaseHelperInstance = null;
- }
- }
-
- /**
- * Private constructor for singleton access.
- *
- * @param context the current activity
- */
- private DatabaseHelper(Context context) {
- super(context, DATABASE_NAME, null, DATABASE_VERSION);
- }
-
- @Override
- public void onCreate(SQLiteDatabase db) {
- // Defines tables for the database, creating columns and constraints
- createBowlerTable(db);
- createLeagueTable(db);
- createSeriesTable(db);
- createGameTable(db);
- createFrameTable(db);
- createMatchPlayTable(db);
- createTeamTable(db);
- createTeamBowlerTable(db);
- createTableIndices(db);
- }
-
- /**
- * Creates indices on the tables for the most commonly used columns for sorting and comparing.
- *
- * @param db database
- */
- private void createTableIndices(SQLiteDatabase db) {
- db.execSQL("CREATE INDEX bowler_id_index ON "
- + BowlerEntry.TABLE_NAME + "(" + BowlerEntry._ID + ")");
- db.execSQL("CREATE INDEX league_id_index ON "
- + LeagueEntry.TABLE_NAME + "(" + LeagueEntry._ID + ")");
- db.execSQL("CREATE INDEX series_id_index ON "
- + SeriesEntry.TABLE_NAME + "(" + SeriesEntry._ID + ")");
- db.execSQL("CREATE INDEX game_id_index ON "
- + GameEntry.TABLE_NAME + "(" + GameEntry._ID + ")");
- db.execSQL("CREATE INDEX frame_id_index ON "
- + FrameEntry.TABLE_NAME + "(" + FrameEntry._ID + ")");
- db.execSQL("CREATE INDEX match_id_index ON "
- + MatchPlayEntry.TABLE_NAME + "(" + MatchPlayEntry._ID + ")");
- db.execSQL("CREATE INDEX team_id_index ON "
- + TeamEntry.TABLE_NAME + "(" + TeamEntry._ID + ")");
-
- db.execSQL("CREATE INDEX league_bowler_fk_index ON "
- + LeagueEntry.TABLE_NAME + "(" + LeagueEntry.COLUMN_BOWLER_ID + ")");
- db.execSQL("CREATE INDEX series_league_fk_index ON "
- + SeriesEntry.TABLE_NAME + "(" + SeriesEntry.COLUMN_LEAGUE_ID + ")");
- db.execSQL("CREATE INDEX game_series_fk_index ON "
- + GameEntry.TABLE_NAME + "(" + GameEntry.COLUMN_SERIES_ID + ")");
- db.execSQL("CREATE INDEX frame_game_fk_index ON "
- + FrameEntry.TABLE_NAME + "(" + FrameEntry.COLUMN_GAME_ID + ")");
- db.execSQL("CREATE INDEX match_game_fk_index ON "
- + MatchPlayEntry.TABLE_NAME + "(" + MatchPlayEntry.COLUMN_GAME_ID + ")");
- db.execSQL("CREATE INDEX team_bowler_bowler_fk_index ON "
- + TeamBowlerEntry.TABLE_NAME + "(" + TeamBowlerEntry.COLUMN_BOWLER_ID + ")");
- db.execSQL("CREATE INDEX team_bowler_team_fk_index ON "
- + TeamBowlerEntry.TABLE_NAME + "(" + TeamBowlerEntry.COLUMN_TEAM_ID + ")");
- }
-
- /**
- * Executes SQL statement to create table to store frames. Must be executed after {@code createGameTable}.
- *
- * @param db database
- */
- private void createFrameTable(SQLiteDatabase db) {
- db.execSQL("CREATE TABLE "
- + FrameEntry.TABLE_NAME + " ("
- + FrameEntry._ID + " INTEGER PRIMARY KEY, "
- + FrameEntry.COLUMN_FRAME_NUMBER + " INTEGER NOT NULL, "
- + FrameEntry.COLUMN_IS_ACCESSED + " INTEGER NOT NULL DEFAULT 0, "
- + FrameEntry.COLUMN_PIN_STATE[0] + " INTEGER NOT NULL DEFAULT 0, "
- + FrameEntry.COLUMN_PIN_STATE[1] + " INTEGER NOT NULL DEFAULT 0, "
- + FrameEntry.COLUMN_PIN_STATE[2] + " INTEGER NOT NULL DEFAULT 0, "
- + FrameEntry.COLUMN_FOULS + " INTEGER NOT NULL DEFAULT 0, "
- + FrameEntry.COLUMN_GAME_ID + " INTEGER NOT NULL"
- + " REFERENCES " + GameEntry.TABLE_NAME
- + " ON UPDATE CASCADE ON DELETE CASCADE, "
- + "CHECK (" + FrameEntry.COLUMN_FRAME_NUMBER + " >= 1 AND "
- + FrameEntry.COLUMN_FRAME_NUMBER + " <= 10), "
- + "CHECK (" + FrameEntry.COLUMN_IS_ACCESSED + " = 0 OR "
- + FrameEntry.COLUMN_IS_ACCESSED + " = 1)"
- + ");");
- }
-
- /**
- * Executes SQL statement to create table to store games. Must be executed after {@code createSeriesTable}.
- *
- * @param db database
- */
- private void createGameTable(SQLiteDatabase db) {
- db.execSQL("CREATE TABLE "
- + GameEntry.TABLE_NAME + " ("
- + GameEntry._ID + " INTEGER PRIMARY KEY, "
- + GameEntry.COLUMN_GAME_NUMBER + " INTEGER NOT NULL, "
- + GameEntry.COLUMN_SCORE + " INTEGER NOT NULL DEFAULT 0, "
- + GameEntry.COLUMN_IS_MANUAL + " INTEGER NOT NULL DEFAULT 0, "
- + GameEntry.COLUMN_IS_LOCKED + " INTEGER NOT NULL DEFAULT 0, "
- + GameEntry.COLUMN_MATCH_PLAY + " INTEGER NOT NULL DEFAULT 0, "
- + GameEntry.COLUMN_SERIES_ID + " INTEGER NOT NULL"
- + " REFERENCES " + SeriesEntry.TABLE_NAME
- + " ON UPDATE CASCADE ON DELETE CASCADE, "
- + "CHECK (" + GameEntry.COLUMN_GAME_NUMBER + " >= 1 AND "
- + GameEntry.COLUMN_GAME_NUMBER + " <= 20), "
- + "CHECK (" + GameEntry.COLUMN_IS_LOCKED + " = 0 OR "
- + GameEntry.COLUMN_IS_LOCKED + " = 1), "
- + "CHECK (" + GameEntry.COLUMN_IS_MANUAL + " = 0 OR "
- + GameEntry.COLUMN_IS_MANUAL + " = 1), "
- + "CHECK (" + GameEntry.COLUMN_SCORE + " >= 0 AND "
- + GameEntry.COLUMN_SCORE + " <= 450), "
- + "CHECK (" + GameEntry.COLUMN_MATCH_PLAY + " >= 0 AND "
- + GameEntry.COLUMN_MATCH_PLAY + " <= 3)"
- + ");");
- }
-
- /**
- * Executes SQL statement to create table to store series. Must be executed after {@code createLeagueTable}.
- *
- * @param db database
- */
- private void createSeriesTable(SQLiteDatabase db) {
- db.execSQL("CREATE TABLE "
- + SeriesEntry.TABLE_NAME + " ("
- + SeriesEntry._ID + " INTEGER PRIMARY KEY, "
- + SeriesEntry.COLUMN_SERIES_DATE + " TEXT NOT NULL, "
- + SeriesEntry.COLUMN_LEAGUE_ID + " INTEGER NOT NULL"
- + " REFERENCES " + LeagueEntry.TABLE_NAME
- + " ON UPDATE CASCADE ON DELETE CASCADE"
- + ");");
- }
-
- /**
- * Executes SQL statement to create table to store leagues. Must be executed after {@code createBowlerTable}.
- *
- * @param db database
- */
- private void createLeagueTable(SQLiteDatabase db) {
- // To keep databases consistent, ignore deprecated database fields
- // noinspection deprecation
- db.execSQL("CREATE TABLE "
- + LeagueEntry.TABLE_NAME + "("
- + LeagueEntry._ID + " INTEGER PRIMARY KEY, "
- + LeagueEntry.COLUMN_LEAGUE_NAME + " TEXT NOT NULL COLLATE NOCASE, "
- + LeagueEntry.COLUMN_NUMBER_OF_GAMES + " INTEGER NOT NULL, "
- + LeagueEntry.COLUMN_BASE_AVERAGE + " INTEGER NOT NULL DEFAULT -1, "
- + LeagueEntry.COLUMN_BASE_GAMES + " INTEGER NOT NULL DEFAULT 0, "
- + LeagueEntry.COLUMN_ADDITIONAL_PINFALL + " INTEGER NOT NULL DEFAULT 0, "
- + LeagueEntry.COLUMN_ADDITIONAL_GAMES + " INTEGER NOT NULL DEFAULT 0, "
- + LeagueEntry.COLUMN_GAME_HIGHLIGHT + " INTEGER NOT NULL DEFAULT -1, "
- + LeagueEntry.COLUMN_SERIES_HIGHLIGHT + " INTEGER NOT NULL DEFAULT -1, "
- + LeagueEntry.COLUMN_DATE_MODIFIED + " TEXT NOT NULL, "
- + LeagueEntry.COLUMN_IS_EVENT + " INTEGER NOT NULL DEFAULT 0, "
- + LeagueEntry.COLUMN_BOWLER_ID + " INTEGER NOT NULL"
- + " REFERENCES " + BowlerEntry.TABLE_NAME
- + " ON UPDATE CASCADE ON DELETE CASCADE, "
- + "CHECK (" + LeagueEntry.COLUMN_NUMBER_OF_GAMES + " > 0 AND "
- + LeagueEntry.COLUMN_NUMBER_OF_GAMES + " <= 20), "
- + "CHECK (" + LeagueEntry.COLUMN_IS_EVENT + " = 0 OR "
- + LeagueEntry.COLUMN_IS_EVENT + " = 1), "
- + "CHECK (" + LeagueEntry.COLUMN_BASE_AVERAGE + " >= -1 AND "
- + LeagueEntry.COLUMN_BASE_AVERAGE + " <= 450), "
- + "CHECK (" + LeagueEntry.COLUMN_BASE_GAMES + " >= 0 AND "
- + LeagueEntry.COLUMN_BASE_GAMES + "<= 100000)"
- + ");");
- }
-
- /**
- * Executes SQL statement to create table to store bowlers.
- *
- * @param db database
- */
- private void createBowlerTable(SQLiteDatabase db) {
- db.execSQL("CREATE TABLE "
- + BowlerEntry.TABLE_NAME + "("
- + BowlerEntry._ID + " INTEGER PRIMARY KEY, "
- + BowlerEntry.COLUMN_BOWLER_NAME + " TEXT NOT NULL COLLATE NOCASE, "
- + BowlerEntry.COLUMN_DATE_MODIFIED + " TEXT NOT NULL"
- + ");");
- }
-
- /**
- * Executes SQL statement to create table to store teams.
- *
- * @param db database
- */
- private void createTeamTable(SQLiteDatabase db) {
- db.execSQL("CREATE TABLE "
- + TeamEntry.TABLE_NAME + "("
- + TeamEntry._ID + " INTEGER PRIMARY KEY, "
- + TeamEntry.COLUMN_TEAM_NAME + " TEXT NOT NULL COLLATE NOCASE, "
- + TeamEntry.COLUMN_DATE_MODIFIED + " TEXT NOT NULL"
- + ");");
- }
-
- /**
- * Executes SQL statement to create table to store team members.
- *
- * @param db database
- */
- private void createTeamBowlerTable(SQLiteDatabase db) {
- db.execSQL("CREATE TABLE "
- + TeamBowlerEntry.TABLE_NAME + "("
- + TeamBowlerEntry.COLUMN_BOWLER_ID + " INTEGER NOT NULL"
- + " REFERENCES " + BowlerEntry.TABLE_NAME
- + " ON UPDATE CASCADE ON DELETE CASCADE, "
- + TeamBowlerEntry.COLUMN_TEAM_ID + " INTEGER NOT NULL"
- + " REFERENCES " + TeamEntry.TABLE_NAME
- + " ON UPDATE CASCADE ON DELETE CASCADE, "
- + "PRIMARY KEY (" + TeamBowlerEntry.COLUMN_BOWLER_ID + ", " + TeamBowlerEntry.COLUMN_TEAM_ID + ")"
- + ");");
- }
-
- /**
- * Executes SQL statement to create table to store match play results.
- *
- * @param db database
- */
- private void createMatchPlayTable(SQLiteDatabase db) {
- db.execSQL("CREATE TABLE "
- + MatchPlayEntry.TABLE_NAME + "("
- + MatchPlayEntry._ID + " INTEGER PRIMARY KEY, "
- + MatchPlayEntry.COLUMN_OPPONENT_NAME + " TEXT COLLATE NOCASE, "
- + MatchPlayEntry.COLUMN_OPPONENT_SCORE + " INTEGER NOT NULL DEFAULT 0, "
- + MatchPlayEntry.COLUMN_GAME_ID + " INTEGER NOT NULL"
- + " REFERENCES " + GameEntry.TABLE_NAME
- + " ON UPDATE CASCADE ON DELETE CASCADE, "
- + "CHECK (" + MatchPlayEntry.COLUMN_OPPONENT_SCORE + " >= 0 AND "
- + MatchPlayEntry.COLUMN_OPPONENT_SCORE + " <= 450)"
- + ");");
- }
-
-
- @Override
- public void onOpen(SQLiteDatabase db) {
- super.onOpen(db);
- if (!db.isReadOnly()) {
- db.execSQL("PRAGMA foreign_keys=ON;");
- }
- }
-
- @SuppressWarnings("CheckStyle")
- @Override
- public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- Log.w(TAG, "Upgrading database from version " + oldVersion + " to " + newVersion);
-
- int upgradeTo = oldVersion + 1;
- while (upgradeTo <= newVersion) {
- switch (upgradeTo) {
- case 2:
- upgradeDatabaseFrom1To2(db);
- break;
- case 3:
- upgradeDatabaseFrom2To3(db);
- break;
- case 4:
- upgradeDatabaseFrom3To4(db);
- break;
- case 5:
- upgradeDatabaseFrom4To5(db);
- break;
- case 6:
- upgradeDatabaseFrom5to6(db);
- break;
- case 7:
- upgradeDatabaseFrom6to7(db);
- break;
- }
- upgradeTo++;
- }
- }
-
- /**
- * Upgrades database from oldVersion 1 to newVersion 2.
- *
- * @param db to upgrade
- */
- @SuppressWarnings("CheckStyle")
- private void upgradeDatabaseFrom1To2(SQLiteDatabase db) {
- // Removes foreign key and check constraints from frame table
- db.execSQL("CREATE TABLE frame2 (" + FrameEntry._ID + " INTEGER PRIMARY KEY, "
- + FrameEntry.COLUMN_FRAME_NUMBER + " INTEGER NOT NULL, "
- + FrameEntry.COLUMN_IS_ACCESSED + " INTEGER NOT NULL DEFAULT 0, "
- + FrameEntry.COLUMN_PIN_STATE[0] + " TEXT NOT NULL DEFAULT '00000', "
- + FrameEntry.COLUMN_PIN_STATE[1] + " TEXT NOT NULL DEFAULT '00000', "
- + FrameEntry.COLUMN_PIN_STATE[2] + " TEXT NOT NULL DEFAULT '00000', "
- + FrameEntry.COLUMN_FOULS + " TEXT NOT NULL DEFAULT '0', "
- + FrameEntry.COLUMN_GAME_ID + " INTEGER NOT NULL"
- + ");");
- db.execSQL("INSERT INTO frame2 ("
- + FrameEntry._ID + ", "
- + FrameEntry.COLUMN_FRAME_NUMBER + ", "
- + FrameEntry.COLUMN_IS_ACCESSED + ", "
- + FrameEntry.COLUMN_PIN_STATE[0] + ", "
- + FrameEntry.COLUMN_PIN_STATE[1] + ", "
- + FrameEntry.COLUMN_PIN_STATE[2] + ", "
- + FrameEntry.COLUMN_FOULS + ", "
- + FrameEntry.COLUMN_GAME_ID + ")"
- + " SELECT "
- + FrameEntry._ID + ", "
- + FrameEntry.COLUMN_FRAME_NUMBER + ", "
- + FrameEntry.COLUMN_IS_ACCESSED + ", "
- + FrameEntry.COLUMN_PIN_STATE[0] + ", "
- + FrameEntry.COLUMN_PIN_STATE[1] + ", "
- + FrameEntry.COLUMN_PIN_STATE[2] + ", "
- + FrameEntry.COLUMN_FOULS + ", "
- + FrameEntry.COLUMN_GAME_ID + " FROM " + FrameEntry.TABLE_NAME);
- db.execSQL("DROP TABLE " + FrameEntry.TABLE_NAME);
- db.execSQL("ALTER TABLE frame2 RENAME TO " + FrameEntry.TABLE_NAME);
-
- // Adds new column and check constraints to game table
- db.execSQL("CREATE TABLE game2 ("
- + GameEntry._ID + " INTEGER PRIMARY KEY, "
- + GameEntry.COLUMN_GAME_NUMBER + " INTEGER NOT NULL, "
- + GameEntry.COLUMN_SCORE + " INTEGER NOT NULL DEFAULT 0, "
- + GameEntry.COLUMN_IS_MANUAL + " INTEGER NOT NULL DEFAULT 0, "
- + GameEntry.COLUMN_IS_LOCKED + " INTEGER NOT NULL DEFAULT 0, "
- + GameEntry.COLUMN_MATCH_PLAY + " INTEGER NOT NULL DEFAULT 0, "
- + GameEntry.COLUMN_SERIES_ID + " INTEGER NOT NULL"
- + " REFERENCES " + SeriesEntry.TABLE_NAME
- + " ON UPDATE CASCADE ON DELETE CASCADE, "
- + "CHECK (" + GameEntry.COLUMN_GAME_NUMBER + " >= 1 AND "
- + GameEntry.COLUMN_GAME_NUMBER + " <= 20), "
- + "CHECK (" + GameEntry.COLUMN_IS_LOCKED + " = 0 OR "
- + GameEntry.COLUMN_IS_LOCKED + " = 1), "
- + "CHECK (" + GameEntry.COLUMN_IS_MANUAL + " = 0 OR "
- + GameEntry.COLUMN_IS_MANUAL + " = 1), "
- + "CHECK (" + GameEntry.COLUMN_SCORE + " >= 0 AND "
- + GameEntry.COLUMN_SCORE + " <= 450), "
- + "CHECK (" + GameEntry.COLUMN_MATCH_PLAY + " >= 0 AND "
- + GameEntry.COLUMN_MATCH_PLAY + " <= 3)"
- + ");");
- db.execSQL("INSERT INTO game2 ("
- + GameEntry._ID + ", "
- + GameEntry.COLUMN_GAME_NUMBER + ", "
- + GameEntry.COLUMN_SCORE + ", "
- + GameEntry.COLUMN_IS_MANUAL + ", "
- + GameEntry.COLUMN_IS_LOCKED + ", "
- + GameEntry.COLUMN_SERIES_ID + ")"
- + " SELECT * FROM " + GameEntry.TABLE_NAME);
- db.execSQL("DROP TABLE " + GameEntry.TABLE_NAME);
- db.execSQL("ALTER TABLE game2 RENAME TO " + GameEntry.TABLE_NAME);
-
- // Adds foreign key and check constraints to frame table
- db.execSQL("CREATE TABLE frame2 ("
- + FrameEntry._ID + " INTEGER PRIMARY KEY, "
- + FrameEntry.COLUMN_FRAME_NUMBER + " INTEGER NOT NULL, "
- + FrameEntry.COLUMN_IS_ACCESSED + " INTEGER NOT NULL DEFAULT 0, "
- + FrameEntry.COLUMN_PIN_STATE[0] + " TEXT NOT NULL DEFAULT '00000', "
- + FrameEntry.COLUMN_PIN_STATE[1] + " TEXT NOT NULL DEFAULT '00000', "
- + FrameEntry.COLUMN_PIN_STATE[2] + " TEXT NOT NULL DEFAULT '00000', "
- + FrameEntry.COLUMN_FOULS + " TEXT NOT NULL DEFAULT '0', "
- + FrameEntry.COLUMN_GAME_ID + " INTEGER NOT NULL"
- + " REFERENCES " + GameEntry.TABLE_NAME
- + " ON UPDATE CASCADE ON DELETE CASCADE, "
- + "CHECK (" + FrameEntry.COLUMN_FRAME_NUMBER + " >= 1 AND "
- + FrameEntry.COLUMN_FRAME_NUMBER + " <= 10), "
- + "CHECK (" + FrameEntry.COLUMN_IS_ACCESSED + " = 0 OR "
- + FrameEntry.COLUMN_IS_ACCESSED + " = 1)"
- + ");");
- db.execSQL("INSERT INTO frame2 ("
- + FrameEntry._ID + ", "
- + FrameEntry.COLUMN_FRAME_NUMBER + ", "
- + FrameEntry.COLUMN_IS_ACCESSED + ", "
- + FrameEntry.COLUMN_PIN_STATE[0] + ", "
- + FrameEntry.COLUMN_PIN_STATE[1] + ", "
- + FrameEntry.COLUMN_PIN_STATE[2] + ", "
- + FrameEntry.COLUMN_FOULS + ", "
- + FrameEntry.COLUMN_GAME_ID + ")"
- + " SELECT "
- + FrameEntry._ID + ", "
- + FrameEntry.COLUMN_FRAME_NUMBER + ", "
- + FrameEntry.COLUMN_IS_ACCESSED + ", "
- + FrameEntry.COLUMN_PIN_STATE[0] + ", "
- + FrameEntry.COLUMN_PIN_STATE[1] + ", "
- + FrameEntry.COLUMN_PIN_STATE[2] + ", "
- + FrameEntry.COLUMN_FOULS + ", "
- + FrameEntry.COLUMN_GAME_ID + " FROM " + FrameEntry.TABLE_NAME);
- db.execSQL("DROP TABLE " + FrameEntry.TABLE_NAME);
- db.execSQL("ALTER TABLE frame2 RENAME TO " + FrameEntry.TABLE_NAME);
-
- db.execSQL("CREATE INDEX game_id_index ON "
- + GameEntry.TABLE_NAME + "(" + GameEntry._ID + ")");
- db.execSQL("CREATE INDEX frame_id_index ON "
- + FrameEntry.TABLE_NAME + "(" + FrameEntry._ID + ")");
- db.execSQL("CREATE INDEX game_series_fk_index ON "
- + GameEntry.TABLE_NAME + "(" + GameEntry.COLUMN_SERIES_ID + ")");
- db.execSQL("CREATE INDEX frame_game_fk_index ON "
- + FrameEntry.TABLE_NAME + "(" + FrameEntry.COLUMN_GAME_ID + ")");
-
-
- }
-
- /**
- * Upgrades database from oldVersion 2 to newVersion 3.
- *
- * @param db to upgrade
- */
- @SuppressWarnings("CheckStyle")
- private void upgradeDatabaseFrom2To3(SQLiteDatabase db) {
- db.execSQL("DROP INDEX IF EXISTS frame_id_index");
- db.execSQL("DROP INDEX IF EXISTS frame_game_fk_index");
-
- db.execSQL("CREATE TABLE frame2 ("
- + FrameEntry._ID + " INTEGER PRIMARY KEY, "
- + FrameEntry.COLUMN_FRAME_NUMBER + " INTEGER NOT NULL, "
- + FrameEntry.COLUMN_IS_ACCESSED + " INTEGER NOT NULL DEFAULT 0, "
- + FrameEntry.COLUMN_PIN_STATE[0] + " INTEGER NOT NULL DEFAULT 0, "
- + FrameEntry.COLUMN_PIN_STATE[1] + " INTEGER NOT NULL DEFAULT 0, "
- + FrameEntry.COLUMN_PIN_STATE[2] + " INTEGER NOT NULL DEFAULT 0, "
- + FrameEntry.COLUMN_FOULS + " INTEGER NOT NULL DEFAULT 0, "
- + FrameEntry.COLUMN_GAME_ID + " INTEGER NOT NULL"
- + " REFERENCES " + GameEntry.TABLE_NAME
- + " ON UPDATE CASCADE ON DELETE CASCADE, "
- + "CHECK (" + FrameEntry.COLUMN_FRAME_NUMBER + " >= 1 AND "
- + FrameEntry.COLUMN_FRAME_NUMBER + " <= 10), "
- + "CHECK (" + FrameEntry.COLUMN_IS_ACCESSED + " = 0 OR "
- + FrameEntry.COLUMN_IS_ACCESSED + " = 1)"
- + ");");
- db.execSQL("INSERT INTO frame2 ("
- + FrameEntry._ID + ", "
- + FrameEntry.COLUMN_FRAME_NUMBER + ", "
- + FrameEntry.COLUMN_IS_ACCESSED + ", "
- + FrameEntry.COLUMN_PIN_STATE[0] + ", "
- + FrameEntry.COLUMN_PIN_STATE[1] + ", "
- + FrameEntry.COLUMN_PIN_STATE[2] + ", "
- + FrameEntry.COLUMN_FOULS + ", "
- + FrameEntry.COLUMN_GAME_ID + ")"
- + " SELECT "
- + FrameEntry._ID + ", "
- + FrameEntry.COLUMN_FRAME_NUMBER + ", "
- + FrameEntry.COLUMN_IS_ACCESSED + ", "
- + FrameEntry.COLUMN_PIN_STATE[0] + ", "
- + FrameEntry.COLUMN_PIN_STATE[1] + ", "
- + FrameEntry.COLUMN_PIN_STATE[2] + ", "
- + FrameEntry.COLUMN_FOULS + ", "
- + FrameEntry.COLUMN_GAME_ID + " FROM " + FrameEntry.TABLE_NAME);
- db.execSQL("DROP TABLE " + FrameEntry.TABLE_NAME);
- db.execSQL("ALTER TABLE frame2 RENAME TO " + FrameEntry.TABLE_NAME);
-
- db.execSQL("CREATE INDEX frame_id_index ON "
- + FrameEntry.TABLE_NAME + "(" + FrameEntry._ID + ")");
- db.execSQL("CREATE INDEX frame_game_fk_index ON "
- + FrameEntry.TABLE_NAME + "(" + FrameEntry.COLUMN_GAME_ID + ")");
-
- try {
- db.beginTransaction();
- for (int i = 0; i < 32; i++) {
- for (int j = 0; j < 3; j++) {
- ContentValues values = new ContentValues();
- values.put(FrameEntry.COLUMN_PIN_STATE[j], i);
- db.update(FrameEntry.TABLE_NAME,
- values,
- FrameEntry.COLUMN_PIN_STATE[j] + "=?",
- new String[]{
- String.format(Locale.CANADA, "%5s", Integer.toBinaryString(i)).replace(' ', '0')
- });
- }
- }
- for (int i = 24; i < 31; i++) {
- ContentValues values = new ContentValues();
- values.put(FrameEntry.COLUMN_FOULS, i);
- db.update(FrameEntry.TABLE_NAME,
- values,
- FrameEntry.COLUMN_FOULS + "=?",
- new String[]{Fouls.INSTANCE.foulIntToString(i)});
- }
- db.setTransactionSuccessful();
- } catch (Exception ex) {
- Log.e(TAG, "Error upgrading db from 2 to 3", ex);
- } finally {
- db.endTransaction();
- }
- }
-
- /**
- * Upgrades database from oldVersion 3 to newVersion 4.
- *
- * @param db to upgrade
- */
- private void upgradeDatabaseFrom3To4(SQLiteDatabase db) {
- createMatchPlayTable(db);
- db.execSQL("CREATE INDEX match_game_fk_index ON "
- + MatchPlayEntry.TABLE_NAME + "(" + MatchPlayEntry.COLUMN_GAME_ID + ")");
- db.execSQL("CREATE INDEX match_id_index ON "
- + MatchPlayEntry.TABLE_NAME + "(" + MatchPlayEntry._ID + ")");
- }
-
- /**
- * Upgrades database from oldVersion 4 to newVersion 5.
- *
- * @param db to upgrade
- */
- private void upgradeDatabaseFrom4To5(SQLiteDatabase db) {
- // To keep databases consistent, ignore deprecated database fields
- // noinspection deprecation
- db.execSQL("ALTER TABLE " + LeagueEntry.TABLE_NAME
- + " ADD COLUMN "
- + LeagueEntry.COLUMN_BASE_AVERAGE + " INTEGER NOT NULL DEFAULT -1;");
- // noinspection deprecation
- db.execSQL("ALTER TABLE " + LeagueEntry.TABLE_NAME
- + " ADD COLUMN "
- + LeagueEntry.COLUMN_BASE_GAMES + " INTEGER NOT NULL DEFAULT 0;");
- try {
- db.beginTransaction();
- ContentValues values = new ContentValues();
- // noinspection deprecation
- values.put(LeagueEntry.COLUMN_BASE_AVERAGE, -1);
- // noinspection deprecation
- values.put(LeagueEntry.COLUMN_BASE_GAMES, 0);
- db.update(LeagueEntry.TABLE_NAME, values, null, null);
- db.setTransactionSuccessful();
- } catch (Exception ex) {
- Log.e(TAG, "Error upgrading from 4 to 5", ex);
- } finally {
- db.endTransaction();
- }
- }
-
- /**
- * Upgrades database from oldVersion 5 to newVersion 6.
- *
- * @param db to upgrade
- */
- private void upgradeDatabaseFrom5to6(SQLiteDatabase db) {
- // To keep databases consistent, ignore deprecated database fields
- try {
- db.beginTransaction();
- ContentValues values = new ContentValues();
- // noinspection deprecation
- values.put(LeagueEntry.COLUMN_BASE_GAMES, 0);
- // noinspection deprecation
- db.update(LeagueEntry.TABLE_NAME,
- values,
- LeagueEntry.COLUMN_BASE_GAMES + "=?",
- new String[]{String.valueOf(-1)});
- db.setTransactionSuccessful();
- } catch (Exception ex) {
- Log.e(TAG, "Error upgrading from 5 to 6");
- } finally {
- db.endTransaction();
- }
- }
-
- /**
- * Upgrades database from oldVersion 6 to newVersion 7.
- *
- * @param db to upgrade
- */
- private void upgradeDatabaseFrom6to7(SQLiteDatabase db) {
- createTeamTable(db);
- createTeamBowlerTable(db);
-
- db.execSQL("ALTER TABLE " + LeagueEntry.TABLE_NAME
- + " ADD COLUMN "
- + LeagueEntry.COLUMN_ADDITIONAL_PINFALL + " INTEGER NOT NULL DEFAULT 0;");
- db.execSQL("ALTER TABLE " + LeagueEntry.TABLE_NAME
- + " ADD COLUMN "
- + LeagueEntry.COLUMN_ADDITIONAL_GAMES + " INTEGER NOT NULL DEFAULT 0;");
- db.execSQL("ALTER TABLE " + LeagueEntry.TABLE_NAME
- + " ADD COLUMN "
- + LeagueEntry.COLUMN_GAME_HIGHLIGHT + " INTEGER NOT NULL DEFAULT -1;");
- db.execSQL("ALTER TABLE " + LeagueEntry.TABLE_NAME
- + " ADD COLUMN "
- + LeagueEntry.COLUMN_SERIES_HIGHLIGHT + " INTEGER NOT NULL DEFAULT -1;");
-
- try {
- db.beginTransaction();
-
- // Replace existing `Practice` leagues from user with `Practice league`
- ContentValues values = new ContentValues();
- values.put(LeagueEntry.COLUMN_LEAGUE_NAME, League.PRACTICE_LEAGUE_NAME + " league");
- db.update(
- LeagueEntry.TABLE_NAME,
- values,
- LeagueEntry.COLUMN_LEAGUE_NAME + "=? AND " + LeagueEntry.COLUMN_IS_EVENT + "=?",
- new String[]{League.PRACTICE_LEAGUE_NAME, "0"}
- );
-
- // Replace existing `Practice` events from user with `Practice event`
- values = new ContentValues();
- values.put(LeagueEntry.COLUMN_LEAGUE_NAME, League.PRACTICE_LEAGUE_NAME + " event");
- db.update(
- LeagueEntry.TABLE_NAME,
- values,
- LeagueEntry.COLUMN_LEAGUE_NAME + "=? AND " + LeagueEntry.COLUMN_IS_EVENT + "=?",
- new String[]{League.PRACTICE_LEAGUE_NAME, "1"}
- );
-
- // Replace `Open` league with `Practice` league
- values = new ContentValues();
- values.put(LeagueEntry.COLUMN_LEAGUE_NAME, League.PRACTICE_LEAGUE_NAME);
- // noinspection deprecation
- db.update(LeagueEntry.TABLE_NAME,
- values,
- LeagueEntry.COLUMN_LEAGUE_NAME + "=?",
- new String[]{League.OPEN_LEAGUE_NAME});
- // noinspection deprecation
- db.execSQL("UPDATE " + LeagueEntry.TABLE_NAME + " SET "
- + LeagueEntry.COLUMN_ADDITIONAL_GAMES + "=" + LeagueEntry.COLUMN_BASE_GAMES + ", "
- + LeagueEntry.COLUMN_ADDITIONAL_PINFALL + "=" + LeagueEntry.COLUMN_BASE_GAMES + "*" + LeagueEntry.COLUMN_BASE_AVERAGE);
-
- db.setTransactionSuccessful();
- } catch (Exception ex) {
- Log.e(TAG, "Error upgrading from 6 to 7", ex);
- } finally {
- db.endTransaction();
- }
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/database/DatabaseManager.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/database/DatabaseManager.kt
deleted file mode 100644
index 31a0a0c5f..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/database/DatabaseManager.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-package ca.josephroque.bowlingcompanion.database
-
-import android.content.Context
-import android.database.sqlite.SQLiteDatabase
-import kotlinx.coroutines.experimental.CommonPool
-import kotlinx.coroutines.experimental.Deferred
-import kotlinx.coroutines.experimental.async
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Manage access to the database.
- */
-object DatabaseManager {
- fun getReadableDatabase(context: Context): Deferred {
- return async(CommonPool) {
- Annihilator.instance.wait().await()
- Saviour.instance.wait().await()
- return@async DatabaseHelper.getInstance(context).readableDatabase
- }
- }
-
- fun getWritableDatabase(context: Context): Deferred {
- return async(CommonPool) {
- Annihilator.instance.wait().await()
- Saviour.instance.wait().await()
- return@async DatabaseHelper.getInstance(context).writableDatabase
- }
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/database/Saviour.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/database/Saviour.kt
deleted file mode 100644
index dbf30dd76..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/database/Saviour.kt
+++ /dev/null
@@ -1,217 +0,0 @@
-package ca.josephroque.bowlingcompanion.database
-
-import android.content.ContentValues
-import android.content.Context
-import android.database.sqlite.SQLiteDatabase
-import android.util.Log
-import ca.josephroque.bowlingcompanion.App
-import ca.josephroque.bowlingcompanion.common.Android
-import ca.josephroque.bowlingcompanion.database.Contract.FrameEntry
-import ca.josephroque.bowlingcompanion.database.Contract.GameEntry
-import ca.josephroque.bowlingcompanion.database.Contract.MatchPlayEntry
-import ca.josephroque.bowlingcompanion.games.Frame
-import ca.josephroque.bowlingcompanion.games.Game
-import ca.josephroque.bowlingcompanion.games.lane.toInt
-import ca.josephroque.bowlingcompanion.matchplay.MatchPlay
-import kotlinx.coroutines.experimental.async
-import kotlinx.coroutines.experimental.delay
-import kotlinx.coroutines.experimental.CommonPool
-import kotlinx.coroutines.experimental.CoroutineStart
-import kotlinx.coroutines.experimental.Deferred
-import kotlinx.coroutines.experimental.Job
-import kotlinx.coroutines.experimental.launch
-import java.lang.ref.WeakReference
-import java.util.LinkedList
-import java.util.Queue
-import java.util.concurrent.atomic.AtomicBoolean
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Managing saving to the database synchronously and waiting for tasks to finish saving before
- * loading happens.
- */
-class Saviour private constructor() {
-
- companion object {
- @Suppress("unused")
- private const val TAG = "Saviour"
-
- val instance: Saviour by lazy { Holder.INSTANCE }
- }
-
- private object Holder { val INSTANCE = Saviour() }
-
- private var saviourIsRunning = AtomicBoolean(false)
-
- private val saveQueue: Queue = LinkedList()
-
- init {
- launchSaviourProcessor()
- }
-
- // MARK: Saviour
-
- fun wait(): Deferred {
- return async(CommonPool) {
- launchSaviourProcessor()
- while (saveQueue.isNotEmpty()) {
- delay(50)
- }
- }
- }
-
- fun saveFrame(weakContext: WeakReference, score: Int, frame: Frame) {
- val job = launch(context = Android, start = CoroutineStart.LAZY) {
- val strongContext = weakContext.get() ?: return@launch
- val database = DatabaseHelper.getInstance(strongContext).writableDatabase
- database.beginTransaction()
- try {
- // Save the score
- val values = ContentValues()
- values.put(GameEntry.COLUMN_SCORE, score)
- database.update(GameEntry.TABLE_NAME,
- values,
- "${GameEntry._ID}=?",
- arrayOf(frame.gameId.toString()))
-
- // Save the frame
- writeFrameToDatabase(database, frame)
-
- database.setTransactionSuccessful()
- } catch (ex: Exception) {
- Log.e(TAG, "Fatal error. Game could not save.")
- throw ex
- } finally {
- database.endTransaction()
- }
- }
-
- saveQueue.offer(job)
- launchSaviourProcessor()
- }
-
- fun saveMatchPlay(weakContext: WeakReference, matchPlay: MatchPlay) {
- val job = launch(context = Android, start = CoroutineStart.LAZY) {
- val strongContext = weakContext.get() ?: return@launch
- val database = DatabaseHelper.getInstance(strongContext).writableDatabase
- database.beginTransaction()
- try {
- writeMatchPlayToDatabase(database, matchPlay)
- database.setTransactionSuccessful()
- } catch (ex: Exception) {
- Log.e(TAG, "Fatal error. Game could not save.")
- throw ex
- } finally {
- database.endTransaction()
- }
- }
-
- saveQueue.offer(job)
- launchSaviourProcessor()
- }
-
- fun saveGame(weakContext: WeakReference, game: Game) {
- val job = launch(context = Android, start = CoroutineStart.LAZY) {
- val strongContext = weakContext.get() ?: return@launch
- val database = DatabaseHelper.getInstance(strongContext).writableDatabase
- database.beginTransaction()
- try {
- val values = ContentValues().apply {
- put(GameEntry.COLUMN_SCORE, game.score)
- put(GameEntry.COLUMN_IS_LOCKED, game.isLocked)
- put(GameEntry.COLUMN_IS_MANUAL, game.isManual)
- }
- database.update(GameEntry.TABLE_NAME,
- values,
- "${GameEntry._ID}=?",
- arrayOf(game.id.toString()))
-
- for (frame in game.frames) {
- writeFrameToDatabase(database, frame)
- }
-
- writeMatchPlayToDatabase(database, game.matchPlay)
-
- database.setTransactionSuccessful()
- } catch (ex: Exception) {
- Log.e(TAG, "Fatal error. Game could not save.")
- throw ex
- } finally {
- database.endTransaction()
- }
- }
-
- saveQueue.offer(job)
- launchSaviourProcessor()
- }
-
- // MARK: Private functions
-
- private fun launchSaviourProcessor() {
- if (saviourIsRunning.get()) {
- return
- }
-
- saviourIsRunning.set(true)
- launch(CommonPool) {
- while (App.isRunning.get() || saveQueue.isNotEmpty()) {
- val saveRoutine = saveQueue.poll()
- if (!saveRoutine.isCompleted && !saveRoutine.isCancelled) {
- saveRoutine.join()
- } else {
- Log.e(TAG, "Saviour thread already processed - $saveRoutine")
- }
- }
-
- saviourIsRunning.set(false)
- }
- }
-
- private fun writeFrameToDatabase(database: SQLiteDatabase, frame: Frame) {
- val values = ContentValues().apply {
- for (i in 0 until Frame.NUMBER_OF_BALLS) {
- put(FrameEntry.COLUMN_PIN_STATE[i], frame.pinState[i].toInt())
- }
- put(FrameEntry.COLUMN_IS_ACCESSED, if (frame.isAccessed) 1 else 0)
- put(FrameEntry.COLUMN_FOULS, frame.dbFouls)
- }
- database.update(FrameEntry.TABLE_NAME,
- values,
- "${FrameEntry._ID}=?",
- arrayOf(frame.id.toString()))
- }
-
- private fun writeMatchPlayToDatabase(database: SQLiteDatabase, matchPlay: MatchPlay) {
- // Save the match play result to the game
- var values = ContentValues()
- values.put(GameEntry.COLUMN_MATCH_PLAY, matchPlay.result.ordinal)
- database.update(GameEntry.TABLE_NAME,
- values,
- "${GameEntry._ID}=?",
- arrayOf(matchPlay.gameId.toString()))
-
- // Save the match play details
- values = ContentValues().apply {
- put(MatchPlayEntry.COLUMN_GAME_ID, matchPlay.gameId)
- put(MatchPlayEntry.COLUMN_OPPONENT_NAME, matchPlay.opponentName)
- put(MatchPlayEntry.COLUMN_OPPONENT_SCORE, matchPlay.opponentScore)
- }
-
- /*
- * Due to the way this method was originally implemented, when match play results were updated,
- * often the wrong row in the table was altered. This bug prevented users from saving match play
- * results under certain circumstances. This has been fixed, but the old data cannot be safely
- * removed all at once, without potentially deleting some of the user's real data. As a fix, when
- * a user now saves match play results, any old results for *only that game* are deleted, and the
- * new results are inserted, as seen below.
- */
- database.delete(MatchPlayEntry.TABLE_NAME,
- "${MatchPlayEntry.COLUMN_GAME_ID}=?",
- arrayOf(matchPlay.gameId.toString()))
-
- database.insert(MatchPlayEntry.TABLE_NAME,
- null,
- values)
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/Frame.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/Frame.kt
deleted file mode 100644
index 4e5b4a855..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/Frame.kt
+++ /dev/null
@@ -1,105 +0,0 @@
-package ca.josephroque.bowlingcompanion.games
-
-import android.os.Parcel
-import ca.josephroque.bowlingcompanion.common.interfaces.IIdentifiable
-import ca.josephroque.bowlingcompanion.common.interfaces.KParcelable
-import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator
-import ca.josephroque.bowlingcompanion.common.interfaces.readBoolean
-import ca.josephroque.bowlingcompanion.common.interfaces.writeBoolean
-import ca.josephroque.bowlingcompanion.games.lane.Deck
-import ca.josephroque.bowlingcompanion.games.lane.Pin
-import ca.josephroque.bowlingcompanion.games.lane.toBooleanArray
-import ca.josephroque.bowlingcompanion.scoring.Fouls
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * A single frame in a game.
- */
-class Frame(
- val gameId: Long,
- override val id: Long,
- val ordinal: Int,
- var isAccessed: Boolean,
- var pinState: Array,
- var ballFouled: BooleanArray
-) : IIdentifiable, KParcelable {
-
- companion object {
- @Suppress("unused")
- private const val TAG = "Frame"
-
- @Suppress("unused")
- @JvmField val CREATOR = parcelableCreator(::Frame)
-
- const val NUMBER_OF_BALLS = 3
- const val LAST_BALL = NUMBER_OF_BALLS - 1
- const val MAX_VALUE = 15
- }
-
- val zeroBasedOrdinal: Int
- get() = ordinal - 1
-
- /** LEGACY: garbage method of serializing fouls to database. */
- private val dbFoulString: String
- get() {
- val builder = StringBuilder()
- ballFouled.forEachIndexed { index, foul -> if (foul) { builder.append(index + 1) } }
- if (builder.isEmpty()) { builder.append(0) }
- return builder.toString()
- }
-
- /** LEGACY: garbage method of serializing fouls to database. */
- val dbFouls: Int
- get() = Fouls.foulStringToInt(dbFoulString)
-
- val pinsLeftOnDeck: Int
- get() = pinState[Frame.NUMBER_OF_BALLS].sumBy { if (it.onDeck) it.value else 0 }
-
- // MARK: Constructors
-
- private constructor(p: Parcel): this(
- gameId = p.readLong(),
- id = p.readLong(),
- ordinal = p.readInt(),
- isAccessed = p.readBoolean(),
- pinState = Array(NUMBER_OF_BALLS) {
- val pins = BooleanArray(Game.NUMBER_OF_PINS)
- p.readBooleanArray(pins)
- return@Array Pin.deckFromBooleanArray(pins)
- },
- ballFouled = BooleanArray(NUMBER_OF_BALLS).apply {
- p.readBooleanArray(this)
- }
- )
-
- private constructor(other: Frame): this(
- gameId = other.gameId,
- id = other.id,
- ordinal = other.ordinal,
- isAccessed = other.isAccessed,
- pinState = Array(NUMBER_OF_BALLS) {
- return@Array other.pinState[it].map { pin -> pin.deepCopy() }.toTypedArray()
- },
- ballFouled = other.ballFouled.clone()
- )
-
- // MARK: Frame
-
- fun deepCopy(): Frame {
- return Frame(this)
- }
-
- // MARK: Parcelable
-
- override fun writeToParcel(dest: Parcel, flags: Int) = with(dest) {
- writeLong(gameId)
- writeLong(id)
- writeInt(ordinal)
- writeBoolean(isAccessed)
- for (i in 0 until NUMBER_OF_BALLS) {
- writeBooleanArray(pinState[i].toBooleanArray())
- }
- writeBooleanArray(ballFouled)
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/Game.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/Game.kt
deleted file mode 100644
index 90c72a0e3..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/Game.kt
+++ /dev/null
@@ -1,409 +0,0 @@
-package ca.josephroque.bowlingcompanion.games
-
-import android.content.Context
-import android.database.Cursor
-import android.os.Parcel
-import ca.josephroque.bowlingcompanion.common.interfaces.IIdentifiable
-import ca.josephroque.bowlingcompanion.common.interfaces.KParcelable
-import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator
-import ca.josephroque.bowlingcompanion.common.interfaces.readBoolean
-import ca.josephroque.bowlingcompanion.common.interfaces.writeBoolean
-import ca.josephroque.bowlingcompanion.database.Contract.FrameEntry
-import ca.josephroque.bowlingcompanion.database.Contract.GameEntry
-import ca.josephroque.bowlingcompanion.database.Contract.MatchPlayEntry
-import ca.josephroque.bowlingcompanion.database.DatabaseManager
-import ca.josephroque.bowlingcompanion.games.lane.arePinsCleared
-import ca.josephroque.bowlingcompanion.games.lane.Ball
-import ca.josephroque.bowlingcompanion.games.lane.ballValue
-import ca.josephroque.bowlingcompanion.games.lane.ballValueDifference
-import ca.josephroque.bowlingcompanion.games.lane.Pin
-import ca.josephroque.bowlingcompanion.games.lane.value
-import ca.josephroque.bowlingcompanion.games.lane.valueDifference
-import ca.josephroque.bowlingcompanion.matchplay.MatchPlay
-import ca.josephroque.bowlingcompanion.matchplay.MatchPlayResult
-import ca.josephroque.bowlingcompanion.scoring.Fouls
-import ca.josephroque.bowlingcompanion.series.Series
-import kotlinx.coroutines.experimental.CommonPool
-import kotlinx.coroutines.experimental.Deferred
-import kotlinx.coroutines.experimental.async
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * A single game recording.
- */
-class Game(
- val series: Series,
- override val id: Long,
- /** Ordinal starts at 1 */
- val ordinal: Int,
- var isLocked: Boolean,
- var initialScore: Int,
- var isManual: Boolean,
- val frames: List,
- val matchPlay: MatchPlay
-) : IIdentifiable, KParcelable {
-
- // MARK: Constructors
-
- private constructor(p: Parcel): this(
- series = p.readParcelable(Series::class.java.classLoader)!!,
- id = p.readLong(),
- ordinal = p.readInt(),
- initialScore = p.readInt(),
- isLocked = p.readBoolean(),
- isManual = p.readBoolean(),
- frames = arrayListOf().apply {
- val parcelableArray = p.readParcelableArray(Frame::class.java.classLoader)!!
- this.addAll(parcelableArray.map {
- return@map it as Frame
- })
- },
- matchPlay = p.readParcelable(MatchPlay::class.java.classLoader)!!
- )
-
- private constructor(other: Game): this(
- series = other.series,
- id = other.id,
- ordinal = other.ordinal,
- initialScore = other.score,
- isLocked = other.isLocked,
- isManual = other.isManual,
- frames = other.frames.map { it.deepCopy() },
- matchPlay = other.matchPlay.deepCopy()
- )
-
- // MARK: Parcelable
-
- override fun writeToParcel(dest: Parcel, flags: Int) = with(dest) {
- writeParcelable(series, 0)
- writeLong(id)
- writeInt(ordinal)
- writeInt(score)
- writeBoolean(isLocked)
- writeBoolean(isManual)
- writeParcelableArray(frames.toTypedArray(), 0)
- writeParcelable(matchPlay, 0)
- }
-
- fun deepCopy(): Game {
- return Game(this)
- }
-
- val firstNewFrame: Int
- get() {
- val firstFrameNotAccessed = frames.indexOfFirst { frame -> !frame.isAccessed }
- return if (firstFrameNotAccessed > -1) firstFrameNotAccessed else Game.LAST_FRAME
- }
-
- private var dirty: Boolean = true
-
- var score: Int = initialScore
- get() {
- if (isManual) { return field }
- if (dirty) { frameScores.hashCode() }
- return field
- }
-
- val fouls: Int
- get() = frames.asSequence().map { frame -> frame.ballFouled.count { it } }.sum()
-
- private var _frameScores: IntArray = IntArray(frames.size)
- get() {
- if (!dirty) { return field }
- val frameScores = IntArray(frames.size)
- for (frameIdx in frames.size - 1 downTo 0) {
- val frame = frames[frameIdx]
-
- // Score last frame differently than other frames
- if (frame.zeroBasedOrdinal == Game.LAST_FRAME) {
- for (ballIdx in Frame.LAST_BALL downTo 0) {
- if (ballIdx == Frame.LAST_BALL) {
- // Always add the value of the 3rd ball
- frameScores[frameIdx] = frame.pinState[ballIdx].value(false)
- } else if (frame.pinState[ballIdx].arePinsCleared) {
- // If all pins were knocked down in a previous ball, add the full
- // value of that ball (it's a strike/spare)
- frameScores[frameIdx] += frame.pinState[ballIdx].value(false)
- }
- }
- } else {
- val nextFrame = frames[frameIdx + 1]
- for (ballIdx in 0 until Frame.NUMBER_OF_BALLS) {
- if (ballIdx == Frame.LAST_BALL) {
- // If the loop is not exited by this point, there's no strike or spare
- // Add basic value of the frame
- frameScores[frameIdx] += frame.pinState[ballIdx].value(false)
- } else if (frame.pinState[ballIdx].arePinsCleared) {
- // Either spare or strike occurred, add ball 0 of this frame and next
- frameScores[frameIdx] += frame.pinState[ballIdx].value(false)
- frameScores[frameIdx] += nextFrame.pinState[0].value(false)
- val double = frameScores[frameIdx] == Frame.MAX_VALUE * 2
-
- // Strike in this frame
- if (ballIdx == 0) {
- if (nextFrame.zeroBasedOrdinal == Game.LAST_FRAME) {
- // 9th frame must get additional scoring from 10th frame only
- if (double) {
- frameScores[frameIdx] += nextFrame.pinState[1].value(false)
- } else {
- frameScores[frameIdx] += nextFrame.pinState[1].valueDifference(nextFrame.pinState[0])
- }
- } else if (!double) {
- frameScores[frameIdx] += nextFrame.pinState[1].valueDifference(nextFrame.pinState[0])
- } else {
- frameScores[frameIdx] += frames[frameIdx + 2].pinState[0].value(false)
- }
- }
- break
- }
- }
- }
- }
-
- // Get the accumulative score for each frame
- var totalScore = 0
- for (i in 0 until frames.size) {
- totalScore += frameScores[i]
- frameScores[i] = totalScore
- }
-
- // Calculate the final score of the game
- if (!isManual) {
- score = maxOf(totalScore - fouls * Game.FOUL_PENALTY, 0)
- dirty = false
- }
-
- field = frameScores
- return frameScores
- }
-
- val frameScores: IntArray
- get() = _frameScores.copyOf()
-
- // MARK: Game
-
- fun getBallTextForFrames(): List> {
- return frames.mapIndexed { index, frame ->
- val balls = Array(Frame.NUMBER_OF_BALLS) { "" }
- if (!frame.isAccessed) {
- return@mapIndexed balls
- }
-
- if (frame.zeroBasedOrdinal == Game.LAST_FRAME) {
- if (frame.pinState[0].arePinsCleared) {
- // If the first ball is a strike, the next two could be strikes/spares
- balls[0] = Ball.Strike.toString()
- if (frame.pinState[1].arePinsCleared) {
- // If the second ball is a strike
- balls[1] = Ball.Strike.toString()
- if (frame.pinState[2].arePinsCleared) {
- balls[2] = Ball.Strike.toString()
- } else {
- balls[2] = frame.pinState[2].ballValue(2, true, false)
- }
- } else {
- // Second ball is not a strike
- balls[1] = frame.pinState[1].ballValue(1, true, false)
- balls[2] = if (frame.pinState[2].arePinsCleared)
- Ball.Spare.toString()
- else
- frame.pinState[2].ballValueDifference(frame.pinState[1], 2, false, false)
- }
- } else {
- // If the first ball is not a strike, check if the second ball is a spare or not and get value
- balls[0] = frame.pinState[0].ballValue(0, false, false)
- if (frame.pinState[1].arePinsCleared) {
- balls[1] = Ball.Spare.toString()
- balls[2] = frame.pinState[2].ballValue(2, true, true)
- } else {
- balls[1] = frame.pinState[1].ballValueDifference(frame.pinState[0], 1, false, false)
- balls[2] = frame.pinState[2].ballValueDifference(frame.pinState[1], 2, false, false)
- }
- }
- } else {
- val nextFrame = frames[index + 1]
- balls[0] = frame.pinState[0].ballValue(0, true, false)
- if (frame.pinState[0].arePinsCleared) {
- // When the first ball is a strike, show the pins knocked down in the next frame, or empty frames
- if (nextFrame.isAccessed) {
- balls[1] = nextFrame.pinState[0].ballValue(1, false, true)
- if (nextFrame.pinState[0].arePinsCleared) {
- // When the next frame is a strike, the 3rd ball will have to come from the frame after
- if (frame.zeroBasedOrdinal < Game.LAST_FRAME - 1) {
- val nextNextFrame = frames[index + 2]
- balls[2] = if (nextNextFrame.isAccessed)
- nextNextFrame.pinState[0].ballValue(2, false, true)
- else
- Ball.None.toString()
- } else {
- // In the 9th frame, the 3rd ball comes from the 10th frame's second ball
- balls[2] = nextFrame.pinState[1].ballValue(2, false, true)
- }
- } else {
- balls[2] = nextFrame.pinState[1].ballValueDifference(nextFrame.pinState[0], 2, false, true)
- }
- } else {
- balls[1] = Ball.None.toString()
- balls[2] = Ball.None.toString()
- }
- } else {
- // When the first ball is not a strike, the second and third ball values need to be calculated
- if (frame.pinState[1].arePinsCleared) {
- balls[1] = Ball.Spare.toString()
- balls[2] = if (nextFrame.isAccessed)
- nextFrame.pinState[0].ballValue(2, false, true)
- else
- Ball.None.toString()
- } else {
- balls[1] = frame.pinState[1].ballValueDifference(frame.pinState[0], 1, false, false)
- balls[2] = frame.pinState[2].ballValueDifference(frame.pinState[1], 2, false, false)
- }
- }
- }
-
- return@mapIndexed balls
- }
- }
-
- fun getScoreTextForFrames(): List {
- return frameScores.mapIndexed { index, score ->
- return@mapIndexed if (index <= Game.LAST_FRAME) {
- if (!frames[index].isAccessed) "" else score.toString()
- } else {
- score.toString()
- }
- }
- }
-
- fun markDirty() {
- dirty = true
- }
-
- companion object {
- @Suppress("unused")
- private const val TAG = "Game"
-
- @Suppress("unused")
- @JvmField val CREATOR = parcelableCreator(::Game)
-
- const val NUMBER_OF_FRAMES = 10
-
- const val LAST_FRAME = NUMBER_OF_FRAMES - 1
-
- const val NUMBER_OF_PINS = 5
-
- const val MAX_SCORE = 450
-
- const val FOUL_PENALTY = 15
-
- fun isValidScore(score: Int): Boolean {
- return score in 0..MAX_SCORE
- }
-
- fun fetchSeriesGames(context: Context, series: Series): Deferred> {
- return async(CommonPool) {
- val gameList: MutableList = ArrayList(series.numberOfGames)
- val database = DatabaseManager.getReadableDatabase(context).await()
-
- val query = ("SELECT " +
- "game.${GameEntry._ID} AS gid, " +
- "game.${GameEntry.COLUMN_GAME_NUMBER}, " +
- "game.${GameEntry.COLUMN_SCORE}, " +
- "game.${GameEntry.COLUMN_IS_LOCKED}, " +
- "game.${GameEntry.COLUMN_IS_MANUAL}, " +
- "game.${GameEntry.COLUMN_MATCH_PLAY}, " +
- "match.${MatchPlayEntry._ID} as mid, " +
- "match.${MatchPlayEntry.COLUMN_OPPONENT_SCORE}, " +
- "match.${MatchPlayEntry.COLUMN_OPPONENT_NAME}, " +
- "frame.${FrameEntry._ID} as fid, " +
- "frame.${FrameEntry.COLUMN_FRAME_NUMBER}, " +
- "frame.${FrameEntry.COLUMN_IS_ACCESSED}, " +
- "frame.${FrameEntry.COLUMN_PIN_STATE[0]}, " +
- "frame.${FrameEntry.COLUMN_PIN_STATE[1]}, " +
- "frame.${FrameEntry.COLUMN_PIN_STATE[2]}, " +
- "frame.${FrameEntry.COLUMN_FOULS} " +
- "FROM ${GameEntry.TABLE_NAME} AS game " +
- "LEFT JOIN ${MatchPlayEntry.TABLE_NAME} as match " +
- "ON gid=${MatchPlayEntry.COLUMN_GAME_ID} " +
- "LEFT JOIN ${FrameEntry.TABLE_NAME} as frame " +
- "ON gid=${FrameEntry.COLUMN_GAME_ID} " +
- "WHERE ${GameEntry.COLUMN_SERIES_ID}=? " +
- "GROUP BY gid, fid " +
- "ORDER BY game.${GameEntry.COLUMN_GAME_NUMBER}, frame.${FrameEntry.COLUMN_FRAME_NUMBER}")
-
- var lastId: Long = -1
- var frames: MutableList = ArrayList(NUMBER_OF_FRAMES)
-
- /**
- * Build a game from a cursor into the database.
- *
- * @param cursor database accessor
- * @return a new game
- */
- fun buildGameFromCursor(cursor: Cursor): Game {
- val id = cursor.getLong(cursor.getColumnIndex("gid"))
- val gameNumber = cursor.getInt(cursor.getColumnIndex(GameEntry.COLUMN_GAME_NUMBER))
- val score = cursor.getInt(cursor.getColumnIndex(GameEntry.COLUMN_SCORE))
- val isLocked = cursor.getInt(cursor.getColumnIndex(GameEntry.COLUMN_IS_LOCKED)) == 1
- val isManual = cursor.getInt(cursor.getColumnIndex(GameEntry.COLUMN_IS_MANUAL)) == 1
- val matchPlayResult = MatchPlayResult.fromInt(cursor.getInt(cursor.getColumnIndex(GameEntry.COLUMN_MATCH_PLAY)))
-
- return Game(
- series = series,
- id = id,
- ordinal = gameNumber,
- initialScore = score,
- isLocked = isLocked,
- isManual = isManual,
- frames = frames,
- matchPlay = MatchPlay(
- gameId = id,
- id = cursor.getLong(cursor.getColumnIndex("mid")),
- opponentName = cursor.getString(cursor.getColumnIndex(MatchPlayEntry.COLUMN_OPPONENT_NAME)) ?: "",
- opponentScore = cursor.getInt(cursor.getColumnIndex(MatchPlayEntry.COLUMN_OPPONENT_SCORE)),
- result = matchPlayResult!!
- )
- )
- }
-
- val cursor = database.rawQuery(query, arrayOf(series.id.toString()))
- if (cursor.moveToFirst()) {
- while (!cursor.isAfterLast) {
- val newId = cursor.getLong(cursor.getColumnIndex("gid"))
- if (newId != lastId && lastId != -1L) {
- cursor.moveToPrevious()
-
- gameList.add(buildGameFromCursor(cursor))
-
- frames = ArrayList(NUMBER_OF_FRAMES)
- cursor.moveToNext()
- }
-
- frames.add(Frame(
- gameId = newId,
- id = cursor.getLong(cursor.getColumnIndex("fid")),
- ordinal = cursor.getInt(cursor.getColumnIndex(FrameEntry.COLUMN_FRAME_NUMBER)),
- isAccessed = cursor.getInt(cursor.getColumnIndex(FrameEntry.COLUMN_IS_ACCESSED)) == 1,
- pinState = Array(Frame.NUMBER_OF_BALLS) {
- return@Array Pin.deckFromInt(cursor.getInt(cursor.getColumnIndex(FrameEntry.COLUMN_PIN_STATE[it])))
- },
- ballFouled = BooleanArray(Frame.NUMBER_OF_BALLS) {
- return@BooleanArray Fouls.foulIntToString(cursor.getInt(cursor.getColumnIndex(FrameEntry.COLUMN_FOULS))).contains((it + 1).toString())
- }
- ))
-
- lastId = newId
- cursor.moveToNext()
- }
-
- cursor.moveToPrevious()
- gameList.add(buildGameFromCursor(cursor))
- }
- cursor.close()
-
- return@async gameList
- }
- }
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/GameAutoEventController.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/GameAutoEventController.kt
deleted file mode 100644
index 72cb59d64..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/GameAutoEventController.kt
+++ /dev/null
@@ -1,149 +0,0 @@
-package ca.josephroque.bowlingcompanion.games
-
-import android.content.SharedPreferences
-import android.os.Handler
-import ca.josephroque.bowlingcompanion.common.Android
-import ca.josephroque.bowlingcompanion.settings.Settings
-import kotlinx.coroutines.experimental.launch
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Control automatic game events.
- */
-class GameAutoEventController(
- preferences: SharedPreferences,
- private val delegate: GameAutoEventDelegate
-) {
-
- companion object {
- @Suppress("unused")
- private const val TAG = "GameAutoEventController"
-
- private var autoAdvanceTotalDelay: Int = 0
- }
-
- init { init(preferences) }
-
- fun init(preferences: SharedPreferences) {
- // Enable auto lock
- val autoLockEnabled = Settings.BooleanSetting.EnableAutoLock.getValue(preferences)
- if (autoLockEnabled) {
- enable(AutoEvent.Lock)
- } else {
- AutoEvent.Lock.isEnabled = false
- }
-
- // Enable auto advance
- val autoAdvanceEnabled = Settings.BooleanSetting.EnableAutoAdvance.getValue(preferences)
- if (autoAdvanceEnabled) {
- enable(AutoEvent.AdvanceFrame)
- } else {
- AutoEvent.AdvanceFrame.isEnabled = false
- }
-
- // Set auto advance delay time
- val strDelay = Settings.StringSetting.AutoAdvanceTime.getValue(preferences)
- val strDelayComponents = strDelay.split(" ")
- autoAdvanceTotalDelay = Integer.valueOf(strDelayComponents[0])
- }
-
- private val autoEventHandler: HashMap by lazy {
- val map = HashMap()
- AutoEvent.values().forEach { map[it] = Handler() }
- return@lazy map
- }
-
- private val autoEventRunnable: HashMap by lazy {
- val map = HashMap()
- map[AutoEvent.AdvanceFrame] = Runnable {
- launch(Android) {
- if (!AutoEvent.AdvanceFrame.isEnabled) { return@launch }
- if (autoAdvanceSecondsRemaining > 0) {
- autoAdvanceSecondsRemaining -= 1
- start(AutoEvent.AdvanceFrame)
- delegate.autoAdvanceCountDown(autoAdvanceSecondsRemaining)
- } else {
- delegate.autoEventFired(AutoEvent.AdvanceFrame)
- }
- }
- }
- map[AutoEvent.Lock] = Runnable {
- launch(Android) {
- if (!AutoEvent.Lock.isEnabled) { return@launch }
- delegate.autoEventFired(AutoEvent.Lock)
- pause(AutoEvent.Lock)
- }
- }
- return@lazy map
- }
-
- private var autoAdvanceSecondsRemaining: Int = 0
-
- // MARK: GameAutoEventController
-
- fun disable(event: AutoEvent) {
- autoEventHandler[event]?.removeCallbacks(autoEventRunnable[event])
- event.isEnabled = false
- }
-
- fun pause(event: AutoEvent) {
- autoEventHandler[event]?.removeCallbacks(autoEventRunnable[event])
- delegate.autoEventPaused(event)
- }
-
- fun start(event: AutoEvent) {
- if (!event.isEnabled) { return }
- autoEventHandler[event]?.postDelayed(autoEventRunnable[event], event.delay)
- }
-
- fun delay(event: AutoEvent) {
- pause(event)
- when (event) {
- AutoEvent.AdvanceFrame -> autoAdvanceSecondsRemaining = autoAdvanceTotalDelay
- AutoEvent.Lock -> {} // Do nothing
- }
- start(event)
- delegate.autoEventDelayed(event)
- }
-
- fun pauseAll() {
- AutoEvent.values().forEach { pause(it) }
- }
-
- // MARK: Private functions
-
- private fun enable(event: AutoEvent) {
- event.isEnabled = true
- }
-
- // MARK: AutoEvent
-
- enum class AutoEvent {
- AdvanceFrame, Lock;
-
- val delay: Long
- get() {
- return when (this) {
- AdvanceFrame -> ADVANCE_FRAME_DELAY
- Lock -> LOCK_DELAY
- }
- }
-
- var isEnabled: Boolean = false
-
- companion object {
- var ADVANCE_FRAME_DELAY = 1000L
- var LOCK_DELAY = 5000L
- }
- }
-
- // MARK: GameAutoEventDelegate
-
- interface GameAutoEventDelegate {
- fun autoEventFired(event: AutoEvent)
- fun autoEventDelayed(event: AutoEvent)
- fun autoEventPaused(event: AutoEvent)
- fun autoAdvanceCountDown(secondsRemaining: Int)
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/GameControllerFragment.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/GameControllerFragment.kt
deleted file mode 100644
index 7defe8a43..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/GameControllerFragment.kt
+++ /dev/null
@@ -1,326 +0,0 @@
-package ca.josephroque.bowlingcompanion.games
-
-import android.content.Context
-import android.os.Bundle
-import android.support.annotation.IdRes
-import android.support.design.widget.TabLayout
-import android.support.v4.app.Fragment
-import android.support.v4.app.FragmentManager
-import android.support.v4.app.FragmentPagerAdapter
-import android.support.v7.preference.PreferenceManager
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.view.WindowManager
-import ca.josephroque.bowlingcompanion.R
-import ca.josephroque.bowlingcompanion.common.NavigationDrawerController
-import ca.josephroque.bowlingcompanion.common.fragments.TabbedFragment
-import ca.josephroque.bowlingcompanion.matchplay.MatchPlayResult
-import ca.josephroque.bowlingcompanion.matchplay.MatchPlaySheet
-import ca.josephroque.bowlingcompanion.series.Series
-import ca.josephroque.bowlingcompanion.settings.Settings
-import ca.josephroque.bowlingcompanion.statistics.interfaces.IStatisticsContext
-import ca.josephroque.bowlingcompanion.statistics.provider.StatisticsProvider
-import ca.josephroque.bowlingcompanion.utils.Analytics
-import kotlinx.android.synthetic.main.fragment_common_tabs.tabbed_fragment_tabs as fragmentTabs
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Manage tabs to show games for the team of bowlers.
- */
-class GameControllerFragment : TabbedFragment(),
- NavigationDrawerController.NavigationDrawerHandler,
- GameFragment.GameFragmentDelegate,
- MatchPlaySheet.MatchPlaySheetDelegate,
- IStatisticsContext {
-
- companion object {
- @Suppress("unused")
- private const val TAG = "GameControllerFragment"
-
- private const val ARG_SERIES_PROVIDER_TYPE = "${TAG}_type"
- private const val ARG_SERIES_PROVIDER = "${TAG}_series"
-
- fun newInstance(seriesProvider: SeriesProvider): GameControllerFragment {
- val fragment = GameControllerFragment()
- fragment.arguments = Bundle().apply {
- putInt(ARG_SERIES_PROVIDER_TYPE, seriesProvider.describeContents())
- putParcelable(ARG_SERIES_PROVIDER, seriesProvider)
- }
- return fragment
- }
- }
-
- override val statisticsProviders: List by lazy {
- val seriesProvider = seriesProvider
- val seriesList = seriesProvider?.seriesList
- val gameFragment = currentGameFragment
-
- return@lazy if (seriesList != null && gameFragment != null) {
- val providers: MutableList = arrayListOf(
- StatisticsProvider.BowlerStatistics(seriesList[currentTab].league.bowler),
- StatisticsProvider.LeagueStatistics(seriesList[currentTab].league),
- StatisticsProvider.SeriesStatistics(seriesList[currentTab])
- )
-
- gameFragment.gamesForStatistics.forEach {
- providers.add(StatisticsProvider.GameStatistics(it))
- }
-
- if (seriesProvider is SeriesProvider.TeamSeries) {
- providers.add(0, StatisticsProvider.TeamStatistics(seriesProvider.team))
- }
-
- providers
- } else {
- emptyList()
- }
- }
-
- override var navigationDrawerProvider: NavigationDrawerController.NavigationDrawerProvider? = null
- private var seriesProvider: SeriesProvider? = null
- private var fabEnabled: Boolean = true
-
- private var ignoreGameChange: Boolean = false
- private var currentGame: Int = 0
- set(value) {
- field = value
-
- if (!ignoreGameChange) {
- currentGameFragment?.gameNumber = currentGame
- }
- navigationDrawerProvider?.navigationDrawerController?.gameNumber = currentGame
-
- Analytics.trackChangedGame()
- }
-
- private val currentGameFragment: GameFragment?
- get() = findFragmentByPosition(currentTab) as? GameFragment
-
- // MARK: Lifecycle functions
-
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
- setHasOptionsMenu(true)
- val seriesType = arguments?.getInt(ARG_SERIES_PROVIDER_TYPE) ?: 0
- seriesProvider = SeriesProvider.getParcelable(arguments, ARG_SERIES_PROVIDER, seriesType)
- return super.onCreateView(inflater, container, savedInstanceState)
- }
-
- override fun onStart() {
- super.onStart()
- val seriesList = seriesProvider?.seriesList ?: return
- if (seriesList.size == 1) {
- fragmentTabs.visibility = View.GONE
- navigationActivity?.supportActionBar?.elevation = resources.getDimension(R.dimen.base_elevation)
- } else {
- fragmentTabs.visibility = View.VISIBLE
- navigationActivity?.supportActionBar?.elevation = 0F
- }
-
- onSeriesChanged()
- activity?.invalidateOptionsMenu()
- navigationDrawerProvider?.navigationDrawerController?.isTeamMember = seriesProvider is SeriesProvider.TeamSeries
- navigationDrawerProvider?.navigationDrawerController?.isEvent = seriesProvider?.isEvent ?: false
-
- activity?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING)
- }
-
- override fun onStop() {
- super.onStop()
- // 0 is the default, as per https://developer.android.com/reference/android/R.attr#windowSoftInputMode
- activity?.window?.setSoftInputMode(0)
- }
-
- override fun onAttach(context: Context?) {
- super.onAttach(context)
- navigationDrawerProvider = context as? NavigationDrawerController.NavigationDrawerProvider
- }
-
- override fun onDetach() {
- super.onDetach()
- navigationDrawerProvider = null
- }
-
- // MARK: TabbedFragment
-
- override fun buildPagerAdapter(tabCount: Int): FragmentPagerAdapter {
- return GameControllerPagerAdapter(childFragmentManager, tabCount, seriesProvider?.seriesList)
- }
-
- override fun addTabs(tabLayout: TabLayout) {
- tabLayout.tabMode = TabLayout.MODE_SCROLLABLE
- seriesProvider?.seriesList?.let {
- it.forEach { series ->
- tabLayout.addTab(tabLayout.newTab().setText(series.league.bowler.name))
- }
- }
- }
-
- override fun handleTabSwitch(newTab: Int) {
- onSeriesChanged()
- }
-
- // MARK: Private functions
-
- private fun onSeriesChanged() {
- seriesProvider?.seriesList?.let {
- navigationDrawerProvider?.navigationDrawerController?.apply {
- numberOfGames = it[currentTab].numberOfGames
- bowlerName = it[currentTab].league.bowler.name
- leagueName = it[currentTab].league.name
- it[currentTab].scores.forEachIndexed { index, score -> updateGameScore(index, score) }
- }
- }
-
- updateToolbarTitle()
- currentGameFragment?.invalidateFab()
- }
-
- // MARK: BaseFragment
-
- override fun updateToolbarTitle() {
- seriesProvider?.seriesList?.let { navigationActivity?.setToolbarTitle(it[currentTab].league.bowler.name) }
- }
-
- // MARK: IFloatingActionButtonHandler
-
- override fun getFabImage(): Int? {
- val context = context ?: return null
- val preferences = PreferenceManager.getDefaultSharedPreferences(context)
- val userPrefFabEnabled = Settings.BooleanSetting.EnableFab.getValue(preferences)
- return if (userPrefFabEnabled && fabEnabled) R.drawable.ic_arrow_forward else null
- }
-
- override fun onFabClick() {
- if (!fabEnabled) { return }
- currentGameFragment?.onFabClick()
- }
-
- // MARK: INavigationDrawerHandler
-
- override fun onNavDrawerItemSelected(@IdRes itemId: Int) {
- val game = NavigationDrawerController.navGameItemIds.indexOf(itemId)
- if (game >= 0) {
- currentGame = game
- }
- }
-
- // MARK: GameControllerFragment
-
- fun prepareToPop() {
- currentGameFragment?.prepareToPause()
- }
-
- // MARK: GameFragmentDelegate
-
- override val isFabEnabled: Boolean
- get() = fabEnabled
-
- override fun enableFab(enabled: Boolean) {
- fabEnabled = enabled
- fabProvider?.invalidateFab()
- }
-
- override val hasNextBowlerOrGame: Boolean
- get() {
- val seriesList = seriesProvider?.seriesList ?: return false
- return seriesList.lastIndex > currentTab || seriesList.any { currentGame < it.numberOfGames - 1 }
- }
-
- override fun nextBowlerOrGame(isEndOfGame: Boolean): GameFragment.NextBowlerResult {
- val seriesList = seriesProvider?.seriesList ?: return GameFragment.NextBowlerResult.None
-
- // Find the next bowler in the remaining list to switch to with games to still play
- var nextSeries = currentTab + 1
- while (nextSeries <= seriesList.lastIndex && seriesList[nextSeries].numberOfGames <= currentGame) {
- nextSeries += 1
- }
-
- // If there's a bowler found, switch to them and exit
- if (nextSeries <= seriesList.lastIndex) {
- currentTab = nextSeries
- currentGameFragment?.gameNumber = currentGame
- return GameFragment.NextBowlerResult.NextBowler
- }
-
- // If we were on the last frame, then the next bowler will be on the next game
- val nextGame = if (isEndOfGame) currentGame + 1 else currentGame
- nextSeries = 0
-
- // Find the first bowler in the list to switch to with games still to play
- while (nextSeries <= seriesList.lastIndex && seriesList[nextSeries].numberOfGames <= nextGame) {
- nextSeries += 1
- }
-
- // If there's a bowler found, switch to them, update the game, and exit
- var switchedBowler = false
- var switchedGame = false
- if (nextSeries <= seriesList.lastIndex) {
- if (currentTab == nextSeries && currentGame == nextGame) {
- // There's no switch happening
- return GameFragment.NextBowlerResult.None
- }
- if (currentTab != nextSeries) {
- currentTab = nextSeries
- switchedBowler = true
- }
- if (currentGame != nextGame) {
- currentGame = nextGame
- switchedGame = true
- }
-
- return when {
- switchedBowler && switchedGame -> GameFragment.NextBowlerResult.NextBowlerGame
- switchedBowler -> GameFragment.NextBowlerResult.NextBowler
- switchedGame -> GameFragment.NextBowlerResult.NextGame
- else -> GameFragment.NextBowlerResult.None
- }
- }
-
- // No next bowler found
- return GameFragment.NextBowlerResult.None
- }
-
- override val isFullscreen: Boolean
- get() = navigationActivity?.isFullscreen == true
-
- override fun toggleFullscreen() {
- navigationActivity?.toggleFullscreen()
- }
-
- override fun updateGameScore(gameIdx: Int, score: Int) {
- navigationDrawerProvider?.navigationDrawerController?.updateGameScore(gameIdx, score)
- }
-
- override fun onGamesLoaded(currentGame: Int, srcFragment: GameFragment) {
- if (srcFragment != currentGameFragment) { return }
- ignoreGameChange = true
- this.currentGame = currentGame
- ignoreGameChange = false
- }
-
- // MARK: MatchPlaySheetDelegate
-
- override fun onFinishedSettingMatchPlayResults(opponentName: String, opponentScore: Int, matchPlayResult: MatchPlayResult, inputValid: Boolean) {
- currentGameFragment?.onFinishedSettingMatchPlayResults(opponentName, opponentScore, matchPlayResult, inputValid)
- }
-
- // MARK: GameControllerPagerAdapter
-
- class GameControllerPagerAdapter(
- fragmentManager: FragmentManager,
- private val tabCount: Int,
- private val seriesList: List?
- ) : FragmentPagerAdapter(fragmentManager) {
- override fun getCount() = tabCount
-
- override fun getItem(position: Int): Fragment? {
- seriesList?.let {
- return GameFragment.newInstance(seriesList[position])
- }
-
- return null
- }
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/GameFragment.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/GameFragment.kt
deleted file mode 100644
index 50ebc21f0..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/GameFragment.kt
+++ /dev/null
@@ -1,606 +0,0 @@
-package ca.josephroque.bowlingcompanion.games
-
-import android.content.Context
-import android.os.Bundle
-import android.preference.PreferenceManager
-import android.support.design.widget.BottomSheetBehavior
-import android.support.v7.app.AlertDialog
-import android.view.LayoutInflater
-import android.view.Menu
-import android.view.MenuInflater
-import android.view.MenuItem
-import android.view.View
-import android.view.ViewGroup
-import ca.josephroque.bowlingcompanion.R
-import ca.josephroque.bowlingcompanion.common.Android
-import ca.josephroque.bowlingcompanion.common.fragments.BaseFragment
-import ca.josephroque.bowlingcompanion.common.interfaces.IFloatingActionButtonHandler
-import ca.josephroque.bowlingcompanion.games.dialogs.ManualScoreDialog
-import ca.josephroque.bowlingcompanion.games.dialogs.PossibleScoreDialog
-import ca.josephroque.bowlingcompanion.games.dialogs.ResetGameDialog
-import ca.josephroque.bowlingcompanion.games.lane.arePinsCleared
-import ca.josephroque.bowlingcompanion.games.overview.GameOverviewFragment
-import ca.josephroque.bowlingcompanion.games.views.FrameView
-import ca.josephroque.bowlingcompanion.games.views.GameFooterView
-import ca.josephroque.bowlingcompanion.games.views.GameHeaderView
-import ca.josephroque.bowlingcompanion.games.views.PinLayout
-import ca.josephroque.bowlingcompanion.matchplay.MatchPlayResult
-import ca.josephroque.bowlingcompanion.matchplay.MatchPlaySheet
-import ca.josephroque.bowlingcompanion.series.Series
-import ca.josephroque.bowlingcompanion.settings.Settings
-import ca.josephroque.bowlingcompanion.utils.Analytics
-import ca.josephroque.bowlingcompanion.utils.BCError
-import kotlinx.android.synthetic.main.fragment_game.game_footer as gameFooter
-import kotlinx.android.synthetic.main.fragment_game.game_header as gameHeader
-import kotlinx.android.synthetic.main.fragment_game.score_sheet as scoreSheet
-import kotlinx.android.synthetic.main.fragment_game.manual_score as manualScore
-import kotlinx.android.synthetic.main.fragment_game.pin_layout as pinLayout
-import kotlinx.android.synthetic.main.fragment_game.tv_auto_advance as autoAdvance
-import kotlinx.android.synthetic.main.sheet_match_play.sheet_match_play as matchPlaySheet
-import kotlinx.android.synthetic.main.sheet_match_play.view.*
-import kotlinx.coroutines.experimental.launch
-import java.lang.ref.WeakReference
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Display game details and allow the user to edit.
- */
-class GameFragment : BaseFragment(),
- IFloatingActionButtonHandler,
- FrameView.FrameInteractionDelegate,
- PinLayout.PinLayoutInteractionDelegate,
- GameFooterView.GameFooterInteractionDelegate,
- GameHeaderView.GameHeaderInteractionDelegate,
- MatchPlaySheet.MatchPlaySheetDelegate {
-
- companion object {
- @Suppress("unused")
- private const val TAG = "GameFragment"
-
- private const val ARG_SERIES = "${TAG}_series"
- private const val ARG_GAME_STATE = "${TAG}_game_state"
-
- fun newInstance(series: Series): GameFragment {
- val fragment = GameFragment()
- fragment.arguments = Bundle().apply { putParcelable(ARG_SERIES, series) }
- return fragment
- }
- }
-
- private var delegate: GameFragmentDelegate? = null
-
- var gameNumber: Int = 0
- set(value) {
- saveCurrentGame(false)
- field = value
- gameState.currentGameIdx = gameNumber
- gameState.currentFrame.isAccessed = true
- render(ballChanged = true, isGameFirstRender = true)
-
- if (gameState.currentGame.isLocked) {
- autoEventController.disable(GameAutoEventController.AutoEvent.Lock)
- autoEventController.disable(GameAutoEventController.AutoEvent.AdvanceFrame)
- } else {
- val preferences = PreferenceManager.getDefaultSharedPreferences(context)
- autoEventController.init(preferences)
- }
- }
-
- val gamesForStatistics: List
- get() = gameState.shareableGames
-
- private lateinit var series: Series
- private lateinit var gameState: GameState
- private lateinit var autoEventController: GameAutoEventController
-
- // MARK: Lifecycle functions
-
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
- setHasOptionsMenu(true)
- val view = inflater.inflate(R.layout.fragment_game, container, false)
-
- setupBottomSheet(view)
-
- return view
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- series = arguments!!.getParcelable(ARG_SERIES)!!
-
- // Enable automatic events
- val preferences = PreferenceManager.getDefaultSharedPreferences(context)
- autoEventController = GameAutoEventController(preferences, autoEventDelegate)
-
- val context = context ?: return
- gameState = if (savedInstanceState == null) {
- GameState(series)
- } else {
- savedInstanceState.getParcelable(ARG_GAME_STATE)!!
- }
- gameState.delegate = gameStateDelegate
- gameState.loadGames(context)
- }
-
- override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
- super.onCreateOptionsMenu(menu, inflater)
- inflater.inflate(R.menu.fragment_game, menu)
- }
-
- override fun onPrepareOptionsMenu(menu: Menu?) {
- super.onPrepareOptionsMenu(menu)
- if (!gameState.gamesLoaded) { return }
- menu?.findItem(R.id.action_set_score)?.isVisible = !gameState.currentGame.isManual
- menu?.findItem(R.id.action_clear_score)?.isVisible = gameState.currentGame.isManual
- menu?.findItem(R.id.action_best_possible)?.isVisible = !gameState.currentGame.isManual
- }
-
- override fun onAttach(context: Context?) {
- super.onAttach(context)
- val parent = parentFragment as? GameFragmentDelegate ?: throw RuntimeException("${parentFragment!!} must implement GameFragmentDelegate")
- delegate = parent
- }
-
- override fun onDetach() {
- super.onDetach()
- delegate = null
- }
-
- override fun onSaveInstanceState(outState: Bundle) {
- super.onSaveInstanceState(outState)
- outState.putParcelable(ARG_GAME_STATE, gameState)
- }
-
- override fun onStart() {
- super.onStart()
-
- // Enable automatic events
- val preferences = PreferenceManager.getDefaultSharedPreferences(context)
- autoEventController.init(preferences)
- autoEventController.pauseAll()
-
- scoreSheet.frameViewDelegate = this
- scoreSheet.shouldHighlightMarks = Settings.BooleanSetting.EnableStrikeHighlights.getValue(preferences)
-
- pinLayout.delegate = this
- gameFooter.delegate = this
- gameHeader.delegate = this
-
- fabProvider?.invalidateFab()
- }
-
- override fun onStop() {
- super.onStop()
- autoEventController.pauseAll()
- }
-
- override fun onResume() {
- super.onResume()
- render(ballChanged = false, isGameFirstRender = false)
- }
-
- override fun onPause() {
- prepareToPause()
- super.onPause()
- }
-
- override fun onOptionsItemSelected(item: MenuItem?): Boolean {
- autoEventController.pauseAll()
- return when (item?.itemId) {
- R.id.action_overview -> {
- showOverview()
- true
- }
- R.id.action_set_score -> {
- showManualScoreDialog()
- true
- }
- R.id.action_clear_score -> {
- showClearManualScoreDialog()
- true
- }
- R.id.action_best_possible -> {
- showBestScorePossible()
- true
- }
- R.id.action_reset_game -> {
- showResetGameDialog()
- true
- }
- else -> return super.onOptionsItemSelected(item)
- }
- }
-
- // MARK: GameFragment
-
- fun prepareToPause() {
- context?.let { gameState.saveGame(WeakReference(it), true) }
- }
-
- fun invalidateFab() {
- val hasNextBowlerOrGame = delegate?.hasNextBowlerOrGame == true
- val isManual = gameState.gamesLoaded && gameState.currentGame.isManual
- if (hasNextBowlerOrGame || !gameState.isLastBall) {
- if (delegate?.isFabEnabled == false) {
- delegate?.enableFab(true)
- }
- } else if (isManual || gameState.isLastBall) {
- delegate?.enableFab(false)
- }
- }
-
- // MARK: BaseFragment
-
- override fun updateToolbarTitle() {
- // Intentionally left blank
- }
-
- // MARK: Private functions
-
- private fun setupBottomSheet(rootView: View) {
- val bottomSheet = rootView.sheet_match_play
- val sheetBehavior = BottomSheetBehavior.from(bottomSheet)
- sheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN
- }
-
- private fun render(ballChanged: Boolean = false, isGameFirstRender: Boolean = false) {
- if (!gameState.gamesLoaded) { return }
- launch(Android) {
- if (view == null) { return@launch }
-
- // Update current game / frame / ball
- gameHeader.currentGame = gameNumber
- scoreSheet.apply(gameState.currentFrameIdx, gameState.currentBallIdx, gameState.currentGame)
-
- // Update up/down pins
- gameState.currentPinState.forEachIndexed { index, pin ->
- pinLayout.updatePinImage(index, pin.isDown)
- }
-
- // Update next/prev frame buttons
- gameHeader.hasPreviousFrame = !gameState.isFirstBall && !gameState.currentGame.isManual
- gameHeader.hasNextFrame = delegate?.isFabEnabled == true
-
- // Set which pins are enabled/disabled
- if (gameState.currentGame.isLocked) {
- for (i in 0 until Game.NUMBER_OF_PINS) { pinLayout.setPinEnabled(i, false) }
- } else {
- gameState.enabledPins.forEach { pinLayout.setPinEnabled(it, true) }
- gameState.disabledPins.forEach { pinLayout.setPinEnabled(it, false) }
- }
-
- // Show/hide the pin layout and score when the score is manual
- gameHeader.isManualScoreSet = gameState.currentGame.isManual
- if (gameState.currentGame.isManual) {
- pinLayout.visibility = View.GONE
- scoreSheet.visibility = View.GONE
-
- manualScore.text = gameState.currentGame.score.toString()
- manualScore.visibility = View.VISIBLE
- } else {
- pinLayout.visibility = View.VISIBLE
- scoreSheet.visibility = View.VISIBLE
-
- manualScore.text = null
- manualScore.visibility = View.GONE
- }
-
- // Set icons in game footer
- gameFooter.apply {
- isFullscreen = this@GameFragment.delegate?.isFullscreen == true
- isFoulActive = gameState.currentFrame.ballFouled[gameState.currentBallIdx]
- isGameLocked = gameState.currentGame.isLocked
- matchPlayResult = gameState.currentGame.matchPlay.result
- clear = when {
- gameState.currentBallIdx == 0 ||
- (gameState.isLastFrame && gameState.currentBallIdx == 1 && gameState.currentFrame.pinState[0].arePinsCleared) ||
- (gameState.isLastFrame && gameState.currentBallIdx == 2 && gameState.currentFrame.pinState[1].arePinsCleared) -> {
- GameFooterView.Companion.Clear.Strike
- }
- gameState.currentBallIdx == 1 ||
- (gameState.currentBallIdx == 2 && gameState.currentFrame.pinState[0].arePinsCleared) -> {
- GameFooterView.Companion.Clear.Spare
- }
- else -> GameFooterView.Companion.Clear.Fifteen
- }
- isManualScoreSet = gameState.currentGame.isManual
- }
-
- // Update the score of this game in the navigation drawer
- gameState.scores.forEachIndexed { index, score -> delegate?.updateGameScore(index, score) }
-
- if (ballChanged || isGameFirstRender) {
- focusOnFrame(isGameFirstRender)
- }
- }
- }
-
- private fun saveCurrentFrame(ignoreManualScore: Boolean) {
- context?.let { gameState.saveFrame(WeakReference(it), ignoreManualScore) }
- }
-
- private fun saveCurrentGame(ignoreManualScore: Boolean) {
- context?.let { gameState.saveGame(WeakReference(it), ignoreManualScore) }
- }
-
- private fun saveMatchPlay() {
- context?.let { gameState.saveMatchPlay(WeakReference(it)) }
- }
-
- private fun focusOnFrame(isGameFirstRender: Boolean) {
- scoreSheet.focusOnFrame(isGameFirstRender, gameState.isLastFrame, gameState.currentFrameIdx)
- }
-
- private fun resetGame() {
- context?.let { gameState.resetGame(WeakReference(it)) }
- }
-
- private fun showOverview() {
- val newFragment = GameOverviewFragment.newInstance(gameState.shareableGames)
- fragmentNavigation?.pushFragment(newFragment)
-
- Analytics.trackViewOverview()
- }
-
- // MARK: IFloatingActionButtonHandler
-
- override fun getFabImage(): Int? {
- return null
- }
-
- override fun onFabClick() {
- onNextBall()
- }
-
- // MARK: FrameInteractionDelegate
-
- override fun onBallSelected(ball: Int, frame: Int) {
- if (gameState.gamesLoaded) { saveCurrentFrame(false) }
- gameState.attemptToSetFrameAndBall(frame, ball)
- }
-
- override fun onFrameSelected(frame: Int) {
- onBallSelected(0, frame)
- }
-
- // MARK: PinLayoutInteractionDelegate
-
- override fun isPinDown(pin: Int): Boolean {
- return if (gameState.gamesLoaded) gameState.currentPinState[pin].isDown else false
- }
-
- override fun setPins(pins: IntArray, isDown: Boolean) {
- gameState.setPins(pins, isDown)
- if (!gameState.currentGame.isLocked && gameState.isLastBall) { autoEventController.delay(GameAutoEventController.AutoEvent.Lock) }
- if (!gameState.currentGame.isLocked && !gameState.isLastBall) { autoEventController.delay(GameAutoEventController.AutoEvent.AdvanceFrame) }
- render()
- }
-
- // MARK: GameFooterInteractionDelegate
-
- override fun onClearPins() {
- setPins((0 until Game.NUMBER_OF_PINS).toList().toIntArray(), true)
- }
-
- override fun onFoulToggle() {
- gameState.toggleFoul()
- scoreSheet.setFoulEnabled(gameState.currentFrameIdx, gameState.currentBallIdx, gameState.currentBallFouled)
- if (!gameState.currentGame.isLocked && gameState.isLastBall) { autoEventController.delay(GameAutoEventController.AutoEvent.Lock) }
- if (!gameState.currentGame.isLocked && !gameState.isLastBall) { autoEventController.delay(GameAutoEventController.AutoEvent.AdvanceFrame) }
- render()
- }
-
- override fun onLockToggle() {
- gameState.toggleLock()
- autoEventController.disable(GameAutoEventController.AutoEvent.Lock)
- autoEventController.pause(GameAutoEventController.AutoEvent.AdvanceFrame)
- saveCurrentGame(false)
- render()
- }
-
- override fun onMatchPlaySettings() {
- val fragment = MatchPlaySheet.newInstance(gameState.currentGame.matchPlay)
- fragmentNavigation?.showBottomSheet(fragment, MatchPlaySheet.FRAGMENT_TAG)
- autoEventController.pause(GameAutoEventController.AutoEvent.AdvanceFrame)
- }
-
- override fun onFullscreenToggle() {
- delegate?.toggleFullscreen()
- render()
- }
-
- // MARK: GameHeaderInteractionDelegate
-
- override fun onNextBall() {
- saveCurrentFrame(false)
-
- if (!gameState.frameHasNextBall || gameState.currentGame.isManual) {
- val nextBowlerResult = delegate?.nextBowlerOrGame(gameState.isLastFrame || gameState.currentGame.isManual)
- when (nextBowlerResult) {
- NextBowlerResult.NextBowlerGame, NextBowlerResult.NextGame -> { return }
- else -> {} // does nothing
- }
- }
-
- gameState.nextBall()
- }
-
- override fun onPrevBall() {
- saveCurrentFrame(false)
- gameState.prevBall()
- }
-
- // MARK: MatchPlaySheetDelegate
-
- override fun onFinishedSettingMatchPlayResults(
- opponentName: String,
- opponentScore: Int,
- matchPlayResult: MatchPlayResult,
- inputValid: Boolean
- ) {
- if (!inputValid) {
- val sheetBehavior = BottomSheetBehavior.from(matchPlaySheet)
- sheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
- return
- }
-
- gameState.setMatchPlay(opponentName, opponentScore, matchPlayResult)
- saveMatchPlay()
- render()
- }
-
- // MARK: GameStateDelegate
-
- private val gameStateDelegate = object : GameState.GameStateDelegate {
- override fun onGamesLoaded() {
- if (gameState.currentGame.isLocked) {
- autoEventController.disable(GameAutoEventController.AutoEvent.Lock)
- autoEventController.disable(GameAutoEventController.AutoEvent.AdvanceFrame)
- }
- gameState.currentFrame.isAccessed = true
- gameNumber = gameState.currentGameIdx
-
- delegate?.onGamesLoaded(gameState.currentGameIdx, this@GameFragment)
- activity?.invalidateOptionsMenu()
- render(ballChanged = true, isGameFirstRender = true)
- }
-
- override fun onBallChanged() {
- render(ballChanged = true)
- invalidateFab()
-
- if (gameState.isLastBall) {
- autoEventController.delay(GameAutoEventController.AutoEvent.Lock)
- } else {
- autoEventController.pause(GameAutoEventController.AutoEvent.Lock)
- }
- autoEventController.pause(GameAutoEventController.AutoEvent.AdvanceFrame)
- }
-
- override fun onManualScoreSet() {
- invalidateFab()
- activity?.invalidateOptionsMenu()
- render(ballChanged = true, isGameFirstRender = false)
- }
-
- override fun onManualScoreCleared() {
- invalidateFab()
- activity?.invalidateOptionsMenu()
- render(ballChanged = true, isGameFirstRender = false)
- }
- }
-
- // MARK: GameAutoEventDelegate
-
- private val autoEventDelegate = object : GameAutoEventController.GameAutoEventDelegate {
- override fun autoAdvanceCountDown(secondsRemaining: Int) {
- val autoAdvanceStringId = if (gameState.frameHasNextBall) R.plurals.time_until_auto_advance_ball else R.plurals.time_until_auto_advance_frame
- autoAdvance.text = resources.getQuantityString(
- autoAdvanceStringId,
- secondsRemaining,
- secondsRemaining
- )
- autoAdvance.visibility = View.VISIBLE
- }
-
- override fun autoEventFired(event: GameAutoEventController.AutoEvent) {
- when (event) {
- GameAutoEventController.AutoEvent.Lock -> {
- gameState.lockGame()
- render()
- }
- GameAutoEventController.AutoEvent.AdvanceFrame -> {
- onNextBall()
- }
- }
- }
-
- override fun autoEventDelayed(event: GameAutoEventController.AutoEvent) {
- when (event) {
- GameAutoEventController.AutoEvent.AdvanceFrame -> {
- autoAdvance.visibility = View.GONE
- }
- GameAutoEventController.AutoEvent.Lock -> {} // Do nothing
- }
- }
-
- override fun autoEventPaused(event: GameAutoEventController.AutoEvent) {
- when (event) {
- GameAutoEventController.AutoEvent.AdvanceFrame -> {
- autoAdvance.visibility = View.GONE
- }
- GameAutoEventController.AutoEvent.Lock -> {} // Do nothing
- }
- }
- }
-
- // MARK: Dialogs
-
- private fun showBestScorePossible() {
- context?.let { PossibleScoreDialog.show(it, gameState.currentGame.deepCopy(), gameState.currentFrameIdx, gameState.currentBallIdx) }
- }
-
- private fun showResetGameDialog() {
- context?.let {
- ResetGameDialog.show(it, WeakReference({
- resetGame()
-
- Analytics.trackResetGame()
- }))
- }
- }
-
- private fun showManualScoreDialog() {
- context?.let { context ->
- AlertDialog.Builder(context)
- .setTitle(R.string.dialog_set_score_title)
- .setMessage(R.string.dialog_set_score_message)
- .setPositiveButton(R.string.set_score) { _, _ ->
- ManualScoreDialog.showSetScoreDialog(context) {
- if (!Game.isValidScore(it)) {
- BCError(
- R.string.error_manual_score_invalid_title,
- R.string.error_manual_score_invalid_message,
- BCError.Severity.Warning
- ).show(context)
- return@showSetScoreDialog
- }
-
- gameState.setManualScore(WeakReference(context), it)
-
- Analytics.trackSetGameManualScore()
- }
- }
- .setNegativeButton(R.string.cancel, null)
- .create()
- .show()
- }
- }
-
- private fun showClearManualScoreDialog() {
- context?.let {
- ManualScoreDialog.showClearScoreDialog(it) {
- gameState.clearManualScore(WeakReference(it))
- }
- }
- }
-
- // MARK: GameFragmentDelegate
-
- interface GameFragmentDelegate {
- val hasNextBowlerOrGame: Boolean
- val isFabEnabled: Boolean
- val isFullscreen: Boolean
-
- fun enableFab(enabled: Boolean)
- fun nextBowlerOrGame(isEndOfGame: Boolean): NextBowlerResult
- fun toggleFullscreen()
- fun updateGameScore(gameIdx: Int, score: Int)
- fun onGamesLoaded(currentGame: Int, srcFragment: GameFragment)
- }
-
- enum class NextBowlerResult {
- NextBowler, NextGame, NextBowlerGame, None;
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/GameState.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/GameState.kt
deleted file mode 100644
index 2e1d2d867..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/GameState.kt
+++ /dev/null
@@ -1,368 +0,0 @@
-package ca.josephroque.bowlingcompanion.games
-
-import android.content.Context
-import android.os.Parcel
-import ca.josephroque.bowlingcompanion.common.Android
-import ca.josephroque.bowlingcompanion.common.interfaces.KParcelable
-import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator
-import ca.josephroque.bowlingcompanion.database.Saviour
-import ca.josephroque.bowlingcompanion.games.lane.Deck
-import ca.josephroque.bowlingcompanion.games.lane.arePinsCleared
-import ca.josephroque.bowlingcompanion.games.lane.reset
-import ca.josephroque.bowlingcompanion.matchplay.MatchPlayResult
-import ca.josephroque.bowlingcompanion.series.Series
-import ca.josephroque.bowlingcompanion.utils.Analytics
-import kotlinx.coroutines.experimental.CommonPool
-import kotlinx.coroutines.experimental.Deferred
-import kotlinx.coroutines.experimental.async
-import kotlinx.coroutines.experimental.launch
-import java.lang.ref.WeakReference
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Manages the loading, saving, and updating of the state of a game.
- */
-class GameState(
- private val series: Series,
- initialGames: MutableList = ArrayList(),
- initialGameIdx: Int = 0,
- initialFrameIdx: Int = 0,
- initialBallIdx: Int = 0
-) : KParcelable {
-
- companion object {
- @Suppress("unused")
- private const val TAG = "GameState"
-
- @Suppress("unused")
- @JvmField val CREATOR = parcelableCreator(::GameState)
- }
-
- // MARK: Parcelable
-
- constructor(p: Parcel): this(
- series = p.readParcelable(Series::class.java.classLoader)!!,
- initialGames = arrayListOf().apply {
- val parcelableArray = p.readParcelableArray(Game::class.java.classLoader)!!
- this.addAll(parcelableArray.map { return@map it as Game })
- },
- initialGameIdx = p.readInt(),
- initialFrameIdx = p.readInt(),
- initialBallIdx = p.readInt()
- )
-
- override fun writeToParcel(dest: Parcel, flags: Int) = with(dest) {
- writeParcelable(series, 0)
- writeParcelableArray(games.toTypedArray(), 0)
- writeInt(currentGameIdx)
- writeInt(currentFrameIdx)
- writeInt(currentBallIdx)
- }
-
- // MARK: Properties
-
- var delegate: GameStateDelegate? = null
-
- var gamesLoaded: Boolean = initialGames.size > 0
-
- private val games: MutableList = initialGames
- get() {
- check(gamesLoaded) { "The games have not been loaded before accessing." }
- return field
- }
-
- var currentGameIdx: Int = initialGameIdx
- set(newGame) {
- if (newGame >= 0 && newGame < series.numberOfGames) {
- field = newGame
- moveToLastSavedFrame()
- }
- }
-
- var currentFrameIdx: Int = initialFrameIdx
- set(newFrame) {
- if (newFrame >= 0 && newFrame < Game.NUMBER_OF_FRAMES) {
- if (newFrame > field) {
- for (i in 0..newFrame) {
- currentGame.frames[i].isAccessed = true
- }
- }
-
- field = newFrame
- if (!skipBallListenerUpdate) {
- currentBallIdx = 0
- }
- }
- }
-
- var currentBallIdx: Int = initialBallIdx
- set(newBall) {
- if (newBall >= 0 && newBall < Frame.NUMBER_OF_BALLS) {
- field = newBall
- delegate?.onBallChanged()
- }
- }
-
- private var skipBallListenerUpdate: Boolean = false
-
- // MARK: Calculated properties
-
- val currentGame: Game
- get() = games[currentGameIdx]
-
- val currentFrame: Frame
- get() = currentGame.frames[currentFrameIdx]
-
- private val isGameEditable: Boolean
- get() = !currentGame.isLocked && !currentGame.isManual
-
- val scores: List
- get() = games.map { it.score }
-
- val currentBallFouled: Boolean
- get() = currentFrame.ballFouled[currentBallIdx]
-
- val currentPinState: Deck
- get() = currentFrame.pinState[currentBallIdx]
-
- val isFirstBall: Boolean
- get() = currentFrameIdx == 0 && currentBallIdx == 0
-
- val isLastBall: Boolean
- get() = isLastFrame && currentBallIdx == Frame.LAST_BALL
-
- val isLastFrame: Boolean
- get() = currentFrameIdx == Game.LAST_FRAME
-
- val frameHasNextBall: Boolean
- get() = currentBallIdx != Frame.LAST_BALL && (isLastFrame || !currentPinState.arePinsCleared)
-
- val enabledPins: IntArray
- get() {
- return (0 until Game.NUMBER_OF_PINS).filter {
- return@filter (
- currentBallIdx == 0 ||
- !currentFrame.pinState[currentBallIdx - 1][it].isDown ||
- (isLastFrame && currentFrame.pinState[currentBallIdx - 1].arePinsCleared)
- )
- }.toIntArray()
- }
-
- val disabledPins: IntArray
- get() {
- val enabled = enabledPins
- return (0 until Game.NUMBER_OF_PINS).filter { it !in enabled }.toIntArray()
- }
-
- val shareableGames: List
- get() = games.map { it.deepCopy() }
-
- // MARK: GameState
-
- fun toggleFoul() {
- if (!isGameEditable) { return }
- currentFrame.ballFouled[currentBallIdx] = !currentFrame.ballFouled[currentBallIdx]
- currentGame.markDirty()
- }
-
- fun toggleLock() {
- if (currentGame.isManual) { return }
- currentGame.isLocked = !currentGame.isLocked
-
- if (currentGame.isLocked) {
- Analytics.trackLockGame()
- }
- }
-
- fun lockGame() {
- if (currentGame.isManual) { return }
- currentGame.isLocked = true
- }
-
- fun setManualScore(context: WeakReference, score: Int) {
- resetGame(context)
- currentGame.isLocked = true
- currentGame.isManual = true
- currentGame.score = score
- saveGame(context, true)
- attemptToSetFrameAndBall(0, 0)
- delegate?.onManualScoreSet()
- }
-
- fun clearManualScore(context: WeakReference) {
- currentGame.isLocked = false
- currentGame.isManual = false
- saveGame(context, false)
- attemptToSetFrameAndBall(0, 0)
- delegate?.onManualScoreCleared()
- }
-
- fun setMatchPlay(opponentName: String, opponentScore: Int, result: MatchPlayResult) {
- currentGame.matchPlay.apply {
- this.opponentName = opponentName
- this.opponentScore = opponentScore
- this.result = result
- }
- currentGame.markDirty()
- }
-
- fun nextBall() {
- if (frameHasNextBall) {
- attemptToSetFrameAndBall(currentFrameIdx, currentBallIdx + 1)
- } else if (!isLastFrame) {
- attemptToSetFrameAndBall(currentFrameIdx + 1, 0)
- }
- }
-
- fun prevBall() {
- if (currentBallIdx == 0 && currentFrameIdx > 0) {
- attemptToSetFrameAndBall(currentFrameIdx - 1, Frame.LAST_BALL)
- } else if (currentBallIdx > 0) {
- attemptToSetFrameAndBall(currentFrameIdx, currentBallIdx - 1)
- }
- }
-
- fun attemptToSetFrameAndBall(frame: Int, ball: Int) {
- skipBallListenerUpdate = true
- currentFrameIdx = frame
- skipBallListenerUpdate = false
-
- if (frame == Game.LAST_FRAME) {
- currentBallIdx = ball
- return
- }
-
- var newBall = 0
- while (newBall < ball && !currentFrame.pinState[newBall].arePinsCleared) {
- newBall++
- }
- currentBallIdx = newBall
-
- if (gamesLoaded) {
- currentGame.markDirty()
- }
- }
-
- private fun moveToLastSavedFrame() {
- var lastSavedFrame = Game.LAST_FRAME
- while (!currentGame.frames[lastSavedFrame].isAccessed && lastSavedFrame > 0) {
- lastSavedFrame--
- }
- attemptToSetFrameAndBall(lastSavedFrame, 0)
- }
-
- fun setPins(pins: IntArray, isDown: Boolean) {
- if (!isGameEditable) { return }
-
- if (isDown) {
- for (i in currentBallIdx..Frame.LAST_BALL) {
- pins.forEach { currentFrame.pinState[i][it].isDown = isDown }
-
- if (currentFrame.pinState[i].arePinsCleared) {
- for (j in (i + 1)..Frame.LAST_BALL) {
- currentFrame.ballFouled[j] = false
-
- if (isLastFrame && currentBallIdx < Frame.LAST_BALL) {
- currentFrame.pinState[j].forEach { it.isDown = false }
- }
- }
-
- if (isLastFrame) {
- break
- }
- }
- }
- } else {
- val werePinsCleared = currentPinState.arePinsCleared
- for (i in currentBallIdx..Frame.LAST_BALL) {
- pins.forEach { currentFrame.pinState[i][it].isDown = isDown }
- }
-
- // In the last frame, when the first/second ball was a strike and no longer is, copy the state to the later balls
- if (isLastFrame) {
- if (currentBallIdx < Frame.LAST_BALL && werePinsCleared && !currentPinState.arePinsCleared) {
- currentPinState.forEachIndexed { index, pin ->
- for (i in (currentBallIdx + 1)..Frame.LAST_BALL) {
- currentFrame.pinState[i][index].isDown = pin.isDown
- }
- }
- }
- }
- }
-
- currentGame.markDirty()
- }
-
- fun resetGame(context: WeakReference) {
- currentGame.isManual = false
- currentGame.isLocked = false
- currentGame.frames.forEach { frame ->
- for (i in 0 until Frame.NUMBER_OF_BALLS) {
- frame.ballFouled[i] = false
- frame.pinState[i].reset()
- }
-
- frame.isAccessed = false
- }
-
- attemptToSetFrameAndBall(0, 0)
- currentFrame.isAccessed = true
- saveGame(context, true)
- }
-
- fun loadGames(context: Context): Deferred {
- return async(CommonPool) {
- if (!gamesLoaded) {
- val games = series.fetchGames(context).await()
- if (games.size != series.numberOfGames) {
- return@async false
- }
-
- gamesLoaded = true
- this@GameState.games.clear()
- this@GameState.games.addAll(games)
-
- moveToLastSavedFrame()
- while (currentFrameIdx == Game.LAST_FRAME && currentGameIdx < games.lastIndex) {
- currentGameIdx += 1
- }
- }
-
- launch(Android) {
- delegate?.onGamesLoaded()
- }
-
- return@async true
- }
- }
-
- fun saveFrame(context: WeakReference, ignoreManualScore: Boolean) {
- if (!gamesLoaded) { return }
- if (!ignoreManualScore && currentGame.isManual) { return }
- val copy = currentFrame.deepCopy()
- Saviour.instance.saveFrame(context, currentGame.score, copy)
- }
-
- fun saveGame(context: WeakReference, ignoreManualScore: Boolean) {
- if (!gamesLoaded) { return }
- if (!ignoreManualScore && currentGame.isManual) { return }
- val copy = currentGame.deepCopy()
- Saviour.instance.saveGame(context, copy)
- }
-
- fun saveMatchPlay(context: WeakReference) {
- if (!gamesLoaded) { return }
- val copy = currentGame.matchPlay.deepCopy()
- Saviour.instance.saveMatchPlay(context, copy)
- }
-
- // MARK: GameStateDelegate
-
- interface GameStateDelegate {
- fun onBallChanged()
- fun onGamesLoaded()
- fun onManualScoreSet()
- fun onManualScoreCleared()
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/SeriesProvider.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/SeriesProvider.kt
deleted file mode 100644
index 72d251a86..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/SeriesProvider.kt
+++ /dev/null
@@ -1,87 +0,0 @@
-package ca.josephroque.bowlingcompanion.games
-
-import android.os.Bundle
-import android.os.Parcel
-import ca.josephroque.bowlingcompanion.common.interfaces.KParcelable
-import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator
-import ca.josephroque.bowlingcompanion.common.interfaces.readBoolean
-import ca.josephroque.bowlingcompanion.common.interfaces.writeBoolean
-import ca.josephroque.bowlingcompanion.series.Series
-import ca.josephroque.bowlingcompanion.teams.Team
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Manage the series being edited.
- */
-sealed class SeriesProvider : KParcelable {
-
- abstract val isEvent: Boolean
-
- // MARK: TeamSeries
-
- data class TeamSeries(val team: Team) : SeriesProvider() {
- companion object {
- @Suppress("unused")
- @JvmField val CREATOR = parcelableCreator(::TeamSeries)
- }
-
- override val isEvent: Boolean = false
- private constructor(p: Parcel): this(p.readParcelable(Team::class.java.classLoader)!!)
- }
-
- // MARK: BowlerSeries
-
- data class BowlerSeries(val series: Series, override val isEvent: Boolean) : SeriesProvider() {
- companion object {
- @Suppress("unused")
- @JvmField val CREATOR = parcelableCreator(::BowlerSeries)
- }
-
- private constructor(p: Parcel): this(
- series = p.readParcelable(Series::class.java.classLoader)!!,
- isEvent = p.readBoolean()
- )
-
- override fun writeToParcel(dest: Parcel, flags: Int) = with(dest) {
- super.writeToParcel(dest, flags)
- writeBoolean(isEvent)
- }
- }
-
- val seriesList: List
- get() {
- return when (this) {
- is TeamSeries -> this.team.series
- is BowlerSeries -> listOf(this.series)
- }
- }
-
- // MARK: Parcelable
-
- override fun writeToParcel(dest: Parcel, flags: Int) = with(dest) {
- when (this@SeriesProvider) {
- is TeamSeries -> writeParcelable(team, 0)
- is BowlerSeries -> writeParcelable(series, 0)
- }
- }
-
- override fun describeContents(): Int {
- // When changing, update `SeriesProvider.getParcelable`
- return when (this) {
- is TeamSeries -> 0
- is BowlerSeries -> 1
- }
- }
-
- companion object {
- fun getParcelable(arguments: Bundle?, key: String, type: Int): SeriesProvider? {
- return when (type) {
- // When changing, update `SeriesProvider::describeContents`
- 0 -> arguments?.getParcelable(key)
- 1 -> arguments?.getParcelable(key)
- else -> throw IllegalArgumentException("SeriesProvider type $type does not exist")
- }
- }
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/dialogs/ManualScoreDialog.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/dialogs/ManualScoreDialog.kt
deleted file mode 100644
index 3d8c497da..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/dialogs/ManualScoreDialog.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-package ca.josephroque.bowlingcompanion.games.dialogs
-
-import android.annotation.SuppressLint
-import android.content.Context
-import android.support.v7.app.AlertDialog
-import ca.josephroque.bowlingcompanion.R
-import android.view.LayoutInflater
-import android.widget.EditText
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Present dialogs to set and clear a manual score in a game.
- */
-object ManualScoreDialog {
-
- @SuppressLint("InflateParams")
- fun showSetScoreDialog(context: Context, onSetScore: (score: Int) -> Unit) {
- val view = LayoutInflater.from(context).inflate(R.layout.dialog_set_score, null, false)
-
- AlertDialog.Builder(context)
- .setTitle(R.string.dialog_set_score_title)
- .setView(view)
- .setPositiveButton(R.string.set_score) { dialog, _ ->
- val scoreText = view.findViewById(R.id.score)
- if (scoreText.length() > 0) {
- val gameScore = try {
- scoreText.text.toString().toInt()
- } catch (ex: NumberFormatException) {
- -1
- }
-
- onSetScore(gameScore)
- }
- dialog.dismiss()
- }
- .setNegativeButton(R.string.cancel) { dialog, _ -> dialog.dismiss() }
- .create()
- .show()
- }
-
- fun showClearScoreDialog(context: Context, onClearScore: () -> Unit) {
- AlertDialog.Builder(context)
- .setTitle(R.string.dialog_clear_score_title)
- .setMessage(R.string.dialog_clear_score_message)
- .setPositiveButton(R.string.clear_score) { _, _ ->
- onClearScore()
- }
- .setNegativeButton(R.string.cancel, null)
- .create()
- .show()
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/dialogs/PossibleScoreDialog.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/dialogs/PossibleScoreDialog.kt
deleted file mode 100644
index f3cfb7023..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/dialogs/PossibleScoreDialog.kt
+++ /dev/null
@@ -1,64 +0,0 @@
-package ca.josephroque.bowlingcompanion.games.dialogs
-
-import android.content.Context
-import android.support.v7.app.AlertDialog
-import ca.josephroque.bowlingcompanion.R
-import ca.josephroque.bowlingcompanion.games.Frame
-import ca.josephroque.bowlingcompanion.games.Game
-import ca.josephroque.bowlingcompanion.games.lane.arePinsCleared
-import ca.josephroque.bowlingcompanion.utils.Analytics
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Calculate the best score the user could possibly get and display it.
- */
-object PossibleScoreDialog {
- fun show(context: Context, currentGame: Game, currentFrame: Int, currentBall: Int) {
- // Get the best ball the user could throw for this frame
- val ball = if (currentFrame == Game.LAST_FRAME) {
- when {
- currentBall == 0 || currentGame.frames[currentFrame].pinState[currentBall - 1].arePinsCleared ->
- context.resources.getString(R.string.best_possible_strike)
- currentBall == 1 || currentGame.frames[currentFrame].pinState[currentBall - 2].arePinsCleared ->
- context.resources.getString(R.string.best_possible_spare)
- else ->
- context.resources.getString(R.string.best_possible_spare)
- }
- context.resources.getString(R.string.best_possible_strike)
- } else {
- when (currentBall) {
- 0 -> context.resources.getString(R.string.best_possible_strike)
- 1 -> context.resources.getString(R.string.best_possible_spare)
- else -> context.resources.getString(R.string.best_possible_fifteen)
- }
- }
-
- // Set rest of game to strikes and clear all fouls
- currentGame.frames.forEachIndexed { index, frame ->
- for (i in 0 until Frame.NUMBER_OF_BALLS) {
- frame.ballFouled[i] = false
-
- if (index > currentFrame) {
- frame.pinState[i].forEach { it.isDown = true }
- }
- }
- }
-
- // Set rest of frame to be cleared
- currentGame.frames[currentFrame].pinState.forEachIndexed { index, deck ->
- if (index >= currentBall) {
- deck.forEach { it.isDown = true }
- }
- }
-
- AlertDialog.Builder(context)
- .setTitle(R.string.dialog_best_possible_title)
- .setMessage(context.resources.getString(R.string.dialog_best_possible_message, ball, currentGame.score))
- .setPositiveButton(R.string.okay, null)
- .create()
- .show()
-
- Analytics.trackViewPossibleScore(currentGame.score, currentFrame)
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/dialogs/ResetGameDialog.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/dialogs/ResetGameDialog.kt
deleted file mode 100644
index fe8bd11ac..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/dialogs/ResetGameDialog.kt
+++ /dev/null
@@ -1,25 +0,0 @@
-package ca.josephroque.bowlingcompanion.games.dialogs
-
-import android.content.Context
-import android.support.v7.app.AlertDialog
-import ca.josephroque.bowlingcompanion.R
-import java.lang.ref.WeakReference
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * A dialog which prompts the user to reset their game.
- */
-object ResetGameDialog {
- fun show(context: Context, onResetGame: WeakReference<() -> Unit>) {
- AlertDialog.Builder(context)
- .setTitle(R.string.dialog_reset_game_title)
- .setMessage(R.string.dialog_reset_game_message)
- .setPositiveButton(R.string.reset) { _, _ ->
- onResetGame.get()?.invoke()
- }
- .setNegativeButton(R.string.cancel, null)
- .create()
- .show()
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/lane/Ball.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/lane/Ball.kt
deleted file mode 100644
index 9fab412bb..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/lane/Ball.kt
+++ /dev/null
@@ -1,41 +0,0 @@
-package ca.josephroque.bowlingcompanion.games.lane
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Possible balls which can result from a frame.
- */
-enum class Ball {
- Strike, Spare, Left, Right, Ace, ChopOff, Split, Split2, HeadPin, HeadPin2, Cleared, None;
-
- override fun toString(): String {
- return when (this) {
- Strike -> "X"
- Spare -> "/"
- Left -> "L"
- Right -> "R"
- Ace -> "A"
- ChopOff -> "C/O"
- Split -> "HS"
- Split2 -> "10"
- HeadPin -> "HP"
- HeadPin2 -> "H2"
- Cleared -> "15"
- None -> "-"
- }
- }
-
- val numeral: String
- get() {
- return when (this) {
- Strike, Spare, Cleared -> "15"
- Left, Right -> "13"
- Ace -> "11"
- ChopOff, Split2 -> "10"
- Split -> "8"
- HeadPin2 -> "7"
- HeadPin -> "5"
- None -> "-"
- }
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/lane/Deck.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/lane/Deck.kt
deleted file mode 100644
index e96b6eb8b..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/lane/Deck.kt
+++ /dev/null
@@ -1,165 +0,0 @@
-package ca.josephroque.bowlingcompanion.games.lane
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Alias for an array of pins.
- */
-
-typealias Deck = Array
-
-// MARK: Pins
-
-val Deck.left2Pin: Pin
- get() = this[0]
-
-val Deck.left3Pin: Pin
- get() = this[1]
-
-val Deck.headPin: Pin
- get() = this[2]
-
-val Deck.right3Pin: Pin
- get() = this[3]
-
-val Deck.right2Pin: Pin
- get() = this[4]
-
-// MARK: First Ball
-
-fun Deck.isHeadPin(countH2asH: Boolean): Boolean {
- return this.isHeadPin || (countH2asH && this.value(false) == 7 && this.headPin.isDown)
-}
-
-val Deck.isHeadPin: Boolean
- get() = this.value(false) == 5 && this.headPin.isDown
-
-val Deck.isLeft: Boolean
- get() = this.value(false) == 13 && this.left2Pin.onDeck
-
-val Deck.isRight: Boolean
- get() = this.value(false) == 13 && this.right2Pin.onDeck
-
-val Deck.isAce: Boolean
- get() = this.value(false) == 11
-
-val Deck.isLeftChopOff: Boolean
- get() = this.value(false) == 10 && this.left2Pin.isDown && this.left3Pin.isDown && this.headPin.isDown
-
-val Deck.isRightChopOff: Boolean
- get() = this.value(false) == 10 && this.right2Pin.isDown && this.right3Pin.isDown && this.headPin.isDown
-
-val Deck.isChopOff: Boolean
- get() = this.isLeftChopOff || this.isRightChopOff
-
-fun Deck.isLeftSplit(countS2asS: Boolean): Boolean {
- return this.isLeftSplit || (countS2asS && this.value(false) == 10 && this.headPin.isDown && this.left3Pin.isDown && this.right2Pin.isDown)
-}
-
-private val Deck.isLeftSplit: Boolean
- get() = this.value(false) == 8 && this.headPin.isDown && this.left3Pin.isDown
-
-fun Deck.isRightSplit(countS2asS: Boolean): Boolean {
- return this.isRightSplit || (countS2asS && this.value(false) == 10 && this.headPin.isDown && this.left2Pin.isDown && this.right3Pin.isDown)
-}
-
-private val Deck.isRightSplit: Boolean
- get() = this.value(false) == 8 && this.headPin.isDown && this.right3Pin.isDown
-
-fun Deck.isSplit(countS2asS: Boolean): Boolean {
- return isLeftSplit(countS2asS) || isRightSplit(countS2asS)
-}
-
-val Deck.isHitLeftOfMiddle: Boolean
- get() = this.headPin.onDeck && (this.left2Pin.isDown || this.left3Pin.isDown)
-
-val Deck.isHitRightOfMiddle: Boolean
- get() = this.headPin.onDeck && (this.right2Pin.isDown || this.right3Pin.isDown)
-
-val Deck.isMiddleHit: Boolean
- get() = this.headPin.isDown
-
-val Deck.isLeftTwelve: Boolean
- get() = this.value(false) == 12 && this.left3Pin.isDown
-
-val Deck.isRightTwelve: Boolean
- get() = this.value(false) == 12 && this.right3Pin.isDown
-
-val Deck.isTwelve: Boolean
- get() = this.isLeftTwelve || this.isRightTwelve
-
-val Deck.arePinsCleared: Boolean
- get() = this.all { it.isDown }
-
-// Functions
-
-fun Deck.toBooleanArray(): BooleanArray {
- return this.map { it.isDown }.toBooleanArray()
-}
-
-fun Deck.deepCopy(): Deck {
- return this.map { pin -> Pin(pin.type).apply { isDown = pin.isDown } }.toTypedArray()
-}
-
-fun Deck.toInt(): Int {
- var ball = 0
- for (i in this.indices) {
- if (this[i].isDown) {
- ball += Math.pow(2.0, (-i + 4).toDouble()).toInt()
- }
- }
- return ball
-}
-
-fun Deck.reset() {
- this.forEach { it.isDown = false }
-}
-
-fun Deck.value(onDeck: Boolean): Int {
- return this.filter { it.onDeck == onDeck }.sumBy { it.value }
-}
-
-fun Deck.ballValue(ballIdx: Int, returnSymbol: Boolean, afterStrike: Boolean): String {
- val ball = when {
- isHeadPin(false) -> Ball.HeadPin
- isHeadPin(true) -> Ball.HeadPin2
- isSplit(false) -> Ball.Split
- isSplit(true) -> Ball.Split2
- isChopOff -> Ball.ChopOff
- isAce -> Ball.Ace
- isLeft -> Ball.Left
- isRight -> Ball.Right
- arePinsCleared -> {
- when {
- ballIdx == 0 -> Ball.Strike
- ballIdx == 1 && !afterStrike -> Ball.Spare
- else -> Ball.Cleared
- }
- }
- else -> Ball.None
- }
-
- return if (ball == Ball.None) {
- val value = this.value(false)
- return if (value == 0) {
- ball.toString()
- } else {
- value.toString()
- }
- } else {
- if (ballIdx == 0 || returnSymbol) ball.toString() else ball.numeral
- }
-}
-
-fun Deck.ballValueDifference(other: Deck, ballIdx: Int, returnSymbol: Boolean, afterStrike: Boolean): String {
- val deck = this.deepCopy()
- for (i in 0 until other.size) {
- if (other[i].isDown) { deck[i].isDown = false }
- }
-
- return deck.ballValue(ballIdx, returnSymbol, afterStrike)
-}
-
-fun Deck.valueDifference(other: Deck): Int {
- return this.filterIndexed { index, pin -> pin.isDown && !other[index].isDown }.sumBy { it.value }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/lane/Pin.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/lane/Pin.kt
deleted file mode 100644
index 0ad79804d..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/lane/Pin.kt
+++ /dev/null
@@ -1,68 +0,0 @@
-package ca.josephroque.bowlingcompanion.games.lane
-
-import java.util.Locale
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Pins with their values.
- */
-class Pin(val type: Type) {
-
- enum class Type {
- Two, Three, Five;
-
- val value: Int
- get() {
- return when (this) {
- Two -> 2
- Three -> 3
- Five -> 5
- }
- }
- }
-
- var isDown: Boolean = false
-
- val onDeck: Boolean
- get() = !isDown
-
- val value: Int
- get() = type.value
-
- fun deepCopy(): Pin {
- val pin = when (type) {
- Type.Two -> Pin(Type.Two)
- Type.Three -> Pin(Type.Three)
- Type.Five -> Pin(Type.Five)
- }
- pin.isDown = this.isDown
- return pin
- }
-
- companion object {
- private fun buildDeck(): Deck {
- return arrayOf(Pin(Type.Two), Pin(Type.Three), Pin(Type.Five), Pin(Type.Three), Pin(Type.Two))
- }
-
- fun deckFromBooleanArray(array: BooleanArray): Deck {
- return buildDeck().apply {
- forEachIndexed { index, pin ->
- pin.isDown = array[index]
- }
- }
- }
-
- fun deckFromInt(ball: Int): Deck {
- if (ball < 0 || ball > 31) {
- throw IllegalArgumentException("cannot convert value: $ball")
- }
- val pinState = buildDeck()
- val ballBinary = String.format(Locale.CANADA, "%5s", Integer.toBinaryString(ball)).replace(' ', '0')
- for (i in pinState.indices) {
- pinState[i].isDown = ballBinary[i] == '1'
- }
- return pinState
- }
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/overview/GameOverviewFragment.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/overview/GameOverviewFragment.kt
deleted file mode 100644
index 1fb152955..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/overview/GameOverviewFragment.kt
+++ /dev/null
@@ -1,229 +0,0 @@
-package ca.josephroque.bowlingcompanion.games.overview
-
-import android.content.Context
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.Menu
-import android.view.MenuInflater
-import android.view.MenuItem
-import android.view.View
-import android.view.ViewGroup
-import ca.josephroque.bowlingcompanion.R
-import ca.josephroque.bowlingcompanion.common.fragments.ListFragment
-import ca.josephroque.bowlingcompanion.common.interfaces.IFloatingActionButtonHandler
-import ca.josephroque.bowlingcompanion.common.interfaces.IIdentifiable
-import ca.josephroque.bowlingcompanion.games.Game
-import kotlinx.coroutines.experimental.CommonPool
-import kotlinx.coroutines.experimental.Deferred
-import kotlinx.coroutines.experimental.async
-import android.support.v7.app.AlertDialog
-import ca.josephroque.bowlingcompanion.statistics.interfaces.IStatisticsContext
-import ca.josephroque.bowlingcompanion.statistics.provider.StatisticsProvider
-import ca.josephroque.bowlingcompanion.utils.Permission
-import ca.josephroque.bowlingcompanion.utils.sharing.ShareUtils
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * A fragment to display an overview of a list of games.
- */
-class GameOverviewFragment : ListFragment(),
- ListFragment.ListFragmentDelegate,
- IFloatingActionButtonHandler,
- IStatisticsContext {
-
- companion object {
- @Suppress("unused")
- private const val TAG = "GameOverviewFragment"
-
- private const val ARG_GAMES = "${TAG}_games"
-
- fun newInstance(games: List): GameOverviewFragment {
- val fragment = GameOverviewFragment()
- fragment.arguments = Bundle().apply {
- putParcelableArrayList(ARG_GAMES, ArrayList(games))
- }
- return fragment
- }
-
- enum class ShareOption {
- Share, Save;
-
- companion object {
- private val map = ShareOption.values().associateBy(ShareOption::ordinal)
- fun fromInt(type: Int) = map[type]
- }
-
- val title: Int
- get() {
- return when (this) {
- Share -> R.string.share
- Save -> R.string.save_to_device
- }
- }
- }
- }
-
- private lateinit var games: List
-
- override val emptyViewImage = R.drawable.empty_view_leagues
- override val emptyViewText = R.string.empty_view_game_overview
-
- private var externalPermissionsGrantedCallback: (() -> Unit)? = null
-
- private var isSharing: Boolean = false
- set(value) {
- field = value
- adapter?.multiSelect = isSharing
-
- if (isSharing) {
- adapter?.setSelectedElementsWithIds(HashSet(games.map { it.id }))
- headerTitle = R.string.sharing_instructions_title
- headerSubtitle = R.string.sharing_instructions_body
- } else {
- headerTitle = null
- headerSubtitle = null
- }
- fabProvider?.invalidateFab()
- }
-
- override val statisticsProviders: List by lazy {
- val providers: MutableList = arrayListOf(
- StatisticsProvider.BowlerStatistics(games[0].series.league.bowler),
- StatisticsProvider.LeagueStatistics(games[0].series.league),
- StatisticsProvider.SeriesStatistics(games[0].series)
- )
-
- providers.addAll(games.map { StatisticsProvider.GameStatistics(it) })
- return@lazy providers
- }
-
- // MARK: Lifecycle functions
-
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
- arguments?.let { games = it.getParcelableArrayList(ARG_GAMES)!! }
- setHasOptionsMenu(true)
- return super.onCreateView(inflater, container, savedInstanceState)
- }
-
- override fun onAttach(context: Context?) {
- canIgnoreDelegate = true
- delegate = this
- super.onAttach(context)
- }
-
- override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
- super.onCreateOptionsMenu(menu, inflater)
- inflater.inflate(R.menu.fragment_game_overview, menu)
-
- menu.findItem(R.id.action_share).isVisible = !isSharing
- menu.findItem(R.id.action_stop_sharing).isVisible = isSharing
- }
-
- override fun onDestroy() {
- super.onDestroy()
- externalPermissionsGrantedCallback = null
- }
-
- override fun onOptionsItemSelected(item: MenuItem): Boolean {
- return when (item.itemId) {
- R.id.action_share -> {
- isSharing = true
- activity?.invalidateOptionsMenu()
- true
- }
- R.id.action_stop_sharing -> {
- isSharing = false
- activity?.invalidateOptionsMenu()
- true
- }
- else -> super.onOptionsItemSelected(item)
- }
- }
-
- // MARK: BaseFragment
-
- override fun updateToolbarTitle() {
- context?.let { navigationActivity?.setToolbarTitle(it.resources.getString(R.string.overview)) }
- }
-
- // MARK: ListFragment
-
- override fun buildAdapter(): GameOverviewRecyclerViewAdapter {
- val adapter = GameOverviewRecyclerViewAdapter(emptyList(), this)
- adapter.swipeable = false
- adapter.longPressable = true
- return adapter
- }
-
- override fun fetchItems(): Deferred> {
- return async(CommonPool) {
- return@async this@GameOverviewFragment.games.toMutableList()
- }
- }
-
- // MARK: AdapterDelegate
-
- override fun onItemSelected(item: IIdentifiable, longPress: Boolean) {
- if (item is Game) {
- if (longPress && adapter?.multiSelect == true) {
- adapter?.setSelectedElementsWithIds(setOf(item.id))
- promptShareGames()
- }
- }
- }
-
- override fun onItemDeleted(item: IIdentifiable) {
- // Intentionally left blank
- }
-
- // MARK: IFloatingActionButtonHandler
-
- override fun getFabImage(): Int? {
- return if (isSharing) R.drawable.ic_share else null
- }
-
- override fun onFabClick() {
- if (isSharing) {
- promptShareGames()
- }
- }
-
- override fun permissionGranted(permission: Permission) {
- when (permission) {
- Permission.WriteExternalStorage -> externalPermissionsGrantedCallback?.invoke()
- }
- }
-
- // MARK: Private functions
-
- private fun promptShareGames() {
- val activity = activity ?: return
- val sortedGames = adapter?.selectedItemsInOrder ?: return
- val options = ShareOption.values().map { activity.resources.getString(it.title) }
-
- val shareBuilder = AlertDialog.Builder(activity)
- shareBuilder.setTitle(R.string.share_or_save)
- .setSingleChoiceItems(options.toTypedArray(), ShareOption.Share.ordinal, null)
- .setPositiveButton(R.string.okay) { dialog, _ ->
- if (dialog is AlertDialog) {
- val selectedItem = ShareOption.fromInt(dialog.listView.checkedItemPosition)!!
- externalPermissionsGrantedCallback = {
- when (selectedItem) {
- ShareOption.Share -> ShareUtils.shareGames(activity, sortedGames)
- ShareOption.Save -> ShareUtils.saveGames(activity, sortedGames)
- }
- externalPermissionsGrantedCallback = null
- }
- when (selectedItem) {
- ShareOption.Share -> ShareUtils.shareGames(activity, sortedGames)
- ShareOption.Save -> ShareUtils.saveGames(activity, sortedGames)
- }
- }
- dialog.dismiss()
- }
- .setNegativeButton(R.string.cancel) { dialog, _ -> dialog.dismiss() }
- .create()
- .show()
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/overview/GameOverviewRecyclerViewAdapter.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/overview/GameOverviewRecyclerViewAdapter.kt
deleted file mode 100644
index 2cc3499ea..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/overview/GameOverviewRecyclerViewAdapter.kt
+++ /dev/null
@@ -1,82 +0,0 @@
-package ca.josephroque.bowlingcompanion.games.overview
-
-import android.support.v7.widget.RecyclerView
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.CheckBox
-import android.widget.TextView
-import ca.josephroque.bowlingcompanion.R
-import ca.josephroque.bowlingcompanion.common.adapters.BaseRecyclerViewAdapter
-import ca.josephroque.bowlingcompanion.games.Game
-import ca.josephroque.bowlingcompanion.games.views.ScoreSheet
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * [RecyclerView.Adapter] that can display a [Game] and makes a call to the specified delegate
- * upon interactions.
- */
-class GameOverviewRecyclerViewAdapter(
- values: List,
- delegate: BaseRecyclerViewAdapter.AdapterDelegate?
-) : BaseRecyclerViewAdapter(values, delegate) {
-
- private var scrollOffsets: MutableMap> = HashMap()
-
- companion object {
- @Suppress("unused")
- private const val TAG = "GameOverviewRVAdapter"
- }
-
- // MARK: BaseRecyclerViewAdapter
-
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseRecyclerViewAdapter.ViewHolder {
- return ViewHolder(LayoutInflater
- .from(parent.context)
- .inflate(R.layout.list_item_game_overview, parent, false))
- }
-
- override fun onBindViewHolder(holder: BaseRecyclerViewAdapter.ViewHolder, position: Int) {
- holder.bind(getItemAt(position))
- }
-
- // MARK: ViewHolder
-
- inner class ViewHolder(view: View) : BaseRecyclerViewAdapter.ViewHolder(view) {
- private val tvGameNumber: TextView? = view.findViewById(R.id.tv_game_number)
- private val scoreSheet: ScoreSheet? = view.findViewById(R.id.score_sheet)
- private val checkBox: CheckBox? = view.findViewById(R.id.checkbox_share)
-
- override fun bind(item: Game) {
- val context = itemView.context
-
- tvGameNumber?.text = context.resources.getString(R.string.game_number).format(item.ordinal)
-
- if (multiSelect) {
- checkBox?.visibility = View.VISIBLE
- checkBox?.isChecked = selectedItems.contains(item)
- } else {
- checkBox?.visibility = View.GONE
- }
-
- scoreSheet?.let {
- it.frameNumbersEnabled = false
- it.apply(-1, -1, item)
-
- // Remember scroll position
- val (x, y) = scrollOffsets[adapterPosition] ?: Pair(0, 0)
- it.scrollTo(x, y)
-
- it.delegate = object : ScoreSheet.SheetScrollListener {
- override fun didScroll(x: Int, y: Int) {
- scrollOffsets[adapterPosition] = Pair(x, y)
- }
- }
- }
-
- itemView.setOnClickListener(this@GameOverviewRecyclerViewAdapter)
- itemView.setOnLongClickListener(this@GameOverviewRecyclerViewAdapter)
- }
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/views/FrameView.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/views/FrameView.kt
deleted file mode 100644
index 1e97e4321..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/views/FrameView.kt
+++ /dev/null
@@ -1,197 +0,0 @@
-package ca.josephroque.bowlingcompanion.games.views
-
-import android.content.Context
-import android.os.Bundle
-import android.os.Parcelable
-import android.support.v4.content.ContextCompat
-import android.util.AttributeSet
-import android.view.LayoutInflater
-import android.view.View
-import android.widget.LinearLayout
-import android.widget.TextView
-import ca.josephroque.bowlingcompanion.R
-import ca.josephroque.bowlingcompanion.games.lane.Ball
-import ca.josephroque.bowlingcompanion.settings.Settings
-import kotlinx.android.synthetic.main.view_frame.view.frame as frame
-import kotlinx.android.synthetic.main.view_frame.view.tv_frame_number as tvFrameNumber
-import kotlinx.android.synthetic.main.view_frame.view.tv_score as tvScore
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Displays context for a single frame.
- */
-class FrameView : LinearLayout, View.OnClickListener {
-
- companion object {
- @Suppress("unused")
- private const val TAG = "FrameView"
-
- private const val SUPER_STATE = "${TAG}_super_state"
- private const val STATE_FRAME_NUMBER = "${TAG}_frame_number"
- private const val STATE_CURRENT_BALL = "${TAG}_current_ball"
- private const val STATE_SCORE = "${TAG}_score"
- private const val STATE_CURRENT_FRAME = "${TAG}_current_frame"
- }
-
- private val ballViewIds = intArrayOf(R.id.tv_ball_1, R.id.tv_ball_2, R.id.tv_ball_3)
- private val foulViewIds = intArrayOf(R.id.tv_foul_1, R.id.tv_foul_2, R.id.tv_foul_3)
-
- var delegate: FrameInteractionDelegate? = null
-
- var frameNumber: Int = 0
- set(value) {
- field = value
- tvFrameNumber.text = (value + 1).toString()
- }
-
- var frameNumberVisible: Boolean = true
- set(value) {
- field = value
- tvFrameNumber.visibility = if (value) View.VISIBLE else View.GONE
- }
-
- var score: Int = 0
- set(value) {
- field = value
- tvScore.text = value.toString()
- }
-
- var currentBall: Int = 0
- set(value) {
- field = value
- updateCurrentFrame()
- }
-
- var isCurrentFrame: Boolean = false
- set(value) {
- field = value
- updateCurrentFrame()
- }
-
- var shouldHighlightMarks: Boolean = Settings.BooleanSetting.EnableStrikeHighlights.default
- set(value) {
- field = value
- for (i in 0..ballViewIds.lastIndex) {
- val ballView = findViewById(ballViewIds[i])
- setBallText(i, ballView.text.toString())
- }
- }
-
- // MARK: Constructors
-
- constructor(context: Context) : this(context, null)
- constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
- constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
- orientation = VERTICAL
- LayoutInflater.from(context).inflate(R.layout.view_frame, this, true)
-
- val frameAttr = context.theme.obtainStyledAttributes(
- attrs,
- R.styleable.Frame,
- 0, 0)
-
- try {
- frameNumber = frameAttr.getInt(R.styleable.Frame_frameNumber, frameNumber)
- } finally {
- frameAttr.recycle()
- }
-
- ballViewIds.forEach {
- findViewById(it).setOnClickListener(this)
- }
- frame.setOnClickListener(this)
- }
-
- // MARK: Lifecycle functions
-
- override fun onSaveInstanceState(): Parcelable {
- return Bundle().apply {
- putParcelable(SUPER_STATE, super.onSaveInstanceState())
- putInt(STATE_FRAME_NUMBER, frameNumber)
- putInt(STATE_SCORE, score)
- putInt(STATE_CURRENT_BALL, currentBall)
- putBoolean(STATE_CURRENT_FRAME, isCurrentFrame)
- }
- }
-
- override fun onRestoreInstanceState(state: Parcelable?) {
- var superState: Parcelable? = null
- if (state is Bundle) {
- frameNumber = state.getInt(STATE_FRAME_NUMBER)
- score = state.getInt(STATE_SCORE)
- currentBall = state.getInt(STATE_CURRENT_BALL)
- isCurrentFrame = state.getBoolean(STATE_CURRENT_FRAME)
- superState = state.getParcelable(SUPER_STATE)
- }
-
- super.onRestoreInstanceState(superState)
- }
-
- override fun onDetachedFromWindow() {
- super.onDetachedFromWindow()
- delegate = null
- }
-
- // MARK: FrameView
-
- fun setBallText(ball: Int, text: String) {
- val ballView = findViewById(ballViewIds[ball])
- ballView.text = text
- if (shouldHighlightMarks && (text == Ball.Strike.toString() || text == Ball.Spare.toString())) {
- ballView.setTextColor(ContextCompat.getColor(context, R.color.colorPrimary))
- } else {
- ballView.setTextColor(ContextCompat.getColor(context, R.color.primaryBlackText))
- }
- }
-
- fun setFrameText(text: String) {
- tvScore.text = text
- }
-
- fun setFoulEnabled(ball: Int, enabled: Boolean) {
- findViewById(foulViewIds[ball]).visibility = if (enabled) {
- View.VISIBLE
- } else {
- View.INVISIBLE
- }
- }
-
- // MARK: OnClickListener
-
- override fun onClick(v: View?) {
- val view = v ?: return
-
- val ballIdIndex = ballViewIds.indexOf(view.id)
- if (ballIdIndex > -1) {
- delegate?.onBallSelected(ballIdIndex, frameNumber)
- return
- }
-
- val isFrame = frame.id == view.id
- if (isFrame) {
- delegate?.onFrameSelected(frameNumber)
- }
- }
-
- // MARK: Private functions
-
- private fun updateCurrentFrame() {
- ballViewIds.forEachIndexed { index, i ->
- val backgroundDrawable = ContextCompat.getDrawable(context,
- if (currentBall == index && isCurrentFrame) R.drawable.frame_background_active else R.drawable.frame_background_inactive)
- findViewById(i).background = backgroundDrawable
- }
-
- val backgroundDrawable = ContextCompat.getDrawable(context,
- if (isCurrentFrame) R.drawable.frame_background_active else R.drawable.frame_background_inactive)
- frame.background = backgroundDrawable
- }
-
- // MARK: FrameInteractionDelegate
-
- interface FrameInteractionDelegate {
- fun onBallSelected(ball: Int, frame: Int)
- fun onFrameSelected(frame: Int)
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/views/GameFooterView.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/views/GameFooterView.kt
deleted file mode 100644
index 0381141a8..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/views/GameFooterView.kt
+++ /dev/null
@@ -1,163 +0,0 @@
-package ca.josephroque.bowlingcompanion.games.views
-
-import android.content.Context
-import android.os.Bundle
-import android.os.Parcelable
-import android.support.constraint.ConstraintLayout
-import android.util.AttributeSet
-import android.view.LayoutInflater
-import android.view.View
-import ca.josephroque.bowlingcompanion.R
-import ca.josephroque.bowlingcompanion.matchplay.MatchPlayResult
-import kotlinx.android.synthetic.main.view_game_footer.view.pins_divider as pinsDivider
-import kotlinx.android.synthetic.main.view_game_footer.view.iv_clear_pins as clearPinsIcon
-import kotlinx.android.synthetic.main.view_game_footer.view.iv_foul as foulIcon
-import kotlinx.android.synthetic.main.view_game_footer.view.iv_lock as lockIcon
-import kotlinx.android.synthetic.main.view_game_footer.view.iv_match_play as matchPlayIcon
-import kotlinx.android.synthetic.main.view_game_footer.view.iv_fullscreen as fullscreenIcon
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Footer view to display game and frame controls at the foot of the game details.
- */
-class GameFooterView : ConstraintLayout {
-
- companion object {
- @Suppress("unused")
- private const val TAG = "GameFooterView"
-
- private const val SUPER_STATE = "${TAG}_super_state"
- private const val STATE_CURRENT_BALL = "${TAG}_current_ball"
- private const val STATE_MATCH_PLAY_RESULT = "${TAG}_match_play_result"
- private const val STATE_LOCK = "${TAG}_lock"
- private const val STATE_FOUL = "${TAG}_foul"
- private const val STATE_MANUAL_SCORE = "${TAG}_manual"
- private const val STATE_FULLSCREEN = "${TAG}_fullscreen"
-
- enum class Clear {
- Strike, Spare, Fifteen;
-
- val image: Int
- get() = when (this) {
- Strike -> R.drawable.ic_clear_pins_strike
- Spare -> R.drawable.ic_clear_pins_spare
- Fifteen -> R.drawable.ic_clear_pins_fifteen
- }
-
- companion object {
- private val map = Clear.values().associateBy(Clear::ordinal)
- fun fromInt(type: Int) = map[type]
- }
- }
- }
-
- var delegate: GameFooterInteractionDelegate? = null
-
- var isFullscreen: Boolean = false
- set(value) {
- field = value
- fullscreenIcon.setImageResource(if (value) R.drawable.ic_fullscreen_exit else R.drawable.ic_fullscreen)
- }
-
- var clear: Clear = Clear.Strike
- set(value) {
- field = value
- clearPinsIcon.setImageResource(value.image)
- }
-
- var matchPlayResult: MatchPlayResult = MatchPlayResult.NONE
- set(value) {
- field = value
- matchPlayIcon.setImageResource(value.getIcon())
- }
-
- var isGameLocked: Boolean = false
- set(value) {
- field = value
- lockIcon.setImageResource(if (value) R.drawable.ic_lock else R.drawable.ic_lock_open)
- }
-
- var isFoulActive: Boolean = false
- set(value) {
- field = value
- foulIcon.setImageResource(if (value) R.drawable.ic_foul_active else R.drawable.ic_foul_inactive)
- }
-
- var isManualScoreSet: Boolean = false
- set(value) {
- field = value
- val visible = if (value) View.GONE else View.VISIBLE
- clearPinsIcon.visibility = visible
- foulIcon.visibility = visible
- pinsDivider.visibility = visible
- }
-
- private val onClickListener = View.OnClickListener {
- when (it.id) {
- R.id.iv_fullscreen -> delegate?.onFullscreenToggle()
- R.id.iv_clear_pins -> delegate?.onClearPins()
- R.id.iv_foul -> delegate?.onFoulToggle()
- R.id.iv_lock -> delegate?.onLockToggle()
- R.id.iv_match_play -> delegate?.onMatchPlaySettings()
- }
- }
-
- // MARK: Constructors
-
- constructor(context: Context) : this(context, null)
- constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
- constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
- LayoutInflater.from(context).inflate(R.layout.view_game_footer, this, true)
-
- fullscreenIcon.setOnClickListener(onClickListener)
- clearPinsIcon.setOnClickListener(onClickListener)
- foulIcon.setOnClickListener(onClickListener)
- lockIcon.setOnClickListener(onClickListener)
- matchPlayIcon.setOnClickListener(onClickListener)
- }
-
- // MARK: Lifecycle functions
-
- override fun onSaveInstanceState(): Parcelable {
- return Bundle().apply {
- putParcelable(SUPER_STATE, super.onSaveInstanceState())
- putInt(STATE_CURRENT_BALL, clear.ordinal)
- putInt(STATE_MATCH_PLAY_RESULT, matchPlayResult.ordinal)
- putBoolean(STATE_LOCK, isGameLocked)
- putBoolean(STATE_FOUL, isFoulActive)
- putBoolean(STATE_MANUAL_SCORE, isManualScoreSet)
- putBoolean(STATE_FULLSCREEN, isFullscreen)
- }
- }
-
- override fun onRestoreInstanceState(state: Parcelable?) {
- var superState: Parcelable? = null
- if (state is Bundle) {
- clear = Clear.fromInt(state.getInt(STATE_CURRENT_BALL))!!
- matchPlayResult = MatchPlayResult.fromInt(state.getInt(STATE_MATCH_PLAY_RESULT))!!
- isGameLocked = state.getBoolean(STATE_LOCK)
- isFoulActive = state.getBoolean(STATE_FOUL)
- isManualScoreSet = state.getBoolean(STATE_MANUAL_SCORE)
- isFullscreen = state.getBoolean(STATE_FULLSCREEN)
- superState = state.getParcelable(SUPER_STATE)
- }
-
- super.onRestoreInstanceState(superState)
- }
-
- override fun onDetachedFromWindow() {
- super.onDetachedFromWindow()
- delegate = null
- }
-
- // MARK: GameFooterInteractionDelegate
-
- interface GameFooterInteractionDelegate {
- fun onLockToggle()
- fun onClearPins()
- fun onFoulToggle()
- fun onMatchPlaySettings()
- fun onFullscreenToggle()
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/views/GameHeaderView.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/views/GameHeaderView.kt
deleted file mode 100644
index 51a8df572..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/views/GameHeaderView.kt
+++ /dev/null
@@ -1,125 +0,0 @@
-package ca.josephroque.bowlingcompanion.games.views
-
-import android.content.Context
-import android.os.Bundle
-import android.os.Parcelable
-import android.support.v4.content.ContextCompat
-import android.util.AttributeSet
-import android.view.LayoutInflater
-import android.view.View
-import android.widget.LinearLayout
-import ca.josephroque.bowlingcompanion.R
-import kotlinx.android.synthetic.main.view_game_header.view.tv_game_number as gameNumber
-import kotlinx.android.synthetic.main.view_game_header.view.tv_next_ball as nextBall
-import kotlinx.android.synthetic.main.view_game_header.view.tv_prev_ball as prevBall
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Header of a game which displays the game number and navigation buttons.
- */
-class GameHeaderView : LinearLayout, View.OnClickListener {
-
- companion object {
- @Suppress("unused")
- private const val TAG = "GameHeaderView"
-
- private const val SUPER_STATE = "${TAG}_super_state"
- private const val STATE_CURRENT_GAME = "${TAG}_current_game"
- private const val STATE_NEXT_FRAME = "${TAG}_next_frame"
- private const val STATE_PREV_FRAME = "${TAG}_prev_frame"
- private const val STATE_MANUAL_SCORE = "${TAG}_manual"
- }
-
- var delegate: GameHeaderInteractionDelegate? = null
-
- var currentGame: Int = 0
- set(value) { gameNumber.text = String.format(resources.getString(R.string.game_number), value + 1) }
-
- var hasPreviousFrame: Boolean = false
- set(value) {
- field = value
- invalidateButtons()
- }
-
- var hasNextFrame: Boolean = true
- set(value) {
- field = value
- invalidateButtons()
- }
-
- var isManualScoreSet: Boolean = false
- set(value) {
- field = value
- invalidateButtons()
- }
-
- // MARK: Constructors
-
- constructor(context: Context) : this(context, null)
- constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
- constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
- LayoutInflater.from(context).inflate(R.layout.view_game_header, this, true)
- setBackgroundColor(ContextCompat.getColor(context, R.color.colorPrimaryLight))
- gameNumber.text = String.format(resources.getString(R.string.game_number), currentGame + 1)
- nextBall.setOnClickListener(this)
- prevBall.setOnClickListener(this)
- }
-
- // MARK: Lifecycle functions
-
- override fun onSaveInstanceState(): Parcelable {
- return Bundle().apply {
- putParcelable(SUPER_STATE, super.onSaveInstanceState())
- putInt(STATE_CURRENT_GAME, currentGame)
- putBoolean(STATE_NEXT_FRAME, hasNextFrame)
- putBoolean(STATE_PREV_FRAME, hasPreviousFrame)
- putBoolean(STATE_MANUAL_SCORE, isManualScoreSet)
- }
- }
-
- override fun onRestoreInstanceState(state: Parcelable?) {
- var superState: Parcelable? = null
- if (state is Bundle) {
- currentGame = state.getInt(STATE_CURRENT_GAME)
- hasNextFrame = state.getBoolean(STATE_NEXT_FRAME)
- hasPreviousFrame = state.getBoolean(STATE_PREV_FRAME)
- isManualScoreSet = state.getBoolean(STATE_MANUAL_SCORE)
- superState = state.getParcelable(SUPER_STATE)
- }
-
- super.onRestoreInstanceState(superState)
- }
-
- override fun onDetachedFromWindow() {
- super.onDetachedFromWindow()
- delegate = null
- }
-
- // MARK: OnClickListener
-
- override fun onClick(v: View?) {
- val view = v ?: return
- when (view.id) {
- R.id.tv_prev_ball -> delegate?.onPrevBall()
- R.id.tv_next_ball -> delegate?.onNextBall()
- }
- }
-
- // MARK: Private functions
-
- private fun invalidateButtons() {
- val prevVisibility = if (!isManualScoreSet && hasPreviousFrame) View.VISIBLE else View.INVISIBLE
- prevBall.post { prevBall.visibility = prevVisibility }
-
- val nextVisibility = if (hasNextFrame) View.VISIBLE else View.INVISIBLE
- nextBall.post { nextBall.visibility = nextVisibility }
- }
-
- // MARK: GameHeaderInteractionDelegate
-
- interface GameHeaderInteractionDelegate {
- fun onNextBall()
- fun onPrevBall()
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/views/GameNumberView.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/views/GameNumberView.kt
deleted file mode 100644
index 6537c079f..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/views/GameNumberView.kt
+++ /dev/null
@@ -1,70 +0,0 @@
-package ca.josephroque.bowlingcompanion.games.views
-
-import android.content.Context
-import android.os.Bundle
-import android.os.Parcelable
-import android.support.constraint.ConstraintLayout
-import android.util.AttributeSet
-import android.view.LayoutInflater
-import ca.josephroque.bowlingcompanion.R
-import kotlinx.android.synthetic.main.view_game_number.view.tv_game_number as tvGameNumber
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Draw the number of a game at a standard size
- */
-class GameNumberView : ConstraintLayout {
-
- companion object {
- @Suppress("unused")
- private const val TAG = "GameNumberView"
-
- private const val SUPER_STATE = "${TAG}_super_state"
- private const val STATE_GAME_NUMBER = "${TAG}_game_number"
- }
-
- var gameNumber: Int = 0
- set(value) {
- field = value
- tvGameNumber.text = context.resources.getString(R.string.game_number).format(gameNumber)
- }
-
- // MARK: Constructors
-
- constructor(context: Context) : this(context, null)
- constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
- constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
- LayoutInflater.from(context).inflate(R.layout.view_game_number, this, true)
-
- val frameAttr = context.theme.obtainStyledAttributes(
- attrs,
- R.styleable.Frame,
- 0, 0)
-
- try {
- gameNumber = frameAttr.getInt(R.styleable.Game_gameNumber, gameNumber)
- } finally {
- frameAttr.recycle()
- }
- }
-
- // MARK: Lifecycle functions
-
- override fun onSaveInstanceState(): Parcelable {
- return Bundle().apply {
- putParcelable(SUPER_STATE, super.onSaveInstanceState())
- putInt(STATE_GAME_NUMBER, gameNumber)
- }
- }
-
- override fun onRestoreInstanceState(state: Parcelable?) {
- var superState: Parcelable? = null
- if (state is Bundle) {
- gameNumber = state.getInt(STATE_GAME_NUMBER)
- superState = state.getParcelable(SUPER_STATE)
- }
-
- super.onRestoreInstanceState(superState)
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/views/PinLayout.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/views/PinLayout.kt
deleted file mode 100644
index bcded24a5..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/views/PinLayout.kt
+++ /dev/null
@@ -1,174 +0,0 @@
-package ca.josephroque.bowlingcompanion.games.views
-
-import android.annotation.SuppressLint
-import android.content.Context
-import android.util.AttributeSet
-import android.view.LayoutInflater
-import android.view.MotionEvent
-import android.widget.ImageView
-import android.widget.LinearLayout
-import ca.josephroque.bowlingcompanion.R
-import ca.josephroque.bowlingcompanion.games.Game
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Display pins and handle touch events for the pins.
- */
-class PinLayout : LinearLayout {
-
- companion object {
- @Suppress("unused")
- private const val TAG = "PinLayout"
- }
-
- private val pinViewIds = intArrayOf(R.id.iv_pin_1, R.id.iv_pin_2, R.id.iv_pin_3, R.id.iv_pin_4, R.id.iv_pin_5)
- private var pinViews: Array
-
- var delegate: PinLayoutInteractionDelegate? = null
-
- private val pinAltered = BooleanArray(Game.NUMBER_OF_PINS)
-
- private var initialStateSet: Boolean = false
-
- private var initialPinDown: Boolean = false
-
- private var isDragging: Boolean = false
-
- private val leftEdgeIgnoreWidth: Float = 20F
- get() {
- return field.times(resources.displayMetrics.density)
- }
-
- // MARK: Constructors
-
- constructor(context: Context) : this(context, null)
- constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
- constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
- LayoutInflater.from(context).inflate(R.layout.view_pin_layout, this, true)
-
- pinViews = arrayOfNulls(pinViewIds.size)
- pinViewIds.forEachIndexed { index, id ->
- pinViews[index] = findViewById(id)
- }
- }
-
- // MARK: Lifecycle functions
-
- override fun onDetachedFromWindow() {
- super.onDetachedFromWindow()
- delegate = null
- }
-
- override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
- return true
- }
-
- @SuppressLint("ClickableViewAccessibility")
- override fun onTouchEvent(event: MotionEvent?): Boolean {
- val ev = event ?: return false
- if (ev.x < leftEdgeIgnoreWidth) {
- event.action = MotionEvent.ACTION_CANCEL
- return true
- }
-
- when (event.actionMasked) {
- MotionEvent.ACTION_DOWN -> {
- isDragging = true
- pinAltered.forEachIndexed { index, _ -> pinAltered[index] = false }
-
- if (!isTouchEventValid(event)) {
- return true
- }
-
- setPinTouched(event, true)
- }
- MotionEvent.ACTION_MOVE -> {
- if (!initialStateSet || !isTouchEventValid(event)) {
- return true
- }
-
- // Flip pins while user is dragging
- setPinTouched(event, false)
- }
- MotionEvent.ACTION_UP -> {
- if (!isDragging) {
- return true
- }
-
- finalizePins()
- }
- }
- return true
- }
-
- // MARK: PinLayout
-
- fun setPinEnabled(pin: Int, enabled: Boolean) {
- pinViews[pin]?.isEnabled = enabled
- }
-
- fun updatePinImage(pin: Int, isDown: Boolean) {
- pinViews[pin]?.post { pinViews[pin]?.setImageResource(if (isDown) R.drawable.pin_disabled else R.drawable.pin_enabled) }
- }
-
- // MARK: Private functions
-
- private fun isTouchEventValid(event: MotionEvent): Boolean {
- return event.x >= 0 && event.x <= width
- }
-
- private fun setPinTouched(event: MotionEvent, firstPin: Boolean) {
- val pinTouched = Math.max(0, Math.min(((event.x / width) * pinViews.size).toInt(), pinViews.lastIndex))
- val pinView = pinViews[pinTouched] ?: return
- val delegate = delegate ?: return
-
- if (pinAltered[pinTouched]) {
- return
- }
-
- if (!pinView.isEnabled) {
- if (firstPin) {
- initialStateSet = false
- }
- return
- }
-
- val pinDown = delegate.isPinDown(pinTouched)
- if (!initialStateSet) {
- if (firstPin) {
- initialStateSet = true
- initialPinDown = pinDown
- } else {
- return
- }
- } else if (pinDown != initialPinDown) {
- return
- }
-
- pinAltered[pinTouched] = true
- // Use the opposite image since we are toggling the pins from down to up or up to down
- updatePinImage(pinTouched, !pinDown)
- }
-
- private fun finalizePins() {
- isDragging = false
- initialStateSet = false
-
- val pins: MutableList = ArrayList()
- pinAltered.forEachIndexed { index, altered ->
- if (altered) {
- pins.add(index)
- }
- }
-
- delegate?.setPins(pins.toIntArray(), !initialPinDown)
- }
-
- // MARK: PinLayoutInteractionDelegate
-
- interface PinLayoutInteractionDelegate {
- fun setPins(pins: IntArray, isDown: Boolean)
- fun isPinDown(pin: Int): Boolean
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/views/ScoreSheet.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/views/ScoreSheet.kt
deleted file mode 100644
index df0efb60e..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/games/views/ScoreSheet.kt
+++ /dev/null
@@ -1,143 +0,0 @@
-package ca.josephroque.bowlingcompanion.games.views
-
-import android.content.Context
-import android.util.AttributeSet
-import android.view.LayoutInflater
-import android.widget.HorizontalScrollView
-import android.widget.LinearLayout
-import ca.josephroque.bowlingcompanion.R
-import ca.josephroque.bowlingcompanion.games.Game
-import ca.josephroque.bowlingcompanion.settings.Settings
-import kotlinx.android.synthetic.main.view_score_sheet.view.tv_final_score as tvFinalScore
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Display a detailed view of the score of a game.
- */
-class ScoreSheet : HorizontalScrollView {
-
- companion object {
- @Suppress("unused")
- private const val TAG = "ScoreSheet"
- }
-
- private val frameViewIds = intArrayOf(R.id.frame_0, R.id.frame_1, R.id.frame_2, R.id.frame_3,
- R.id.frame_4, R.id.frame_5, R.id.frame_6, R.id.frame_7, R.id.frame_8, R.id.frame_9)
-
- private var frameViews: Array
-
- var delegate: SheetScrollListener? = null
-
- var frameViewDelegate: FrameView.FrameInteractionDelegate? = null
- set(value) {
- field = value
- frameViews.forEach {
- it?.delegate = value
- }
- }
-
- var shouldHighlightMarks: Boolean = Settings.BooleanSetting.EnableStrikeHighlights.default
- set(value) {
- field = value
- frameViews.forEach {
- it?.shouldHighlightMarks = value
- }
- }
-
- var frameNumbersEnabled: Boolean = true
- set(value) {
- field = value
- frameViews.forEach { it?.frameNumberVisible = value }
-
- val layoutParams = tvFinalScore.layoutParams as? LinearLayout.LayoutParams ?: return
- layoutParams.bottomMargin = if (value) {
- context.resources.getDimensionPixelSize(R.dimen.frame_number_height)
- } else {
- 0
- }
- tvFinalScore.layoutParams = layoutParams
- }
-
- var finalScore: Int = 0
- set(value) {
- field = value
- tvFinalScore.text = value.toString()
- }
-
- // MARK: Constructors
-
- constructor(context: Context) : this(context, null)
- constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
- constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
- LayoutInflater.from(context).inflate(R.layout.view_score_sheet, this, true)
-
- frameViews = arrayOfNulls(frameViewIds.size)
- frameViewIds.forEachIndexed { index, it ->
- frameViews[index] = findViewById(it)
- }
- }
-
- // MARK: Lifecycle functions
-
- override fun onDetachedFromWindow() {
- super.onDetachedFromWindow()
- frameViewDelegate = null
- delegate = null
- }
-
- override fun onScrollChanged(l: Int, t: Int, oldl: Int, oldt: Int) {
- super.onScrollChanged(l, t, oldl, oldt)
- delegate?.didScroll(l, t)
- }
-
- // MARK: ScoreSheet
-
- fun apply(currentFrameIdx: Int, currentBallIdx: Int, game: Game) {
- // Set final score of the game
- finalScore = game.score
-
- // Update frames with marks and pins
- val scores = game.getScoreTextForFrames()
- val balls = game.getBallTextForFrames()
- updateFrames(currentFrameIdx, currentBallIdx, scores, balls)
-
- // Update fouls
- game.frames.forEachIndexed { frameIdx, frame ->
- frame.ballFouled.forEachIndexed { ballIdx, foul ->
- setFoulEnabled(frameIdx, ballIdx, foul)
- }
- }
- }
-
- fun updateFrames(currentFrameIdx: Int, currentBallIdx: Int, scores: List, balls: List>) {
- frameViews.forEachIndexed { frameIdx, it ->
- it?.isCurrentFrame = (frameIdx == currentFrameIdx)
- it?.currentBall = currentBallIdx
- it?.setFrameText(scores[frameIdx])
- balls[frameIdx].forEachIndexed { ballIdx, ball ->
- it?.setBallText(ballIdx, ball)
- }
- }
- }
-
- fun setFoulEnabled(frameIdx: Int, ballIdx: Int, enabled: Boolean) {
- frameViews[frameIdx]?.setFoulEnabled(ballIdx, enabled)
- }
-
- fun focusOnFrame(isFirstFocus: Boolean, isLastFrame: Boolean, frameIdx: Int) {
- val left = if (frameIdx >= 1 && !(isFirstFocus && isLastFrame)) {
- val prevFrame = frameViews[frameIdx - 1] ?: return
- prevFrame.left
- } else {
- val frame = frameViews[frameIdx] ?: return
- frame.left
- }
-
- post { smoothScrollTo(left, 0) }
- }
-
- interface SheetScrollListener {
- fun didScroll(x: Int, y: Int)
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/leagues/League.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/leagues/League.kt
deleted file mode 100644
index 26f151a42..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/leagues/League.kt
+++ /dev/null
@@ -1,580 +0,0 @@
-package ca.josephroque.bowlingcompanion.leagues
-
-import android.content.ContentValues
-import android.content.Context
-import android.content.DialogInterface
-import android.database.Cursor
-import android.database.sqlite.SQLiteDatabase
-import android.os.Parcel
-import android.support.v7.app.AlertDialog
-import android.support.v7.preference.PreferenceManager
-import android.util.Log
-import android.widget.NumberPicker
-import ca.josephroque.bowlingcompanion.R
-import ca.josephroque.bowlingcompanion.bowlers.Bowler
-import ca.josephroque.bowlingcompanion.common.interfaces.INameAverage
-import ca.josephroque.bowlingcompanion.common.interfaces.KParcelable
-import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator
-import ca.josephroque.bowlingcompanion.common.interfaces.readBoolean
-import ca.josephroque.bowlingcompanion.common.interfaces.writeBoolean
-import ca.josephroque.bowlingcompanion.database.Annihilator
-import ca.josephroque.bowlingcompanion.database.Contract.GameEntry
-import ca.josephroque.bowlingcompanion.database.Contract.LeagueEntry
-import ca.josephroque.bowlingcompanion.database.Contract.SeriesEntry
-import ca.josephroque.bowlingcompanion.database.DatabaseManager
-import ca.josephroque.bowlingcompanion.games.Game
-import ca.josephroque.bowlingcompanion.scoring.Average
-import ca.josephroque.bowlingcompanion.series.Series
-import ca.josephroque.bowlingcompanion.utils.BCError
-import ca.josephroque.bowlingcompanion.utils.Preferences
-import kotlinx.coroutines.experimental.CommonPool
-import kotlinx.coroutines.experimental.Deferred
-import kotlinx.coroutines.experimental.async
-import java.lang.ref.WeakReference
-import java.text.SimpleDateFormat
-import java.util.Date
-import java.util.Locale
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * A single League, which has a set of series.
- */
-data class League(
- val bowler: Bowler,
- override val id: Long,
- override val name: String,
- override val average: Double,
- val isEvent: Boolean,
- val gamesPerSeries: Int,
- val additionalPinfall: Int,
- val additionalGames: Int,
- val gameHighlight: Int,
- val seriesHighlight: Int
-) : INameAverage, KParcelable {
-
- private var _isDeleted: Boolean = false
- override val isDeleted: Boolean
- get() = _isDeleted
-
- val isPractice: Boolean
- get() = name == PRACTICE_LEAGUE_NAME
-
- // MARK: Constructors
-
- private constructor(p: Parcel): this(
- bowler = p.readParcelable(Bowler::class.java.classLoader)!!,
- id = p.readLong(),
- name = p.readString()!!,
- average = p.readDouble(),
- isEvent = p.readBoolean(),
- gamesPerSeries = p.readInt(),
- additionalPinfall = p.readInt(),
- additionalGames = p.readInt(),
- gameHighlight = p.readInt(),
- seriesHighlight = p.readInt()
- )
-
- constructor(league: League): this(
- bowler = league.bowler,
- id = league.id,
- name = league.name,
- average = league.average,
- isEvent = league.isEvent,
- gamesPerSeries = league.gamesPerSeries,
- additionalPinfall = league.additionalPinfall,
- additionalGames = league.additionalGames,
- gameHighlight = league.gameHighlight,
- seriesHighlight = league.seriesHighlight
- )
-
- // MARK: Parcelable
-
- override fun writeToParcel(dest: Parcel, flags: Int) = with(dest) {
- writeParcelable(bowler, 0)
- writeLong(id)
- writeString(name)
- writeDouble(average)
- writeBoolean(isEvent)
- writeInt(gamesPerSeries)
- writeInt(additionalPinfall)
- writeInt(additionalGames)
- writeInt(gameHighlight)
- writeInt(seriesHighlight)
- }
-
- // MARK: League
-
- fun fetchSeries(context: Context): Deferred> {
- return Series.fetchAll(context, this)
- }
-
- fun createNewSeries(context: Context, openDatabase: SQLiteDatabase? = null, numberOfPracticeGamesOverride: Int? = null): Deferred> {
- val numberOfGames = if (isPractice) {
- numberOfPracticeGamesOverride ?: gamesPerSeries
- } else {
- gamesPerSeries
- }
-
- return async(CommonPool) {
- return@async Series.save(
- context = context,
- league = this@League,
- id = -1,
- date = Date(),
- numberOfGames = numberOfGames,
- scores = IntArray(numberOfGames).toList(),
- matchPlay = ByteArray(numberOfGames).toList(),
- openDatabase = openDatabase
- ).await()
- }
- }
-
- // MARK: IDeletable
-
- override fun markForDeletion(): League {
- val newInstance = League(this)
- newInstance._isDeleted = true
- return newInstance
- }
-
- override fun cleanDeletion(): League {
- val newInstance = League(this)
- newInstance._isDeleted = false
- return newInstance
- }
-
- override fun delete(context: Context): Deferred {
- return async(CommonPool) {
- if (id < 0) {
- return@async
- }
-
- Annihilator.instance.delete(
- weakContext = WeakReference(context),
- tableName = LeagueEntry.TABLE_NAME,
- whereClause = "${LeagueEntry._ID}=?",
- whereArgs = arrayOf(id.toString())
- )
- }
- }
-
- companion object {
- @Suppress("unused")
- private const val TAG = "League"
-
- @Suppress("unused")
- @JvmField val CREATOR = parcelableCreator(::League)
-
- private val REGEX_NAME = Bowler.REGEX_NAME
-
- @Deprecated("Replaced with PRACTICE_LEAGUE_NAME")
- const val OPEN_LEAGUE_NAME = "Open"
- const val PRACTICE_LEAGUE_NAME = "Practice"
-
- const val MAX_NUMBER_OF_GAMES = 20
- const val MIN_NUMBER_OF_GAMES = 1
-
- const val DEFAULT_NUMBER_OF_GAMES = 1
- const val DEFAULT_GAME_HIGHLIGHT = 300
- val DEFAULT_SERIES_HIGHLIGHT = intArrayOf(250, 500, 750, 1000, 1250, 1500, 1750, 2000, 2250, 2500, 2750, 3000, 3250, 3500, 3750, 4000, 4250, 4500, 4750, 5000)
-
- enum class Sort {
- Alphabetically,
- LastModified;
-
- companion object {
- private val map = Sort.values().associateBy(Sort::ordinal)
- fun fromInt(type: Int) = map[type]
- }
- }
-
- fun showPracticeGamesPicker(context: Context, completionHandler: (Int) -> Unit) {
- val numberPicker = NumberPicker(context)
- numberPicker.maxValue = League.MAX_NUMBER_OF_GAMES
- numberPicker.minValue = 1
- numberPicker.wrapSelectorWheel = false
-
- val listener = DialogInterface.OnClickListener { dialog, which ->
- if (which == DialogInterface.BUTTON_POSITIVE) {
- completionHandler(numberPicker.value)
- }
- dialog.dismiss()
- }
-
- AlertDialog.Builder(context)
- .setTitle(R.string.how_many_practice_games)
- .setView(numberPicker)
- .setPositiveButton(R.string.bowl, listener)
- .setNegativeButton(R.string.cancel, listener)
- .create()
- .show()
- }
-
- private fun isLeagueNameValid(name: String): Boolean = REGEX_NAME.matches(name)
-
- private fun nameMatchesPracticeLeague(name: String): Boolean = PRACTICE_LEAGUE_NAME.toLowerCase().equals(name.toLowerCase())
-
- private fun isLeagueNameUnique(context: Context, name: String, id: Long = -1): Deferred {
- return async(CommonPool) {
- val database = DatabaseManager.getReadableDatabase(context).await()
-
- var cursor: Cursor? = null
- try {
- cursor = database.query(
- LeagueEntry.TABLE_NAME,
- arrayOf(LeagueEntry.COLUMN_LEAGUE_NAME),
- "${LeagueEntry.COLUMN_LEAGUE_NAME}=? AND ${LeagueEntry._ID}!=?",
- arrayOf(name, id.toString()),
- "",
- "",
- ""
- )
-
- if ((cursor?.count ?: 0) > 0) {
- return@async false
- }
- } finally {
- if (cursor != null && !cursor.isClosed) {
- cursor.close()
- }
- }
-
- true
- }
- }
-
- private fun validateSavePreconditions(
- context: Context,
- id: Long,
- name: String,
- isEvent: Boolean,
- gamesPerSeries: Int,
- additionalPinfall: Int,
- additionalGames: Int,
- gameHighlight: Int,
- seriesHighlight: Int
- ): Deferred {
- return async(CommonPool) {
- val errorTitle = if (isEvent) R.string.issue_saving_event else R.string.issue_saving_league
- val errorMessage: Int?
- if (nameMatchesPracticeLeague(name)) {
- errorMessage = R.string.error_league_name_is_practice
- } else if (!isLeagueNameValid(name)) {
- errorMessage = if (isEvent) R.string.error_event_name_invalid else R.string.error_league_name_invalid
- } else if (!isLeagueNameUnique(context, name, id).await()) {
- errorMessage = R.string.error_league_name_in_use
- } else if (name == PRACTICE_LEAGUE_NAME) {
- errorMessage = R.string.error_cannot_edit_practice_league
- } else if (
- (isEvent && (additionalPinfall != 0 || additionalGames != 0)) ||
- (additionalPinfall < 0 || additionalGames < 0) ||
- (additionalPinfall > 0 && additionalGames == 0) ||
- (additionalPinfall.toDouble() / additionalGames.toDouble() > 450)
- ) {
- errorMessage = R.string.error_league_additional_info_unbalanced
- } else if (
- gameHighlight < 0 || gameHighlight > Game.MAX_SCORE ||
- seriesHighlight < 0 || seriesHighlight > Game.MAX_SCORE * gamesPerSeries
- ) {
- errorMessage = R.string.error_league_highlight_invalid
- } else if (gamesPerSeries < MIN_NUMBER_OF_GAMES || gamesPerSeries > MAX_NUMBER_OF_GAMES) {
- errorMessage = R.string.error_league_number_of_games_invalid
- } else {
- errorMessage = null
- }
-
- return@async if (errorMessage != null) {
- BCError(errorTitle, errorMessage, BCError.Severity.Warning)
- } else {
- null
- }
- }
- }
-
- fun save(
- context: Context,
- bowler: Bowler,
- id: Long,
- name: String,
- isEvent: Boolean,
- gamesPerSeries: Int,
- additionalPinfall: Int,
- additionalGames: Int,
- gameHighlight: Int,
- seriesHighlight: Int,
- average: Double = 0.0
- ): Deferred> {
- return if (id < 0) {
- createNewAndSave(context, bowler, name, isEvent, gamesPerSeries, additionalPinfall, additionalGames, gameHighlight, seriesHighlight)
- } else {
- update(context, bowler, id, name, average, isEvent, gamesPerSeries, additionalPinfall, additionalGames, gameHighlight, seriesHighlight)
- }
- }
-
- private fun createNewAndSave(
- context: Context,
- bowler: Bowler,
- name: String,
- isEvent: Boolean,
- gamesPerSeries: Int,
- additionalPinfall: Int,
- additionalGames: Int,
- gameHighlight: Int,
- seriesHighlight: Int
- ): Deferred> {
- return async(CommonPool) {
- val error = validateSavePreconditions(
- context = context,
- id = -1,
- name = name,
- isEvent = isEvent,
- gamesPerSeries = gamesPerSeries,
- additionalPinfall = additionalPinfall,
- additionalGames = additionalGames,
- gameHighlight = gameHighlight,
- seriesHighlight = seriesHighlight
-
- ).await()
- if (error != null) {
- return@async Pair(null, error)
- }
-
- val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CANADA)
- val currentDate = dateFormat.format(Date())
-
- val values = ContentValues().apply {
- put(LeagueEntry.COLUMN_LEAGUE_NAME, name)
- put(LeagueEntry.COLUMN_DATE_MODIFIED, currentDate)
- put(LeagueEntry.COLUMN_BOWLER_ID, bowler.id)
- put(LeagueEntry.COLUMN_ADDITIONAL_PINFALL, additionalPinfall)
- put(LeagueEntry.COLUMN_ADDITIONAL_GAMES, additionalGames)
- put(LeagueEntry.COLUMN_NUMBER_OF_GAMES, gamesPerSeries)
- put(LeagueEntry.COLUMN_IS_EVENT, isEvent)
- put(LeagueEntry.COLUMN_GAME_HIGHLIGHT, gameHighlight)
- put(LeagueEntry.COLUMN_SERIES_HIGHLIGHT, seriesHighlight)
- }
-
- val league: League
- val database = DatabaseManager.getWritableDatabase(context).await()
- database.beginTransaction()
- try {
- val leagueId = database.insert(LeagueEntry.TABLE_NAME, null, values)
- league = League(
- bowler = bowler,
- id = leagueId,
- name = name,
- average = 0.0,
- isEvent = isEvent,
- gamesPerSeries = gamesPerSeries,
- additionalPinfall = additionalPinfall,
- additionalGames = additionalGames,
- gameHighlight = gameHighlight,
- seriesHighlight = seriesHighlight
- )
-
- if (league.id != -1L && isEvent) {
- /*
- * If the new entry is an event, its series is also created at this time
- * since there is only a single series to an event
- */
- val (series, seriesError) = league.createNewSeries(context, database).await()
- if (seriesError != null || (series?.id ?: -1L) == -1L) {
- throw IllegalStateException("Series was not saved.")
- }
- }
-
- database.setTransactionSuccessful()
- } catch (ex: Exception) {
- Log.e(TAG, "Could not create a new league")
- return@async Pair(
- null,
- BCError(R.string.error_saving_league, R.string.error_league_not_saved)
- )
- } finally {
- database.endTransaction()
- }
-
- Pair(league, null)
- }
- }
-
- fun update(
- context: Context,
- bowler: Bowler,
- id: Long,
- name: String,
- average: Double,
- isEvent: Boolean,
- gamesPerSeries: Int,
- additionalPinfall: Int,
- additionalGames: Int,
- gameHighlight: Int,
- seriesHighlight: Int
- ): Deferred> {
- return async(CommonPool) {
- val error = validateSavePreconditions(
- context = context,
- id = id,
- name = name,
- isEvent = isEvent,
- gamesPerSeries = gamesPerSeries,
- additionalPinfall = additionalPinfall,
- additionalGames = additionalGames,
- gameHighlight = gameHighlight,
- seriesHighlight = seriesHighlight
-
- ).await()
- if (error != null) {
- return@async Pair(null, error)
- }
-
- val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CANADA)
- val currentDate = dateFormat.format(Date())
-
- val values = ContentValues().apply {
- put(LeagueEntry.COLUMN_LEAGUE_NAME, name)
- put(LeagueEntry.COLUMN_ADDITIONAL_PINFALL, additionalPinfall)
- put(LeagueEntry.COLUMN_ADDITIONAL_GAMES, additionalGames)
- put(LeagueEntry.COLUMN_GAME_HIGHLIGHT, gameHighlight)
- put(LeagueEntry.COLUMN_SERIES_HIGHLIGHT, seriesHighlight)
- put(LeagueEntry.COLUMN_DATE_MODIFIED, currentDate)
- }
-
- val database = DatabaseManager.getWritableDatabase(context).await()
- database.beginTransaction()
- try {
- database.update(LeagueEntry.TABLE_NAME, values, LeagueEntry._ID + "=?", arrayOf(id.toString()))
- database.setTransactionSuccessful()
- } catch (ex: Exception) {
- Log.e(TAG, "Error updating league/event details ($name, $additionalPinfall, $additionalGames)", ex)
- } finally {
- database.endTransaction()
- }
-
- Pair(League(
- bowler = bowler,
- id = id,
- name = name,
- average = average,
- isEvent = isEvent,
- gamesPerSeries = gamesPerSeries,
- additionalPinfall = additionalPinfall,
- additionalGames = additionalGames,
- gameHighlight = gameHighlight,
- seriesHighlight = seriesHighlight
- ), null)
- }
- }
-
- fun fetchAll(
- context: Context,
- bowler: Bowler,
- includeLeagues: Boolean = true,
- includeEvents: Boolean = false
- ): Deferred> {
- return async(CommonPool) {
- val leagues: MutableList = ArrayList()
- val database = DatabaseManager.getReadableDatabase(context).await()
-
- val preferences = PreferenceManager.getDefaultSharedPreferences(context)
- val sortBy = Sort.fromInt(preferences.getInt(Preferences.LEAGUE_SORT_ORDER, Sort.Alphabetically.ordinal))
-
- val orderQueryBy = if (sortBy == Sort.Alphabetically) {
- "ORDER BY league.${LeagueEntry.COLUMN_LEAGUE_NAME} "
- } else {
- "ORDER BY league.${LeagueEntry.COLUMN_DATE_MODIFIED} DESC "
- }
-
- val rawLeagueEventQuery = ("SELECT " +
- "league.${LeagueEntry._ID} AS lid, " +
- "${LeagueEntry.COLUMN_LEAGUE_NAME}, " +
- "${LeagueEntry.COLUMN_IS_EVENT}, " +
- "${LeagueEntry.COLUMN_ADDITIONAL_PINFALL}, " +
- "${LeagueEntry.COLUMN_ADDITIONAL_GAMES}, " +
- "${LeagueEntry.COLUMN_GAME_HIGHLIGHT}, " +
- "${LeagueEntry.COLUMN_SERIES_HIGHLIGHT}, " +
- "${LeagueEntry.COLUMN_NUMBER_OF_GAMES}, " +
- "${GameEntry.COLUMN_SCORE} " +
- "FROM ${LeagueEntry.TABLE_NAME} AS league " +
- "LEFT JOIN ${SeriesEntry.TABLE_NAME} AS series " +
- "ON league.${LeagueEntry._ID}=series.${SeriesEntry.COLUMN_LEAGUE_ID} " +
- "LEFT JOIN ${GameEntry.TABLE_NAME} AS game " +
- "ON series.${SeriesEntry._ID}=game.${GameEntry.COLUMN_SERIES_ID} " +
- "WHERE ${LeagueEntry.COLUMN_BOWLER_ID}=? " +
- orderQueryBy)
-
- val cursor = database.rawQuery(rawLeagueEventQuery, arrayOf(bowler.id.toString()))
- var lastId: Long = -1
- var leagueNumberOfGames = 0
- var leagueTotal = 0
-
- fun isCurrentLeagueEvent(cursor: Cursor): Boolean {
- return cursor.getInt(cursor.getColumnIndex(LeagueEntry.COLUMN_IS_EVENT)) == 1
- }
-
- fun buildLeagueFromCursor(cursor: Cursor): League {
- val id = cursor.getLong(cursor.getColumnIndex("lid"))
- val name = cursor.getString(cursor.getColumnIndex(LeagueEntry.COLUMN_LEAGUE_NAME))
- val additionalPinfall = cursor.getInt(cursor.getColumnIndex(LeagueEntry.COLUMN_ADDITIONAL_PINFALL))
- val additionalGames = cursor.getInt(cursor.getColumnIndex(LeagueEntry.COLUMN_ADDITIONAL_GAMES))
- val gameHighlight = cursor.getInt(cursor.getColumnIndex(LeagueEntry.COLUMN_GAME_HIGHLIGHT))
- val seriesHighlight = cursor.getInt(cursor.getColumnIndex(LeagueEntry.COLUMN_SERIES_HIGHLIGHT))
- val gamesPerSeries = cursor.getInt(cursor.getColumnIndex(LeagueEntry.COLUMN_NUMBER_OF_GAMES))
- val average = Average.getAdjustedAverage(leagueTotal, leagueNumberOfGames, additionalPinfall, additionalGames)
- val isEvent = isCurrentLeagueEvent(cursor)
-
- return League(
- bowler,
- id,
- name,
- average,
- isEvent,
- gamesPerSeries,
- additionalPinfall,
- additionalGames,
- gameHighlight,
- seriesHighlight
- )
- }
-
- if (cursor.moveToFirst()) {
- while (!cursor.isAfterLast) {
- val newId = cursor.getLong(cursor.getColumnIndex("lid"))
- if (newId != lastId && lastId != -1L) {
- cursor.moveToPrevious()
-
- val isEvent = isCurrentLeagueEvent(cursor)
- if ((includeEvents && isEvent) || (includeLeagues && !isEvent)) {
- leagues.add(buildLeagueFromCursor(cursor))
- }
-
- leagueTotal = 0
- leagueNumberOfGames = 0
-
- cursor.moveToNext()
- }
- val score = cursor.getShort(cursor.getColumnIndex(GameEntry.COLUMN_SCORE))
- if (score > 0) {
- leagueTotal += score.toInt()
- leagueNumberOfGames++
- }
-
- lastId = newId
- cursor.moveToNext()
- }
- cursor.moveToPrevious()
-
- val isEvent = isCurrentLeagueEvent(cursor)
- if ((includeEvents && isEvent) || (includeLeagues && !isEvent)) {
- leagues.add(buildLeagueFromCursor(cursor))
- }
- }
-
- cursor.close()
-
- leagues
- }
- }
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/leagues/LeagueDialog.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/leagues/LeagueDialog.kt
deleted file mode 100644
index cf5443183..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/leagues/LeagueDialog.kt
+++ /dev/null
@@ -1,460 +0,0 @@
-package ca.josephroque.bowlingcompanion.leagues
-
-import android.app.Dialog
-import android.content.Context
-import android.os.Bundle
-import android.support.v4.app.DialogFragment
-import android.support.v7.app.AlertDialog
-import android.text.Editable
-import android.text.TextWatcher
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.view.Window
-import android.view.inputmethod.EditorInfo
-import android.view.inputmethod.InputMethodManager
-import android.widget.EditText
-import ca.josephroque.bowlingcompanion.App
-import ca.josephroque.bowlingcompanion.R
-import ca.josephroque.bowlingcompanion.bowlers.Bowler
-import ca.josephroque.bowlingcompanion.common.Android
-import ca.josephroque.bowlingcompanion.common.ThousandsTextWatcher
-import ca.josephroque.bowlingcompanion.common.fragments.BaseDialogFragment
-import ca.josephroque.bowlingcompanion.utils.Analytics
-import ca.josephroque.bowlingcompanion.utils.BCError
-import ca.josephroque.bowlingcompanion.utils.Color
-import ca.josephroque.bowlingcompanion.utils.safeLet
-import kotlinx.android.synthetic.main.dialog_league.checkbox_additional_games as additionalGamesCheckbox
-import kotlinx.android.synthetic.main.dialog_league.input_additional_games as additionalGamesInput
-import kotlinx.android.synthetic.main.dialog_league.input_additional_pinfall as additionalPinfallInput
-import kotlinx.android.synthetic.main.dialog_league.input_game_highlight as gameHighlightInput
-import kotlinx.android.synthetic.main.dialog_league.input_series_highlight as seriesHighlightInput
-import kotlinx.android.synthetic.main.dialog_league.input_name as nameInput
-import kotlinx.android.synthetic.main.dialog_league.input_number_of_games as numberOfGamesInput
-import kotlinx.android.synthetic.main.dialog_league.layout_additional_games_details as additionalGamesLayoutDetails
-import kotlinx.android.synthetic.main.dialog_league.layout_additional_games as additionalGamesLayout
-import kotlinx.android.synthetic.main.dialog_league.layout_delete_league as deleteLeagueLayout
-import kotlinx.android.synthetic.main.dialog_league.layout_highlights as highlightsLayout
-import kotlinx.android.synthetic.main.dialog_league.layout_new_league_event as newLeagueEventLayout
-import kotlinx.android.synthetic.main.dialog_league.toolbar_league as leagueToolbar
-import kotlinx.android.synthetic.main.dialog_league.view.*
-import kotlinx.coroutines.experimental.launch
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Dialog to create a new league.
- */
-class LeagueDialog : BaseDialogFragment() {
-
- companion object {
- @Suppress("unused")
- private const val TAG = "LeagueDialog"
-
- private const val ARG_BOWLER = "${TAG}_bowler"
- private const val ARG_LEAGUE = "${TAG}_league"
- private const val ARG_IS_EVENT = "${TAG}_is_event"
-
- fun newInstance(bowler: Bowler, league: League?, isEvent: Boolean): LeagueDialog {
- val dialog = LeagueDialog()
- dialog.arguments = Bundle().apply {
- putParcelable(ARG_BOWLER, bowler)
- putBoolean(ARG_IS_EVENT, isEvent)
- league?.let { putParcelable(ARG_LEAGUE, league) }
- }
- return dialog
- }
- }
-
- private var bowler: Bowler? = null
- private var league: League? = null
- private var isEvent: Boolean = false
- private var delegate: LeagueDialogDelegate? = null
-
- private var onClickListener: View.OnClickListener? = View.OnClickListener {
- val clicked = it ?: return@OnClickListener
- when (clicked.id) {
- R.id.radio_event -> {
- isEvent = true
- setLeagueOptionsVisible()
- setImeOptions()
- }
- R.id.radio_league -> {
- isEvent = false
- setLeagueOptionsVisible()
- setImeOptions()
- }
- R.id.btn_delete -> {
- safeLet(context, league) { context, league ->
- AlertDialog.Builder(context)
- .setTitle(String.format(context.resources.getString(R.string.query_delete_item), league.name))
- .setMessage(R.string.dialog_delete_item_message)
- .setPositiveButton(R.string.delete) { _, _ ->
- delegate?.onDeleteLeague(league)
- dismiss()
- }
- .setNegativeButton(R.string.cancel, null)
- .show()
- }
- }
- }
- }
-
- // MARK: Lifecycle functions
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setStyle(DialogFragment.STYLE_NORMAL, R.style.Dialog)
- }
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View? {
- arguments?.let {
- bowler = it.getParcelable(ARG_BOWLER)
- league = it.getParcelable(ARG_LEAGUE)
- isEvent = it.getBoolean(ARG_IS_EVENT)
- }
-
- val rootView = inflater.inflate(R.layout.dialog_league, container, false)
-
- league?.let { resetInputs(it, rootView) }
- setupToolbar(rootView)
- setupLeagueTypeInput(rootView)
- setupNameInput(rootView)
- setupAdditionalGamesInput(rootView)
-
- return rootView
- }
-
- override fun onAttach(context: Context?) {
- super.onAttach(context)
- val parent = parentFragment as? LeagueDialogDelegate ?: throw RuntimeException("${parentFragment!!} must implement LeagueDialogDelegate")
- delegate = parent
- }
-
- override fun onDetach() {
- super.onDetach()
- delegate = null
- onClickListener = null
- }
-
- override fun onStart() {
- super.onStart()
- dialog.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
-
- // Requesting input focus and showing keyboard
- nameInput.requestFocus()
- val imm = activity?.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
- imm.showSoftInput(nameInput, InputMethodManager.SHOW_IMPLICIT)
-
- league?.let {
- deleteLeagueLayout.visibility = View.VISIBLE
- newLeagueEventLayout.visibility = View.GONE
- numberOfGamesInput.isEnabled = false
-
- if (it.isEvent) {
- additionalGamesLayout?.visibility = View.GONE
- highlightsLayout?.visibility = View.GONE
- } else {
- highlightsLayout?.visibility = View.VISIBLE
- if (it.additionalPinfall > 0 || it.additionalGames > 0) {
- additionalGamesCheckbox?.isChecked = true
- additionalGamesLayoutDetails.visibility = View.VISIBLE
- additionalGamesInput.setText(it.additionalGames.toString())
- additionalPinfallInput.setText(it.additionalPinfall.toString())
- } else {
- additionalGamesCheckbox?.isChecked = false
- additionalGamesLayoutDetails.visibility = View.GONE
- additionalGamesInput.setText("")
- additionalPinfallInput.setText("")
- }
- }
- }
-
- nameInput.text?.let { nameInput.setSelection(it.length) }
- setImeOptions()
- setLeagueOptionsVisible()
- updateSaveButton()
- }
-
- override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
- val dialog = super.onCreateDialog(savedInstanceState)
- dialog.requestWindowFeature(Window.FEATURE_NO_TITLE)
- return dialog
- }
-
- override fun dismiss() {
- App.hideSoftKeyBoard(activity!!)
- activity?.supportFragmentManager?.popBackStack()
- super.dismiss()
- }
-
- // MARK: Private functions
-
- private fun setupToolbar(rootView: View) {
- if (league == null) {
- rootView.toolbar_league.setTitle(R.string.new_league)
- } else {
- rootView.toolbar_league.setTitle(R.string.edit_league)
- }
-
- rootView.btn_delete.setOnClickListener(onClickListener)
- rootView.toolbar_league.apply {
- inflateMenu(R.menu.dialog_league)
- menu.findItem(R.id.action_save).isEnabled = league?.name?.isNotEmpty() == true
- setNavigationIcon(R.drawable.ic_dismiss)
- setNavigationOnClickListener {
- dismiss()
- }
- setOnMenuItemClickListener {
- when (it.itemId) {
- R.id.action_save -> {
- saveLeague()
- true
- }
- else -> super.onOptionsItemSelected(it)
- }
- }
- }
- }
-
- private fun setupLeagueTypeInput(rootView: View) {
- rootView.radio_league.setOnClickListener(onClickListener)
- rootView.radio_event.setOnClickListener(onClickListener)
-
- rootView.radio_league.isChecked = !isEvent
- rootView.radio_event.isChecked = isEvent
- }
-
- private fun setupNameInput(rootView: View) {
- rootView.input_name.addTextChangedListener(object : TextWatcher {
- override fun afterTextChanged(s: Editable?) {}
- override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
- override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
- updateSaveButton()
- }
- })
- }
-
- private fun setupAdditionalGamesInput(rootView: View) {
- val additionalGames = rootView.input_additional_games
- val textWatcherGames = object : ThousandsTextWatcher(",", ".") {
- override fun afterTextChanged(s: Editable?) {
- super.afterTextChanged(s)
- updateSaveButton()
- }
- }
- additionalGames.addTextChangedListener(textWatcherGames)
-
- val additionalPinfall = rootView.input_additional_pinfall
- val textWatcherPinfall = object : ThousandsTextWatcher(",", ".") {
- override fun afterTextChanged(s: Editable?) {
- super.afterTextChanged(s)
- updateSaveButton()
- }
- }
- additionalPinfall.addTextChangedListener(textWatcherPinfall)
-
- rootView.checkbox_additional_games.setOnCheckedChangeListener { _, isChecked ->
- additionalGamesLayoutDetails.visibility = if (isChecked) View.VISIBLE else View.GONE
- setImeOptions()
- }
- }
-
- private fun canSave(): Boolean {
- val name = nameInput.text.toString()
- val hasAdditional = additionalGamesCheckbox.isChecked
- val additionalGames = additionalGamesInput.text.toString()
- val additionalPinfall = additionalPinfallInput.text.toString()
-
- return (isEvent && name.isNotEmpty()) ||
- (!isEvent && name.isNotEmpty() && ((hasAdditional && additionalPinfall.isNotEmpty() && additionalGames.isNotEmpty()) || !hasAdditional))
- }
-
- private fun resetInputs(league: League, rootView: View? = null) {
- val nameInput = rootView?.input_name ?: this.nameInput
- val numberOfGamesInput = rootView?.input_number_of_games ?: this.numberOfGamesInput
- val additionalGamesCheckbox = rootView?.checkbox_additional_games ?: this.additionalGamesCheckbox
- val gameHighlightInput = rootView?.input_game_highlight ?: this.gameHighlightInput
- val seriesHighlightInput = rootView?.input_series_highlight ?: this.seriesHighlightInput
- val additionalGamesInput = rootView?.input_additional_games ?: this.additionalGamesInput
- val additionalPinfallInput = rootView?.input_additional_pinfall ?: this.additionalPinfallInput
-
- nameInput.setText(league.name)
- numberOfGamesInput.setText(league.gamesPerSeries.toString())
- additionalGamesCheckbox.isChecked = league.additionalPinfall > 0 || league.additionalGames > 0
- gameHighlightInput.setText(league.gameHighlight.toString())
- seriesHighlightInput.setText(league.seriesHighlight.toString())
- additionalGamesInput.setText(league.additionalGames.toString())
- additionalPinfallInput.setText(league.additionalPinfall.toString())
- }
-
- private fun setImeOptions() {
- nameInput.imeOptions = if (league == null || !isEvent) {
- EditorInfo.IME_FLAG_NO_FULLSCREEN or EditorInfo.IME_ACTION_NEXT
- } else {
- EditorInfo.IME_FLAG_NO_FULLSCREEN or EditorInfo.IME_ACTION_DONE
- }
-
- numberOfGamesInput.imeOptions = if (!isEvent) {
- EditorInfo.IME_FLAG_NO_FULLSCREEN or EditorInfo.IME_ACTION_NEXT
- } else {
- EditorInfo.IME_FLAG_NO_FULLSCREEN or EditorInfo.IME_ACTION_DONE
- }
-
- seriesHighlightInput.imeOptions = if (!isEvent && additionalGamesCheckbox.isChecked) {
- EditorInfo.IME_FLAG_NO_FULLSCREEN or EditorInfo.IME_ACTION_NEXT
- } else {
- EditorInfo.IME_FLAG_NO_FULLSCREEN or EditorInfo.IME_ACTION_DONE
- }
-
- val activity = activity ?: return
- val focusedField = view?.findFocus() as? EditText ?: return
-
- App.hideSoftKeyBoard(activity)
- val imm = activity.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
- imm.restartInput(focusedField)
- App.showSoftKeyBoard(activity)
- }
-
- private fun setLeagueOptionsVisible() {
- if (isEvent) {
- additionalGamesLayout?.visibility = View.GONE
- highlightsLayout?.visibility = View.GONE
- } else {
- additionalGamesLayout?.visibility = View.VISIBLE
- highlightsLayout?.visibility = View.VISIBLE
- }
- }
-
- private fun updateSaveButton() {
- val saveButton = leagueToolbar?.menu?.findItem(R.id.action_save)
- if (canSave()) {
- saveButton?.isEnabled = true
- saveButton?.icon?.alpha = Color.ALPHA_ENABLED
- } else {
- saveButton?.isEnabled = false
- saveButton?.icon?.alpha = Color.ALPHA_DISABLED
- }
- }
-
- private fun saveLeague() {
- val bowler = bowler ?: return
-
- launch(Android) {
- this@LeagueDialog.context?.let { context ->
- if (canSave()) {
- val name = nameInput.text.toString()
- val numberOfGamesStr = numberOfGamesInput.text.toString()
- val numberOfGames: Int
-
- val hasAdditional = additionalGamesCheckbox.isChecked
- val additionalGamesStr = additionalGamesInput.text.toString().replace(",", "")
- val additionalPinfallStr = additionalPinfallInput.text.toString().replace(",", "")
- var additionalPinfall = 0
- var additionalGames = 0
-
- val gameHighlightStr = gameHighlightInput.text.toString().replace(",", "")
- val seriesHighlightStr = seriesHighlightInput.text.toString().replace(",", "")
- var gameHighlight = 0
- var seriesHighlight = 0
-
- try {
- numberOfGames = numberOfGamesStr.toInt()
- } catch (ex: NumberFormatException) {
- BCError(
- R.string.issue_saving_league,
- R.string.error_league_number_of_games_invalid,
- BCError.Severity.Warning
- ).show(context)
- return@launch
- }
-
- if (hasAdditional && additionalGamesStr.isNotEmpty() && additionalPinfallStr.isNotEmpty()) {
- try {
- additionalPinfall = additionalPinfallStr.toInt()
- additionalGames = additionalGamesStr.toInt()
- } catch (ex: NumberFormatException) {
- BCError(
- R.string.issue_saving_league,
- R.string.error_league_additional_info_invalid,
- BCError.Severity.Warning
- ).show(context)
- return@launch
- }
- }
-
- if (!isEvent && gameHighlightStr.isNotEmpty() && seriesHighlightStr.isNotEmpty()) {
- try {
- gameHighlight = gameHighlightStr.toInt()
- seriesHighlight = seriesHighlightStr.toInt()
- } catch (ex: NumberFormatException) {
- BCError(
- R.string.issue_saving_league,
- R.string.error_league_highlight_invalid,
- BCError.Severity.Warning
- ).show(context)
- return@launch
- }
- }
-
- val oldLeague = league
- val (newLeague, error) = if (oldLeague != null) {
- League.save(
- context = context,
- id = oldLeague.id,
- bowler = oldLeague.bowler,
- name = name,
- average = oldLeague.average,
- isEvent = oldLeague.isEvent,
- gamesPerSeries = oldLeague.gamesPerSeries,
- additionalPinfall = additionalPinfall,
- additionalGames = additionalGames,
- gameHighlight = gameHighlight,
- seriesHighlight = seriesHighlight
- ).await()
- } else {
- League.save(
- context = context,
- id = -1,
- bowler = bowler,
- name = name,
- isEvent = isEvent,
- gamesPerSeries = numberOfGames,
- additionalPinfall = additionalPinfall,
- additionalGames = additionalGames,
- gameHighlight = gameHighlight,
- seriesHighlight = seriesHighlight
- ).await()
- }
-
- if (error != null) {
- error.show(context)
-
- league?.let { resetInputs(it) }
- setImeOptions()
- } else if (newLeague != null) {
- dismiss()
- delegate?.onFinishLeague(newLeague)
-
- if (oldLeague == null) {
- Analytics.trackCreateLeague(isEvent, numberOfGames, hasAdditional)
- } else {
- Analytics.trackEditLeague()
- }
- }
- }
- }
- }
- }
-
- // MARK: LeagueDialogDelegate
-
- interface LeagueDialogDelegate {
- fun onFinishLeague(league: League)
- fun onDeleteLeague(league: League)
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/leagues/LeagueListFragment.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/leagues/LeagueListFragment.kt
deleted file mode 100644
index 7cc77bb72..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/leagues/LeagueListFragment.kt
+++ /dev/null
@@ -1,210 +0,0 @@
-package ca.josephroque.bowlingcompanion.leagues
-
-import android.content.Context
-import android.graphics.Color
-import android.os.Bundle
-import android.preference.PreferenceManager
-import android.support.v7.app.AlertDialog
-import android.view.LayoutInflater
-import android.view.Menu
-import android.view.MenuInflater
-import android.view.MenuItem
-import android.view.View
-import android.view.ViewGroup
-import ca.josephroque.bowlingcompanion.R
-import ca.josephroque.bowlingcompanion.bowlers.Bowler
-import ca.josephroque.bowlingcompanion.common.adapters.NameAverageRecyclerViewAdapter
-import ca.josephroque.bowlingcompanion.common.fragments.ListFragment
-import ca.josephroque.bowlingcompanion.utils.Analytics
-import ca.josephroque.bowlingcompanion.utils.BCError
-import ca.josephroque.bowlingcompanion.utils.Preferences
-import kotlinx.coroutines.experimental.CommonPool
-import kotlinx.coroutines.experimental.Deferred
-import kotlinx.coroutines.experimental.async
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * A fragment representing a list of leagues.
- */
-class LeagueListFragment : ListFragment>() {
-
- companion object {
- @Suppress("unused")
- private const val TAG = "LeagueListFragment"
-
- private const val ARG_BOWLER = "${TAG}_bowler"
- private const val ARG_BOWLER_ID = "${TAG}_bowler_id"
- private const val ARG_SHOW = "${TAG}_show"
- private const val ARG_SINGLE_SELECT_MODE = "${TAG}_single_select"
-
- fun newInstance(bowler: Bowler, show: Show, singleSelectMode: Boolean = false): LeagueListFragment {
- val fragment = LeagueListFragment()
- fragment.arguments = Bundle().apply {
- putParcelable(ARG_BOWLER, bowler)
- putInt(ARG_SHOW, show.ordinal)
- putBoolean(ARG_SINGLE_SELECT_MODE, singleSelectMode)
- }
- return fragment
- }
-
- fun newInstance(bowlerId: Long, show: Show, singleSelectMode: Boolean = false): LeagueListFragment {
- val fragment = LeagueListFragment()
- fragment.arguments = Bundle().apply {
- putLong(ARG_BOWLER_ID, bowlerId)
- putInt(ARG_SHOW, show.ordinal)
- putBoolean(ARG_SINGLE_SELECT_MODE, singleSelectMode)
- }
- return fragment
- }
-
- enum class Show {
- Events,
- Leagues,
- Both;
-
- companion object {
- private val map = Show.values().associateBy(Show::ordinal)
- fun fromInt(type: Int) = map[type]
- }
- }
- }
-
- private var bowler: Bowler? = null
- private var bowlerId: Long? = null
- private var show: Show = Show.Both
- private var singleSelectMode: Boolean = false
-
- override val emptyViewImage: Int
- get() = if (show == Show.Events) R.drawable.empty_view_events else R.drawable.empty_view_leagues
- override val emptyViewText: Int
- get() = if (show == Show.Events) R.string.empty_view_events else R.string.empty_view_leagues
-
- // MARK: Lifecycle functions
-
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
- arguments?.let {
- bowler = it.getParcelable(ARG_BOWLER)
- bowlerId = it.getLong(ARG_BOWLER_ID)
- show = Show.fromInt(it.getInt(ARG_SHOW))!!
- singleSelectMode = it.getBoolean(ARG_SINGLE_SELECT_MODE)
- }
-
- setHasOptionsMenu(!singleSelectMode)
-
- return super.onCreateView(inflater, container, savedInstanceState)
- }
-
- override fun onAttach(context: Context?) {
- super.onAttach(context)
- delegate = if (!singleSelectMode) {
- parentFragment as? ListFragmentDelegate ?: return
- } else {
- null
- }
- }
-
- override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
- super.onCreateOptionsMenu(menu, inflater)
- inflater.inflate(R.menu.fragment_leagues, menu)
- }
-
- override fun onOptionsItemSelected(item: MenuItem): Boolean {
- return when (item.itemId) {
- R.id.action_sort_by -> {
- showSortByDialog()
- true
- }
- else -> super.onOptionsItemSelected(item)
- }
- }
-
- // MARK: BaseFragment
-
- override fun updateToolbarTitle() {
- // Intentionally left blank
- }
-
- // MARK: ListFragment
-
- override fun buildAdapter(): NameAverageRecyclerViewAdapter {
- val adapter = NameAverageRecyclerViewAdapter(emptyList(), this)
- adapter.swipeable = !singleSelectMode
- adapter.longPressable = !singleSelectMode
- adapter.buildImageResource = { item, _ ->
- if (item.isEvent) {
- Pair(R.drawable.ic_event, Color.BLACK)
- } else {
- Pair(R.drawable.ic_league, Color.BLACK)
- }
- }
- return adapter
- }
-
- override fun fetchItems(): Deferred> {
- return async(CommonPool) {
- this@LeagueListFragment.context?.let { context ->
- val bowlerId = bowlerId
- if (bowler == null && bowlerId != null) {
- bowler = Bowler.fetch(context, bowlerId).await()
- }
-
- bowler?.let {
- when (show) {
- Show.Events -> return@async it.fetchEvents(context).await()
- Show.Leagues -> return@async it.fetchLeagues(context).await()
- Show.Both -> return@async it.fetchLeaguesAndEvents(context).await()
- }
- }
- }
-
- mutableListOf()
- }
- }
-
- // MARK: Private functions
-
- private fun showSortByDialog() {
- context?.let {
- AlertDialog.Builder(it)
- .setTitle(it.resources.getString(R.string.sort_items))
- .setItems(R.array.league_sort_options) { _, which: Int ->
- val order = League.Companion.Sort.fromInt(which)
- order?.let { sort ->
- PreferenceManager.getDefaultSharedPreferences(context)
- .edit()
- .putInt(Preferences.LEAGUE_SORT_ORDER, sort.ordinal)
- .apply()
- refreshList()
-
- Analytics.trackSortedLeagues(order)
- }
- }
- .show()
- }
- }
-
- // MARK: AdapterDelegate
-
- override fun onItemDelete(item: League) {
- // Disable deleting the practice league
- if (item.isPractice) {
- context?.let {
- BCError(
- R.string.error_deleting_league,
- R.string.error_cannot_delete_practice_league,
- BCError.Severity.Warning
- ).show(it)
- }
- return
- }
-
- super.onItemDelete(item)
- }
-
- override fun onItemLongClick(item: League) {
- // Disable long pressing the practice league
- if (item.isPractice) return
- super.onItemLongClick(item)
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/matchplay/MatchPlay.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/matchplay/MatchPlay.kt
deleted file mode 100644
index 89ff099b4..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/matchplay/MatchPlay.kt
+++ /dev/null
@@ -1,60 +0,0 @@
-package ca.josephroque.bowlingcompanion.matchplay
-
-import android.os.Parcel
-import ca.josephroque.bowlingcompanion.common.interfaces.IIdentifiable
-import ca.josephroque.bowlingcompanion.common.interfaces.KParcelable
-import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Data about Match Play for a single game.
- */
-class MatchPlay(
- val gameId: Long,
- override val id: Long,
- var opponentName: String,
- var opponentScore: Int,
- var result: MatchPlayResult
-) : IIdentifiable, KParcelable {
-
- // MARK: Constructors
-
- private constructor(p: Parcel): this(
- gameId = p.readLong(),
- id = p.readLong(),
- opponentName = p.readString()!!,
- opponentScore = p.readInt(),
- result = MatchPlayResult.fromInt(p.readInt())!!
- )
-
- private constructor(other: MatchPlay): this(
- gameId = other.gameId,
- id = other.id,
- opponentName = other.opponentName,
- opponentScore = other.opponentScore,
- result = other.result
- )
-
- // MARK: Parcelable
-
- override fun writeToParcel(dest: Parcel, flags: Int) = with(dest) {
- writeLong(gameId)
- writeLong(id)
- writeString(opponentName)
- writeInt(opponentScore)
- writeInt(result.ordinal)
- }
-
- fun deepCopy(): MatchPlay {
- return MatchPlay(this)
- }
-
- companion object {
- @Suppress("unused")
- private const val TAG = "MatchPlay"
-
- @Suppress("unused")
- @JvmField val CREATOR = parcelableCreator(::MatchPlay)
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/matchplay/MatchPlayResult.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/matchplay/MatchPlayResult.kt
deleted file mode 100644
index 95220ee44..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/matchplay/MatchPlayResult.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-package ca.josephroque.bowlingcompanion.matchplay
-
-import ca.josephroque.bowlingcompanion.R
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Possible match play results of a game.
- */
-enum class MatchPlayResult {
- NONE, WON, LOST, TIED;
-
- companion object {
- private val map = MatchPlayResult.values().associateBy(MatchPlayResult::ordinal)
- fun fromInt(type: Int) = map[type]
- }
-
- fun getIcon(): Int {
- return when (this) {
- NONE -> R.drawable.ic_match_play_none
- WON -> R.drawable.ic_match_play_won
- LOST -> R.drawable.ic_match_play_lost
- TIED -> R.drawable.ic_match_play_tied
- }
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/matchplay/MatchPlaySheet.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/matchplay/MatchPlaySheet.kt
deleted file mode 100644
index 92726de25..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/matchplay/MatchPlaySheet.kt
+++ /dev/null
@@ -1,143 +0,0 @@
-package ca.josephroque.bowlingcompanion.matchplay
-
-import android.app.Dialog
-import android.content.DialogInterface
-import android.os.Bundle
-import android.support.design.widget.BottomSheetBehavior
-import android.support.design.widget.BottomSheetDialog
-import android.support.v4.app.DialogFragment
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import ca.josephroque.bowlingcompanion.R
-import ca.josephroque.bowlingcompanion.common.fragments.BaseBottomSheetDialogFragment
-import ca.josephroque.bowlingcompanion.games.Game
-import ca.josephroque.bowlingcompanion.utils.Analytics
-import ca.josephroque.bowlingcompanion.utils.BCError
-import kotlinx.android.synthetic.main.sheet_match_play.view.*
-import kotlinx.android.synthetic.main.sheet_match_play.input_opponent_name as opponentName
-import kotlinx.android.synthetic.main.sheet_match_play.input_opponent_score as opponentScore
-import kotlinx.android.synthetic.main.sheet_match_play.radio_match_play_lost as matchPlayLost
-import kotlinx.android.synthetic.main.sheet_match_play.radio_match_play_won as matchPlayWon
-import kotlinx.android.synthetic.main.sheet_match_play.radio_match_play_tied as matchPlayTied
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Displays settings for the match play results of a game.
- */
-class MatchPlaySheet : BaseBottomSheetDialogFragment() {
-
- companion object {
- @Suppress("unused")
- private const val TAG = "MatchPlaySheet"
-
- const val FRAGMENT_TAG = TAG
- const val ARG_MATCH_PLAY = "${TAG}_match_play"
-
- fun newInstance(matchPlay: MatchPlay): MatchPlaySheet {
- val fragment = MatchPlaySheet()
- fragment.arguments = Bundle().apply { putParcelable(ARG_MATCH_PLAY, matchPlay) }
- return fragment
- }
- }
-
- // MARK: Lifecycle functions
-
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
- val rootView = inflater.inflate(R.layout.sheet_match_play, container, false)
-
- if (savedInstanceState == null) {
- val initialMatchPlay: MatchPlay = arguments?.getParcelable(ARG_MATCH_PLAY)!!
- populateInitialInputs(rootView, initialMatchPlay)
- }
-
- return rootView
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setStyle(DialogFragment.STYLE_NORMAL, R.style.BottomSheetDialogStyle)
- }
-
- override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
- val dialog = super.onCreateDialog(savedInstanceState)
- dialog.setOnShowListener {
- if (it is BottomSheetDialog) {
- val bottomSheet = it.findViewById(android.support.design.R.id.design_bottom_sheet)
- val bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet)
- bottomSheetBehavior.setBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
-
- override fun onStateChanged(bottomSheet: View, newState: Int) {
- if (newState == BottomSheetBehavior.STATE_HIDDEN) {
- handleUserExit()
- dismiss()
- }
- }
-
- override fun onSlide(bottomSheet: View, slideOffset: Float) {}
- })
- }
- }
-
- return dialog
- }
-
- override fun onCancel(dialog: DialogInterface?) {
- super.onCancel(dialog)
- handleUserExit()
- }
-
- // MARK: Private functions
-
- private fun populateInitialInputs(rootView: View, initialMatchPlay: MatchPlay) {
- rootView.input_opponent_name.setText(initialMatchPlay.opponentName)
- rootView.input_opponent_score.setText(initialMatchPlay.opponentScore.toString())
- rootView.radio_match_play_none.isChecked = initialMatchPlay.result == MatchPlayResult.NONE
- rootView.radio_match_play_lost.isChecked = initialMatchPlay.result == MatchPlayResult.LOST
- rootView.radio_match_play_tied.isChecked = initialMatchPlay.result == MatchPlayResult.TIED
- rootView.radio_match_play_won.isChecked = initialMatchPlay.result == MatchPlayResult.WON
- }
-
- private fun handleUserExit() {
- var inputValid = true
- val name = opponentName.text.toString()
- val matchPlayResult = when {
- matchPlayWon.isChecked -> MatchPlayResult.WON
- matchPlayLost.isChecked -> MatchPlayResult.LOST
- matchPlayTied.isChecked -> MatchPlayResult.TIED
- else -> MatchPlayResult.NONE
- }
- var score: Int = -1
- try {
- score = opponentScore.text.toString().toInt()
- if (score < 0 || score > Game.MAX_SCORE) {
- throw NumberFormatException()
- }
- } catch (ex: NumberFormatException) {
- inputValid = false
- context?.let {
- BCError(
- R.string.issue_setting_match_play,
- R.string.error_match_play_score_invalid,
- BCError.Severity.Warning
- ).show(it)
- }
- }
-
- delegate?.getBottomSheetDelegate()?.onFinishedSettingMatchPlayResults(name, score, matchPlayResult, inputValid)
-
- Analytics.trackRecordMatchPlay()
- }
-
- // MARK: MatchPlaySheetDelegate
-
- interface MatchPlaySheetDelegate {
- fun onFinishedSettingMatchPlayResults(
- opponentName: String,
- opponentScore: Int,
- matchPlayResult: MatchPlayResult,
- inputValid: Boolean
- )
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/onboarding/SplashActivity.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/onboarding/SplashActivity.kt
deleted file mode 100644
index 3b6c240f1..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/onboarding/SplashActivity.kt
+++ /dev/null
@@ -1,145 +0,0 @@
-package ca.josephroque.bowlingcompanion.onboarding
-
-import ca.josephroque.bowlingcompanion.common.activities.BaseActivity
-import android.content.Intent
-import android.os.Bundle
-import android.preference.PreferenceManager
-import android.support.v4.app.Fragment
-import android.support.v4.app.FragmentManager
-import android.support.v4.app.FragmentPagerAdapter
-import android.support.v4.view.ViewPager
-import android.view.View
-import ca.josephroque.bowlingcompanion.NavigationActivity
-import ca.josephroque.bowlingcompanion.R
-import kotlinx.android.synthetic.main.activity_splash.tv_next as tvNext
-import kotlinx.android.synthetic.main.activity_splash.tv_prev as tvPrev
-import kotlinx.android.synthetic.main.activity_splash.tv_skip as tvSkip
-import kotlinx.android.synthetic.main.activity_splash.view_pager as viewPager
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Activity to introduce the user to the app.
- */
-class SplashActivity : BaseActivity() {
-
- companion object {
- @Suppress("unused")
- private const val TAG = "SplashActivity"
-
- private const val TUTORIAL_WATCHED = "tutorial_watched"
- }
-
- private var tutorialWatched: Boolean
- get() = PreferenceManager.getDefaultSharedPreferences(this).getBoolean(TUTORIAL_WATCHED, false)
- set(value) = PreferenceManager.getDefaultSharedPreferences(this).edit().putBoolean(TUTORIAL_WATCHED, value).apply()
-
- private val onClickListener = View.OnClickListener {
- when (it.id) {
- R.id.tv_next -> {
- if (viewPager.currentItem < TutorialFragment.Companion.TutorialItem.values().lastIndex) {
- viewPager.currentItem += 1
- } else {
- openMainActivity()
- }
- }
- R.id.tv_prev -> {
- if (viewPager.currentItem > 0) {
- viewPager.currentItem -= 1
- }
- }
- R.id.tv_skip -> {
- openMainActivity()
- }
- }
-
- updateToolbar()
- }
-
- private val onPageChangeListener = object : ViewPager.OnPageChangeListener {
- override fun onPageScrollStateChanged(state: Int) {
- // Intentionally left blank
- }
-
- override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
- // Intentionally left blank
- }
-
- override fun onPageSelected(position: Int) {
- updateToolbar()
- }
- }
-
- // MARK: Lifecycle functions
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_splash)
-
- if (tutorialWatched) {
- openMainActivity()
- return
- }
-
- tutorialWatched = true
- setupViewPager()
- setupNavigation()
- updateToolbar()
- }
-
- override fun onDestroy() {
- super.onDestroy()
- viewPager.removeOnPageChangeListener(onPageChangeListener)
- }
-
- override fun onResume() {
- super.onResume()
- updateToolbar()
- }
-
- // MARK: Private functions
-
- private fun updateToolbar() {
- tvPrev.visibility = if (viewPager.currentItem > 0) {
- View.VISIBLE
- } else {
- View.GONE
- }
-
- tvNext.setText(if (viewPager.currentItem < TutorialFragment.Companion.TutorialItem.values().lastIndex) {
- R.string.next
- } else {
- R.string.done
- })
- }
-
- private fun setupViewPager() {
- val adapter = SplashPagerAdapter(supportFragmentManager)
- viewPager.adapter = adapter
- viewPager.addOnPageChangeListener(onPageChangeListener)
- }
-
- private fun setupNavigation() {
- tvPrev.setOnClickListener(onClickListener)
- tvNext.setOnClickListener(onClickListener)
- tvSkip.setOnClickListener(onClickListener)
- }
-
- private fun openMainActivity() {
- val mainActivityIntent = Intent(this, NavigationActivity::class.java)
- startActivity(mainActivityIntent)
- finish()
- }
-
- // MARK: SplashPagerAdapter
-
- class SplashPagerAdapter(fm: FragmentManager) : FragmentPagerAdapter(fm) {
- override fun getItem(position: Int): Fragment {
- return TutorialFragment.newInstance(position)
- }
-
- override fun getCount(): Int {
- return TutorialFragment.Companion.TutorialItem.values().size
- }
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/onboarding/TutorialFragment.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/onboarding/TutorialFragment.kt
deleted file mode 100644
index f81e40895..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/onboarding/TutorialFragment.kt
+++ /dev/null
@@ -1,64 +0,0 @@
-package ca.josephroque.bowlingcompanion.onboarding
-
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import ca.josephroque.bowlingcompanion.R
-import ca.josephroque.bowlingcompanion.common.fragments.BaseFragment
-import kotlinx.android.synthetic.main.fragment_tutorial.iv_tutorial as ivTutorial
-import kotlinx.android.synthetic.main.fragment_tutorial.tv_tutorial as tvTutorial
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Present an instructional message to the user.
- */
-class TutorialFragment : BaseFragment() {
-
- companion object {
- @Suppress("unused")
- private const val TAG = "TutorialFragment"
-
- private const val ARG_TUTORIAL_ITEM = "tutorial_item"
-
- enum class TutorialItem(val title: Int, val image: Int) {
- Welcome(R.string.tutorial_welcome, R.drawable.pin_enabled),
- Bowlers(R.string.tutorial_bowlers, R.drawable.tutorial_bowlers),
- Recording(R.string.tutorial_recording, R.drawable.tutorial_recording),
- Statistics(R.string.tutorial_statistics, R.drawable.tutorial_statistics);
-
- companion object {
- private val map = TutorialItem.values().associateBy(TutorialItem::ordinal)
- fun fromInt(type: Int) = map[type]
- }
- }
-
- fun newInstance(page: Int): TutorialFragment {
- val fragment = TutorialFragment()
- fragment.arguments = Bundle().apply { putInt(ARG_TUTORIAL_ITEM, page) }
- return fragment
- }
- }
-
- private lateinit var tutorialItem: TutorialItem
-
- // MARK: Lifecycle functions
-
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
- tutorialItem = TutorialItem.fromInt(arguments?.getInt(ARG_TUTORIAL_ITEM)!!)!!
- return inflater.inflate(R.layout.fragment_tutorial, container, false)
- }
-
- override fun onStart() {
- super.onStart()
- tvTutorial.setText(tutorialItem.title)
- ivTutorial.setImageResource(tutorialItem.image)
- }
-
- // MARK: BaseFragment
-
- override fun updateToolbarTitle() {
- // Intentionally left blank
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/scoring/Average.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/scoring/Average.kt
deleted file mode 100644
index 68d785847..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/scoring/Average.kt
+++ /dev/null
@@ -1,19 +0,0 @@
-package ca.josephroque.bowlingcompanion.scoring
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Provides utility methods for calculating averages.
- */
-object Average {
-
- @Suppress("unused")
- private const val TAG = "Average"
-
- fun getAdjustedAverage(trackedPinfall: Int, trackedGames: Int, additionalPinfall: Int, additionalGames: Int): Double {
- val totalPinfall = trackedPinfall + additionalPinfall
- val totalGames = trackedGames + additionalGames
-
- return if (totalGames > 0) totalPinfall.div(totalGames.toDouble()) else 0.0
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/scoring/Fouls.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/scoring/Fouls.kt
deleted file mode 100644
index d966ce99f..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/scoring/Fouls.kt
+++ /dev/null
@@ -1,52 +0,0 @@
-package ca.josephroque.bowlingcompanion.scoring
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Provides utility methods for scoring fouls.
- */
-object Fouls {
-
- @Suppress("unused")
- private const val TAG = "Fouls"
-
- /**
- * Gets the String representation of a number of fouls.
- * Historic implementation from pre-3.0.0, required for database format
- *
- * @param i integer representation of the number of fouls
- * @return string representation of the number of fouls
- */
- fun foulIntToString(i: Int): String {
- return when (i) {
- 24 -> "3"
- 25 -> "2"
- 26 -> "23"
- 27 -> "1"
- 28 -> "13"
- 29 -> "12"
- 30 -> "123"
- else -> "0"
- }
- }
-
- /**
- * Gets the int representation of a String of fouls in a frame.
- * Historic implementation from pre-3.0.0, required for database format
- *
- * @param s string representation of the number of fouls
- * @return integer representation of the number of fouls
- */
- fun foulStringToInt(s: String): Int {
- return when (s) {
- "3" -> 24
- "2" -> 25
- "23" -> 26
- "1" -> 27
- "13" -> 28
- "12" -> 29
- "123" -> 30
- else -> 0
- }
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/series/Series.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/series/Series.kt
deleted file mode 100644
index d9f285e85..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/series/Series.kt
+++ /dev/null
@@ -1,343 +0,0 @@
-package ca.josephroque.bowlingcompanion.series
-
-import android.content.ContentValues
-import android.content.Context
-import android.database.Cursor
-import android.database.sqlite.SQLiteDatabase
-import android.os.Parcel
-import android.util.Log
-import ca.josephroque.bowlingcompanion.R
-import ca.josephroque.bowlingcompanion.common.interfaces.IIdentifiable
-import ca.josephroque.bowlingcompanion.common.interfaces.IDeletable
-import ca.josephroque.bowlingcompanion.common.interfaces.KParcelable
-import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator
-import ca.josephroque.bowlingcompanion.common.interfaces.readDate
-import ca.josephroque.bowlingcompanion.common.interfaces.writeDate
-import ca.josephroque.bowlingcompanion.database.Annihilator
-import ca.josephroque.bowlingcompanion.database.Contract.FrameEntry
-import ca.josephroque.bowlingcompanion.database.Contract.GameEntry
-import ca.josephroque.bowlingcompanion.database.Contract.SeriesEntry
-import ca.josephroque.bowlingcompanion.database.DatabaseManager
-import ca.josephroque.bowlingcompanion.games.Game
-import ca.josephroque.bowlingcompanion.leagues.League
-import ca.josephroque.bowlingcompanion.utils.BCError
-import ca.josephroque.bowlingcompanion.utils.DateUtils
-import kotlinx.coroutines.experimental.CommonPool
-import kotlinx.coroutines.experimental.Deferred
-import kotlinx.coroutines.experimental.async
-import java.lang.ref.WeakReference
-import java.util.Date
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * A series of games in a [League].
- */
-class Series(
- val league: League,
- override val id: Long,
- val date: Date,
- val numberOfGames: Int,
- val scores: List,
- val matchPlay: List
-) : IIdentifiable, IDeletable, KParcelable {
-
- private var _isDeleted: Boolean = false
- override val isDeleted: Boolean
- get() = _isDeleted
-
- val prettyDate: String
- get() = DateUtils.dateToPretty(date)
-
- val total: Int
- get() = scores.sum()
-
- // MARK: Constructors
-
- private constructor(p: Parcel): this(
- league = p.readParcelable(League::class.java.classLoader)!!,
- id = p.readLong(),
- date = p.readDate()!!,
- numberOfGames = p.readInt(),
- scores = arrayListOf().apply {
- val size = p.readInt()
- val scoresArray = IntArray(size)
- p.readIntArray(scoresArray)
- this.addAll(scoresArray.toList())
- },
- matchPlay = arrayListOf().apply {
- val size = p.readInt()
- val matchesArray = kotlin.ByteArray(size)
- p.readByteArray(matchesArray)
- this.addAll(matchesArray.toList())
- }
- )
-
- constructor(series: Series): this(
- league = series.league,
- id = series.id,
- date = series.date,
- numberOfGames = series.numberOfGames,
- scores = series.scores,
- matchPlay = series.matchPlay
- )
-
- // MARK: Parcelable
-
- override fun writeToParcel(dest: Parcel, flags: Int) = with(dest) {
- writeParcelable(league, 0)
- writeLong(id)
- writeDate(date)
- writeInt(numberOfGames)
- writeInt(scores.size)
- writeIntArray(scores.toIntArray())
- writeInt(matchPlay.size)
- writeByteArray(matchPlay.toByteArray())
- }
-
- // MARK: IDeletable
-
- override fun markForDeletion(): Series {
- val newInstance = Series(this)
- newInstance._isDeleted = true
- return newInstance
- }
-
- override fun cleanDeletion(): Series {
- val newInstance = Series(this)
- newInstance._isDeleted = false
- return newInstance
- }
-
- override fun delete(context: Context): Deferred {
- return async(CommonPool) {
- if (id < 0) {
- return@async
- }
-
- Annihilator.instance.delete(
- weakContext = WeakReference(context),
- tableName = SeriesEntry.TABLE_NAME,
- whereClause = "${SeriesEntry._ID}=?",
- whereArgs = arrayOf(id.toString())
- )
- }
- }
-
- // MARK: Series
-
- fun fetchGames(context: Context): Deferred> {
- return Game.fetchSeriesGames(context, this)
- }
-
- companion object {
- @Suppress("unused")
- private const val TAG = "Series"
-
- @Suppress("unused")
- @JvmField val CREATOR = parcelableCreator(::Series)
-
- const val PREFERRED_VIEW = "series_preferred_view"
-
- enum class View {
- Expanded, Condensed;
-
- companion object {
- private val map = View.values().associateBy(View::ordinal)
- fun fromInt(type: Int) = map[type]
- }
- }
-
- fun save(
- context: Context,
- league: League,
- id: Long,
- date: Date,
- numberOfGames: Int,
- scores: List,
- matchPlay: List,
- openDatabase: SQLiteDatabase? = null
- ): Deferred> {
- return if (id < 0) {
- createNewAndSave(context, league, date, numberOfGames, scores, matchPlay, openDatabase)
- } else {
- update(context, id, league, date, numberOfGames, scores, matchPlay, openDatabase)
- }
- }
-
- private fun createNewAndSave(
- context: Context,
- league: League,
- date: Date,
- numberOfGames: Int,
- scores: List,
- matchPlay: List,
- openDatabase: SQLiteDatabase? = null
- ): Deferred> {
- return async(CommonPool) {
- val database = openDatabase ?: DatabaseManager.getWritableDatabase(context).await()
- val inTransaction = openDatabase != null && openDatabase.inTransaction()
- var values = ContentValues().apply {
- put(SeriesEntry.COLUMN_SERIES_DATE, DateUtils.dateToSeriesDate(date))
- put(SeriesEntry.COLUMN_LEAGUE_ID, league.id)
- }
-
- val seriesId: Long
- if (!inTransaction) {
- database.beginTransaction()
- }
- try {
- seriesId = database.insert(SeriesEntry.TABLE_NAME, null, values)
-
- if (seriesId != -1L) {
- for (gameIdx in 0 until numberOfGames) {
- values = ContentValues().apply {
- put(GameEntry.COLUMN_GAME_NUMBER, gameIdx + 1)
- put(GameEntry.COLUMN_SCORE, 0)
- put(GameEntry.COLUMN_SERIES_ID, seriesId)
- }
- val gameId = database.insert(GameEntry.TABLE_NAME, null, values)
-
- if (gameId != -1L) {
- for (frameIdx in 0 until Game.NUMBER_OF_FRAMES) {
- values = ContentValues().apply {
- put(FrameEntry.COLUMN_FRAME_NUMBER, frameIdx + 1)
- put(FrameEntry.COLUMN_GAME_ID, gameId)
- put(FrameEntry.COLUMN_IS_ACCESSED, gameIdx == 0 && frameIdx == 0)
- }
- database.insert(FrameEntry.TABLE_NAME, null, values)
- }
- } else {
- throw IllegalStateException("Game was not saved, ID is -1")
- }
- }
- }
-
- if (!inTransaction) {
- database.setTransactionSuccessful()
- }
- } catch (ex: Exception) {
- Log.e(TAG, "Could not create a new series")
- return@async Pair(
- null,
- BCError(R.string.error_saving_series, R.string.error_series_not_saved)
- )
- } finally {
- if (!inTransaction) {
- database.endTransaction()
- }
- }
-
- Pair(Series(
- league = league,
- id = seriesId,
- date = date,
- numberOfGames = numberOfGames,
- scores = scores,
- matchPlay = matchPlay
- ), null)
- }
- }
-
- private fun update(
- context: Context,
- id: Long,
- league: League,
- date: Date,
- numberOfGames: Int,
- scores: List,
- matchPlay: List,
- openDatabase: SQLiteDatabase? = null
- ): Deferred> {
- return async(CommonPool) {
- val database = openDatabase ?: DatabaseManager.getWritableDatabase(context).await()
- val inTransaction = openDatabase != null && openDatabase.inTransaction()
- val values = ContentValues().apply {
- put(SeriesEntry.COLUMN_SERIES_DATE, DateUtils.dateToSeriesDate(date))
- }
-
- if (!inTransaction) {
- database.beginTransaction()
- }
- try {
- database.update(SeriesEntry.TABLE_NAME, values, "${SeriesEntry._ID}=?", arrayOf(id.toString()))
- if (!inTransaction) {
- database.setTransactionSuccessful()
- }
- } catch (ex: Exception) {
- Log.e(TAG, "Error updating series details ($id, $date)", ex)
- } finally {
- if (!inTransaction) {
- database.endTransaction()
- }
- }
-
- Pair(Series(league, id, date, numberOfGames, scores, matchPlay), null)
- }
- }
-
- fun fetchAll(context: Context, league: League): Deferred> {
- return async(CommonPool) {
- val seriesList: MutableList = ArrayList()
- val database = DatabaseManager.getReadableDatabase(context).await()
-
- val rawSeriesQuery = ("SELECT " +
- "series.${SeriesEntry._ID} AS sid, " +
- "${SeriesEntry.COLUMN_SERIES_DATE}, " +
- "${GameEntry.COLUMN_SCORE}, " +
- "${GameEntry.COLUMN_GAME_NUMBER}, " +
- "${GameEntry.COLUMN_MATCH_PLAY} " +
- "FROM ${SeriesEntry.TABLE_NAME} AS series " +
- "INNER JOIN ${GameEntry.TABLE_NAME} " +
- "ON sid=${GameEntry.COLUMN_SERIES_ID} " +
- "WHERE ${SeriesEntry.COLUMN_LEAGUE_ID}=? " +
- "ORDER BY ${SeriesEntry.COLUMN_SERIES_DATE} DESC, ${GameEntry.COLUMN_GAME_NUMBER}")
-
- var lastId: Long = -1
- var scores: MutableList = ArrayList()
- var matchPlay: MutableList = ArrayList()
-
- fun buildSeriesFromCursor(cursor: Cursor): Series {
- val id = cursor.getLong(cursor.getColumnIndex("sid"))
- val seriesDate = DateUtils.seriesDateToDate(cursor.getString(cursor.getColumnIndex(SeriesEntry.COLUMN_SERIES_DATE)))
- return Series(
- league,
- id,
- seriesDate,
- scores.size,
- scores,
- matchPlay
- )
- }
-
- val cursor = database.rawQuery(rawSeriesQuery, arrayOf(league.id.toString()))
- if (cursor.moveToFirst()) {
- while (!cursor.isAfterLast) {
- val newId = cursor.getLong(cursor.getColumnIndex("sid"))
- if (newId != lastId && lastId != -1L) {
- cursor.moveToPrevious()
-
- seriesList.add(buildSeriesFromCursor(cursor))
-
- scores = ArrayList()
- matchPlay = ArrayList()
- cursor.moveToNext()
- }
-
- scores.add(cursor.getInt(cursor.getColumnIndex(GameEntry.COLUMN_SCORE)))
- matchPlay.add(cursor.getInt(cursor.getColumnIndex(GameEntry.COLUMN_MATCH_PLAY)).toByte())
-
- lastId = newId
- cursor.moveToNext()
- }
-
- cursor.moveToPrevious()
- seriesList.add(buildSeriesFromCursor(cursor))
- }
- cursor.close()
-
- seriesList
- }
- }
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/series/SeriesDialog.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/series/SeriesDialog.kt
deleted file mode 100644
index d7b7aa77d..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/series/SeriesDialog.kt
+++ /dev/null
@@ -1,220 +0,0 @@
-package ca.josephroque.bowlingcompanion.series
-
-import android.app.DatePickerDialog
-import android.app.Dialog
-import android.content.Context
-import android.os.Bundle
-import android.support.v4.app.DialogFragment
-import android.support.v7.app.AlertDialog
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.view.Window
-import android.widget.DatePicker
-import ca.josephroque.bowlingcompanion.App
-import ca.josephroque.bowlingcompanion.R
-import ca.josephroque.bowlingcompanion.common.Android
-import ca.josephroque.bowlingcompanion.common.fragments.BaseDialogFragment
-import ca.josephroque.bowlingcompanion.common.fragments.DatePickerFragment
-import ca.josephroque.bowlingcompanion.utils.Analytics
-import ca.josephroque.bowlingcompanion.utils.Color
-import ca.josephroque.bowlingcompanion.utils.DateUtils
-import ca.josephroque.bowlingcompanion.utils.safeLet
-import kotlinx.android.synthetic.main.dialog_series.tv_date as dateText
-import kotlinx.android.synthetic.main.dialog_series.toolbar_series as seriesToolbar
-import kotlinx.android.synthetic.main.dialog_series.view.*
-import kotlinx.coroutines.experimental.launch
-import java.util.Calendar
-import java.util.Date
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Dialog to edit a series.
- */
-class SeriesDialog : BaseDialogFragment(), DatePickerDialog.OnDateSetListener {
-
- companion object {
- @Suppress("unused")
- private const val TAG = "SeriesDialog"
-
- private const val ARG_SERIES = "${TAG}_series"
- private const val ARG_DATE = "${TAG}_date"
-
- fun newInstance(series: Series): SeriesDialog {
- val dialog = SeriesDialog()
- dialog.arguments = Bundle().apply { putParcelable(ARG_SERIES, series) }
- return dialog
- }
- }
-
- private var series: Series? = null
-
- private var currentDate: Date? = null
-
- private var delegate: SeriesDialogDelegate? = null
-
- private var onClickListener: View.OnClickListener? = View.OnClickListener {
- val clicked = it ?: return@OnClickListener
- when (clicked.id) {
- R.id.btn_change_date -> {
- showChangeDateDialog()
- }
- R.id.btn_delete -> {
- safeLet(context, series) { context, series ->
- AlertDialog.Builder(context)
- .setTitle(String.format(context.resources.getString(R.string.query_delete_item), series.prettyDate))
- .setMessage(R.string.dialog_delete_item_message)
- .setPositiveButton(R.string.delete) { _, _ ->
- delegate?.onDeleteSeries(series)
- dismiss()
- }
- .setNegativeButton(R.string.cancel, null)
- .show()
- }
- }
- }
- }
-
- // MARK: Lifecycle functions
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setStyle(DialogFragment.STYLE_NORMAL, R.style.Dialog)
- }
-
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
- series = arguments?.getParcelable(ARG_SERIES)
- currentDate = Date(savedInstanceState?.getLong(ARG_DATE) ?: series?.date?.time ?: 0)
-
- val rootView = inflater.inflate(R.layout.dialog_series, container, false)
- rootView.btn_change_date.setOnClickListener(onClickListener)
- rootView.btn_delete.setOnClickListener(onClickListener)
- setupToolbar(rootView)
- return rootView
- }
-
- override fun onAttach(context: Context?) {
- super.onAttach(context)
- val parentFragment = parentFragment as? SeriesDialogDelegate ?: throw RuntimeException("${parentFragment!!} must implement SeriesDialogDelegate")
- delegate = parentFragment
- }
-
- override fun onDetach() {
- super.onDetach()
- delegate = null
- onClickListener = null
- }
-
- override fun onStart() {
- super.onStart()
- dialog.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
-
- currentDate?.let { dateText.text = DateUtils.dateToPretty(it) }
- updateSaveButton()
- }
-
- override fun onSaveInstanceState(outState: Bundle) {
- super.onSaveInstanceState(outState)
- currentDate?.let {
- outState.putLong(ARG_DATE, it.time)
- }
- }
-
- override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
- val dialog = super.onCreateDialog(savedInstanceState)
- dialog.requestWindowFeature(Window.FEATURE_NO_TITLE)
- return dialog
- }
-
- override fun dismiss() {
- App.hideSoftKeyBoard(activity!!)
- activity?.supportFragmentManager?.popBackStack()
- super.dismiss()
- }
-
- // MARK: Private functions
-
- private fun setupToolbar(rootView: View) {
- rootView.toolbar_series.apply {
- setTitle(R.string.edit_series)
- inflateMenu(R.menu.dialog_series)
- setNavigationIcon(R.drawable.ic_dismiss)
- setNavigationOnClickListener {
- dismiss()
- }
- setOnMenuItemClickListener {
- when (it.itemId) {
- R.id.action_save -> {
- saveSeries()
- true
- }
- else -> super.onOptionsItemSelected(it)
- }
- }
- }
- }
-
- private fun updateSaveButton() {
- val saveButton = seriesToolbar?.menu?.findItem(R.id.action_save)
- saveButton?.isEnabled = true
- saveButton?.icon?.alpha = Color.ALPHA_ENABLED
- }
-
- private fun saveSeries() {
- launch(Android) {
- val context = this@SeriesDialog.context ?: return@launch
- val oldSeries = this@SeriesDialog.series ?: return@launch
- val currentDate = this@SeriesDialog.currentDate ?: return@launch
-
- val (newSeries, error) = Series.save(
- context = context,
- id = oldSeries.id,
- league = oldSeries.league,
- date = currentDate,
- numberOfGames = oldSeries.numberOfGames,
- scores = oldSeries.scores,
- matchPlay = oldSeries.matchPlay
- ).await()
-
- if (error != null) {
- error.show(context)
- dateText.text = oldSeries.prettyDate
- } else if (newSeries != null) {
- dismiss()
- delegate?.onFinishSeries(newSeries)
-
- Analytics.trackEditSeries()
- }
- }
- }
-
- private fun showChangeDateDialog() {
- val supportFragmentManager = activity?.supportFragmentManager ?: return
-
- val calendar = Calendar.getInstance()
- calendar.time = currentDate
- val dialog = DatePickerFragment.newInstance(calendar)
- dialog.listener = this
-
- dialog.show(supportFragmentManager, "DatePickerFragment")
- }
-
- // MARK: OnDateSetListener
-
- override fun onDateSet(view: DatePicker?, year: Int, month: Int, dayOfMonth: Int) {
- val calendar = Calendar.getInstance()
- calendar.set(Calendar.YEAR, year)
- calendar.set(Calendar.MONTH, month)
- calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth)
- currentDate = calendar.time
- dateText.text = DateUtils.dateToPretty(currentDate!!)
- }
-
- // MARK: SeriesDialogDelegate
-
- interface SeriesDialogDelegate {
- fun onFinishSeries(series: Series)
- fun onDeleteSeries(series: Series)
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/series/SeriesListFragment.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/series/SeriesListFragment.kt
deleted file mode 100644
index 0f304c299..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/series/SeriesListFragment.kt
+++ /dev/null
@@ -1,275 +0,0 @@
-package ca.josephroque.bowlingcompanion.series
-
-import android.content.Context
-import android.os.Bundle
-import android.support.v7.preference.PreferenceManager
-import android.view.LayoutInflater
-import android.view.Menu
-import android.view.MenuInflater
-import android.view.MenuItem
-import android.view.View
-import android.view.ViewGroup
-import ca.josephroque.bowlingcompanion.R
-import ca.josephroque.bowlingcompanion.common.Android
-import ca.josephroque.bowlingcompanion.common.interfaces.IFloatingActionButtonHandler
-import ca.josephroque.bowlingcompanion.common.interfaces.IIdentifiable
-import ca.josephroque.bowlingcompanion.common.fragments.ListFragment
-import ca.josephroque.bowlingcompanion.games.GameControllerFragment
-import ca.josephroque.bowlingcompanion.games.SeriesProvider
-import ca.josephroque.bowlingcompanion.leagues.League
-import ca.josephroque.bowlingcompanion.settings.Settings
-import ca.josephroque.bowlingcompanion.statistics.interfaces.IStatisticsContext
-import ca.josephroque.bowlingcompanion.statistics.provider.StatisticsProvider
-import ca.josephroque.bowlingcompanion.utils.Analytics
-import ca.josephroque.bowlingcompanion.utils.safeLet
-import kotlinx.coroutines.experimental.CommonPool
-import kotlinx.coroutines.experimental.Deferred
-import kotlinx.coroutines.experimental.async
-import kotlinx.coroutines.experimental.launch
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * A fragment representing a list of series.
- */
-class SeriesListFragment : ListFragment(),
- SeriesDialog.SeriesDialogDelegate,
- ListFragment.ListFragmentDelegate,
- IFloatingActionButtonHandler,
- IStatisticsContext {
-
- companion object {
- @Suppress("unused")
- private const val TAG = "SeriesListFragment"
-
- private const val ARG_LEAGUE = "${TAG}_league"
- private const val ARG_SINGLE_SELECT_MODE = "${TAG}_single_select"
-
- fun newInstance(league: League, singleSelectMode: Boolean = false): SeriesListFragment {
- val fragment = SeriesListFragment()
- fragment.arguments = Bundle().apply {
- putParcelable(ARG_LEAGUE, league)
- putBoolean(ARG_SINGLE_SELECT_MODE, singleSelectMode)
- }
- return fragment
- }
- }
-
- override val emptyViewImage = R.drawable.empty_view_series
- override val emptyViewText = R.string.empty_view_series
-
- override val statisticsProviders: List by lazy {
- val league = league
- return@lazy if (league != null) {
- arrayListOf(
- StatisticsProvider.BowlerStatistics(league.bowler),
- StatisticsProvider.LeagueStatistics(league)
- )
- } else {
- emptyList()
- }
- }
-
- private var league: League? = null
- private var singleSelectMode: Boolean = false
-
- private var seriesView: Series.Companion.View = Series.Companion.View.Expanded
- set(value) {
- context?.let {
- PreferenceManager.getDefaultSharedPreferences(it)
- .edit()
- .putInt(Series.PREFERRED_VIEW, value.ordinal)
- .apply()
- }
-
- activity?.invalidateOptionsMenu()
- adapter?.seriesView = value
- field = value
- }
-
- // MARK: Lifecycle functions
-
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
- arguments?.let {
- league = it.getParcelable(ARG_LEAGUE)
- singleSelectMode = it.getBoolean(ARG_SINGLE_SELECT_MODE)
- }
- context?.let {
- seriesView = Series.Companion.View.fromInt(PreferenceManager.getDefaultSharedPreferences(it)
- .getInt(Series.PREFERRED_VIEW, Series.Companion.View.Expanded.ordinal))!!
- }
-
- setHasOptionsMenu(!singleSelectMode)
- return super.onCreateView(inflater, container, savedInstanceState)
- }
-
- override fun onAttach(context: Context?) {
- canIgnoreDelegate = true
- delegate = this
- super.onAttach(context)
- singleSelectMode = arguments?.getBoolean(ARG_SINGLE_SELECT_MODE) ?: false
- if (singleSelectMode) {
- val parent = parentFragment as? ListFragmentDelegate ?: return
- delegate = parent
- }
- }
-
- override fun onStart() {
- super.onStart()
- val context = context ?: return
- val league = league ?: return
-
- val preferences = PreferenceManager.getDefaultSharedPreferences(context)
- val shouldHighlightSeries = Settings.BooleanSetting.HighlightSeriesEnabled.getValue(preferences)
- val shouldHighlightScores = Settings.BooleanSetting.HighlightScoreEnabled.getValue(preferences)
-
- adapter?.let {
- it.gameHighlightMin = league.gameHighlight
- it.seriesHighlightMin = league.seriesHighlight
- it.shouldHighlightSeries = shouldHighlightSeries
- it.shouldHighlightScores = shouldHighlightScores
- }
- }
-
- override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
- super.onCreateOptionsMenu(menu, inflater)
- inflater.inflate(R.menu.fragment_series, menu)
-
- when (seriesView) {
- Series.Companion.View.Expanded -> {
- menu.findItem(R.id.action_series_expanded_view).isVisible = false
- menu.findItem(R.id.action_series_condensed_view).isVisible = true
- }
- Series.Companion.View.Condensed -> {
- menu.findItem(R.id.action_series_expanded_view).isVisible = true
- menu.findItem(R.id.action_series_condensed_view).isVisible = false
- }
- }
- }
-
- override fun onOptionsItemSelected(item: MenuItem): Boolean {
- return when (item.itemId) {
- R.id.action_series_condensed_view, R.id.action_series_expanded_view -> {
- val view = if (item.itemId == R.id.action_series_expanded_view) Series.Companion.View.Expanded else Series.Companion.View.Condensed
- seriesView = view
-
- Analytics.trackToggledSeriesView(view)
- return true
- }
- else -> super.onOptionsItemSelected(item)
- }
- }
-
- // MARK: BaseFragment
-
- override fun updateToolbarTitle() {
- if (!singleSelectMode) {
- league?.let { navigationActivity?.setToolbarTitle(it.bowler.name, it.name) }
- }
- }
-
- // MARK: ListFragment
-
- override fun buildAdapter(): SeriesRecyclerViewAdapter {
- val adapter = SeriesRecyclerViewAdapter(emptyList(), this)
- adapter.swipeable = !singleSelectMode
- adapter.longPressable = !singleSelectMode
- adapter.seriesView = seriesView
- return adapter
- }
-
- override fun fetchItems(): Deferred> {
- return async(CommonPool) {
- this@SeriesListFragment.context?.let { context ->
- league?.let {
- return@async it.fetchSeries(context).await()
- }
- }
-
- mutableListOf()
- }
- }
-
- // MARK: IFloatingActionButtonHandler
-
- override fun getFabImage(): Int? {
- return R.drawable.ic_add
- }
-
- override fun onFabClick() {
- promptAddOrEditSeries()
- }
-
- // MARK: SeriesDialogDelegate
-
- override fun onFinishSeries(series: Series) {
- refreshList(series)
- }
-
- override fun onDeleteSeries(series: Series) {
- onItemDelete(series)
- }
-
- // MARK: AdapterDelegate
-
- override fun onItemSelected(item: IIdentifiable, longPress: Boolean) {
- if (item is Series) {
- if (longPress) {
- promptAddOrEditSeries(item)
- } else {
- showGameDetails(item)
-
- Analytics.trackSelectSeries()
- }
- }
- }
-
- override fun onItemDeleted(item: IIdentifiable) {
- if (item is Series) {
- Analytics.trackDeleteSeries()
- }
- }
-
- // MARK: Private functions
-
- private fun promptAddOrEditSeries(series: Series? = null) {
- if (series != null) {
- val newFragment = SeriesDialog.newInstance(series)
- fragmentNavigation?.pushDialogFragment(newFragment)
- } else {
- val league = league ?: return
- if (league.isPractice) {
- promptPracticeSeriesNumberOfGames()
- } else {
- createNewSeries(league.gamesPerSeries)
- }
- }
- }
-
- private fun promptPracticeSeriesNumberOfGames() {
- val context = context ?: return
- League.showPracticeGamesPicker(context, ::createNewSeries)
- }
-
- private fun createNewSeries(numberOfGames: Int) {
- safeLet(context, league) { context, league ->
- launch(Android) {
- val (newSeries, seriesError) = league.createNewSeries(context, numberOfPracticeGamesOverride = numberOfGames).await()
- if (seriesError != null) {
- seriesError.show(context)
- } else if (newSeries != null) {
- showGameDetails(newSeries)
-
- Analytics.trackCreateSeries()
- }
- }
- }
- }
-
- private fun showGameDetails(series: Series) {
- val newFragment = GameControllerFragment.newInstance(SeriesProvider.BowlerSeries(series, false))
- fragmentNavigation?.pushFragment(newFragment)
-
- Analytics.trackSelectSeries()
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/series/SeriesRecyclerViewAdapter.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/series/SeriesRecyclerViewAdapter.kt
deleted file mode 100644
index 1f00b6cb8..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/series/SeriesRecyclerViewAdapter.kt
+++ /dev/null
@@ -1,247 +0,0 @@
-package ca.josephroque.bowlingcompanion.series
-
-import android.support.design.chip.Chip
-import android.support.design.chip.ChipGroup
-import android.support.v4.content.ContextCompat
-import android.support.v7.preference.PreferenceManager
-import android.support.v7.widget.RecyclerView
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.TextView
-import ca.josephroque.bowlingcompanion.R
-import ca.josephroque.bowlingcompanion.common.adapters.BaseRecyclerViewAdapter
-import ca.josephroque.bowlingcompanion.matchplay.MatchPlayResult
-import ca.josephroque.bowlingcompanion.leagues.League
-import ca.josephroque.bowlingcompanion.settings.Settings
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * [RecyclerView.Adapter] that can display a [Series] and makes a call to the specified delegate
- * upon interactions.
- */
-class SeriesRecyclerViewAdapter(
- values: List,
- delegate: BaseRecyclerViewAdapter.AdapterDelegate?
-) : BaseRecyclerViewAdapter(values, delegate) {
-
- companion object {
- @Suppress("unused")
- private const val TAG = "SeriesRVAdapter"
-
- private enum class ViewType {
- Condensed,
- Expanded,
- Deleted;
-
- companion object {
- private val map = ViewType.values().associateBy(ViewType::ordinal)
- fun fromInt(type: Int) = map[type]
- }
- }
- }
-
- var seriesView: Series.Companion.View = Series.Companion.View.Expanded
- set(value) {
- notifyDataSetChanged()
- field = value
- }
-
- var seriesHighlightMin: Int = 0
- set(value) {
- notifyDataSetChanged()
- field = value
- }
-
- var gameHighlightMin: Int = 0
- set(value) {
- notifyDataSetChanged()
- field = value
- }
-
- var shouldHighlightSeries: Boolean = true
- set(value) {
- notifyDataSetChanged()
- field = value
- }
-
- var shouldHighlightScores: Boolean = true
- set(value) {
- notifyDataSetChanged()
- field = value
- }
-
- // MARK: BaseRecyclerViewAdapter
-
- override fun getItemViewType(position: Int): Int {
- return when {
- getItemAt(position).isDeleted -> ViewType.Deleted.ordinal
- !getItemAt(position).isDeleted && seriesView == Series.Companion.View.Condensed -> ViewType.Condensed.ordinal
- !getItemAt(position).isDeleted && seriesView == Series.Companion.View.Expanded -> ViewType.Expanded.ordinal
- else -> throw IllegalArgumentException("Position `$position` is invalid")
- }
- }
-
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseRecyclerViewAdapter.ViewHolder {
- return when (ViewType.fromInt(viewType)) {
- ViewType.Condensed -> ViewHolderCondensed(LayoutInflater
- .from(parent.context)
- .inflate(R.layout.list_item_series_condensed, parent, false))
- ViewType.Expanded -> ViewHolderExpanded(LayoutInflater
- .from(parent.context)
- .inflate(R.layout.list_item_series_expanded, parent, false))
- ViewType.Deleted -> ViewHolderDeleted(LayoutInflater
- .from(parent.context)
- .inflate(R.layout.list_item_deleted, parent, false))
- else -> throw IllegalArgumentException("View Type `$viewType` is invalid")
- }
- }
-
- override fun onBindViewHolder(holder: BaseRecyclerViewAdapter.ViewHolder, position: Int) {
- holder.bind(getItemAt(position))
- }
-
- // MARK: SeriesRecyclerViewAdapter
-
- private fun shouldHighlightSeries(seriesTotal: Int, numberOfGames: Int): Boolean {
- return when {
- seriesHighlightMin > 0 -> shouldHighlightSeries && seriesTotal > seriesHighlightMin
- seriesHighlightMin == -1 -> shouldHighlightSeries && seriesTotal > League.DEFAULT_SERIES_HIGHLIGHT[numberOfGames - 1]
- else -> false
- }
- }
-
- private fun shouldHighlightGame(score: Int): Boolean {
- return when {
- gameHighlightMin > 0 -> shouldHighlightScores && score > gameHighlightMin
- gameHighlightMin == -1 -> shouldHighlightScores && score > League.DEFAULT_GAME_HIGHLIGHT
- else -> false
- }
- }
-
- // MARK: ViewHolderCondensed
-
- inner class ViewHolderCondensed(view: View) : BaseRecyclerViewAdapter.ViewHolder(view) {
- private val tvDate: TextView? = view.findViewById(R.id.tv_date)
- private val tvTotal: TextView? = view.findViewById(R.id.tv_total)
-
- override fun bind(item: Series) {
- val context = itemView.context
- val seriesTotal = item.scores.sum()
-
- tvDate?.text = item.prettyDate
- tvTotal?.text = seriesTotal.toString()
-
- if (shouldHighlightSeries(seriesTotal, item.numberOfGames)) {
- tvTotal?.setTextColor(ContextCompat.getColor(context, R.color.seriesHighlight))
- } else {
- tvTotal?.setTextColor(ContextCompat.getColor(context, R.color.primaryBlackText))
- }
-
- itemView.setOnClickListener(this@SeriesRecyclerViewAdapter)
- itemView.setOnLongClickListener(this@SeriesRecyclerViewAdapter)
- }
- }
-
- // MARK: ViewHolderExpanded
-
- inner class ViewHolderExpanded(view: View) : BaseRecyclerViewAdapter.ViewHolder(view) {
- private val tvDate: TextView? = view.findViewById(R.id.tv_date)
- private val tvTotal: TextView? = view.findViewById(R.id.tv_total)
-
- private val chipGroupScores: ChipGroup? = view.findViewById(R.id.cg_scores)
-
- override fun bind(item: Series) {
- val context = itemView.context
- val seriesTotal = item.scores.sum()
-
- tvDate?.text = item.prettyDate
- tvTotal?.text = seriesTotal.toString()
-
- if (shouldHighlightSeries(seriesTotal, item.numberOfGames)) {
- tvTotal?.setTextColor(ContextCompat.getColor(context, R.color.seriesHighlight))
- } else {
- tvTotal?.setTextColor(ContextCompat.getColor(context, R.color.primaryBlackText))
- }
-
- val preferences = PreferenceManager.getDefaultSharedPreferences(context)
- val shouldShowMatchPlayResult = Settings.BooleanSetting.ShowMatchResults.getValue(preferences)
- val shouldHighlightMatchPlayResult = Settings.BooleanSetting.HighlightMatchResults.getValue(preferences)
-
- chipGroupScores?.removeAllViews()
- if (shouldShowMatchPlayResult) {
- for (i in 0 until item.scores.size) {
- val viewId = View.generateViewId()
- val score = item.scores[i]
- val matchPlayResult = MatchPlayResult.fromInt(item.matchPlay[i].toInt())!!
-
- val chipIconResource: Int?
- val chipIconTintResource: Int?
- when (matchPlayResult) {
- MatchPlayResult.WON -> {
- chipIconResource = R.drawable.ic_match_play_won_chip
- chipIconTintResource = if (shouldHighlightMatchPlayResult) R.color.matchPlayWin else R.color.primaryBlackText
- }
- MatchPlayResult.LOST -> {
- chipIconResource = R.drawable.ic_match_play_lost_chip
- chipIconTintResource = if (shouldHighlightMatchPlayResult) R.color.matchPlayLoss else R.color.primaryBlackText
- }
- MatchPlayResult.TIED -> {
- chipIconResource = R.drawable.ic_match_play_tied_chip
- chipIconTintResource = if (shouldHighlightMatchPlayResult) R.color.matchPlayTie else R.color.primaryBlackText
- }
- MatchPlayResult.NONE -> {
- chipIconResource = null
- chipIconTintResource = null
- }
- }
- val chipTextColorResource = if (shouldHighlightGame(score)) R.color.gameHighlight else R.color.primaryBlackText
-
- val chip = Chip(context).apply {
- id = viewId
- isFocusable = false
- isClickable = false
- text = score.toString()
- setTextColor(ContextCompat.getColor(context, chipTextColorResource))
- chipIconResource?.let { setChipIconResource(it) }
- chipIconTintResource?.let { setChipIconTintResource(it) }
- setChipBackgroundColorResource(R.color.colorListContrast)
- }
-
- chipGroupScores?.addView(chip)
- }
- }
-
- itemView.setOnClickListener(this@SeriesRecyclerViewAdapter)
- itemView.setOnLongClickListener(this@SeriesRecyclerViewAdapter)
- }
- }
-
- // MARK: ViewHolderDeleted
-
- inner class ViewHolderDeleted(view: View) : BaseRecyclerViewAdapter.ViewHolder(view) {
- private val tvDeleted: TextView? = view.findViewById(R.id.tv_deleted)
- private val tvUndo: TextView? = view.findViewById(R.id.tv_undo)
-
- override fun bind(item: Series) {
- val context = itemView.context
-
- tvDeleted?.text = String.format(
- context.resources.getString(R.string.query_delete_item),
- getItemAt(adapterPosition).prettyDate
- )
-
- val deletedItemListener = View.OnClickListener {
- if (it.id == R.id.tv_undo) {
- delegate?.onItemSwipe(getItemAt(adapterPosition))
- } else {
- delegate?.onItemDelete(getItemAt(adapterPosition))
- }
- }
- itemView.setOnClickListener(deletedItemListener)
- itemView.setOnLongClickListener(null)
- tvUndo?.setOnClickListener(deletedItemListener)
- }
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/settings/Settings.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/settings/Settings.kt
deleted file mode 100644
index 15f44fa7e..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/settings/Settings.kt
+++ /dev/null
@@ -1,141 +0,0 @@
-package ca.josephroque.bowlingcompanion.settings
-
-import android.content.SharedPreferences
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Keys for user settings.
- */
-object Settings {
-
- interface BaseSetting {
- val prefName: String
- }
-
- enum class BooleanSetting(override val prefName: String) : BaseSetting {
- /** Identifier for preference to accumulate statistics over time vs week by week. */
- AccumulateStatistics("accumulate_statistics"),
-
- /* Keep in line with preferences_defaults.xml */
-
- // MARK: General
-
- /** Identifier for preference to enable highlighting scores. */
- HighlightScoreEnabled("pref_highlight_score_enabled"),
-
- /** Identifier for preference to enable highlighting series. */
- HighlightSeriesEnabled("pref_highlight_series_enabled"),
-
- /** Identifier for preference to show averages with up to 1 decimal place of accuracy. */
- AverageAsDecimal("pref_average_as_decimal"),
-
- /** Identifier for preference for if app should ask user to combine similar series. */
- // FIXME: setting unused
- AskCombine("pref_ask_combine"),
-
- // MARK: Scoring
-
- /** Identifier for preference which allows user to enable auto advancing frames. */
- EnableAutoAdvance("pref_enable_auto_advance"),
-
- /** Identifier for preference for strikes and spares should be highlighted while editing a game. */
- EnableAutoLock("pref_enable_auto_lock"),
-
- /** Identifier for preference for if floating buttons should be shown when editing a game. */
- EnableFab("pref_enable_fab"),
-
- /** Identifier for preference for strikes and spares should be highlighted while editing a game. */
- EnableStrikeHighlights("pref_enable_strike_highlights"),
-
- // MARK: Stats
-
- /** Identifier for preference which indicates if events should be included in stats. */
- IncludeEvents("pref_include_events"),
-
- /** Identifier for preference which indicates if open games should be included in stats. */
- IncludeOpen("pref_include_open"),
-
- /** Identifier for preference to count Headpin + 2 as a Headpin in statistics. */
- CountH2AsH("pref_count_h2_as_h"),
-
- /** Identifier for preference to count Split + 2 as a Split in statistics. */
- CountS2AsS("pref_count_s2_as_s"),
-
- // MARK: Match play
-
- /** Identifier for preference to show or hide match play results in series view. */
- ShowMatchResults("pref_show_match_results"),
-
- /** Identifier for preference to highlight match play results in series view. */
- HighlightMatchResults("pref_highlight_match_results");
-
- val default: Boolean
- get() {
- return when (this) {
- HighlightScoreEnabled -> true
- HighlightSeriesEnabled -> true
- AverageAsDecimal -> true
- AskCombine -> true
- EnableAutoAdvance -> false
- EnableAutoLock -> false
- EnableFab -> true
- EnableStrikeHighlights -> true
- IncludeEvents -> true
- IncludeOpen -> true
- CountH2AsH -> false
- CountS2AsS -> false
- ShowMatchResults -> true
- HighlightMatchResults -> true
- AccumulateStatistics -> false
- }
- }
-
- fun getValue(preferences: SharedPreferences): Boolean {
- return preferences.getBoolean(this.prefName, this.default)
- }
- }
-
- enum class StringSetting(override val prefName: String) : BaseSetting {
- /** Identifier for preference which allows user to select time interval before auto advance. */
- AutoAdvanceTime("pref_auto_advance_time");
-
- val default: String
- get() {
- return when (this) {
- AutoAdvanceTime -> "10 seconds"
- }
- }
-
- fun getValue(preferences: SharedPreferences): String {
- return preferences.getString(this.prefName, this.default)!!
- }
- }
-
- enum class StaticSetting(override val prefName: String) : BaseSetting {
- // MARK: Feedback
-
- /** Identifier for preference which should open app in play store. */
- Rate("pref_rate"),
-
- /** Identifier for preference which should open email intent to report a bug. */
- ReportBug("pref_report_bug"),
-
- /** Identifier for preference which should open email intent to send feedback. */
- SendFeedback("pref_send_feedback"),
-
- // MARK: Other
-
- /** Identifier for preference to view developer's website. */
- DeveloperWebsite("pref_developer_website"),
-
- /** Identifier for preference to view open source repository. */
- ViewSource("pref_view_source"),
-
- /** Identifier for preference to display legal attribution from Open Source Software. */
- Attributions("pref_attributions"),
-
- /** Identifier for preference to show current app version. */
- VersionName("pref_version_name");
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/settings/SettingsActivity.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/settings/SettingsActivity.kt
deleted file mode 100644
index fe412faef..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/settings/SettingsActivity.kt
+++ /dev/null
@@ -1,47 +0,0 @@
-package ca.josephroque.bowlingcompanion.settings
-
-import android.os.Bundle
-import android.support.v7.app.AppCompatActivity
-import android.view.MenuItem
-import ca.josephroque.bowlingcompanion.R
-import kotlinx.android.synthetic.main.activity_navigation.toolbar as toolbar
-
-/**
- * Base activity to change user settings.
- */
-class SettingsActivity : AppCompatActivity() {
-
- // MARK: Lifecycle functions
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setTheme(R.style.SettingTheme)
- setContentView(R.layout.activity_settings)
- setupToolbar()
-
- if (savedInstanceState == null) {
- val preferenceFragment = SettingsFragment.newInstance()
- val ft = supportFragmentManager.beginTransaction()
- ft.add(R.id.pref_container, preferenceFragment)
- ft.commit()
- }
- }
-
- override fun onOptionsItemSelected(item: MenuItem): Boolean {
- if (item.itemId == android.R.id.home) {
- onBackPressed()
- return true
- }
-
- return super.onOptionsItemSelected(item)
- }
-
- // MARK: Private functions
-
- private fun setupToolbar() {
- setSupportActionBar(toolbar)
- supportActionBar?.setDisplayHomeAsUpEnabled(true)
- supportActionBar?.setDisplayShowHomeEnabled(true)
- supportActionBar?.setTitle(R.string.title_activity_settings)
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/settings/SettingsFragment.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/settings/SettingsFragment.kt
deleted file mode 100644
index 0587a9aee..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/settings/SettingsFragment.kt
+++ /dev/null
@@ -1,206 +0,0 @@
-package ca.josephroque.bowlingcompanion.settings
-
-import android.content.Intent
-import android.content.SharedPreferences
-import android.net.Uri
-import android.os.Bundle
-import android.support.v4.app.FragmentTransaction
-import android.support.v7.preference.Preference
-import android.support.v7.preference.PreferenceFragmentCompat
-import ca.josephroque.bowlingcompanion.BuildConfig
-import ca.josephroque.bowlingcompanion.R
-import ca.josephroque.bowlingcompanion.common.ScrollableTextDialog
-import ca.josephroque.bowlingcompanion.utils.Analytics
-import ca.josephroque.bowlingcompanion.utils.Email
-import ca.josephroque.bowlingcompanion.utils.Files
-import ca.josephroque.bowlingcompanion.utils.toSpanned
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Display user preferences and allow manipulation.
- */
-class SettingsFragment : PreferenceFragmentCompat(),
- SharedPreferences.OnSharedPreferenceChangeListener {
-
- companion object {
- @Suppress("unused")
- private const val TAG = "SettingsFragment"
-
- fun newInstance(): SettingsFragment {
- return SettingsFragment()
- }
- }
-
- private val onPreferenceChangedListener = SharedPreferences.OnSharedPreferenceChangeListener { preferences, key ->
- when (key) {
- Settings.BooleanSetting.EnableAutoAdvance.prefName -> {
- val enabled = Settings.BooleanSetting.EnableAutoAdvance.getValue(preferences)
- if (enabled) {
- Analytics.trackEnableAutoAdvance()
- } else {
- Analytics.trackDisableAutoAdvance()
- }
- }
- Settings.BooleanSetting.EnableAutoLock.prefName -> {
- val enabled = Settings.BooleanSetting.EnableAutoLock.getValue(preferences)
- if (enabled) {
- Analytics.trackEnableAutoLock()
- } else {
- Analytics.trackDisableAutoLock()
- }
- }
- else -> {} // Do nothing
- }
- }
-
- private val onPreferenceClickListener = Preference.OnPreferenceClickListener {
- when (it.key) {
- Settings.StaticSetting.ReportBug.prefName -> {
- sendBugReportEmail()
- true
- }
- Settings.StaticSetting.SendFeedback.prefName -> {
- sendFeedbackEmail()
- true
- }
- Settings.StaticSetting.Rate.prefName -> {
- displayPlayStoreListing()
- true
- }
- Settings.StaticSetting.Attributions.prefName -> {
- displayAttributions()
- true
- }
- Settings.StaticSetting.DeveloperWebsite.prefName -> {
- Analytics.trackViewWebsite()
- true
- }
- Settings.StaticSetting.ViewSource.prefName -> {
- Analytics.trackViewSource()
- true
- }
- else -> false // Does nothing
- }
- }
-
- // MARK: Lifecycle functions
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- preferenceManager.sharedPreferences.registerOnSharedPreferenceChangeListener(onPreferenceChangedListener)
- }
-
- override fun onDestroy() {
- super.onDestroy()
- preferenceManager.sharedPreferences.unregisterOnSharedPreferenceChangeListener(onPreferenceChangedListener)
- }
-
- override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
- addPreferencesFromResource(R.xml.pref_app)
-
- findPreference(Settings.StaticSetting.ReportBug.prefName).onPreferenceClickListener = onPreferenceClickListener
- findPreference(Settings.StaticSetting.SendFeedback.prefName).onPreferenceClickListener = onPreferenceClickListener
- findPreference(Settings.StaticSetting.Rate.prefName).onPreferenceClickListener = onPreferenceClickListener
- findPreference(Settings.StaticSetting.Attributions.prefName).onPreferenceClickListener = onPreferenceClickListener
- findPreference(Settings.StaticSetting.DeveloperWebsite.prefName).onPreferenceClickListener = onPreferenceClickListener
- findPreference(Settings.StaticSetting.ViewSource.prefName).onPreferenceClickListener = onPreferenceClickListener
- }
-
- override fun onStart() {
- super.onStart()
- preferenceScreen.sharedPreferences.registerOnSharedPreferenceChangeListener(this)
- updatePreferenceSummaries()
- }
-
- override fun onStop() {
- super.onStop()
- preferenceScreen.sharedPreferences.unregisterOnSharedPreferenceChangeListener(this)
- }
-
- override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
- updatePreferenceSummaries()
- }
-
- // MARK: Private function
-
- private fun sendBugReportEmail() {
- val emailName = resources.getString(R.string.feedback_email_recipient_name);
- val emailDomain = resources.getString(R.string.feedback_email_recipient_domain);
- val emailTld = resources.getString(R.string.feedback_email_recipient_tld);
-
- activity?.let {
- Email.sendEmail(
- it,
- "$emailName@$emailDomain.$emailTld",
- resources.getString(R.string.bug_email_subject),
- resources.getString(R.string.bug_email_body)
- )
- }
-
- Analytics.trackReportBug()
- }
-
- private fun sendFeedbackEmail() {
- val emailName = resources.getString(R.string.feedback_email_recipient_name);
- val emailDomain = resources.getString(R.string.feedback_email_recipient_domain);
- val emailTld = resources.getString(R.string.feedback_email_recipient_tld);
-
- activity?.let {
- Email.sendEmail(
- it,
- "$emailName@$emailDomain.$emailTld",
- resources.getString(R.string.feedback_email_subject),
- null
- )
- }
-
- Analytics.trackSendFeedback()
- }
-
- private fun displayPlayStoreListing() {
- activity?.let {
- val appPackageName = it.packageName
- val marketIntent = try {
- Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=$appPackageName"))
- } catch (ex: android.content.ActivityNotFoundException) {
- Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=$appPackageName"))
- }
-
- startActivity(marketIntent)
- }
-
- Analytics.trackRate()
- }
-
- private fun displayAttributions() {
- activity?.let { activity ->
- val licenseText = Files.retrieveTextFileAsset(activity, "licenses.txt")
-
- licenseText?.let {
- val fragment = ScrollableTextDialog.newInstance(
- R.string.open_source_libraries,
- it.replace("\n", "
").toSpanned()
- )
-
- activity.supportFragmentManager.beginTransaction().apply {
- setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
- add(android.R.id.content, fragment)
- addToBackStack(null)
- commit()
- }
- }
- }
-
- Analytics.trackViewAttributions()
- }
-
- private fun updatePreferenceSummaries() {
- val prefs = preferenceScreen.sharedPreferences
- findPreference(Settings.StaticSetting.VersionName.prefName).summary = BuildConfig.VERSION_NAME
-
- val autoAdvanceTime = Settings.StringSetting.AutoAdvanceTime.getValue(prefs)
- val timeComponents = autoAdvanceTime.split(" ")
- findPreference(Settings.StringSetting.AutoAdvanceTime.prefName).summary = resources.getString(R.string.pref_auto_advance_time_summary_seconds, timeComponents[0])
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/Statistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/Statistic.kt
deleted file mode 100644
index 7de617182..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/Statistic.kt
+++ /dev/null
@@ -1,199 +0,0 @@
-package ca.josephroque.bowlingcompanion.statistics
-
-import android.content.SharedPreferences
-import android.content.res.Resources
-import android.os.Parcel
-import ca.josephroque.bowlingcompanion.common.interfaces.KParcelable
-import ca.josephroque.bowlingcompanion.statistics.immutable.StatFrame
-import ca.josephroque.bowlingcompanion.statistics.immutable.StatGame
-import ca.josephroque.bowlingcompanion.statistics.immutable.StatSeries
-import ca.josephroque.bowlingcompanion.statistics.list.StatisticListItem
-import ca.josephroque.bowlingcompanion.statistics.unit.StatisticsUnit
-import java.text.DecimalFormat
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * A statistic to display.
- */
-interface Statistic : StatisticListItem, KParcelable {
-
- companion object {
- const val EMPTY_STATISTIC = "—"
- }
-
- val titleId: Int
- val displayValue: String
- val category: StatisticsCategory
- val canBeGraphed: Boolean
- val primaryGraphY: Float?
- val secondaryGraphY: Float?
-
- val primaryGraphDataLabelId: Int
- get() = titleId
-
- val secondaryGraphDataLabelId: Int?
-
- fun updatePreferences(preferences: SharedPreferences) {}
-
- fun getSubtitle(): String? { return null }
-
- fun isModifiedBy(unit: StatisticsUnit) = false
-
- fun modify(unit: StatisticsUnit) {}
-
- fun isModifiedBy(series: StatSeries) = false
-
- fun modify(series: StatSeries) {}
-
- fun isModifiedBy(game: StatGame) = false
-
- fun modify(game: StatGame) {}
-
- fun isModifiedBy(frame: StatFrame) = false
-
- fun modify(frame: StatFrame) {}
-
- fun zero()
-
- fun getTitle(resources: Resources): String {
- return resources.getString(titleId)
- }
-
- override fun describeContents(): Int {
- return titleId
- }
-}
-
-interface PercentageStatistic : Statistic {
-
- var numerator: Int
- var denominator: Int
-
- override val canBeGraphed
- get() = true
-
- override val primaryGraphY: Float?
- get() = numerator.toFloat()
-
- override val secondaryGraphY: Float?
- get() = denominator.toFloat()
-
- val percentage: Double
- get() {
- return if (denominator > 0) {
- numerator.div(denominator.toDouble()).times(100)
- } else {
- 0.0
- }
- }
-
- override val displayValue: String
- get() = if (denominator > 0) {
- val formatter = DecimalFormat("#.##")
- "${formatter.format(percentage)}% [$numerator/$denominator]"
- } else {
- Statistic.EMPTY_STATISTIC
- }
-
- override fun writeToParcel(dest: Parcel, flags: Int) = with(dest) {
- writeInt(numerator)
- writeInt(denominator)
- }
-
- override fun zero() {
- numerator = 0
- denominator = 0
- }
-}
-
-interface AverageStatistic : Statistic {
-
- var total: Int
- var divisor: Int
-
- val average: Double
- get() = if (divisor > 0) total.div(divisor.toDouble()) else 0.0
-
- override val primaryGraphY: Float?
- get() = average.toFloat()
-
- override val secondaryGraphY: Float?
- get() = null
-
- override val secondaryGraphDataLabelId: Int?
- get() = null
-
- override val displayValue: String
- get() {
- val formatter = DecimalFormat("#.##")
- return if (average > 0) formatter.format(average) else Statistic.EMPTY_STATISTIC
- }
-
- override val canBeGraphed
- get() = true
-
- override fun writeToParcel(dest: Parcel, flags: Int) = with(dest) {
- writeInt(total)
- writeInt(divisor)
- }
-
- override fun zero() {
- total = 0
- divisor = 0
- }
-}
-
-interface IntegerStatistic : Statistic {
- var value: Int
-
- override val displayValue: String
- get() = value.toString()
-
- override val canBeGraphed
- get() = true
-
- override val primaryGraphY: Float?
- get() = value.toFloat()
-
- override val secondaryGraphY: Float?
- get() = null
-
- override val secondaryGraphDataLabelId: Int?
- get() = null
-
- override fun writeToParcel(dest: Parcel, flags: Int) = with(dest) {
- writeInt(value)
- }
-
- override fun zero() {
- value = 0
- }
-}
-
-interface StringStatistic : Statistic {
- var value: String
-
- override val displayValue: String
- get() = value
-
- override val canBeGraphed
- get() = false
-
- override val primaryGraphY: Float?
- get() = throw IllegalAccessException("StringStatistic does not have a primaryGraphY")
-
- override val secondaryGraphY: Float?
- get() = throw IllegalAccessException("StringStatistic does not have a secondaryGraphY")
-
- override val secondaryGraphDataLabelId: Int?
- get() = throw IllegalAccessException("StringStatistic does not have a secondaryGraphDataLabelId")
-
- override fun writeToParcel(dest: Parcel, flags: Int) = with(dest) {
- writeString(value)
- }
-
- override fun zero() {
- value = ""
- }
-}
diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/StatisticHelper.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/StatisticHelper.kt
deleted file mode 100644
index 7d1b5231c..000000000
--- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/StatisticHelper.kt
+++ /dev/null
@@ -1,387 +0,0 @@
-package ca.josephroque.bowlingcompanion.statistics
-
-import android.os.Parcel
-import ca.josephroque.bowlingcompanion.statistics.impl.average.Game10AverageStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.average.Game11AverageStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.average.Game12AverageStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.average.Game13AverageStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.average.Game14AverageStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.average.Game15AverageStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.average.Game16AverageStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.average.Game17AverageStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.average.Game18AverageStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.average.Game19AverageStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.average.Game1AverageStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.average.Game20AverageStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.average.Game2AverageStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.average.Game3AverageStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.average.Game4AverageStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.average.Game5AverageStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.average.Game6AverageStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.average.Game7AverageStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.average.Game8AverageStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.average.Game9AverageStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.firstball.AcesSparedStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.firstball.AcesStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.firstball.ChopOffsSparedStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.firstball.ChopOffsStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.firstball.HeadPinsSparedStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.firstball.HeadPinsStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.firstball.LeftChopOffsSparedStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.firstball.LeftChopOffsStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.firstball.LeftSplitsSparedStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.firstball.LeftSplitsStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.firstball.LeftTwelvesSparedStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.firstball.LeftTwelvesStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.firstball.LeftsSparedStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.firstball.LeftsStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.firstball.RightChopOffsSparedStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.firstball.RightChopOffsStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.firstball.RightSplitsSparedStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.firstball.RightSplitsStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.firstball.RightTwelvesSparedStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.firstball.RightTwelvesStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.firstball.RightsSparedStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.firstball.RightsStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.firstball.SplitsSparedStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.firstball.SplitsStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.firstball.TwelvesSparedStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.firstball.TwelvesStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.foul.FoulsStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.general.BowlerNameStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.general.GameNameStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.general.LeagueNameStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.general.SeriesNameStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.matchplay.GamesLostStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.matchplay.GamesTiedStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.matchplay.GamesWonStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.overall.GameAverageStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.overall.HighSingleStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.overall.LeftOfMiddleHitsStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.overall.MiddleHitsStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.overall.NumberOfGamesStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.overall.RightOfMiddleHitsStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.overall.SpareConversionsStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.overall.StrikeMiddleHitsStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.overall.StrikesStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.overall.TotalPinfallStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.pinsleftondeck.AveragePinsLeftStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.pinsleftondeck.TotalPinsLeftStatistic
-import ca.josephroque.bowlingcompanion.statistics.impl.series.HighSeriesOf10Statistic
-import ca.josephroque.bowlingcompanion.statistics.impl.series.HighSeriesOf11Statistic
-import ca.josephroque.bowlingcompanion.statistics.impl.series.HighSeriesOf12Statistic
-import ca.josephroque.bowlingcompanion.statistics.impl.series.HighSeriesOf13Statistic
-import ca.josephroque.bowlingcompanion.statistics.impl.series.HighSeriesOf14Statistic
-import ca.josephroque.bowlingcompanion.statistics.impl.series.HighSeriesOf15Statistic
-import ca.josephroque.bowlingcompanion.statistics.impl.series.HighSeriesOf16Statistic
-import ca.josephroque.bowlingcompanion.statistics.impl.series.HighSeriesOf17Statistic
-import ca.josephroque.bowlingcompanion.statistics.impl.series.HighSeriesOf18Statistic
-import ca.josephroque.bowlingcompanion.statistics.impl.series.HighSeriesOf19Statistic
-import ca.josephroque.bowlingcompanion.statistics.impl.series.HighSeriesOf20Statistic
-import ca.josephroque.bowlingcompanion.statistics.impl.series.HighSeriesOf2Statistic
-import ca.josephroque.bowlingcompanion.statistics.impl.series.HighSeriesOf3Statistic
-import ca.josephroque.bowlingcompanion.statistics.impl.series.HighSeriesOf4Statistic
-import ca.josephroque.bowlingcompanion.statistics.impl.series.HighSeriesOf5Statistic
-import ca.josephroque.bowlingcompanion.statistics.impl.series.HighSeriesOf6Statistic
-import ca.josephroque.bowlingcompanion.statistics.impl.series.HighSeriesOf7Statistic
-import ca.josephroque.bowlingcompanion.statistics.impl.series.HighSeriesOf8Statistic
-import ca.josephroque.bowlingcompanion.statistics.impl.series.HighSeriesOf9Statistic
-
-/**
- * Copyright (C) 2018 Joseph Roque
- *
- * Helper methods for statistics.
- */
-object StatisticHelper {
-
- fun getFreshStatistics(): MutableList = mutableListOf(
- // General
- BowlerNameStatistic(),
- LeagueNameStatistic(),
- SeriesNameStatistic(),
- GameNameStatistic(),
- // Overall
- HighSingleStatistic(),
- TotalPinfallStatistic(),
- NumberOfGamesStatistic(),
- GameAverageStatistic(),
- MiddleHitsStatistic(),
- LeftOfMiddleHitsStatistic(),
- RightOfMiddleHitsStatistic(),
- StrikeMiddleHitsStatistic(),
- StrikesStatistic(),
- SpareConversionsStatistic(),
- // First Ball
- HeadPinsStatistic(),
- HeadPinsSparedStatistic(),
- LeftsStatistic(),
- LeftsSparedStatistic(),
- RightsStatistic(),
- RightsSparedStatistic(),
- AcesStatistic(),
- AcesSparedStatistic(),
- ChopOffsStatistic(),
- ChopOffsSparedStatistic(),
- LeftChopOffsStatistic(),
- LeftChopOffsSparedStatistic(),
- RightChopOffsStatistic(),
- RightChopOffsSparedStatistic(),
- SplitsStatistic(),
- SplitsSparedStatistic(),
- LeftSplitsStatistic(),
- LeftSplitsSparedStatistic(),
- RightSplitsStatistic(),
- RightSplitsSparedStatistic(),
- TwelvesStatistic(),
- TwelvesSparedStatistic(),
- LeftTwelvesStatistic(),
- LeftTwelvesSparedStatistic(),
- RightTwelvesStatistic(),
- RightTwelvesSparedStatistic(),
- // Fouls
- FoulsStatistic(),
- // Pins Left on Deck
- TotalPinsLeftStatistic(),
- AveragePinsLeftStatistic(),
- // Average
- Game1AverageStatistic(),
- Game2AverageStatistic(),
- Game3AverageStatistic(),
- Game4AverageStatistic(),
- Game5AverageStatistic(),
- Game6AverageStatistic(),
- Game7AverageStatistic(),
- Game8AverageStatistic(),
- Game9AverageStatistic(),
- Game10AverageStatistic(),
- Game11AverageStatistic(),
- Game12AverageStatistic(),
- Game13AverageStatistic(),
- Game14AverageStatistic(),
- Game15AverageStatistic(),
- Game16AverageStatistic(),
- Game17AverageStatistic(),
- Game18AverageStatistic(),
- Game19AverageStatistic(),
- Game20AverageStatistic(),
- // Series
- HighSeriesOf2Statistic(),
- HighSeriesOf3Statistic(),
- HighSeriesOf4Statistic(),
- HighSeriesOf5Statistic(),
- HighSeriesOf6Statistic(),
- HighSeriesOf7Statistic(),
- HighSeriesOf8Statistic(),
- HighSeriesOf9Statistic(),
- HighSeriesOf10Statistic(),
- HighSeriesOf11Statistic(),
- HighSeriesOf12Statistic(),
- HighSeriesOf13Statistic(),
- HighSeriesOf14Statistic(),
- HighSeriesOf15Statistic(),
- HighSeriesOf16Statistic(),
- HighSeriesOf17Statistic(),
- HighSeriesOf18Statistic(),
- HighSeriesOf19Statistic(),
- HighSeriesOf20Statistic(),
- // Match Play
- GamesWonStatistic(),
- GamesLostStatistic(),
- GamesTiedStatistic()
- )
-
- fun readParcelable(p: Parcel, id: Int): Statistic {
- return when (id) {
- BowlerNameStatistic.Id -> p.readParcelable(BowlerNameStatistic::class.java.classLoader)!!
- LeagueNameStatistic.Id -> p.readParcelable(LeagueNameStatistic::class.java.classLoader)!!
- SeriesNameStatistic.Id -> p.readParcelable(SeriesNameStatistic::class.java.classLoader)!!
- GameNameStatistic.Id -> p.readParcelable(GameNameStatistic::class.java.classLoader)!!
- HighSingleStatistic.Id -> p.readParcelable(HighSingleStatistic::class.java.classLoader)!!
- TotalPinfallStatistic.Id -> p.readParcelable(TotalPinfallStatistic::class.java.classLoader)!!
- NumberOfGamesStatistic.Id -> p.readParcelable(NumberOfGamesStatistic::class.java.classLoader)!!
- GameAverageStatistic.Id -> p.readParcelable(GameAverageStatistic::class.java.classLoader)!!
- MiddleHitsStatistic.Id -> p.readParcelable(MiddleHitsStatistic::class.java.classLoader)!!
- LeftOfMiddleHitsStatistic.Id -> p.readParcelable(LeftOfMiddleHitsStatistic::class.java.classLoader)!!
- RightOfMiddleHitsStatistic.Id -> p.readParcelable(RightOfMiddleHitsStatistic::class.java.classLoader)!!
- StrikeMiddleHitsStatistic.Id -> p.readParcelable(StrikeMiddleHitsStatistic::class.java.classLoader)!!
- StrikesStatistic.Id -> p.readParcelable(StrikesStatistic::class.java.classLoader)!!
- SpareConversionsStatistic.Id -> p.readParcelable(SpareConversionsStatistic::class.java.classLoader)!!
- HeadPinsStatistic.Id -> p.readParcelable(HeadPinsStatistic::class.java.classLoader)!!
- HeadPinsSparedStatistic.Id -> p.readParcelable(HeadPinsSparedStatistic::class.java.classLoader)!!
- LeftsStatistic.Id -> p.readParcelable(LeftsStatistic::class.java.classLoader)!!
- LeftsSparedStatistic.Id -> p.readParcelable(LeftsSparedStatistic::class.java.classLoader)!!
- RightsStatistic.Id -> p.readParcelable(RightsStatistic::class.java.classLoader)!!
- RightsSparedStatistic.Id -> p.readParcelable(RightsSparedStatistic::class.java.classLoader)!!
- AcesStatistic.Id -> p.readParcelable(AcesStatistic::class.java.classLoader)!!
- AcesSparedStatistic.Id -> p.readParcelable(AcesSparedStatistic::class.java.classLoader)!!
- ChopOffsStatistic.Id -> p.readParcelable(ChopOffsStatistic::class.java.classLoader)!!
- ChopOffsSparedStatistic.Id -> p.readParcelable(ChopOffsSparedStatistic::class.java.classLoader)!!
- LeftChopOffsStatistic.Id -> p.readParcelable(LeftChopOffsStatistic::class.java.classLoader)!!
- LeftChopOffsSparedStatistic.Id -> p.readParcelable(LeftChopOffsSparedStatistic::class.java.classLoader)!!
- RightChopOffsStatistic.Id -> p.readParcelable(RightChopOffsStatistic::class.java.classLoader)!!
- RightChopOffsSparedStatistic.Id -> p.readParcelable(RightChopOffsSparedStatistic::class.java.classLoader)!!
- SplitsStatistic.Id -> p.readParcelable(SplitsStatistic::class.java.classLoader)!!
- SplitsSparedStatistic.Id -> p.readParcelable(SplitsSparedStatistic::class.java.classLoader)!!
- LeftSplitsStatistic.Id -> p.readParcelable(LeftSplitsStatistic::class.java.classLoader)!!
- LeftSplitsSparedStatistic.Id -> p.readParcelable(LeftsSparedStatistic::class.java.classLoader)!!
- RightSplitsStatistic.Id -> p.readParcelable(RightSplitsStatistic::class.java.classLoader)!!
- RightSplitsSparedStatistic.Id -> p.readParcelable(RightsSparedStatistic::class.java.classLoader)!!
- TwelvesStatistic.Id -> p.readParcelable(TwelvesStatistic::class.java.classLoader)!!
- TwelvesSparedStatistic.Id -> p.readParcelable(TwelvesSparedStatistic::class.java.classLoader)!!
- LeftTwelvesStatistic.Id -> p.readParcelable(LeftTwelvesStatistic::class.java.classLoader)!!
- LeftTwelvesSparedStatistic.Id -> p.readParcelable(LeftTwelvesSparedStatistic::class.java.classLoader)!!
- RightTwelvesStatistic.Id -> p.readParcelable(RightTwelvesStatistic::class.java.classLoader)!!
- RightTwelvesSparedStatistic.Id -> p.readParcelable(RightTwelvesSparedStatistic::class.java.classLoader)!!
- FoulsStatistic.Id -> p.readParcelable(FoulsStatistic::class.java.classLoader)!!
- TotalPinsLeftStatistic.Id -> p.readParcelable(TotalPinsLeftStatistic::class.java.classLoader)!!
- AveragePinsLeftStatistic.Id -> p.readParcelable(AveragePinsLeftStatistic::class.java.classLoader)!!
- Game1AverageStatistic.Id -> p.readParcelable(Game1AverageStatistic::class.java.classLoader)!!
- Game2AverageStatistic.Id -> p.readParcelable(Game2AverageStatistic::class.java.classLoader)!!
- Game3AverageStatistic.Id -> p.readParcelable(Game3AverageStatistic::class.java.classLoader)!!
- Game4AverageStatistic.Id -> p.readParcelable(Game4AverageStatistic::class.java.classLoader)!!
- Game5AverageStatistic.Id -> p.readParcelable(Game5AverageStatistic::class.java.classLoader)!!
- Game6AverageStatistic.Id -> p.readParcelable(Game6AverageStatistic::class.java.classLoader)!!
- Game7AverageStatistic.Id -> p.readParcelable(Game7AverageStatistic::class.java.classLoader)!!
- Game8AverageStatistic.Id -> p.readParcelable(Game8AverageStatistic::class.java.classLoader)!!
- Game9AverageStatistic.Id -> p.readParcelable(Game9AverageStatistic::class.java.classLoader)!!
- Game10AverageStatistic.Id -> p.readParcelable(Game10AverageStatistic::class.java.classLoader)!!
- Game11AverageStatistic.Id -> p.readParcelable(Game11AverageStatistic::class.java.classLoader)!!
- Game12AverageStatistic.Id -> p.readParcelable(Game12AverageStatistic::class.java.classLoader)!!
- Game13AverageStatistic.Id -> p.readParcelable(Game13AverageStatistic::class.java.classLoader)!!
- Game14AverageStatistic.Id -> p.readParcelable(Game14AverageStatistic::class.java.classLoader)!!
- Game15AverageStatistic.Id -> p.readParcelable(Game15AverageStatistic::class.java.classLoader)!!
- Game16AverageStatistic.Id -> p.readParcelable(Game16AverageStatistic::class.java.classLoader)!!
- Game17AverageStatistic.Id -> p.readParcelable