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(Game17AverageStatistic::class.java.classLoader)!! - Game18AverageStatistic.Id -> p.readParcelable(Game18AverageStatistic::class.java.classLoader)!! - Game19AverageStatistic.Id -> p.readParcelable(Game19AverageStatistic::class.java.classLoader)!! - Game20AverageStatistic.Id -> p.readParcelable(Game20AverageStatistic::class.java.classLoader)!! - HighSeriesOf2Statistic.Id -> p.readParcelable(HighSeriesOf2Statistic::class.java.classLoader)!! - HighSeriesOf3Statistic.Id -> p.readParcelable(HighSeriesOf3Statistic::class.java.classLoader)!! - HighSeriesOf4Statistic.Id -> p.readParcelable(HighSeriesOf4Statistic::class.java.classLoader)!! - HighSeriesOf5Statistic.Id -> p.readParcelable(HighSeriesOf5Statistic::class.java.classLoader)!! - HighSeriesOf6Statistic.Id -> p.readParcelable(HighSeriesOf6Statistic::class.java.classLoader)!! - HighSeriesOf7Statistic.Id -> p.readParcelable(HighSeriesOf7Statistic::class.java.classLoader)!! - HighSeriesOf8Statistic.Id -> p.readParcelable(HighSeriesOf8Statistic::class.java.classLoader)!! - HighSeriesOf9Statistic.Id -> p.readParcelable(HighSeriesOf9Statistic::class.java.classLoader)!! - HighSeriesOf10Statistic.Id -> p.readParcelable(HighSeriesOf10Statistic::class.java.classLoader)!! - HighSeriesOf11Statistic.Id -> p.readParcelable(HighSeriesOf11Statistic::class.java.classLoader)!! - HighSeriesOf12Statistic.Id -> p.readParcelable(HighSeriesOf12Statistic::class.java.classLoader)!! - HighSeriesOf13Statistic.Id -> p.readParcelable(HighSeriesOf13Statistic::class.java.classLoader)!! - HighSeriesOf14Statistic.Id -> p.readParcelable(HighSeriesOf14Statistic::class.java.classLoader)!! - HighSeriesOf15Statistic.Id -> p.readParcelable(HighSeriesOf15Statistic::class.java.classLoader)!! - HighSeriesOf16Statistic.Id -> p.readParcelable(HighSeriesOf16Statistic::class.java.classLoader)!! - HighSeriesOf17Statistic.Id -> p.readParcelable(HighSeriesOf17Statistic::class.java.classLoader)!! - HighSeriesOf18Statistic.Id -> p.readParcelable(HighSeriesOf18Statistic::class.java.classLoader)!! - HighSeriesOf19Statistic.Id -> p.readParcelable(HighSeriesOf19Statistic::class.java.classLoader)!! - HighSeriesOf20Statistic.Id -> p.readParcelable(HighSeriesOf20Statistic::class.java.classLoader)!! - GamesWonStatistic.Id -> p.readParcelable(GamesWonStatistic::class.java.classLoader)!! - GamesLostStatistic.Id -> p.readParcelable(GamesLostStatistic::class.java.classLoader)!! - GamesTiedStatistic.Id -> p.readParcelable(GamesTiedStatistic::class.java.classLoader)!! - else -> throw IllegalArgumentException("$id is not a valid Statistic id.") - } - } - - fun getStatistic(id: Long): Statistic { - return when (id.toInt()) { - BowlerNameStatistic.Id -> BowlerNameStatistic() - LeagueNameStatistic.Id -> LeagueNameStatistic() - SeriesNameStatistic.Id -> SeriesNameStatistic() - GameNameStatistic.Id -> GameNameStatistic() - HighSingleStatistic.Id -> HighSingleStatistic() - TotalPinfallStatistic.Id -> TotalPinfallStatistic() - NumberOfGamesStatistic.Id -> NumberOfGamesStatistic() - GameAverageStatistic.Id -> GameAverageStatistic() - MiddleHitsStatistic.Id -> MiddleHitsStatistic() - LeftOfMiddleHitsStatistic.Id -> LeftOfMiddleHitsStatistic() - RightOfMiddleHitsStatistic.Id -> RightOfMiddleHitsStatistic() - StrikeMiddleHitsStatistic.Id -> StrikeMiddleHitsStatistic() - StrikesStatistic.Id -> StrikesStatistic() - SpareConversionsStatistic.Id -> SpareConversionsStatistic() - HeadPinsStatistic.Id -> HeadPinsStatistic() - HeadPinsSparedStatistic.Id -> HeadPinsSparedStatistic() - LeftsStatistic.Id -> LeftsStatistic() - LeftsSparedStatistic.Id -> LeftsSparedStatistic() - RightsStatistic.Id -> RightsStatistic() - RightsSparedStatistic.Id -> RightsSparedStatistic() - AcesStatistic.Id -> AcesStatistic() - AcesSparedStatistic.Id -> AcesSparedStatistic() - ChopOffsStatistic.Id -> ChopOffsStatistic() - ChopOffsSparedStatistic.Id -> ChopOffsSparedStatistic() - LeftChopOffsStatistic.Id -> LeftChopOffsStatistic() - LeftChopOffsSparedStatistic.Id -> LeftChopOffsSparedStatistic() - RightChopOffsStatistic.Id -> RightChopOffsStatistic() - RightChopOffsSparedStatistic.Id -> RightChopOffsSparedStatistic() - SplitsStatistic.Id -> SplitsStatistic() - SplitsSparedStatistic.Id -> SplitsSparedStatistic() - LeftSplitsStatistic.Id -> LeftSplitsStatistic() - LeftSplitsSparedStatistic.Id -> LeftSplitsSparedStatistic() - RightSplitsStatistic.Id -> RightSplitsStatistic() - RightSplitsSparedStatistic.Id -> RightSplitsSparedStatistic() - TwelvesStatistic.Id -> TwelvesStatistic() - TwelvesSparedStatistic.Id -> TwelvesSparedStatistic() - LeftTwelvesStatistic.Id -> LeftTwelvesStatistic() - LeftTwelvesSparedStatistic.Id -> LeftTwelvesSparedStatistic() - RightTwelvesStatistic.Id -> RightTwelvesStatistic() - RightTwelvesSparedStatistic.Id -> RightTwelvesSparedStatistic() - FoulsStatistic.Id -> FoulsStatistic() - TotalPinsLeftStatistic.Id -> TotalPinsLeftStatistic() - AveragePinsLeftStatistic.Id -> AveragePinsLeftStatistic() - Game1AverageStatistic.Id -> Game1AverageStatistic() - Game2AverageStatistic.Id -> Game2AverageStatistic() - Game3AverageStatistic.Id -> Game3AverageStatistic() - Game4AverageStatistic.Id -> Game4AverageStatistic() - Game5AverageStatistic.Id -> Game5AverageStatistic() - Game6AverageStatistic.Id -> Game6AverageStatistic() - Game7AverageStatistic.Id -> Game7AverageStatistic() - Game8AverageStatistic.Id -> Game8AverageStatistic() - Game9AverageStatistic.Id -> Game9AverageStatistic() - Game10AverageStatistic.Id -> Game10AverageStatistic() - Game11AverageStatistic.Id -> Game11AverageStatistic() - Game12AverageStatistic.Id -> Game12AverageStatistic() - Game13AverageStatistic.Id -> Game13AverageStatistic() - Game14AverageStatistic.Id -> Game14AverageStatistic() - Game15AverageStatistic.Id -> Game15AverageStatistic() - Game16AverageStatistic.Id -> Game16AverageStatistic() - Game17AverageStatistic.Id -> Game17AverageStatistic() - Game18AverageStatistic.Id -> Game18AverageStatistic() - Game19AverageStatistic.Id -> Game19AverageStatistic() - Game20AverageStatistic.Id -> Game20AverageStatistic() - HighSeriesOf2Statistic.Id -> HighSeriesOf2Statistic() - HighSeriesOf3Statistic.Id -> HighSeriesOf3Statistic() - HighSeriesOf4Statistic.Id -> HighSeriesOf4Statistic() - HighSeriesOf5Statistic.Id -> HighSeriesOf5Statistic() - HighSeriesOf6Statistic.Id -> HighSeriesOf6Statistic() - HighSeriesOf7Statistic.Id -> HighSeriesOf7Statistic() - HighSeriesOf8Statistic.Id -> HighSeriesOf8Statistic() - HighSeriesOf9Statistic.Id -> HighSeriesOf9Statistic() - HighSeriesOf10Statistic.Id -> HighSeriesOf10Statistic() - HighSeriesOf11Statistic.Id -> HighSeriesOf11Statistic() - HighSeriesOf12Statistic.Id -> HighSeriesOf12Statistic() - HighSeriesOf13Statistic.Id -> HighSeriesOf13Statistic() - HighSeriesOf14Statistic.Id -> HighSeriesOf14Statistic() - HighSeriesOf15Statistic.Id -> HighSeriesOf15Statistic() - HighSeriesOf16Statistic.Id -> HighSeriesOf16Statistic() - HighSeriesOf17Statistic.Id -> HighSeriesOf17Statistic() - HighSeriesOf18Statistic.Id -> HighSeriesOf18Statistic() - HighSeriesOf19Statistic.Id -> HighSeriesOf19Statistic() - HighSeriesOf20Statistic.Id -> HighSeriesOf20Statistic() - GamesWonStatistic.Id -> GamesWonStatistic() - GamesLostStatistic.Id -> GamesLostStatistic() - GamesTiedStatistic.Id -> GamesTiedStatistic() - else -> throw IllegalArgumentException("$id is not a valid Statistic id.") - } - } - - fun getAdjacentStatistics(id: Long, canBeGraphed: Boolean = true): Pair { - // FIXME: Find a better way to get the title of multiple statistics than creating the entire list - val statistics = getFreshStatistics() - if (canBeGraphed) statistics.retainAll { it.canBeGraphed } - val statIndex = statistics.indexOfFirst { it.id == id } - - var previousStatistic: Statistic? = null - var nextStatistic: Statistic? = null - if (statIndex > 0) previousStatistic = statistics[statIndex - 1] - if (statIndex < statistics.lastIndex) nextStatistic = statistics[statIndex + 1] - return Pair(previousStatistic, nextStatistic) - } -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/StatisticsCategory.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/StatisticsCategory.kt deleted file mode 100644 index a221394ae..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/StatisticsCategory.kt +++ /dev/null @@ -1,37 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics - -import android.content.res.Resources -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.statistics.list.StatisticListItem - -/** - * Copyright (C) 2018 Joseph Roque - * - * Categorize similar statistics into groups to be displayed together - * in a list. - */ -enum class StatisticsCategory : StatisticListItem { - General, - Overall, - FirstBall, - Fouls, - PinsOnDeck, - MatchPlay, - Average, - Series; - - fun getTitle(resources: Resources): String { - return when (this) { - General -> resources.getString(R.string.statistics_category_general) - FirstBall -> resources.getString(R.string.statistics_category_first_ball) - Fouls -> resources.getString(R.string.statistics_category_fouls) - PinsOnDeck -> resources.getString(R.string.statistics_category_pins_on_deck) - Average -> resources.getString(R.string.statistics_category_average) - Series -> resources.getString(R.string.statistics_category_series) - MatchPlay -> resources.getString(R.string.statistics_category_match_play) - Overall -> resources.getString(R.string.statistics_category_overall) - } - } - - override val id: Long = this.ordinal.toLong() -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/graph/StatisticGraphFragment.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/graph/StatisticGraphFragment.kt deleted file mode 100644 index d04884bf7..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/graph/StatisticGraphFragment.kt +++ /dev/null @@ -1,229 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.graph - -import android.content.Context -import android.os.Bundle -import android.preference.PreferenceManager -import android.support.v4.content.ContextCompat -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.fragments.BaseFragment -import ca.josephroque.bowlingcompanion.settings.Settings -import ca.josephroque.bowlingcompanion.statistics.Statistic -import ca.josephroque.bowlingcompanion.statistics.StatisticHelper -import ca.josephroque.bowlingcompanion.statistics.unit.StatisticsUnit -import com.github.mikephil.charting.components.Description -import com.github.mikephil.charting.components.Legend -import com.github.mikephil.charting.data.LineData -import com.github.mikephil.charting.data.LineDataSet -import com.github.mikephil.charting.formatter.IAxisValueFormatter -import kotlinx.coroutines.experimental.CommonPool -import kotlinx.coroutines.experimental.Deferred -import kotlinx.coroutines.experimental.async -import kotlinx.coroutines.experimental.launch -import kotlinx.android.synthetic.main.fragment_statistic_graph.tv_title as textTitle -import kotlinx.android.synthetic.main.fragment_statistic_graph.tv_prev_statistic as textPrevStatistic -import kotlinx.android.synthetic.main.fragment_statistic_graph.tv_next_statistic as textNextStatistic -import kotlinx.android.synthetic.main.fragment_statistic_graph.chart as chart -import kotlinx.android.synthetic.main.fragment_statistic_graph.tv_accumulate as textAccumulate -import kotlinx.android.synthetic.main.fragment_statistic_graph.switch_accumulate as switchAccumulate -import kotlinx.android.synthetic.main.fragment_statistic_graph.view.* - -/** - * Copyright (C) 2018 Joseph Roque - * - * Create a graph of a [Statistic] over time. - */ -class StatisticGraphFragment : BaseFragment(), - View.OnClickListener { - - companion object { - @Suppress("unused") - private const val TAG = "StatisticGraphFragment" - - private const val ARG_STATISTIC_UNIT = "${TAG}_unit" - private const val ARG_STATISTIC = "${TAG}_statistic" - - private const val MAX_GRAPH_LABELS = 10 - - fun newInstance(unit: StatisticsUnit, statisticId: Long): StatisticGraphFragment { - return StatisticGraphFragment().apply { - arguments = Bundle().apply { - putParcelable(ARG_STATISTIC_UNIT, unit) - putLong(ARG_STATISTIC, statisticId) - } - } - } - } - - private var delegate: StatisticGraphDelegate? = null - private lateinit var unit: StatisticsUnit - private var statisticId: Long = 0 - - private var inputsEnabled: Boolean = false - set(value) { - field = value - launch(Android) { - val context = this@StatisticGraphFragment.context ?: return@launch - switchAccumulate.isEnabled = value - val textColorId = if (value) R.color.primaryWhiteText else R.color.secondaryWhiteText - val textColor = ContextCompat.getColor(context, textColorId) - textPrevStatistic.setTextColor(textColor) - textNextStatistic.setTextColor(textColor) - } - } - - // MARK: Lifecycle functions - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - unit = arguments?.getParcelable(ARG_STATISTIC_UNIT)!! - statisticId = arguments?.getLong(ARG_STATISTIC)!! - - val view = inflater.inflate(R.layout.fragment_statistic_graph, container, false) - view.tv_next_statistic.setOnClickListener(this) - view.tv_prev_statistic.setOnClickListener(this) - view.switch_accumulate.setOnCheckedChangeListener { _, isChecked -> - PreferenceManager.getDefaultSharedPreferences(context) - .edit() - .putBoolean(Settings.BooleanSetting.AccumulateStatistics.prefName, isChecked) - .apply() - updateAccumulateText() - buildChart() - } - return view - } - - override fun onAttach(context: Context?) { - super.onAttach(context) - - val parent = parentFragment as? StatisticGraphDelegate ?: throw RuntimeException("${parentFragment!!} must implement StatisticGraphDelegate") - delegate = parent - } - - override fun onDetach() { - super.onDetach() - delegate = null - } - - override fun onStart() { - super.onStart() - - updateAccumulateText() - updateStatisticTitles() - buildChart() - inputsEnabled = false - } - - // MARK: BaseFragment - - override fun updateToolbarTitle() { - // Intentionally left blank - } - - // MARK: Private functions - - private fun updateStatisticTitles() { - val statistic = StatisticHelper.getStatistic(statisticId) - val (previousStatistic, nextStatistic) = StatisticHelper.getAdjacentStatistics(statisticId) - - textTitle.setText(statistic.titleId) - if (previousStatistic != null) { - textPrevStatistic.setText(previousStatistic.titleId) - } else { - textPrevStatistic.text = null - } - - if (nextStatistic != null) { - textNextStatistic.setText(nextStatistic.titleId) - } else { - textNextStatistic.text = null - } - } - - private fun updateAccumulateText() { - val preferences = PreferenceManager.getDefaultSharedPreferences(context) - val accumulateOverTime = Settings.BooleanSetting.AccumulateStatistics.getValue(preferences) - switchAccumulate.isChecked = accumulateOverTime - - if (accumulateOverTime) { - textAccumulate.setText(R.string.statistic_graph_accumulate) - } else { - textAccumulate.setText(R.string.statistic_graph_week_by_week) - } - } - - private fun addChartDataStyling(context: Context, dataSet: LineDataSet, lineIndex: Int) { - val color = when (lineIndex) { - 0 -> ContextCompat.getColor(context, R.color.colorPrimary) - 1 -> ContextCompat.getColor(context, R.color.dangerRed) - else -> throw IllegalAccessException("Only up to 2 lines are available for a statistic.") - } - - dataSet.color = color - dataSet.setCircleColor(color) - } - - private fun buildChartData(context: Context, graphLines: List): Deferred { - return async(CommonPool) { - val dataSets: List = graphLines.mapIndexed { index, graphLine -> - val dataSet = LineDataSet(graphLine.entries, graphLine.label) - addChartDataStyling(context, dataSet, index) - return@mapIndexed dataSet - } - - return@async LineData(dataSets.toList()) - } - } - - private fun buildChartXAxisFormatter(graphLabels: List): IAxisValueFormatter { - return IAxisValueFormatter { value, _ -> graphLabels[value.toInt()] } - } - - private fun buildChart() { - val context = context ?: return - launch(Android) { - val (graphLines, graphLabels) = unit.getStatisticGraphData(context, statisticId, switchAccumulate.isChecked).await() - if (graphLines.isEmpty() || graphLines[0].entries.size <= 1 || graphLabels.size <= 1) { - return@launch - } - - chart.data = buildChartData(context, graphLines).await() - chart.xAxis.valueFormatter = buildChartXAxisFormatter(graphLabels) - chart.xAxis.labelCount = Math.min(graphLabels.size, MAX_GRAPH_LABELS) - fixChartProperties() - chart.invalidate() - inputsEnabled = true - } - } - - private fun fixChartProperties() { - chart.description = Description().apply { text = "" } - chart.legend.apply { - isEnabled = true - textSize = resources.getDimension(R.dimen.text_caption_dp) / resources.displayMetrics.density - horizontalAlignment = Legend.LegendHorizontalAlignment.LEFT - verticalAlignment = Legend.LegendVerticalAlignment.BOTTOM - form = Legend.LegendForm.LINE - } - } - - // MARK: OnClickListener - - override fun onClick(v: View?) { - if (!inputsEnabled) { return } - when (v?.id) { - R.id.tv_prev_statistic -> delegate?.prevStatistic(statisticId) - R.id.tv_next_statistic -> delegate?.nextStatistic(statisticId) - else -> {} // Do nothing - } - } - - // MARK: StatisticGraphDelegate - - interface StatisticGraphDelegate { - fun nextStatistic(statisticId: Long) - fun prevStatistic(statisticId: Long) - } -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/graph/StatisticsGraphLine.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/graph/StatisticsGraphLine.kt deleted file mode 100644 index ea8b8cf22..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/graph/StatisticsGraphLine.kt +++ /dev/null @@ -1,10 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.graph - -import com.github.mikephil.charting.data.Entry - -/** - * Copyright (C) 2018 Joseph Roque - * - * Wrapper which provides the data to render a line on a statistics graph. - */ -data class StatisticsGraphLine(val label: String, val entries: List) diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/immutable/StatFrame.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/immutable/StatFrame.kt deleted file mode 100644 index e7dd26eb2..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/immutable/StatFrame.kt +++ /dev/null @@ -1,80 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.immutable - -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.games.Frame -import ca.josephroque.bowlingcompanion.games.Game -import ca.josephroque.bowlingcompanion.games.lane.Deck -import ca.josephroque.bowlingcompanion.games.lane.Pin -import ca.josephroque.bowlingcompanion.games.lane.toBooleanArray - -/** - * Copyright (C) 2018 Joseph Roque - * - * An immutable Frame for calculating statistics, loaded from the database. - */ -class StatFrame( - override val id: Long, - val ordinal: Int, - val isAccessed: Boolean, - val pinState: Array, - val ballFouled: BooleanArray -) : IIdentifiable, KParcelable { - - val pinsLeftOnDeck: Int - get() = pinState[Frame.LAST_BALL].sumBy { if (it.onDeck) it.value else 0 } - - val zeroBasedOrdinal: Int - get() = ordinal - 1 - - // MARK: Constructor - - private constructor(p: Parcel): this( - id = p.readLong(), - ordinal = p.readInt(), - isAccessed = p.readBoolean(), - pinState = Array(Frame.NUMBER_OF_BALLS) { - val pins = BooleanArray(Game.NUMBER_OF_PINS) - p.readBooleanArray(pins) - return@Array Pin.deckFromBooleanArray(pins) - }, - ballFouled = BooleanArray(Frame.NUMBER_OF_BALLS).apply { - p.readBooleanArray(this) - } - ) - - // MARK: Parcelable - - override fun writeToParcel(dest: Parcel, flags: Int) = with(dest) { - writeLong(id) - writeInt(ordinal) - writeBoolean(isAccessed) - for (i in 0 until Frame.NUMBER_OF_BALLS) { - writeBooleanArray(pinState[i].toBooleanArray()) - } - writeBooleanArray(ballFouled) - } - - companion object { - @Suppress("unused") - private const val TAG = "StatFrame" - - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::StatFrame) - - val QUERY_FIELDS = arrayOf( - "frame.${FrameEntry._ID} as fid", - "frame.${FrameEntry.COLUMN_FRAME_NUMBER}", - "frame.${FrameEntry.COLUMN_IS_ACCESSED}", - "frame.${FrameEntry.COLUMN_FOULS}", - "frame.${FrameEntry.COLUMN_PIN_STATE[0]}", - "frame.${FrameEntry.COLUMN_PIN_STATE[1]}", - "frame.${FrameEntry.COLUMN_PIN_STATE[2]}" - ) - } -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/immutable/StatGame.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/immutable/StatGame.kt deleted file mode 100644 index 806b59097..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/immutable/StatGame.kt +++ /dev/null @@ -1,68 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.immutable - -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.GameEntry -import ca.josephroque.bowlingcompanion.matchplay.MatchPlayResult - -/** - * Copyright (C) 2018 Joseph Roque - * - * An immutable Game for calculating statistics, created from the database. - */ -class StatGame( - override val id: Long, - val ordinal: Int, - val score: Int, - val isManual: Boolean, - val frames: List, - val matchPlay: MatchPlayResult -) : IIdentifiable, KParcelable { - - // MARK: Constructor - - private constructor(p: Parcel): this( - id = p.readLong(), - ordinal = p.readInt(), - score = p.readInt(), - isManual = p.readBoolean(), - frames = arrayListOf().apply { - val parcelableArray = p.readParcelableArray(StatFrame::class.java.classLoader)!! - this.addAll(parcelableArray.map { - return@map it as StatFrame - }) - }, - matchPlay = MatchPlayResult.fromInt(p.readInt())!! - ) - - // MARK: Parcelable - - override fun writeToParcel(dest: Parcel, flags: Int) = with(dest) { - writeLong(id) - writeInt(ordinal) - writeInt(score) - writeBoolean(isManual) - writeParcelableArray(frames.toTypedArray(), 0) - writeInt(matchPlay.ordinal) - } - - companion object { - @Suppress("unused") - private const val TAG = "StatGame" - - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::StatGame) - - val QUERY_FIELDS = arrayOf( - "game.${GameEntry._ID} as gid", - "game.${GameEntry.COLUMN_GAME_NUMBER}", - "game.${GameEntry.COLUMN_SCORE}", - "game.${GameEntry.COLUMN_IS_MANUAL}", - "game.${GameEntry.COLUMN_MATCH_PLAY}" - ) - } -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/immutable/StatSeries.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/immutable/StatSeries.kt deleted file mode 100644 index 7c06cd878..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/immutable/StatSeries.kt +++ /dev/null @@ -1,256 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.immutable - -import android.content.Context -import android.database.Cursor -import android.os.Parcel -import android.support.v7.preference.PreferenceManager -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.readDate -import ca.josephroque.bowlingcompanion.common.interfaces.writeDate -import ca.josephroque.bowlingcompanion.database.Contract.BowlerEntry -import ca.josephroque.bowlingcompanion.database.Contract.GameEntry -import ca.josephroque.bowlingcompanion.database.Contract.FrameEntry -import ca.josephroque.bowlingcompanion.database.Contract.LeagueEntry -import ca.josephroque.bowlingcompanion.database.Contract.SeriesEntry -import ca.josephroque.bowlingcompanion.database.Contract.TeamBowlerEntry -import ca.josephroque.bowlingcompanion.database.DatabaseHelper -import ca.josephroque.bowlingcompanion.games.Frame -import ca.josephroque.bowlingcompanion.games.Game -import ca.josephroque.bowlingcompanion.games.lane.Pin -import ca.josephroque.bowlingcompanion.leagues.League -import ca.josephroque.bowlingcompanion.matchplay.MatchPlayResult -import ca.josephroque.bowlingcompanion.scoring.Fouls -import ca.josephroque.bowlingcompanion.settings.Settings -import ca.josephroque.bowlingcompanion.utils.DateUtils -import kotlinx.coroutines.experimental.CommonPool -import kotlinx.coroutines.experimental.Deferred -import kotlinx.coroutines.experimental.async -import java.util.Date - -/** - * Copyright (C) 2018 Joseph Roque - * - * An immutable Series for calculating statistics, created from the database. - */ -class StatSeries( - override val id: Long, - val games: List, - val date: Date -) : IIdentifiable, KParcelable { - - val total: Int - get() = games.sumBy { it.score } - - // MARK: Constructor - - private constructor(p: Parcel): this( - id = p.readLong(), - games = arrayListOf().apply { - val parcelableArray = p.readParcelableArray(StatGame::class.java.classLoader)!! - this.addAll(parcelableArray.map { - return@map it as StatGame - }) - }, - date = p.readDate()!! - ) - - // MARK: Parcelable - - override fun writeToParcel(dest: Parcel, flags: Int) = with(dest) { - writeLong(id) - writeParcelableArray(games.toTypedArray(), 0) - writeDate(date) - } - - companion object { - @Suppress("unused") - private const val TAG = "StatSeries" - - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::StatSeries) - - private val queryFields = ( - "series.${SeriesEntry._ID} as sid, " + - "series.${SeriesEntry.COLUMN_SERIES_DATE}, " + - "${StatGame.QUERY_FIELDS.joinToString(separator = ", ")}, " + - "${StatFrame.QUERY_FIELDS.joinToString(separator = ", ")} ") - - fun loadSeriesForTeam(context: Context, teamId: Long): Deferred> { - val preferences = PreferenceManager.getDefaultSharedPreferences(context) - val includeOpen = Settings.BooleanSetting.IncludeOpen.getValue(preferences) - val includeEvents = Settings.BooleanSetting.IncludeEvents.getValue(preferences) - - val query = ("SELECT " + - queryFields + - "FROM ${TeamBowlerEntry.TABLE_NAME} as teamBowlers " + - "INNER JOIN ${BowlerEntry.TABLE_NAME} as bowler " + - "ON ${TeamBowlerEntry.COLUMN_BOWLER_ID}=bowler.${BowlerEntry._ID} " + - "INNER JOIN ${LeagueEntry.TABLE_NAME} as league " + - "ON bowler.${BowlerEntry._ID}=${LeagueEntry.COLUMN_BOWLER_ID} " + - "INNER JOIN ${SeriesEntry.TABLE_NAME} as series " + - "ON league.${LeagueEntry._ID}=${SeriesEntry.COLUMN_LEAGUE_ID} " + - "INNER JOIN ${GameEntry.TABLE_NAME} as game " + - "ON series.${SeriesEntry._ID}=game.${GameEntry.COLUMN_SERIES_ID} " + - "INNER JOIN ${FrameEntry.TABLE_NAME} as frame " + - "ON game.${GameEntry._ID}=frame.${FrameEntry.COLUMN_GAME_ID} " + - "WHERE teamBowlers.${TeamBowlerEntry.COLUMN_TEAM_ID}=? " + - (if (!includeOpen) "AND league.${LeagueEntry.COLUMN_LEAGUE_NAME}!=? " else "") + - (if (!includeEvents) "AND league.${LeagueEntry.COLUMN_IS_EVENT}!=? " else "") + - "ORDER BY " + - "series.${SeriesEntry.COLUMN_SERIES_DATE}, " + - "series.${SeriesEntry._ID}, " + - "game.${GameEntry.COLUMN_GAME_NUMBER}, " + - "frame.${FrameEntry.COLUMN_FRAME_NUMBER}") - val args = listOfNotNull( - teamId.toString(), - if (!includeOpen) League.PRACTICE_LEAGUE_NAME else null, - if (!includeEvents) "1" else null - ).toTypedArray() - return loadSeries(context, query, args) - } - - fun loadSeriesForBowler(context: Context, bowlerId: Long): Deferred> { - val preferences = PreferenceManager.getDefaultSharedPreferences(context) - val includeOpen = Settings.BooleanSetting.IncludeOpen.getValue(preferences) - val includeEvents = Settings.BooleanSetting.IncludeEvents.getValue(preferences) - - val query = ("SELECT " + - queryFields + - "FROM ${LeagueEntry.TABLE_NAME} as league " + - "INNER JOIN ${SeriesEntry.TABLE_NAME} as series " + - "ON league.${LeagueEntry._ID}=${SeriesEntry.COLUMN_LEAGUE_ID} " + - "INNER JOIN ${GameEntry.TABLE_NAME} as game " + - "ON series.${SeriesEntry._ID}=game.${GameEntry.COLUMN_SERIES_ID} " + - "INNER JOIN ${FrameEntry.TABLE_NAME} as frame " + - "ON game.${GameEntry._ID}=frame.${FrameEntry.COLUMN_GAME_ID} " + - "WHERE league.${LeagueEntry.COLUMN_BOWLER_ID}=? " + - (if (!includeOpen) "AND league.${LeagueEntry.COLUMN_LEAGUE_NAME}!=? " else "") + - (if (!includeEvents) "AND league.${LeagueEntry.COLUMN_IS_EVENT}!=? " else "") + - "ORDER BY " + - "series.${SeriesEntry.COLUMN_SERIES_DATE}, " + - "series.${SeriesEntry._ID}, " + - "game.${GameEntry.COLUMN_GAME_NUMBER}, " + - "frame.${FrameEntry.COLUMN_FRAME_NUMBER}") - val args = listOfNotNull( - bowlerId.toString(), - if (!includeOpen) League.PRACTICE_LEAGUE_NAME else null, - if (!includeEvents) "1" else null - ).toTypedArray() - return loadSeries(context, query, args) - } - - fun loadSeriesForLeague(context: Context, leagueId: Long): Deferred> { - val query = ("SELECT " + - queryFields + - "FROM ${SeriesEntry.TABLE_NAME} as series " + - "INNER JOIN ${GameEntry.TABLE_NAME} as game " + - "ON series.${SeriesEntry._ID}=game.${GameEntry.COLUMN_SERIES_ID} " + - "INNER JOIN ${FrameEntry.TABLE_NAME} as frame " + - "ON game.${GameEntry._ID}=frame.${FrameEntry.COLUMN_GAME_ID} " + - "WHERE series.${SeriesEntry.COLUMN_LEAGUE_ID}=? " + - "ORDER BY " + - "series.${SeriesEntry.COLUMN_SERIES_DATE}, " + - "series.${SeriesEntry._ID}, " + - "game.${GameEntry.COLUMN_GAME_NUMBER}, " + - "frame.${FrameEntry.COLUMN_FRAME_NUMBER}") - val args = arrayOf(leagueId.toString()) - return loadSeries(context, query, args) - } - - fun loadSeriesForSeries(context: Context, seriesId: Long): Deferred> { - val query = ("SELECT " + - queryFields + - "FROM ${SeriesEntry.TABLE_NAME} as series " + - "INNER JOIN ${GameEntry.TABLE_NAME} as game " + - "ON series.${SeriesEntry._ID}=game.${GameEntry.COLUMN_SERIES_ID} " + - "INNER JOIN ${FrameEntry.TABLE_NAME} as frame " + - "ON game.${GameEntry._ID}=frame.${FrameEntry.COLUMN_GAME_ID} " + - "WHERE series.${SeriesEntry._ID}=? " + - "ORDER BY game.${GameEntry.COLUMN_GAME_NUMBER}, frame.${FrameEntry.COLUMN_FRAME_NUMBER}") - val args = arrayOf(seriesId.toString()) - return loadSeries(context, query, args) - } - - private fun loadSeries(context: Context, query: String, args: Array): Deferred> { - return async(CommonPool) { - val db = DatabaseHelper.getInstance(context).writableDatabase - - var lastGameId: Long = -1 - var lastSeriesId: Long = -1 - val series: MutableList = ArrayList() - var games: MutableList = ArrayList(League.MAX_NUMBER_OF_GAMES) - var frames: MutableList = ArrayList(Game.NUMBER_OF_FRAMES) - - fun buildSeriesFromCursor(cursor: Cursor): StatSeries { - return StatSeries( - id = cursor.getLong(cursor.getColumnIndex("sid")), - games = games, - date = DateUtils.seriesDateToDate(cursor.getString(cursor.getColumnIndex(SeriesEntry.COLUMN_SERIES_DATE))) - ) - } - - fun buildGameFromCursor(cursor: Cursor): StatGame { - return StatGame( - id = cursor.getLong(cursor.getColumnIndex("gid")), - ordinal = cursor.getInt(cursor.getColumnIndex(GameEntry.COLUMN_GAME_NUMBER)), - score = cursor.getInt(cursor.getColumnIndex(GameEntry.COLUMN_SCORE)), - isManual = cursor.getInt(cursor.getColumnIndex(GameEntry.COLUMN_IS_MANUAL)) == 1, - frames = frames, - matchPlay = MatchPlayResult.fromInt(cursor.getInt(cursor.getColumnIndex(GameEntry.COLUMN_MATCH_PLAY)))!! - ) - } - - var cursor: Cursor? = null - try { - cursor = db.rawQuery(query, args, null) - if (cursor.moveToFirst()) { - while (!cursor.isAfterLast) { - val newGameId = cursor.getLong(cursor.getColumnIndex("gid")) - if (newGameId != lastGameId && lastGameId != -1L) { - cursor.moveToPrevious() - games.add(buildGameFromCursor(cursor)) - frames = ArrayList(Game.NUMBER_OF_FRAMES) - cursor.moveToNext() - } - - val newSeriesId = cursor.getLong(cursor.getColumnIndex("sid")) - if (newSeriesId != lastSeriesId && lastSeriesId != -1L) { - cursor.moveToPrevious() - series.add(buildSeriesFromCursor(cursor)) - games = ArrayList(League.MAX_NUMBER_OF_GAMES) - cursor.moveToNext() - } - - frames.add(StatFrame( - 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()) - } - )) - - lastSeriesId = newSeriesId - lastGameId = newGameId - cursor.moveToNext() - } - - cursor.moveToPrevious() - - games.add(buildGameFromCursor(cursor)) - series.add(buildSeriesFromCursor(cursor)) - } - } finally { - cursor?.close() - } - - return@async series - } - } - } -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game10AverageStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game10AverageStatistic.kt deleted file mode 100644 index 231a8e7a7..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game10AverageStatistic.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.average - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator - -/** - * Copyright (C) 2018 Joseph Roque - * - * Average score in the 10th game of a series. - */ -class Game10AverageStatistic(total: Int = 0, divisor: Int = 0) : PerGameAverageStatistic(total, divisor) { - - // MARK: Overrides - - override val gameNumber = 10 - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::Game10AverageStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_average_10 - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(total = p.readInt(), divisor = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game11AverageStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game11AverageStatistic.kt deleted file mode 100644 index 1d729973b..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game11AverageStatistic.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.average - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator - -/** - * Copyright (C) 2018 Joseph Roque - * - * Average score in the 11th game of a series. - */ -class Game11AverageStatistic(total: Int = 0, divisor: Int = 0) : PerGameAverageStatistic(total, divisor) { - - // MARK: Overrides - - override val gameNumber = 11 - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::Game11AverageStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_average_11 - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(total = p.readInt(), divisor = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game12AverageStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game12AverageStatistic.kt deleted file mode 100644 index 679574a67..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game12AverageStatistic.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.average - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator - -/** - * Copyright (C) 2018 Joseph Roque - * - * Average score in the 12th game of a series. - */ -class Game12AverageStatistic(total: Int = 0, divisor: Int = 0) : PerGameAverageStatistic(total, divisor) { - - // MARK: Overrides - - override val gameNumber = 12 - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::Game12AverageStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_average_12 - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(total = p.readInt(), divisor = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game13AverageStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game13AverageStatistic.kt deleted file mode 100644 index ae77728ea..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game13AverageStatistic.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.average - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator - -/** - * Copyright (C) 2018 Joseph Roque - * - * Average score in the 13th game of a series. - */ -class Game13AverageStatistic(total: Int = 0, divisor: Int = 0) : PerGameAverageStatistic(total, divisor) { - - // MARK: Overrides - - override val gameNumber = 13 - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::Game13AverageStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_average_13 - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(total = p.readInt(), divisor = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game14AverageStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game14AverageStatistic.kt deleted file mode 100644 index a31393dff..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game14AverageStatistic.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.average - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator - -/** - * Copyright (C) 2018 Joseph Roque - * - * Average score in the 14th game of a series. - */ -class Game14AverageStatistic(total: Int = 0, divisor: Int = 0) : PerGameAverageStatistic(total, divisor) { - - // MARK: Overrides - - override val gameNumber = 14 - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::Game14AverageStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_average_14 - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(total = p.readInt(), divisor = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game15AverageStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game15AverageStatistic.kt deleted file mode 100644 index 35f5da705..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game15AverageStatistic.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.average - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator - -/** - * Copyright (C) 2018 Joseph Roque - * - * Average score in the 15th game of a series. - */ -class Game15AverageStatistic(total: Int = 0, divisor: Int = 0) : PerGameAverageStatistic(total, divisor) { - - // MARK: Overrides - - override val gameNumber = 15 - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::Game15AverageStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_average_15 - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(total = p.readInt(), divisor = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game16AverageStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game16AverageStatistic.kt deleted file mode 100644 index 5d78e3789..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game16AverageStatistic.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.average - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator - -/** - * Copyright (C) 2018 Joseph Roque - * - * Average score in the 16th game of a series. - */ -class Game16AverageStatistic(total: Int = 0, divisor: Int = 0) : PerGameAverageStatistic(total, divisor) { - - // MARK: Overrides - - override val gameNumber = 16 - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::Game16AverageStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_average_16 - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(total = p.readInt(), divisor = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game17AverageStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game17AverageStatistic.kt deleted file mode 100644 index 9ba245232..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game17AverageStatistic.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.average - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator - -/** - * Copyright (C) 2018 Joseph Roque - * - * Average score in the 17th game of a series. - */ -class Game17AverageStatistic(total: Int = 0, divisor: Int = 0) : PerGameAverageStatistic(total, divisor) { - - // MARK: Overrides - - override val gameNumber = 17 - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::Game17AverageStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_average_17 - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(total = p.readInt(), divisor = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game18AverageStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game18AverageStatistic.kt deleted file mode 100644 index 1b05a44f9..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game18AverageStatistic.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.average - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator - -/** - * Copyright (C) 2018 Joseph Roque - * - * Average score in the 18th game of a series. - */ -class Game18AverageStatistic(total: Int = 0, divisor: Int = 0) : PerGameAverageStatistic(total, divisor) { - - // MARK: Overrides - - override val gameNumber = 18 - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::Game18AverageStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_average_18 - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(total = p.readInt(), divisor = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game19AverageStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game19AverageStatistic.kt deleted file mode 100644 index 09357c749..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game19AverageStatistic.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.average - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator - -/** - * Copyright (C) 2018 Joseph Roque - * - * Average score in the 19th game of a series. - */ -class Game19AverageStatistic(total: Int = 0, divisor: Int = 0) : PerGameAverageStatistic(total, divisor) { - - // MARK: Overrides - - override val gameNumber = 19 - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::Game19AverageStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_average_19 - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(total = p.readInt(), divisor = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game1AverageStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game1AverageStatistic.kt deleted file mode 100644 index 0febce432..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game1AverageStatistic.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.average - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator - -/** - * Copyright (C) 2018 Joseph Roque - * - * Average score in the 1st game of a series. - */ -class Game1AverageStatistic(total: Int = 0, divisor: Int = 0) : PerGameAverageStatistic(total, divisor) { - - // MARK: Overrides - - override val gameNumber = 1 - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::Game1AverageStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_average_1 - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(total = p.readInt(), divisor = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game20AverageStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game20AverageStatistic.kt deleted file mode 100644 index 17ec9ab14..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game20AverageStatistic.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.average - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator - -/** - * Copyright (C) 2018 Joseph Roque - * - * Average score in the 20th game of a series. - */ -class Game20AverageStatistic(total: Int = 0, divisor: Int = 0) : PerGameAverageStatistic(total, divisor) { - - // MARK: Overrides - - override val gameNumber = 20 - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::Game20AverageStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_average_20 - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(total = p.readInt(), divisor = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game2AverageStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game2AverageStatistic.kt deleted file mode 100644 index b4bdb6f37..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game2AverageStatistic.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.average - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator - -/** - * Copyright (C) 2018 Joseph Roque - * - * Average score in the 2nd game of a series. - */ -class Game2AverageStatistic(total: Int = 0, divisor: Int = 0) : PerGameAverageStatistic(total, divisor) { - - // MARK: Overrides - - override val gameNumber = 2 - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::Game2AverageStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_average_2 - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(total = p.readInt(), divisor = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game3AverageStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game3AverageStatistic.kt deleted file mode 100644 index 04c2b59cf..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game3AverageStatistic.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.average - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator - -/** - * Copyright (C) 2018 Joseph Roque - * - * Average score in the 3rd game of a series. - */ -class Game3AverageStatistic(total: Int = 0, divisor: Int = 0) : PerGameAverageStatistic(total, divisor) { - - // MARK: Overrides - - override val gameNumber = 3 - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::Game3AverageStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_average_3 - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(total = p.readInt(), divisor = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game4AverageStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game4AverageStatistic.kt deleted file mode 100644 index 5299ff684..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game4AverageStatistic.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.average - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator - -/** - * Copyright (C) 2018 Joseph Roque - * - * Average score in the 4th game of a series. - */ -class Game4AverageStatistic(total: Int = 0, divisor: Int = 0) : PerGameAverageStatistic(total, divisor) { - - // MARK: Overrides - - override val gameNumber = 4 - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::Game4AverageStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_average_4 - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(total = p.readInt(), divisor = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game5AverageStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game5AverageStatistic.kt deleted file mode 100644 index 5913c5cb4..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game5AverageStatistic.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.average - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator - -/** - * Copyright (C) 2018 Joseph Roque - * - * Average score in the 5th game of a series. - */ -class Game5AverageStatistic(total: Int = 0, divisor: Int = 0) : PerGameAverageStatistic(total, divisor) { - - // MARK: Overrides - - override val gameNumber = 5 - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::Game5AverageStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_average_5 - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(total = p.readInt(), divisor = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game6AverageStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game6AverageStatistic.kt deleted file mode 100644 index aeeacd574..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game6AverageStatistic.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.average - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator - -/** - * Copyright (C) 2018 Joseph Roque - * - * Average score in the 6th game of a series. - */ -class Game6AverageStatistic(total: Int = 0, divisor: Int = 0) : PerGameAverageStatistic(total, divisor) { - - // MARK: Overrides - - override val gameNumber = 6 - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::Game6AverageStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_average_6 - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(total = p.readInt(), divisor = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game7AverageStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game7AverageStatistic.kt deleted file mode 100644 index fedece016..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game7AverageStatistic.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.average - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator - -/** - * Copyright (C) 2018 Joseph Roque - * - * Average score in the 7th game of a series. - */ -class Game7AverageStatistic(total: Int = 0, divisor: Int = 0) : PerGameAverageStatistic(total, divisor) { - - // MARK: Overrides - - override val gameNumber = 7 - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::Game7AverageStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_average_7 - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(total = p.readInt(), divisor = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game8AverageStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game8AverageStatistic.kt deleted file mode 100644 index bedb57229..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game8AverageStatistic.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.average - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator - -/** - * Copyright (C) 2018 Joseph Roque - * - * Average score in the 8th game of a series. - */ -class Game8AverageStatistic(total: Int = 0, divisor: Int = 0) : PerGameAverageStatistic(total, divisor) { - - // MARK: Overrides - - override val gameNumber = 8 - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::Game8AverageStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_average_8 - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(total = p.readInt(), divisor = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game9AverageStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game9AverageStatistic.kt deleted file mode 100644 index 4919563e4..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/Game9AverageStatistic.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.average - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator - -/** - * Copyright (C) 2018 Joseph Roque - * - * Average score in the 9th game of a series. - */ -class Game9AverageStatistic(total: Int = 0, divisor: Int = 0) : PerGameAverageStatistic(total, divisor) { - - // MARK: Overrides - - override val gameNumber = 9 - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::Game9AverageStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_average_9 - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(total = p.readInt(), divisor = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/PerGameAverageStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/PerGameAverageStatistic.kt deleted file mode 100644 index 785bf4c9c..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/average/PerGameAverageStatistic.kt +++ /dev/null @@ -1,33 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.average - -import ca.josephroque.bowlingcompanion.statistics.AverageStatistic -import ca.josephroque.bowlingcompanion.statistics.StatisticsCategory -import ca.josephroque.bowlingcompanion.statistics.immutable.StatGame - -/** - * Copyright (C) 2018 Joseph Roque - * - * Average score in the nth game of a series. - */ -abstract class PerGameAverageStatistic(override var total: Int = 0, override var divisor: Int = 0) : AverageStatistic { - - // MARK: Modifiers - - /** @Override */ - override fun modify(game: StatGame) { - if (game.ordinal == gameNumber) { - divisor++ - total += game.score - } - } - - // MARK: Overrides - - override val category = StatisticsCategory.Average - override fun isModifiedBy(game: StatGame) = game.score > 0 - - // MARK: PerGameAverageStatistic - - /** Number of game in the series. */ - abstract val gameNumber: Int -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/AcesSparedStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/AcesSparedStatistic.kt deleted file mode 100644 index b6e0016e1..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/AcesSparedStatistic.kt +++ /dev/null @@ -1,45 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.firstball - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.games.lane.Deck -import ca.josephroque.bowlingcompanion.games.lane.arePinsCleared -import ca.josephroque.bowlingcompanion.games.lane.isAce - -/** - * Copyright (C) 2018 Joseph Roque - * - * Percentage of possible aces which the user successfully spared. - */ -class AcesSparedStatistic(numerator: Int = 0, denominator: Int = 0) : SecondBallStatistic(numerator, denominator) { - - // MARK: Modifiers - - /** @Override */ - override fun isModifiedByFirstBall(deck: Deck) = deck.isAce - - /** @Override */ - override fun isModifiedBySecondBall(deck: Deck) = deck.arePinsCleared - - override val titleId = Id - override val id = Id.toLong() - override val secondaryGraphDataLabelId = R.string.statistic_total_aces - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::AcesSparedStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_aces_spared - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(numerator = p.readInt(), denominator = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/AcesStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/AcesStatistic.kt deleted file mode 100644 index 282092d75..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/AcesStatistic.kt +++ /dev/null @@ -1,40 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.firstball - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.games.lane.Deck -import ca.josephroque.bowlingcompanion.games.lane.isAce - -/** - * Copyright (C) 2018 Joseph Roque - * - * Percentage of shots which are aces. - */ -class AcesStatistic(numerator: Int = 0, denominator: Int = 0) : FirstBallStatistic(numerator, denominator) { - - // MARK: Modifiers - - /** @Override */ - override fun isModifiedBy(deck: Deck) = deck.isAce - - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::AcesStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_aces - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(numerator = p.readInt(), denominator = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/ChopOffsSparedStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/ChopOffsSparedStatistic.kt deleted file mode 100644 index 93f106999..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/ChopOffsSparedStatistic.kt +++ /dev/null @@ -1,45 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.firstball - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.games.lane.Deck -import ca.josephroque.bowlingcompanion.games.lane.arePinsCleared -import ca.josephroque.bowlingcompanion.games.lane.isChopOff - -/** - * Copyright (C) 2018 Joseph Roque - * - * Percentage of possible chop offs which the user successfully spared. - */ -class ChopOffsSparedStatistic(numerator: Int = 0, denominator: Int = 0) : SecondBallStatistic(numerator, denominator) { - - // MARK: Modifiers - - /** @Override */ - override fun isModifiedByFirstBall(deck: Deck) = deck.isChopOff - - /** @Override */ - override fun isModifiedBySecondBall(deck: Deck) = deck.arePinsCleared - - override val titleId = Id - override val id = Id.toLong() - override val secondaryGraphDataLabelId = R.string.statistic_total_chop_offs - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::ChopOffsSparedStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_chops_spared - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(numerator = p.readInt(), denominator = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/ChopOffsStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/ChopOffsStatistic.kt deleted file mode 100644 index 5fb72f57a..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/ChopOffsStatistic.kt +++ /dev/null @@ -1,40 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.firstball - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.games.lane.Deck -import ca.josephroque.bowlingcompanion.games.lane.isChopOff - -/** - * Copyright (C) 2018 Joseph Roque - * - * Percentage of shots which are chop offs. - */ -class ChopOffsStatistic(numerator: Int = 0, denominator: Int = 0) : FirstBallStatistic(numerator, denominator) { - - // MARK: Modifiers - - /** @Override */ - override fun isModifiedBy(deck: Deck) = deck.isChopOff - - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::ChopOffsStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_chops - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(numerator = p.readInt(), denominator = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/FirstBallStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/FirstBallStatistic.kt deleted file mode 100644 index b4821a5b5..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/FirstBallStatistic.kt +++ /dev/null @@ -1,50 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.firstball - -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.games.Game -import ca.josephroque.bowlingcompanion.games.lane.Deck -import ca.josephroque.bowlingcompanion.games.lane.arePinsCleared -import ca.josephroque.bowlingcompanion.statistics.PercentageStatistic -import ca.josephroque.bowlingcompanion.statistics.StatisticsCategory -import ca.josephroque.bowlingcompanion.statistics.immutable.StatFrame - -/** - * Copyright (C) 2018 Joseph Roque - * - * Parent class for statistics which are calculated based on the user throwing at a - * full deck of pins. - */ -abstract class FirstBallStatistic(override var numerator: Int = 0, override var denominator: Int = 0) : PercentageStatistic { - - override fun isModifiedBy(frame: StatFrame) = true - override val category = StatisticsCategory.FirstBall - override val secondaryGraphDataLabelId = R.string.statistic_total_shots_at_middle - - // MARK: Statistic - - override fun modify(frame: StatFrame) { - // This function has a similar construction to `StrikeMiddleHitsStatistic.modify(StatFrame) - // and the two should remain aligned - - // Every frame adds 1 possible hit - denominator++ - numerator += if (isModifiedBy(frame.pinState[0])) 1 else 0 - if (frame.zeroBasedOrdinal == Game.LAST_FRAME) { - // In the 10th frame, for each time the first or second ball cleared the lane, add - // another middle hit chance, and check if the statistic is modified - if (frame.pinState[0].arePinsCleared) { - denominator++ - numerator += if (isModifiedBy(frame.pinState[1])) 1 else 0 - } - - if (frame.pinState[1].arePinsCleared) { - denominator++ - numerator += if (isModifiedBy(frame.pinState[2])) 1 else 0 - } - } - } - - // MARK: FirstBallStatistic - - abstract fun isModifiedBy(deck: Deck): Boolean -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/HeadPinsSparedStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/HeadPinsSparedStatistic.kt deleted file mode 100644 index 059150791..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/HeadPinsSparedStatistic.kt +++ /dev/null @@ -1,45 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.firstball - -import android.content.SharedPreferences -import android.os.Parcel -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.games.lane.Deck -import ca.josephroque.bowlingcompanion.games.lane.arePinsCleared -import ca.josephroque.bowlingcompanion.games.lane.isHeadPin -import ca.josephroque.bowlingcompanion.settings.Settings - -/** - * Copyright (C) 2018 Joseph Roque - * - * Percentage of possible head pins which the user successfully spared. - */ -class HeadPinsSparedStatistic(numerator: Int = 0, denominator: Int = 0) : SecondBallStatistic(numerator, denominator) { - - companion object { - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::HeadPinsSparedStatistic) - - const val Id = R.string.statistic_head_pins_spared - } - - override val titleId = Id - override val id = Id.toLong() - override val secondaryGraphDataLabelId = R.string.statistic_total_head_pins - - private var countH2asH: Boolean = Settings.BooleanSetting.CountH2AsH.default - - // MARK: Statistic - - override fun isModifiedByFirstBall(deck: Deck) = deck.isHeadPin(countH2asH) - - override fun isModifiedBySecondBall(deck: Deck) = deck.arePinsCleared - - override fun updatePreferences(preferences: SharedPreferences) { - countH2asH = Settings.BooleanSetting.CountH2AsH.getValue(preferences) - } - - // MARK: Constructors - - private constructor(p: Parcel): this(numerator = p.readInt(), denominator = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/HeadPinsStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/HeadPinsStatistic.kt deleted file mode 100644 index 300e3d081..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/HeadPinsStatistic.kt +++ /dev/null @@ -1,41 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.firstball - -import android.content.SharedPreferences -import android.os.Parcel -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.games.lane.Deck -import ca.josephroque.bowlingcompanion.games.lane.isHeadPin -import ca.josephroque.bowlingcompanion.settings.Settings - -/** - * Copyright (C) 2018 Joseph Roque - * - * Percentage of shots which are head pins. - */ -class HeadPinsStatistic(numerator: Int = 0, denominator: Int = 0) : FirstBallStatistic(numerator, denominator) { - - companion object { - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::HeadPinsStatistic) - - const val Id = R.string.statistic_head_pins - } - - private var countH2asH: Boolean = Settings.BooleanSetting.CountH2AsH.default - - override val titleId = Id - override val id = Id.toLong() - - // MARK: Statistic - - override fun isModifiedBy(deck: Deck) = deck.isHeadPin(countH2asH) - - override fun updatePreferences(preferences: SharedPreferences) { - countH2asH = Settings.BooleanSetting.CountH2AsH.getValue(preferences) - } - - // MARK: Constructors - - private constructor(p: Parcel) : this(numerator = p.readInt(), denominator = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/LeftChopOffsSparedStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/LeftChopOffsSparedStatistic.kt deleted file mode 100644 index da805d5fc..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/LeftChopOffsSparedStatistic.kt +++ /dev/null @@ -1,47 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.firstball - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.games.lane.Deck -import ca.josephroque.bowlingcompanion.games.lane.arePinsCleared -import ca.josephroque.bowlingcompanion.games.lane.isLeftChopOff - -/** - * Copyright (C) 2018 Joseph Roque - * - * Percentage of possible left chop offs which the user successfully spared. - */ -class LeftChopOffsSparedStatistic(numerator: Int = 0, denominator: Int = 0) : SecondBallStatistic(numerator, denominator) { - - // MARK: Modifiers - - /** @Override */ - override fun isModifiedByFirstBall(deck: Deck) = deck.isLeftChopOff - - /** @Override */ - override fun isModifiedBySecondBall(deck: Deck) = deck.arePinsCleared - - // MARK: Overrides - - override val titleId = Id - override val id = Id.toLong() - override val secondaryGraphDataLabelId = R.string.statistic_total_left_chop_offs - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::LeftChopOffsSparedStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_left_chops_spared - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(numerator = p.readInt(), denominator = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/LeftChopOffsStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/LeftChopOffsStatistic.kt deleted file mode 100644 index e726174d4..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/LeftChopOffsStatistic.kt +++ /dev/null @@ -1,42 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.firstball - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.games.lane.Deck -import ca.josephroque.bowlingcompanion.games.lane.isLeftChopOff - -/** - * Copyright (C) 2018 Joseph Roque - * - * Percentage of shots which are left chop offs. - */ -class LeftChopOffsStatistic(numerator: Int = 0, denominator: Int = 0) : FirstBallStatistic(numerator, denominator) { - - // MARK: Modifiers - - /** @Override */ - override fun isModifiedBy(deck: Deck) = deck.isLeftChopOff - - // MARK: Overrides - - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::LeftChopOffsStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_left_chops - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(numerator = p.readInt(), denominator = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/LeftSplitsSparedStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/LeftSplitsSparedStatistic.kt deleted file mode 100644 index 8ad5f41ed..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/LeftSplitsSparedStatistic.kt +++ /dev/null @@ -1,45 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.firstball - -import android.content.SharedPreferences -import android.os.Parcel -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.games.lane.Deck -import ca.josephroque.bowlingcompanion.games.lane.arePinsCleared -import ca.josephroque.bowlingcompanion.games.lane.isLeftSplit -import ca.josephroque.bowlingcompanion.settings.Settings - -/** - * Copyright (C) 2018 Joseph Roque - * - * Percentage of possible left splits which the user successfully spared. - */ -class LeftSplitsSparedStatistic(numerator: Int = 0, denominator: Int = 0) : SecondBallStatistic(numerator, denominator) { - - companion object { - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::LeftSplitsSparedStatistic) - - const val Id = R.string.statistic_left_splits_spared - } - - override val titleId = Id - override val id = Id.toLong() - override val secondaryGraphDataLabelId = R.string.statistic_total_left_splits - - private var countS2asS: Boolean = Settings.BooleanSetting.CountS2AsS.default - - // MARK: Statistic - - override fun isModifiedByFirstBall(deck: Deck) = deck.isLeftSplit(countS2asS) - - override fun isModifiedBySecondBall(deck: Deck) = deck.arePinsCleared - - override fun updatePreferences(preferences: SharedPreferences) { - countS2asS = Settings.BooleanSetting.CountS2AsS.getValue(preferences) - } - - // MARK: Constructors - - private constructor(p: Parcel): this(numerator = p.readInt(), denominator = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/LeftSplitsStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/LeftSplitsStatistic.kt deleted file mode 100644 index 74fdd4118..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/LeftSplitsStatistic.kt +++ /dev/null @@ -1,41 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.firstball - -import android.content.SharedPreferences -import android.os.Parcel -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.games.lane.Deck -import ca.josephroque.bowlingcompanion.games.lane.isLeftSplit -import ca.josephroque.bowlingcompanion.settings.Settings - -/** - * Copyright (C) 2018 Joseph Roque - * - * Percentage of shots which are left splits. - */ -class LeftSplitsStatistic(numerator: Int = 0, denominator: Int = 0) : FirstBallStatistic(numerator, denominator) { - - companion object { - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::LeftSplitsStatistic) - - const val Id = R.string.statistic_left_splits - } - - override val titleId = Id - override val id = Id.toLong() - - private var countS2asS: Boolean = Settings.BooleanSetting.CountS2AsS.default - - // MARK: Statistics - - override fun isModifiedBy(deck: Deck) = deck.isLeftSplit(countS2asS) - - override fun updatePreferences(preferences: SharedPreferences) { - countS2asS = Settings.BooleanSetting.CountS2AsS.getValue(preferences) - } - - // MARK: Constructors - - private constructor(p: Parcel): this(numerator = p.readInt(), denominator = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/LeftTwelvesSparedStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/LeftTwelvesSparedStatistic.kt deleted file mode 100644 index 75be09747..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/LeftTwelvesSparedStatistic.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.firstball - -import android.os.Parcel -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.games.lane.Deck -import ca.josephroque.bowlingcompanion.games.lane.arePinsCleared -import ca.josephroque.bowlingcompanion.games.lane.isLeftTwelve - -/** - * Copyright (C) 2018 Joseph Roque - * - * Percentage of possible left twelves which the user successfully spared - */ -class LeftTwelvesSparedStatistic(numerator: Int = 0, denominator: Int = 0) : SecondBallStatistic(numerator, denominator) { - - // MARK: Statistic - - override fun isModifiedByFirstBall(deck: Deck) = deck.isLeftTwelve - override fun isModifiedBySecondBall(deck: Deck) = deck.arePinsCleared - - override val titleId = Id - override val id = Id.toLong() - override val secondaryGraphDataLabelId = R.string.statistic_left_twelves - - // MARK: Parcelable - - companion object { - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::LeftTwelvesSparedStatistic) - - const val Id = R.string.statistic_left_twelves_spared - } - - private constructor(p: Parcel): this(numerator = p.readInt(), denominator = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/LeftTwelvesStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/LeftTwelvesStatistic.kt deleted file mode 100644 index d329d0368..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/LeftTwelvesStatistic.kt +++ /dev/null @@ -1,33 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.firstball - -import android.os.Parcel -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.games.lane.Deck -import ca.josephroque.bowlingcompanion.games.lane.isLeftTwelve - -/** - * Copyright (C) 2018 Joseph Roque - * - * Percentage of first ball shots which scored 12 points, knocking over the left 3 pin. - */ -class LeftTwelvesStatistic(numerator: Int = 0, denominator: Int = 0) : FirstBallStatistic(numerator, denominator) { - - // MARK: Statistic - - override fun isModifiedBy(deck: Deck) = deck.isLeftTwelve - - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::LeftTwelvesStatistic) - - const val Id = R.string.statistic_left_twelves - } - - private constructor(p: Parcel): this(numerator = p.readInt(), denominator = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/LeftsSparedStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/LeftsSparedStatistic.kt deleted file mode 100644 index 1feeca81f..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/LeftsSparedStatistic.kt +++ /dev/null @@ -1,37 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.firstball - -import android.os.Parcel -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.games.lane.Deck -import ca.josephroque.bowlingcompanion.games.lane.arePinsCleared -import ca.josephroque.bowlingcompanion.games.lane.isLeft - -/** - * Copyright (C) 2018 Joseph Roque - * - * Percentage of possible lefts which the user successfully spared. - */ -class LeftsSparedStatistic(numerator: Int = 0, denominator: Int = 0) : SecondBallStatistic(numerator, denominator) { - - companion object { - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::LeftsSparedStatistic) - - const val Id = R.string.statistic_lefts_spared - } - - override val titleId = Id - override val id = Id.toLong() - override val secondaryGraphDataLabelId = R.string.statistic_total_lefts - - // MARK: Statistic - - override fun isModifiedByFirstBall(deck: Deck) = deck.isLeft - - override fun isModifiedBySecondBall(deck: Deck) = deck.arePinsCleared - - // MARK: Constructors - - private constructor(p: Parcel): this(numerator = p.readInt(), denominator = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/LeftsStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/LeftsStatistic.kt deleted file mode 100644 index 9aa0f9420..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/LeftsStatistic.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.firstball - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.games.lane.Deck -import ca.josephroque.bowlingcompanion.games.lane.isLeft - -/** - * Copyright (C) 2018 Joseph Roque - * - * Percentage of shots which are lefts. - */ -class LeftsStatistic(numerator: Int = 0, denominator: Int = 0) : FirstBallStatistic(numerator, denominator) { - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::LeftsStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_lefts - } - - override val titleId = Id - override val id = Id.toLong() - - // MARK: Statistic - - override fun isModifiedBy(deck: Deck) = deck.isLeft - - // MARK: Constructors - - private constructor(p: Parcel): this(numerator = p.readInt(), denominator = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/RightChopOffsSparedStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/RightChopOffsSparedStatistic.kt deleted file mode 100644 index 76b1fef06..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/RightChopOffsSparedStatistic.kt +++ /dev/null @@ -1,47 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.firstball - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.games.lane.Deck -import ca.josephroque.bowlingcompanion.games.lane.arePinsCleared -import ca.josephroque.bowlingcompanion.games.lane.isRightChopOff - -/** - * Copyright (C) 2018 Joseph Roque - * - * Percentage of possible right chop offs which the user successfully spared. - */ -class RightChopOffsSparedStatistic(numerator: Int = 0, denominator: Int = 0) : SecondBallStatistic(numerator, denominator) { - - // MARK: Modifiers - - /** @Override */ - override fun isModifiedByFirstBall(deck: Deck) = deck.isRightChopOff - - /** @Override */ - override fun isModifiedBySecondBall(deck: Deck) = deck.arePinsCleared - - // MARK: Overrides - - override val titleId = Id - override val id = Id.toLong() - override val secondaryGraphDataLabelId = R.string.statistic_total_right_chop_offs - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::RightChopOffsSparedStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_right_chops_spared - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(numerator = p.readInt(), denominator = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/RightChopOffsStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/RightChopOffsStatistic.kt deleted file mode 100644 index 118af687f..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/RightChopOffsStatistic.kt +++ /dev/null @@ -1,42 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.firstball - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.games.lane.Deck -import ca.josephroque.bowlingcompanion.games.lane.isRightChopOff - -/** - * Copyright (C) 2018 Joseph Roque - * - * Percentage of shots which are right chop offs. - */ -class RightChopOffsStatistic(numerator: Int = 0, denominator: Int = 0) : FirstBallStatistic(numerator, denominator) { - - // MARK: Modifiers - - /** @Override */ - override fun isModifiedBy(deck: Deck) = deck.isRightChopOff - - // MARK: Overrides - - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::RightChopOffsStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_right_chops - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(numerator = p.readInt(), denominator = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/RightSplitsSparedStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/RightSplitsSparedStatistic.kt deleted file mode 100644 index bca95b5fb..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/RightSplitsSparedStatistic.kt +++ /dev/null @@ -1,45 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.firstball - -import android.content.SharedPreferences -import android.os.Parcel -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.games.lane.Deck -import ca.josephroque.bowlingcompanion.games.lane.arePinsCleared -import ca.josephroque.bowlingcompanion.games.lane.isRightSplit -import ca.josephroque.bowlingcompanion.settings.Settings - -/** - * Copyright (C) 2018 Joseph Roque - * - * Percentage of possible right splits which the user successfully spared. - */ -class RightSplitsSparedStatistic(numerator: Int = 0, denominator: Int = 0) : SecondBallStatistic(numerator, denominator) { - - companion object { - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::RightSplitsSparedStatistic) - - const val Id = R.string.statistic_right_splits_spared - } - - override val titleId = Id - override val id = Id.toLong() - override val secondaryGraphDataLabelId = R.string.statistic_total_right_splits - - private var countS2asS: Boolean = Settings.BooleanSetting.CountS2AsS.default - - // MARK: Statistic - - override fun isModifiedByFirstBall(deck: Deck) = deck.isRightSplit(countS2asS) - - override fun isModifiedBySecondBall(deck: Deck) = deck.arePinsCleared - - override fun updatePreferences(preferences: SharedPreferences) { - countS2asS = Settings.BooleanSetting.CountS2AsS.getValue(preferences) - } - - // MARK: Constructors - - private constructor(p: Parcel): this(numerator = p.readInt(), denominator = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/RightSplitsStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/RightSplitsStatistic.kt deleted file mode 100644 index 178840c5f..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/RightSplitsStatistic.kt +++ /dev/null @@ -1,41 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.firstball - -import android.content.SharedPreferences -import android.os.Parcel -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.games.lane.Deck -import ca.josephroque.bowlingcompanion.games.lane.isRightSplit -import ca.josephroque.bowlingcompanion.settings.Settings - -/** - * Copyright (C) 2018 Joseph Roque - * - * Percentage of shots which are right splits. - */ -class RightSplitsStatistic(numerator: Int = 0, denominator: Int = 0) : FirstBallStatistic(numerator, denominator) { - - companion object { - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::RightSplitsStatistic) - - const val Id = R.string.statistic_right_splits - } - - override val titleId = Id - override val id = Id.toLong() - - private var countS2asS: Boolean = Settings.BooleanSetting.CountS2AsS.default - - // MARK: Statistic - - override fun isModifiedBy(deck: Deck) = deck.isRightSplit(countS2asS) - - override fun updatePreferences(preferences: SharedPreferences) { - countS2asS = Settings.BooleanSetting.CountS2AsS.getValue(preferences) - } - - // MARK: Constructors - - private constructor(p: Parcel): this(numerator = p.readInt(), denominator = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/RightTwelvesSparedStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/RightTwelvesSparedStatistic.kt deleted file mode 100644 index d3305562b..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/RightTwelvesSparedStatistic.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.firstball - -import android.os.Parcel -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.games.lane.Deck -import ca.josephroque.bowlingcompanion.games.lane.arePinsCleared -import ca.josephroque.bowlingcompanion.games.lane.isRightTwelve - -/** - * Copyright (C) 2018 Joseph Roque - * - * Percentage of possible right twelves which the user successfully spared - */ -class RightTwelvesSparedStatistic(numerator: Int = 0, denominator: Int = 0) : SecondBallStatistic(numerator, denominator) { - - // MARK: Statistic - - override fun isModifiedByFirstBall(deck: Deck) = deck.isRightTwelve - override fun isModifiedBySecondBall(deck: Deck) = deck.arePinsCleared - - override val titleId = Id - override val id = Id.toLong() - override val secondaryGraphDataLabelId = R.string.statistic_right_twelves - - // MARK: Parcelable - - companion object { - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::RightTwelvesSparedStatistic) - - const val Id = R.string.statistic_right_twelves_spared - } - - private constructor(p: Parcel): this(numerator = p.readInt(), denominator = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/RightTwelvesStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/RightTwelvesStatistic.kt deleted file mode 100644 index 487ecb6d0..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/RightTwelvesStatistic.kt +++ /dev/null @@ -1,33 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.firstball - -import android.os.Parcel -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.games.lane.Deck -import ca.josephroque.bowlingcompanion.games.lane.isRightTwelve - -/** - * Copyright (C) 2018 Joseph Roque - * - * Percentage of first ball shots which scored 12 points, knocking over the right 3 pin. - */ -class RightTwelvesStatistic(numerator: Int = 0, denominator: Int = 0) : FirstBallStatistic(numerator, denominator) { - - // MARK: Statistic - - override fun isModifiedBy(deck: Deck) = deck.isRightTwelve - - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::RightTwelvesStatistic) - - const val Id = R.string.statistic_right_twelves - } - - private constructor(p: Parcel): this(numerator = p.readInt(), denominator = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/RightsSparedStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/RightsSparedStatistic.kt deleted file mode 100644 index a4a0d4ed2..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/RightsSparedStatistic.kt +++ /dev/null @@ -1,47 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.firstball - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.games.lane.Deck -import ca.josephroque.bowlingcompanion.games.lane.arePinsCleared -import ca.josephroque.bowlingcompanion.games.lane.isRight - -/** - * Copyright (C) 2018 Joseph Roque - * - * Percentage of possible rights which the user successfully spared. - */ -class RightsSparedStatistic(numerator: Int = 0, denominator: Int = 0) : SecondBallStatistic(numerator, denominator) { - - // MARK: Modifiers - - /** @Override */ - override fun isModifiedByFirstBall(deck: Deck) = deck.isRight - - /** @Override */ - override fun isModifiedBySecondBall(deck: Deck) = deck.arePinsCleared - - // MARK: Overrides - - override val titleId = Id - override val id = Id.toLong() - override val secondaryGraphDataLabelId = R.string.statistic_total_rights - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::RightsSparedStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_rights_spared - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(numerator = p.readInt(), denominator = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/RightsStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/RightsStatistic.kt deleted file mode 100644 index e41c19118..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/RightsStatistic.kt +++ /dev/null @@ -1,42 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.firstball - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.games.lane.Deck -import ca.josephroque.bowlingcompanion.games.lane.isRight - -/** - * Copyright (C) 2018 Joseph Roque - * - * Percentage of shots which are rights. - */ -class RightsStatistic(numerator: Int = 0, denominator: Int = 0) : FirstBallStatistic(numerator, denominator) { - - // MARK: Modifiers - - /** @Override */ - override fun isModifiedBy(deck: Deck) = deck.isRight - - // MARK: Overrides - - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::RightsStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_rights - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(numerator = p.readInt(), denominator = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/SecondBallStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/SecondBallStatistic.kt deleted file mode 100644 index 5e13b9c90..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/SecondBallStatistic.kt +++ /dev/null @@ -1,78 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.firstball - -import ca.josephroque.bowlingcompanion.games.Game -import ca.josephroque.bowlingcompanion.games.lane.Deck -import ca.josephroque.bowlingcompanion.games.lane.arePinsCleared -import ca.josephroque.bowlingcompanion.statistics.PercentageStatistic -import ca.josephroque.bowlingcompanion.statistics.StatisticsCategory -import ca.josephroque.bowlingcompanion.statistics.immutable.StatFrame - -/** - * Copyright (C) 2018 Joseph Roque - * - * Parent class for statistics which are calculated based on the user throwing a second ball - * at a deck of pins. - */ -abstract class SecondBallStatistic(override var numerator: Int = 0, override var denominator: Int = 0, private var incompatible: Int = 0) : PercentageStatistic { - - // MARK: Modifiers - - /** @Override */ - override fun modify(frame: StatFrame) { - if (frame.zeroBasedOrdinal == Game.LAST_FRAME) { - // In the 10th frame, for each time the first or second ball cleared the lane, - // add another second ball opportunity and check if the statistic is modified - if (!frame.pinState[0].arePinsCleared && isModifiedByFirstBall(frame.pinState[0], frame.pinState[1])) { - denominator++ - numerator += if (isModifiedBySecondBall(frame.pinState[1])) 1 else 0 - } else if (frame.pinState[0].arePinsCleared && !frame.pinState[1].arePinsCleared && isModifiedByFirstBall(frame.pinState[1], frame.pinState[2])) { - denominator++ - numerator += if (isModifiedBySecondBall(frame.pinState[2])) 1 else 0 - } else if (frame.pinState[1].arePinsCleared && !frame.pinState[2].arePinsCleared && isModifiedByFirstBall(frame.pinState[2])) { - incompatible++ - } - } else { - // Every frame which is not a strike adds 1 possible hit - if (!frame.pinState[0].arePinsCleared && isModifiedByFirstBall(frame.pinState[0], frame.pinState[1])) { - denominator++ - numerator += if (isModifiedBySecondBall(frame.pinState[1])) 1 else 0 - } - } - } - - // MARK: SecondBallStatistic - - /** - * Indicates if this statistic will be modified by a given [Deck] which is the first ball of a frame. - * Also provides the second ball for special cases. By default calls `isModifiedByFirstBall(deck)`. - * - * @param firstBall the first ball of the two shots - * @param secondBall the second ball of the two shots - * @return true if the first ball provides the right conditions for the statistic to be valid, false otherwise - */ - open fun isModifiedByFirstBall(firstBall: Deck, secondBall: Deck): Boolean { - return isModifiedByFirstBall(firstBall) - } - - /** Indicates if this statistic will be modified by a given [Deck] which is the first ball of a frame. */ - open fun isModifiedByFirstBall(deck: Deck): Boolean { - return false - } - - /** Indicates if this statistic will be modified by a given [Deck] which is the second ball of a frame. */ - abstract fun isModifiedBySecondBall(deck: Deck): Boolean - - // MARK: Overrides - - override val category = StatisticsCategory.FirstBall - override fun isModifiedBy(frame: StatFrame) = true - - /** @Override */ - override fun getSubtitle(): String? { - return if (incompatible > 0) { - "$incompatible thrown in 10th frame on the last ball could not be spared" - } else { - null - } - } -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/SplitsSparedStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/SplitsSparedStatistic.kt deleted file mode 100644 index c9de4cd63..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/SplitsSparedStatistic.kt +++ /dev/null @@ -1,45 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.firstball - -import android.content.SharedPreferences -import android.os.Parcel -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.games.lane.Deck -import ca.josephroque.bowlingcompanion.games.lane.arePinsCleared -import ca.josephroque.bowlingcompanion.games.lane.isSplit -import ca.josephroque.bowlingcompanion.settings.Settings - -/** - * Copyright (C) 2018 Joseph Roque - * - * Percentage of possible splits which the user successfully spared. - */ -class SplitsSparedStatistic(numerator: Int = 0, denominator: Int = 0) : SecondBallStatistic(numerator, denominator) { - - companion object { - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::SplitsSparedStatistic) - - const val Id = R.string.statistic_splits_spared - } - - override val titleId = Id - override val id = Id.toLong() - override val secondaryGraphDataLabelId = R.string.statistic_total_splits - - private var countS2asS: Boolean = Settings.BooleanSetting.CountS2AsS.default - - // MARK: Statistic - - override fun isModifiedByFirstBall(deck: Deck) = deck.isSplit(countS2asS) - - override fun isModifiedBySecondBall(deck: Deck) = deck.arePinsCleared - - override fun updatePreferences(preferences: SharedPreferences) { - countS2asS = Settings.BooleanSetting.CountS2AsS.getValue(preferences) - } - - // MARK: Constructors - - private constructor(p: Parcel): this(numerator = p.readInt(), denominator = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/SplitsStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/SplitsStatistic.kt deleted file mode 100644 index b6002c88b..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/SplitsStatistic.kt +++ /dev/null @@ -1,41 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.firstball - -import android.content.SharedPreferences -import android.os.Parcel -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.games.lane.Deck -import ca.josephroque.bowlingcompanion.games.lane.isSplit -import ca.josephroque.bowlingcompanion.settings.Settings - -/** - * Copyright (C) 2018 Joseph Roque - * - * Percentage of shots which are splits. - */ -class SplitsStatistic(numerator: Int = 0, denominator: Int = 0) : FirstBallStatistic(numerator, denominator) { - - companion object { - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::SplitsStatistic) - - const val Id = R.string.statistic_splits - } - - override val titleId = Id - override val id = Id.toLong() - - private var countS2asS: Boolean = Settings.BooleanSetting.CountS2AsS.default - - // MARK: Statistic - - override fun isModifiedBy(deck: Deck) = deck.isSplit(countS2asS) - - override fun updatePreferences(preferences: SharedPreferences) { - countS2asS = Settings.BooleanSetting.CountS2AsS.getValue(preferences) - } - - // MARK: Constructors - - private constructor(p: Parcel): this(numerator = p.readInt(), denominator = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/TwelvesSparedStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/TwelvesSparedStatistic.kt deleted file mode 100644 index fdae3594c..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/TwelvesSparedStatistic.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.firstball - -import android.os.Parcel -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.games.lane.Deck -import ca.josephroque.bowlingcompanion.games.lane.arePinsCleared -import ca.josephroque.bowlingcompanion.games.lane.isTwelve - -/** - * Copyright (C) 2018 Joseph Roque - * - * Percentage of possible twelves which the user successfully spared - */ -class TwelvesSparedStatistic(numerator: Int = 0, denominator: Int = 0) : SecondBallStatistic(numerator, denominator) { - - // MARK: Statistic - - override fun isModifiedByFirstBall(deck: Deck) = deck.isTwelve - override fun isModifiedBySecondBall(deck: Deck) = deck.arePinsCleared - - override val titleId = Id - override val id = Id.toLong() - override val secondaryGraphDataLabelId = R.string.statistic_twelves - - // MARK: Parcelable - - companion object { - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::TwelvesSparedStatistic) - - const val Id = R.string.statistic_twelves_spared - } - - private constructor(p: Parcel): this(numerator = p.readInt(), denominator = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/TwelvesStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/TwelvesStatistic.kt deleted file mode 100644 index 51d409f2a..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/firstball/TwelvesStatistic.kt +++ /dev/null @@ -1,33 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.firstball - -import android.os.Parcel -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.games.lane.Deck -import ca.josephroque.bowlingcompanion.games.lane.isTwelve - -/** - * Copyright (C) 2018 Joseph Roque - * - * Percentage of first ball shots which scored 12 points. - */ -class TwelvesStatistic(numerator: Int = 0, denominator: Int = 0) : FirstBallStatistic(numerator, denominator) { - - // MARK: Statistic - - override fun isModifiedBy(deck: Deck) = deck.isTwelve - - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::TwelvesStatistic) - - const val Id = R.string.statistic_twelves - } - - private constructor(p: Parcel): this(numerator = p.readInt(), denominator = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/foul/FoulsStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/foul/FoulsStatistic.kt deleted file mode 100644 index 958a9425e..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/foul/FoulsStatistic.kt +++ /dev/null @@ -1,47 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.foul - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.statistics.IntegerStatistic -import ca.josephroque.bowlingcompanion.statistics.StatisticsCategory -import ca.josephroque.bowlingcompanion.statistics.immutable.StatFrame - -/** - * Copyright (C) 2018 Joseph Roque - * - * Total number of fouls. - */ -class FoulsStatistic(override var value: Int = 0) : IntegerStatistic { - - // MARK: Modifiers - - /** @Override */ - override fun modify(frame: StatFrame) { - value += frame.ballFouled.sumBy { if (it) 1 else 0 } - } - - // MARK: Overrides - - override val titleId = Id - override val id = Id.toLong() - override val category = StatisticsCategory.Fouls - override fun isModifiedBy(frame: StatFrame) = true - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::FoulsStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_fouls - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(value = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/general/BowlerNameStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/general/BowlerNameStatistic.kt deleted file mode 100644 index 9d3af245c..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/general/BowlerNameStatistic.kt +++ /dev/null @@ -1,56 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.general - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.statistics.StatisticsCategory -import ca.josephroque.bowlingcompanion.statistics.StringStatistic -import ca.josephroque.bowlingcompanion.statistics.unit.BowlerUnit -import ca.josephroque.bowlingcompanion.statistics.unit.GameUnit -import ca.josephroque.bowlingcompanion.statistics.unit.LeagueUnit -import ca.josephroque.bowlingcompanion.statistics.unit.SeriesUnit -import ca.josephroque.bowlingcompanion.statistics.unit.StatisticsUnit - -/** - * Copyright (C) 2018 Joseph Roque - * - * Name of the bowler whose statistics are on display. - */ -class BowlerNameStatistic(override var value: String = "") : StringStatistic { - - // MARK: Modifiers - - /** @Override */ - override fun modify(unit: StatisticsUnit) { - when (unit) { - is GameUnit -> value = unit.bowlerName - is SeriesUnit -> value = unit.bowlerName - is LeagueUnit -> value = unit.bowlerName - is BowlerUnit -> value = unit.name - } - } - - // MARK: Overrides - - override val titleId = Id - override val id = Id.toLong() - override val category = StatisticsCategory.General - override fun isModifiedBy(unit: StatisticsUnit) = true - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::BowlerNameStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_bowler - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(value = p.readString()!!) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/general/GameNameStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/general/GameNameStatistic.kt deleted file mode 100644 index 122e4b133..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/general/GameNameStatistic.kt +++ /dev/null @@ -1,50 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.general - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.statistics.StatisticsCategory -import ca.josephroque.bowlingcompanion.statistics.StringStatistic -import ca.josephroque.bowlingcompanion.statistics.unit.GameUnit -import ca.josephroque.bowlingcompanion.statistics.unit.StatisticsUnit - -/** - * Copyright (C) 2018 Joseph Roque - * - * Name of the game whose statistics are on display. - */ -class GameNameStatistic(override var value: String = "") : StringStatistic { - - // MARK: Modifiers - - /** @Override */ - override fun modify(unit: StatisticsUnit) { - when (unit) { - is GameUnit -> value = unit.name - } - } - - // MARK: Overrides - - override val titleId = Id - override val id = Id.toLong() - override val category = StatisticsCategory.General - override fun isModifiedBy(unit: StatisticsUnit) = true - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::GameNameStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_game - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(value = p.readString()!!) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/general/LeagueNameStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/general/LeagueNameStatistic.kt deleted file mode 100644 index d41eb929c..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/general/LeagueNameStatistic.kt +++ /dev/null @@ -1,54 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.general - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.statistics.StatisticsCategory -import ca.josephroque.bowlingcompanion.statistics.StringStatistic -import ca.josephroque.bowlingcompanion.statistics.unit.GameUnit -import ca.josephroque.bowlingcompanion.statistics.unit.LeagueUnit -import ca.josephroque.bowlingcompanion.statistics.unit.SeriesUnit -import ca.josephroque.bowlingcompanion.statistics.unit.StatisticsUnit - -/** - * Copyright (C) 2018 Joseph Roque - * - * Name of the league whose statistics are on display. - */ -class LeagueNameStatistic(override var value: String = "") : StringStatistic { - - // MARK: Modifiers - - /** @Override */ - override fun modify(unit: StatisticsUnit) { - when (unit) { - is GameUnit -> value = unit.leagueName - is SeriesUnit -> value = unit.leagueName - is LeagueUnit -> value = unit.name - } - } - - // MARK: Overrides - - override val titleId = Id - override val id = Id.toLong() - override val category = StatisticsCategory.General - override fun isModifiedBy(unit: StatisticsUnit) = true - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::LeagueNameStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_league - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(value = p.readString()!!) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/general/SeriesNameStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/general/SeriesNameStatistic.kt deleted file mode 100644 index 89af80506..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/general/SeriesNameStatistic.kt +++ /dev/null @@ -1,52 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.general - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.statistics.StatisticsCategory -import ca.josephroque.bowlingcompanion.statistics.StringStatistic -import ca.josephroque.bowlingcompanion.statistics.unit.GameUnit -import ca.josephroque.bowlingcompanion.statistics.unit.SeriesUnit -import ca.josephroque.bowlingcompanion.statistics.unit.StatisticsUnit - -/** - * Copyright (C) 2018 Joseph Roque - * - * Name of the series whose statistics are on display. - */ -class SeriesNameStatistic(override var value: String = "") : StringStatistic { - - // MARK: Modifiers - - /** @Override */ - override fun modify(unit: StatisticsUnit) { - when (unit) { - is GameUnit -> value = unit.prettySeriesDate - is SeriesUnit -> value = unit.name - } - } - - // MARK: Overrides - - override val titleId = Id - override val id = Id.toLong() - override val category = StatisticsCategory.General - override fun isModifiedBy(unit: StatisticsUnit) = true - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::SeriesNameStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_series - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(value = p.readString()!!) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/matchplay/GamesLostStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/matchplay/GamesLostStatistic.kt deleted file mode 100644 index cc6605a00..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/matchplay/GamesLostStatistic.kt +++ /dev/null @@ -1,52 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.matchplay - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.matchplay.MatchPlayResult -import ca.josephroque.bowlingcompanion.statistics.PercentageStatistic -import ca.josephroque.bowlingcompanion.statistics.StatisticsCategory -import ca.josephroque.bowlingcompanion.statistics.immutable.StatGame - -/** - * Copyright (C) 2018 Joseph Roque - * - * Percentage of match play games lost. - */ -class GamesLostStatistic(override var numerator: Int = 0, override var denominator: Int = 0) : PercentageStatistic { - - // MARK: Modifiers - - /** @Override */ - override fun modify(game: StatGame) { - denominator++ - if (game.matchPlay == MatchPlayResult.LOST) { - numerator++ - } - } - - // MARK: Overrides - - override val titleId = Id - override val id = Id.toLong() - override val category = StatisticsCategory.MatchPlay - override val secondaryGraphDataLabelId = R.string.statistic_total_match_play_games_recorded - override fun isModifiedBy(game: StatGame) = game.matchPlay != MatchPlayResult.NONE - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::GamesLostStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_games_lost - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(numerator = p.readInt(), denominator = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/matchplay/GamesTiedStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/matchplay/GamesTiedStatistic.kt deleted file mode 100644 index ac7e1fd67..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/matchplay/GamesTiedStatistic.kt +++ /dev/null @@ -1,52 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.matchplay - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.matchplay.MatchPlayResult -import ca.josephroque.bowlingcompanion.statistics.PercentageStatistic -import ca.josephroque.bowlingcompanion.statistics.StatisticsCategory -import ca.josephroque.bowlingcompanion.statistics.immutable.StatGame - -/** - * Copyright (C) 2018 Joseph Roque - * - * Percentage of match play games tied. - */ -class GamesTiedStatistic(override var numerator: Int = 0, override var denominator: Int = 0) : PercentageStatistic { - - // MARK: Modifiers - - /** @Override */ - override fun modify(game: StatGame) { - denominator++ - if (game.matchPlay == MatchPlayResult.TIED) { - numerator++ - } - } - - // MARK: Overrides - - override val titleId = Id - override val id = Id.toLong() - override val category = StatisticsCategory.MatchPlay - override val secondaryGraphDataLabelId = R.string.statistic_total_match_play_games_recorded - override fun isModifiedBy(game: StatGame) = game.matchPlay != MatchPlayResult.NONE - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::GamesTiedStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_games_tied - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(numerator = p.readInt(), denominator = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/matchplay/GamesWonStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/matchplay/GamesWonStatistic.kt deleted file mode 100644 index 93d19ef69..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/matchplay/GamesWonStatistic.kt +++ /dev/null @@ -1,52 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.matchplay - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.matchplay.MatchPlayResult -import ca.josephroque.bowlingcompanion.statistics.PercentageStatistic -import ca.josephroque.bowlingcompanion.statistics.StatisticsCategory -import ca.josephroque.bowlingcompanion.statistics.immutable.StatGame - -/** - * Copyright (C) 2018 Joseph Roque - * - * Percentage of match play games won. - */ -class GamesWonStatistic(override var numerator: Int = 0, override var denominator: Int = 0) : PercentageStatistic { - - // MARK: Modifiers - - /** @Override */ - override fun modify(game: StatGame) { - denominator++ - if (game.matchPlay == MatchPlayResult.WON) { - numerator++ - } - } - - // MARK: Overrides - - override val titleId = Id - override val id = Id.toLong() - override val category = StatisticsCategory.MatchPlay - override val secondaryGraphDataLabelId = R.string.statistic_total_match_play_games_recorded - override fun isModifiedBy(game: StatGame) = game.matchPlay != MatchPlayResult.NONE - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::GamesWonStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_games_won - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(numerator = p.readInt(), denominator = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/overall/GameAverageStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/overall/GameAverageStatistic.kt deleted file mode 100644 index 347822567..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/overall/GameAverageStatistic.kt +++ /dev/null @@ -1,48 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.overall - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.statistics.AverageStatistic -import ca.josephroque.bowlingcompanion.statistics.StatisticsCategory -import ca.josephroque.bowlingcompanion.statistics.immutable.StatGame - -/** - * Copyright (C) 2018 Joseph Roque - * - * Average score across all games. - */ -class GameAverageStatistic(override var total: Int = 0, override var divisor: Int = 0) : AverageStatistic { - - // MARK: Modifiers - - /** @Override */ - override fun modify(game: StatGame) { - divisor++ - total += game.score - } - - // MARK: Overrides - - override val titleId = Id - override val id = Id.toLong() - override val category = StatisticsCategory.Overall - override fun isModifiedBy(game: StatGame) = game.score > 0 - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::GameAverageStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_average - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(total = p.readInt(), divisor = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/overall/HighSingleStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/overall/HighSingleStatistic.kt deleted file mode 100644 index 0ca2d73b1..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/overall/HighSingleStatistic.kt +++ /dev/null @@ -1,47 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.overall - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.statistics.IntegerStatistic -import ca.josephroque.bowlingcompanion.statistics.StatisticsCategory -import ca.josephroque.bowlingcompanion.statistics.immutable.StatGame - -/** - * Copyright (C) 2018 Joseph Roque - * - * Highest score across games. - */ -class HighSingleStatistic(override var value: Int = 0) : IntegerStatistic { - - // MARK: Modifiers - - /** @Override */ - override fun modify(game: StatGame) { - value = maxOf(value, game.score) - } - - // MARK: Overrides - - override val titleId = Id - override val id = Id.toLong() - override val category = StatisticsCategory.Overall - override fun isModifiedBy(game: StatGame) = true - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::HighSingleStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_high_single - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(value = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/overall/LeftOfMiddleHitsStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/overall/LeftOfMiddleHitsStatistic.kt deleted file mode 100644 index 702221343..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/overall/LeftOfMiddleHitsStatistic.kt +++ /dev/null @@ -1,45 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.overall - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.games.lane.Deck -import ca.josephroque.bowlingcompanion.games.lane.isHitLeftOfMiddle -import ca.josephroque.bowlingcompanion.statistics.StatisticsCategory -import ca.josephroque.bowlingcompanion.statistics.impl.firstball.FirstBallStatistic - -/** - * Copyright (C) 2018 Joseph Roque - * - * Percentage of shots which hit left of the middle pin. - */ -class LeftOfMiddleHitsStatistic(numerator: Int = 0, denominator: Int = 0) : FirstBallStatistic(numerator, denominator) { - - // MARK: Modifiers - - /** @Override */ - override fun isModifiedBy(deck: Deck) = deck.isHitLeftOfMiddle - - // MARK: Overrides - - override val titleId = Id - override val id = Id.toLong() - override val category = StatisticsCategory.Overall - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::LeftOfMiddleHitsStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_left_of_middle - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(numerator = p.readInt(), denominator = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/overall/MiddleHitsStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/overall/MiddleHitsStatistic.kt deleted file mode 100644 index 13cef6405..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/overall/MiddleHitsStatistic.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.overall - -import android.os.Parcel -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.games.lane.Deck -import ca.josephroque.bowlingcompanion.games.lane.isMiddleHit -import ca.josephroque.bowlingcompanion.statistics.StatisticsCategory -import ca.josephroque.bowlingcompanion.statistics.impl.firstball.FirstBallStatistic - -/** - * Copyright (C) 2018 Joseph Roque - * - * Percentage of shots which hit the middle pin. - */ -class MiddleHitsStatistic(numerator: Int = 0, denominator: Int = 0) : FirstBallStatistic(numerator, denominator) { - - companion object { - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::MiddleHitsStatistic) - - const val Id = R.string.statistic_middle_hits - } - - override val titleId = Id - override val id = Id.toLong() - override val category = StatisticsCategory.Overall - - // MARK: Statistic - - override fun isModifiedBy(deck: Deck) = deck.isMiddleHit - - // MARK: Constructors - - private constructor(p: Parcel): this(numerator = p.readInt(), denominator = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/overall/NumberOfGamesStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/overall/NumberOfGamesStatistic.kt deleted file mode 100644 index 6e51ad99e..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/overall/NumberOfGamesStatistic.kt +++ /dev/null @@ -1,47 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.overall - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.statistics.IntegerStatistic -import ca.josephroque.bowlingcompanion.statistics.StatisticsCategory -import ca.josephroque.bowlingcompanion.statistics.immutable.StatGame - -/** - * Copyright (C) 2018 Joseph Roque - * - * Total number of games. - */ -class NumberOfGamesStatistic(override var value: Int = 0) : IntegerStatistic { - - // MARK: Modifiers - - /** @Override */ - override fun modify(game: StatGame) { - value++ - } - - // MARK: Overrides - - override val titleId = Id - override val id = Id.toLong() - override val category = StatisticsCategory.Overall - override fun isModifiedBy(game: StatGame) = game.score > 0 - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::NumberOfGamesStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_number_of_games - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(value = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/overall/RightOfMiddleHitsStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/overall/RightOfMiddleHitsStatistic.kt deleted file mode 100644 index b17ed0808..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/overall/RightOfMiddleHitsStatistic.kt +++ /dev/null @@ -1,45 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.overall - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.games.lane.Deck -import ca.josephroque.bowlingcompanion.games.lane.isHitRightOfMiddle -import ca.josephroque.bowlingcompanion.statistics.StatisticsCategory -import ca.josephroque.bowlingcompanion.statistics.impl.firstball.FirstBallStatistic - -/** - * Copyright (C) 2018 Joseph Roque - * - * Percentage of shots which hit right of the middle pin. - */ -class RightOfMiddleHitsStatistic(numerator: Int = 0, denominator: Int = 0) : FirstBallStatistic(numerator, denominator) { - - // MARK: Modifiers - - /** @Override */ - override fun isModifiedBy(deck: Deck) = deck.isHitRightOfMiddle - - // MARK: Overrides - - override val titleId = Id - override val id = Id.toLong() - override val category = StatisticsCategory.Overall - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::RightOfMiddleHitsStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_right_of_middle - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(numerator = p.readInt(), denominator = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/overall/SpareConversionsStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/overall/SpareConversionsStatistic.kt deleted file mode 100644 index 457149588..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/overall/SpareConversionsStatistic.kt +++ /dev/null @@ -1,55 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.overall - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.games.lane.Deck -import ca.josephroque.bowlingcompanion.games.lane.arePinsCleared -import ca.josephroque.bowlingcompanion.games.lane.isAce -import ca.josephroque.bowlingcompanion.games.lane.isHeadPin -import ca.josephroque.bowlingcompanion.games.lane.isSplit -import ca.josephroque.bowlingcompanion.statistics.StatisticsCategory -import ca.josephroque.bowlingcompanion.statistics.impl.firstball.SecondBallStatistic - -/** - * Copyright (C) 2018 Joseph Roque - * - * Percentage of possible spares which the user successfully spared. - */ -class SpareConversionsStatistic(numerator: Int = 0, denominator: Int = 0) : SecondBallStatistic(numerator, denominator) { - - // MARK: Modifiers - - /** @Override */ - override fun isModifiedByFirstBall(firstBall: Deck, secondBall: Deck): Boolean { - // Don't add a spare chance if the first ball was a split / head pin / aces, unless the second shot was a spare - return !firstBall.arePinsCleared && ((!firstBall.isAce && !firstBall.isHeadPin && !firstBall.isSplit(countS2asS = true)) || secondBall.arePinsCleared) - } - - /** @Override */ - override fun isModifiedBySecondBall(deck: Deck) = deck.arePinsCleared - - // MARK: Overrides - - override val titleId = Id - override val id = Id.toLong() - override val category = StatisticsCategory.Overall - override val secondaryGraphDataLabelId = R.string.statistic_total_spare_chances - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::SpareConversionsStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_spare_conversion - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(numerator = p.readInt(), denominator = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/overall/StrikeMiddleHitsStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/overall/StrikeMiddleHitsStatistic.kt deleted file mode 100644 index c5e5b8995..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/overall/StrikeMiddleHitsStatistic.kt +++ /dev/null @@ -1,60 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.overall - -import android.os.Parcel -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.games.Game -import ca.josephroque.bowlingcompanion.games.lane.arePinsCleared -import ca.josephroque.bowlingcompanion.games.lane.isMiddleHit -import ca.josephroque.bowlingcompanion.statistics.PercentageStatistic -import ca.josephroque.bowlingcompanion.statistics.StatisticsCategory -import ca.josephroque.bowlingcompanion.statistics.immutable.StatFrame - -/** - * Copyright (C) 2018 Joseph Roque - * - * Percentage of middle hits which were strikes - */ -class StrikeMiddleHitsStatistic(override var numerator: Int = 0, override var denominator: Int = 0) : PercentageStatistic { - - companion object { - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::StrikeMiddleHitsStatistic) - - const val Id = R.string.statistic_strike_middle_hits - } - - override val titleId = Id - override val id = Id.toLong() - override val category = StatisticsCategory.Overall - override val secondaryGraphDataLabelId = R.string.statistic_total_shots_at_middle - override fun isModifiedBy(frame: StatFrame) = true - - // MARK: Statistic - - override fun modify(frame: StatFrame) { - // This function has a similar construction to `FirstBallStatistic.modify(StatFrame) - // and the two should remain aligned - - // Every frame adds 1 possible hit - denominator += if (frame.pinState[0].isMiddleHit) 1 else 0 - numerator += if (frame.pinState[0].arePinsCleared) 1 else 0 - if (frame.zeroBasedOrdinal == Game.LAST_FRAME) { - // In the 10th frame, for each time the first or second ball cleared the lane, - // check if the statistic is modified - if (frame.pinState[0].arePinsCleared) { - denominator += if (frame.pinState[1].isMiddleHit) 1 else 0 - numerator += if (frame.pinState[1].arePinsCleared) 1 else 0 - } - - if (frame.pinState[1].arePinsCleared) { - denominator += if (frame.pinState[2].isMiddleHit) 1 else 0 - numerator += if (frame.pinState[2].arePinsCleared) 1 else 0 - } - } - } - - // MARK: Constructors - - private constructor(p: Parcel): this(numerator = p.readInt(), denominator = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/overall/StrikesStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/overall/StrikesStatistic.kt deleted file mode 100644 index e17c0bb8c..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/overall/StrikesStatistic.kt +++ /dev/null @@ -1,45 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.overall - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.games.lane.Deck -import ca.josephroque.bowlingcompanion.games.lane.arePinsCleared -import ca.josephroque.bowlingcompanion.statistics.StatisticsCategory -import ca.josephroque.bowlingcompanion.statistics.impl.firstball.FirstBallStatistic - -/** - * Copyright (C) 2018 Joseph Roque - * - * Percentage of shots which are strikes. - */ -class StrikesStatistic(numerator: Int = 0, denominator: Int = 0) : FirstBallStatistic(numerator, denominator) { - - // MARK: Modifiers - - /** @Override */ - override fun isModifiedBy(deck: Deck) = deck.arePinsCleared - - // MARK: Overrides - - override val titleId = Id - override val id = Id.toLong() - override val category = StatisticsCategory.Overall - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::StrikesStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_strikes - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(numerator = p.readInt(), denominator = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/overall/TotalPinfallStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/overall/TotalPinfallStatistic.kt deleted file mode 100644 index 5f816ac14..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/overall/TotalPinfallStatistic.kt +++ /dev/null @@ -1,47 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.overall - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.statistics.IntegerStatistic -import ca.josephroque.bowlingcompanion.statistics.StatisticsCategory -import ca.josephroque.bowlingcompanion.statistics.immutable.StatGame - -/** - * Copyright (C) 2018 Joseph Roque - * - * Total pinfall. - */ -class TotalPinfallStatistic(override var value: Int = 0) : IntegerStatistic { - - // MARK: Modifiers - - /** @Override */ - override fun modify(game: StatGame) { - value += game.score - } - - // MARK: Overrides - - override val titleId = Id - override val id = Id.toLong() - override val category = StatisticsCategory.Overall - override fun isModifiedBy(game: StatGame) = true - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::TotalPinfallStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_total_pinfall - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(value = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/pinsleftondeck/AveragePinsLeftStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/pinsleftondeck/AveragePinsLeftStatistic.kt deleted file mode 100644 index c9990816f..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/pinsleftondeck/AveragePinsLeftStatistic.kt +++ /dev/null @@ -1,55 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.pinsleftondeck - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.statistics.AverageStatistic -import ca.josephroque.bowlingcompanion.statistics.StatisticsCategory -import ca.josephroque.bowlingcompanion.statistics.immutable.StatFrame -import ca.josephroque.bowlingcompanion.statistics.immutable.StatGame - -/** - * Copyright (C) 2018 Joseph Roque - * - * Average pins left on deck per game. - */ -class AveragePinsLeftStatistic(override var total: Int = 0, override var divisor: Int = 0) : AverageStatistic { - - // MARK: Modifiers - - /** @Override */ - override fun modify(frame: StatFrame) { - total += frame.pinsLeftOnDeck - } - - /** @Override */ - override fun modify(game: StatGame) { - divisor++ - } - - // MARK: Overrides - - override val titleId = Id - override val id = Id.toLong() - override val category = StatisticsCategory.PinsOnDeck - override fun isModifiedBy(frame: StatFrame) = true - override fun isModifiedBy(game: StatGame) = !game.isManual && game.score > 0 - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField - val CREATOR = parcelableCreator(::AveragePinsLeftStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_average_pins_left - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel) : this(total = p.readInt(), divisor = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/pinsleftondeck/TotalPinsLeftStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/pinsleftondeck/TotalPinsLeftStatistic.kt deleted file mode 100644 index 3cbc06441..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/pinsleftondeck/TotalPinsLeftStatistic.kt +++ /dev/null @@ -1,47 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.pinsleftondeck - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.statistics.IntegerStatistic -import ca.josephroque.bowlingcompanion.statistics.StatisticsCategory -import ca.josephroque.bowlingcompanion.statistics.immutable.StatFrame - -/** - * Copyright (C) 2018 Joseph Roque - * - * Totals pins left on deck across all games. - */ -class TotalPinsLeftStatistic(override var value: Int = 0) : IntegerStatistic { - - // MARK: Modifiers - - /** @Override */ - override fun modify(frame: StatFrame) { - value += frame.pinsLeftOnDeck - } - - // MARK: Overrides - - override val titleId = Id - override val id = Id.toLong() - override val category = StatisticsCategory.PinsOnDeck - override fun isModifiedBy(frame: StatFrame) = true - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::TotalPinsLeftStatistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_pins_left - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(value = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf10Statistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf10Statistic.kt deleted file mode 100644 index 7602decaf..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf10Statistic.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.series - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator - -/** - * Copyright (C) 2018 Joseph Roque - * - * Highest series of 10 games. - */ -class HighSeriesOf10Statistic(value: Int = 0) : HighSeriesStatistic(value) { - - // MARK: Overrides - - override val seriesSize = 10 - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::HighSeriesOf10Statistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_high_series_of_10 - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(value = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf11Statistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf11Statistic.kt deleted file mode 100644 index 61c427626..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf11Statistic.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.series - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator - -/** - * Copyright (C) 2018 Joseph Roque - * - * Highest series of 11 games. - */ -class HighSeriesOf11Statistic(value: Int = 0) : HighSeriesStatistic(value) { - - // MARK: Overrides - - override val seriesSize = 11 - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::HighSeriesOf11Statistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_high_series_of_11 - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(value = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf12Statistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf12Statistic.kt deleted file mode 100644 index 7c14a407a..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf12Statistic.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.series - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator - -/** - * Copyright (C) 2018 Joseph Roque - * - * Highest series of 12 games. - */ -class HighSeriesOf12Statistic(value: Int = 0) : HighSeriesStatistic(value) { - - // MARK: Overrides - - override val seriesSize = 12 - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::HighSeriesOf12Statistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_high_series_of_12 - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(value = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf13Statistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf13Statistic.kt deleted file mode 100644 index 3ae35569d..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf13Statistic.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.series - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator - -/** - * Copyright (C) 2018 Joseph Roque - * - * Highest series of 13 games. - */ -class HighSeriesOf13Statistic(value: Int = 0) : HighSeriesStatistic(value) { - - // MARK: Overrides - - override val seriesSize = 13 - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::HighSeriesOf13Statistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_high_series_of_13 - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(value = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf14Statistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf14Statistic.kt deleted file mode 100644 index e93994e50..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf14Statistic.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.series - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator - -/** - * Copyright (C) 2018 Joseph Roque - * - * Highest series of 14 games. - */ -class HighSeriesOf14Statistic(value: Int = 0) : HighSeriesStatistic(value) { - - // MARK: Overrides - - override val seriesSize = 14 - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::HighSeriesOf14Statistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_high_series_of_14 - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(value = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf15Statistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf15Statistic.kt deleted file mode 100644 index 102a2f13b..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf15Statistic.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.series - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator - -/** - * Copyright (C) 2018 Joseph Roque - * - * Highest series of 15 games. - */ -class HighSeriesOf15Statistic(value: Int = 0) : HighSeriesStatistic(value) { - - // MARK: Overrides - - override val seriesSize = 15 - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::HighSeriesOf15Statistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_high_series_of_15 - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(value = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf16Statistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf16Statistic.kt deleted file mode 100644 index 8bdef9029..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf16Statistic.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.series - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator - -/** - * Copyright (C) 2018 Joseph Roque - * - * Highest series of 16 games. - */ -class HighSeriesOf16Statistic(value: Int = 0) : HighSeriesStatistic(value) { - - // MARK: Overrides - - override val seriesSize = 16 - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::HighSeriesOf16Statistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_high_series_of_16 - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(value = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf17Statistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf17Statistic.kt deleted file mode 100644 index ca4c6c978..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf17Statistic.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.series - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator - -/** - * Copyright (C) 2018 Joseph Roque - * - * Highest series of 17 games. - */ -class HighSeriesOf17Statistic(value: Int = 0) : HighSeriesStatistic(value) { - - // MARK: Overrides - - override val seriesSize = 17 - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::HighSeriesOf17Statistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_high_series_of_17 - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(value = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf18Statistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf18Statistic.kt deleted file mode 100644 index 379f201d8..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf18Statistic.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.series - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator - -/** - * Copyright (C) 2018 Joseph Roque - * - * Highest series of 18 games. - */ -class HighSeriesOf18Statistic(value: Int = 0) : HighSeriesStatistic(value) { - - // MARK: Overrides - - override val seriesSize = 18 - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::HighSeriesOf18Statistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_high_series_of_18 - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(value = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf19Statistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf19Statistic.kt deleted file mode 100644 index 535e1e527..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf19Statistic.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.series - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator - -/** - * Copyright (C) 2018 Joseph Roque - * - * Highest series of 19 games. - */ -class HighSeriesOf19Statistic(value: Int = 0) : HighSeriesStatistic(value) { - - // MARK: Overrides - - override val seriesSize = 19 - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::HighSeriesOf19Statistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_high_series_of_19 - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(value = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf20Statistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf20Statistic.kt deleted file mode 100644 index ef3ebaaf6..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf20Statistic.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.series - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator - -/** - * Copyright (C) 2018 Joseph Roque - * - * Highest series of 20 games. - */ -class HighSeriesOf20Statistic(value: Int = 0) : HighSeriesStatistic(value) { - - // MARK: Overrides - - override val seriesSize = 20 - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::HighSeriesOf20Statistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_high_series_of_20 - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(value = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf2Statistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf2Statistic.kt deleted file mode 100644 index 584749b5a..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf2Statistic.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.series - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator - -/** - * Copyright (C) 2018 Joseph Roque - * - * Highest series of 2 games. - */ -class HighSeriesOf2Statistic(value: Int = 0) : HighSeriesStatistic(value) { - - // MARK: Overrides - - override val seriesSize = 2 - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::HighSeriesOf2Statistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_high_series_of_2 - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(value = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf3Statistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf3Statistic.kt deleted file mode 100644 index d395a24be..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf3Statistic.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.series - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator - -/** - * Copyright (C) 2018 Joseph Roque - * - * Highest series of 3 games. - */ -class HighSeriesOf3Statistic(value: Int = 0) : HighSeriesStatistic(value) { - - // MARK: Overrides - - override val seriesSize = 3 - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::HighSeriesOf3Statistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_high_series_of_3 - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(value = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf4Statistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf4Statistic.kt deleted file mode 100644 index b8548e6ea..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf4Statistic.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.series - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator - -/** - * Copyright (C) 2018 Joseph Roque - * - * Highest series of 4 games. - */ -class HighSeriesOf4Statistic(value: Int = 0) : HighSeriesStatistic(value) { - - // MARK: Overrides - - override val seriesSize = 4 - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::HighSeriesOf4Statistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_high_series_of_4 - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(value = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf5Statistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf5Statistic.kt deleted file mode 100644 index 911d8853a..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf5Statistic.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.series - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator - -/** - * Copyright (C) 2018 Joseph Roque - * - * Highest series of 5 games. - */ -class HighSeriesOf5Statistic(value: Int = 0) : HighSeriesStatistic(value) { - - // MARK: Overrides - - override val seriesSize = 5 - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::HighSeriesOf5Statistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_high_series_of_5 - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(value = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf6Statistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf6Statistic.kt deleted file mode 100644 index e0214132a..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf6Statistic.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.series - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator - -/** - * Copyright (C) 2018 Joseph Roque - * - * Highest series of 6 games. - */ -class HighSeriesOf6Statistic(value: Int = 0) : HighSeriesStatistic(value) { - - // MARK: Overrides - - override val seriesSize = 6 - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::HighSeriesOf6Statistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_high_series_of_6 - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(value = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf7Statistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf7Statistic.kt deleted file mode 100644 index 244cb67e1..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf7Statistic.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.series - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator - -/** - * Copyright (C) 2018 Joseph Roque - * - * Highest series of 7 games. - */ -class HighSeriesOf7Statistic(value: Int = 0) : HighSeriesStatistic(value) { - - // MARK: Overrides - - override val seriesSize = 7 - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::HighSeriesOf7Statistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_high_series_of_7 - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(value = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf8Statistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf8Statistic.kt deleted file mode 100644 index a9e8d9247..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf8Statistic.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.series - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator - -/** - * Copyright (C) 2018 Joseph Roque - * - * Highest series of 8 games. - */ -class HighSeriesOf8Statistic(value: Int = 0) : HighSeriesStatistic(value) { - - // MARK: Overrides - - override val seriesSize = 8 - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::HighSeriesOf8Statistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_high_series_of_8 - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(value = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf9Statistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf9Statistic.kt deleted file mode 100644 index 6ac5ba612..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesOf9Statistic.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.series - -import android.os.Parcel -import android.os.Parcelable -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator - -/** - * Copyright (C) 2018 Joseph Roque - * - * Highest series of 9 games. - */ -class HighSeriesOf9Statistic(value: Int = 0) : HighSeriesStatistic(value) { - - // MARK: Overrides - - override val seriesSize = 9 - override val titleId = Id - override val id = Id.toLong() - - // MARK: Parcelable - - companion object { - /** Creator, required by [Parcelable]. */ - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::HighSeriesOf9Statistic) - - /** Unique ID for the statistic. */ - const val Id = R.string.statistic_high_series_of_9 - } - - /** - * Construct this statistic from a [Parcel]. - */ - private constructor(p: Parcel): this(value = p.readInt()) -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesStatistic.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesStatistic.kt deleted file mode 100644 index a88208e81..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/impl/series/HighSeriesStatistic.kt +++ /dev/null @@ -1,33 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.impl.series - -import ca.josephroque.bowlingcompanion.statistics.IntegerStatistic -import ca.josephroque.bowlingcompanion.statistics.StatisticsCategory -import ca.josephroque.bowlingcompanion.statistics.immutable.StatSeries - -/** - * Copyright (C) 2018 Joseph Roque - * - * Parent class for statistics which are high series of different number of games. - */ -abstract class HighSeriesStatistic(override var value: Int = 0) : IntegerStatistic { - - // MARK: Modifiers - - /** @Override */ - override fun modify(series: StatSeries) { - if (series.games.size == seriesSize) { - value = maxOf(series.total, value) - } - } - - // MARK: Overrides - override val secondaryGraphDataLabelId: Int? - get() = null - override fun isModifiedBy(series: StatSeries) = true - override val category = StatisticsCategory.Series - - // MARK: HighSeriesStatistic - - /** Size of the series to get statistics on */ - abstract val seriesSize: Int -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/interfaces/IStatisticsContext.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/interfaces/IStatisticsContext.kt deleted file mode 100644 index 04405361e..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/interfaces/IStatisticsContext.kt +++ /dev/null @@ -1,12 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.interfaces - -import ca.josephroque.bowlingcompanion.statistics.provider.StatisticsProvider - -/** - * Copyright (C) 2018 Joseph Roque - * - * Provides context for the statistics to be displayed. - */ -interface IStatisticsContext { - val statisticsProviders: List -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/list/StatisticsListFragment.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/list/StatisticsListFragment.kt deleted file mode 100644 index 48a34fb9e..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/list/StatisticsListFragment.kt +++ /dev/null @@ -1,76 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.list - -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.ListFragment -import ca.josephroque.bowlingcompanion.common.interfaces.IIdentifiable -import ca.josephroque.bowlingcompanion.statistics.unit.StatisticsUnit -import kotlinx.coroutines.experimental.CommonPool -import kotlinx.coroutines.experimental.Deferred -import kotlinx.coroutines.experimental.async - -/** - * Copyright (C) 2018 Joseph Roque - * - * A fragment which displays the stats of a [StatisticsUnit] in a list. - */ -class StatisticsListFragment : ListFragment() { - - companion object { - @Suppress("unused") - private const val TAG = "StatisticsListFragment" - - private const val ARG_UNIT = "${TAG}_unit" - - fun newInstance(unit: StatisticsUnit): StatisticsListFragment { - return StatisticsListFragment().apply { - arguments = Bundle().apply { putParcelable(ARG_UNIT, unit) } - } - } - } - - override val emptyViewImage = R.drawable.empty_view_statistics - override val emptyViewText = R.string.empty_view_statistics - - private lateinit var unit: StatisticsUnit - - // MARK: Lifecycle functions - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - unit = arguments?.getParcelable(ARG_UNIT)!! - return super.onCreateView(inflater, container, savedInstanceState) - } - - override fun onStop() { - super.onStop() - unit.clearCache() - } - - // MARK: BaseFragment - - override fun updateToolbarTitle() { - // Intentionally left blank - } - - // MARK: ListFragment - - override fun buildAdapter(): StatisticsRecyclerViewAdapter { - return StatisticsRecyclerViewAdapter(emptyList(), this) - } - - override fun fetchItems(): Deferred> { - context?.let { - return unit.getStatistics(it) - } - - return async(CommonPool) { - mutableListOf() - } - } -} - -/** An item to display in the list of statistics. */ -interface StatisticListItem : IIdentifiable diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/list/StatisticsRecyclerViewAdapter.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/list/StatisticsRecyclerViewAdapter.kt deleted file mode 100644 index cae83c90c..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/list/StatisticsRecyclerViewAdapter.kt +++ /dev/null @@ -1,103 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.list - -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.statistics.Statistic -import ca.josephroque.bowlingcompanion.statistics.StatisticsCategory - -/** - * Copyright (C) 2018 Joseph Roque - * - * [RecyclerView.Adapter] that can display a list of [StatisticListItem]s and makes calls to the specified delegate - * upon interactions. - */ -class StatisticsRecyclerViewAdapter( - values: List, - delegate: BaseRecyclerViewAdapter.AdapterDelegate? -) : BaseRecyclerViewAdapter(values, delegate) { - - companion object { - @Suppress("unused") - private const val TAG = "StatisticsRVAdapter" - - private enum class ViewType { - Header, - Item; - - companion object { - private val map = ViewType.values().associateBy(ViewType::ordinal) - fun fromInt(type: Int) = map[type] - } - } - } - - // MARK: BaseRecyclerViewAdapter - - override fun getItemViewType(position: Int): Int { - return when (getItemAt(position)) { - is StatisticsCategory -> ViewType.Header.ordinal - is Statistic -> ViewType.Item.ordinal - else -> throw IllegalArgumentException("StatisticListItems can only be Statistic or StatisticsCategory.") - } - } - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { - return when (ViewType.fromInt(viewType)) { - ViewType.Header -> HeaderViewHolder(LayoutInflater - .from(parent.context) - .inflate(R.layout.list_item_statistic_category, parent, false)) - ViewType.Item -> ItemViewHolder(LayoutInflater - .from(parent.context) - .inflate(R.layout.list_item_statistic, parent, false)) - else -> throw IllegalArgumentException("View Type `$viewType` is invalid.") - } - } - - override fun onBindViewHolder(holder: ViewHolder, position: Int) { - holder.bind(getItemAt(position)) - } - - // MARK: HeaderViewHolder - - inner class HeaderViewHolder(view: View) : BaseRecyclerViewAdapter.ViewHolder(view) { - private val tvTitle: TextView? = view.findViewById(R.id.tv_title) - - override fun bind(item: StatisticListItem) { - val context = itemView.context - val header = item as StatisticsCategory - - tvTitle?.text = header.getTitle(context.resources) - } - } - - // MARK: ItemViewHolder - - inner class ItemViewHolder(view: View) : BaseRecyclerViewAdapter.ViewHolder(view) { - private val tvTitle: TextView? = view.findViewById(R.id.tv_title) - private val tvValue: TextView? = view.findViewById(R.id.tv_value) - private val tvSubtitle: TextView? = view.findViewById(R.id.tv_subtitle) - - override fun bind(item: StatisticListItem) { - val context = itemView.context - val statistic = item as Statistic - - tvTitle?.text = statistic.getTitle(context.resources) - tvValue?.text = statistic.displayValue - - val subtitle = statistic.getSubtitle() - if (subtitle != null) { - tvSubtitle?.text = subtitle - tvSubtitle?.visibility = View.VISIBLE - } else { - tvSubtitle?.visibility = View.GONE - } - - itemView.setOnClickListener(this@StatisticsRecyclerViewAdapter) - } - } -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/provider/StatisticsProvider.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/provider/StatisticsProvider.kt deleted file mode 100644 index 6ae03abd0..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/provider/StatisticsProvider.kt +++ /dev/null @@ -1,163 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.provider - -import android.os.Bundle -import android.os.Parcel -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.bowlers.Bowler -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.games.Game -import ca.josephroque.bowlingcompanion.leagues.League -import ca.josephroque.bowlingcompanion.series.Series -import ca.josephroque.bowlingcompanion.statistics.unit.BowlerUnit -import ca.josephroque.bowlingcompanion.statistics.unit.GameUnit -import ca.josephroque.bowlingcompanion.statistics.unit.LeagueUnit -import ca.josephroque.bowlingcompanion.statistics.unit.SeriesUnit -import ca.josephroque.bowlingcompanion.statistics.unit.StatisticsUnit -import ca.josephroque.bowlingcompanion.statistics.unit.TeamUnit -import ca.josephroque.bowlingcompanion.teams.Team - -/** - * Copyright (C) 2018 Joseph Roque - * - * Provide statistics from a bowler to display. - */ -sealed class StatisticsProvider : IIdentifiable, KParcelable { - - abstract val name: String - abstract val typeName: Int - abstract val canShowGraphs: Boolean - - // MARK: TeamStatistics - - data class TeamStatistics(val team: Team) : StatisticsProvider() { - companion object { - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(StatisticsProvider::TeamStatistics) - } - - private constructor(p: Parcel): this(p.readParcelable(Team::class.java.classLoader)!!) - - override val id = team.id.and(0xF00000000000000L) - override val name = team.name - override val typeName = R.string.team - override val canShowGraphs = true - } - - // MARK: BowlerStatistics - - data class BowlerStatistics(val bowler: Bowler) : StatisticsProvider() { - companion object { - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(StatisticsProvider::BowlerStatistics) - } - - private constructor(p: Parcel): this(p.readParcelable(Bowler::class.java.classLoader)!!) - - override val id = bowler.id.and(0xE00000000000000L) - override val name = bowler.name - override val typeName = R.string.bowler - override val canShowGraphs = true - } - - // MARK: LeagueStatistics - - data class LeagueStatistics(val league: League) : StatisticsProvider() { - companion object { - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(StatisticsProvider::LeagueStatistics) - } - - private constructor(p: Parcel): this(p.readParcelable(League::class.java.classLoader)!!) - - override val id = league.id.and(0xD00000000000000L) - override val name = league.name - override val typeName = R.string.league - override val canShowGraphs = true - } - - // MARK: SeriesStatistics - - data class SeriesStatistics(val series: Series) : StatisticsProvider() { - companion object { - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(StatisticsProvider::SeriesStatistics) - } - - private constructor(p: Parcel): this(p.readParcelable(Series::class.java.classLoader)!!) - - override val id = series.id.and(0xC00000000000000L) - override val name = series.prettyDate - override val typeName = R.string.series - override val canShowGraphs = false - } - - // MARK: GameStatistics - - data class GameStatistics(val game: Game) : StatisticsProvider() { - companion object { - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(StatisticsProvider::GameStatistics) - } - - private constructor(p: Parcel): this(p.readParcelable(Game::class.java.classLoader)!!) - - override val id = game.id.and(0xB00000000000000L) - override val name = "Game ${game.ordinal}" - override val typeName = R.string.game - override val canShowGraphs = false - } - - val units: List by lazy { - return@lazy when (this) { - is TeamStatistics -> { - val units: MutableList = ArrayList(team.members.size + 1) - units.add(TeamUnit(team.id, team.name)) - units.addAll(team.members.map { BowlerUnit(it.bowlerId, it.bowlerName) }) - units - } - is BowlerStatistics -> listOf(BowlerUnit(bowler.id, bowler.name)) - is LeagueStatistics -> listOf(LeagueUnit(league.bowler.name, league.id, league.name)) - is SeriesStatistics -> listOf(SeriesUnit(series.league.bowler.name, series.league.name, series.id, series.date)) - is GameStatistics -> listOf(GameUnit(game.series.league.bowler.name, game.series.league.name, game.series.date, game.series.id, game.id, game.ordinal)) - } - } - - // MARK: Parcelable - - override fun writeToParcel(dest: Parcel, flags: Int) = with(dest) { - when (this@StatisticsProvider) { - is TeamStatistics -> writeParcelable(team, 0) - is BowlerStatistics -> writeParcelable(bowler, 0) - is LeagueStatistics -> writeParcelable(league, 0) - is SeriesStatistics -> writeParcelable(series, 0) - is GameStatistics -> writeParcelable(game, 0) - } - } - - override fun describeContents(): Int { - return when (this) { - // When changing, update `StatisticsProvider.getParcelable` - is TeamStatistics -> 0 - is BowlerStatistics -> 1 - is LeagueStatistics -> 2 - is SeriesStatistics -> 3 - is GameStatistics -> 4 - } - } - - companion object { - fun getParcelable(arguments: Bundle?, key: String, type: Int): StatisticsProvider? { - return when (type) { - // When changing, update `StatisticsProvider::describeContents` - 0 -> arguments?.getParcelable(key) - 1 -> arguments?.getParcelable(key) - 2 -> arguments?.getParcelable(key) - 3 -> arguments?.getParcelable(key) - 4 -> arguments?.getParcelable(key) - else -> throw IllegalArgumentException("StatisticsProvider type $type does not exist") - } - } - } -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/provider/StatisticsProviderListFragment.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/provider/StatisticsProviderListFragment.kt deleted file mode 100644 index a2dd0f2f6..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/provider/StatisticsProviderListFragment.kt +++ /dev/null @@ -1,114 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.provider - -import android.content.Context -import android.os.Bundle -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.fragments.ListFragment -import ca.josephroque.bowlingcompanion.common.interfaces.IIdentifiable -import ca.josephroque.bowlingcompanion.statistics.unit.StatisticsUnitTabbedFragment -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 [StatisticsProvider]s. - */ -class StatisticsProviderListFragment : ListFragment(), - ListFragment.ListFragmentDelegate { - - companion object { - @Suppress("unused") - private const val TAG = "SPListFragment" - - private const val ARG_STATISTIC_PROVIDERS_COUNT = "${TAG}_count" - private const val ARG_STATISTIC_PROVIDERS_TYPE = "${TAG}_type" - private const val ARG_STATISTIC_PROVIDER = "${TAG}_providers" - - fun newInstance(): StatisticsProviderListFragment { - return StatisticsProviderListFragment() - } - - fun buildArguments(statisticsProviders: List): Bundle { - return Bundle().apply { - putInt(ARG_STATISTIC_PROVIDERS_COUNT, statisticsProviders.size) - statisticsProviders.forEachIndexed { index, provider -> - putInt("${ARG_STATISTIC_PROVIDERS_TYPE}_$index", provider.describeContents()) - putParcelable("${ARG_STATISTIC_PROVIDER}_$index", provider) - } - } - } - } - - override val emptyViewImage = R.drawable.empty_view_statistics - override val emptyViewText = R.string.empty_view_statistics_providers - - private lateinit var statisticsProviders: List - - // MARK: Lifecycle functions - - override fun onStart() { - initStatisticsProviders() - super.onStart() - } - - override fun onAttach(context: Context?) { - canIgnoreDelegate = true - delegate = this - super.onAttach(context) - } - - override fun setArguments(args: Bundle?) { - super.setArguments(args) - if (activity == null) { return } - initStatisticsProviders() - refresh() - } - - // MARK: BaseFragment - - override fun updateToolbarTitle() { - navigationActivity?.setToolbarTitle(resources.getString(R.string.statistics)) - } - - // MARK: ListFragment - - override fun buildAdapter(): StatisticsProviderRecyclerViewAdapter { - return StatisticsProviderRecyclerViewAdapter(emptyList(), this) - } - - override fun fetchItems(): Deferred> { - return async(CommonPool) { - statisticsProviders.toMutableList() - } - } - - // MARK: ListFragmentDelegate - - override fun onItemSelected(item: IIdentifiable, longPress: Boolean) { - if (item is StatisticsProvider) { - val newFragment = StatisticsUnitTabbedFragment.newInstance(item) - fragmentNavigation?.pushFragment(newFragment) - } - } - - override fun onItemDeleted(item: IIdentifiable) { - // Intentionally left blank - } - - // MARK: Private functions - - private fun initStatisticsProviders() { - val providers: MutableList = ArrayList() - statisticsProviders = providers - - val arguments = arguments ?: return - val providerCount = arguments.getInt(ARG_STATISTIC_PROVIDERS_COUNT) - for (i in 0 until providerCount) { - val type = arguments.getInt("${ARG_STATISTIC_PROVIDERS_TYPE}_$i") - providers.add(StatisticsProvider.getParcelable(arguments, "${ARG_STATISTIC_PROVIDER}_$i", type)!!) - } - } -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/provider/StatisticsProviderRecyclerViewAdapter.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/provider/StatisticsProviderRecyclerViewAdapter.kt deleted file mode 100644 index 4b4857833..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/provider/StatisticsProviderRecyclerViewAdapter.kt +++ /dev/null @@ -1,55 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.provider - -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 - -/** - * Copyright (C) 2018 Joseph Roque - * - * [RecyclerView.Adapter] that can display a [StatisticsProvider] and makes a call to the - * specified delegate. - */ -class StatisticsProviderRecyclerViewAdapter( - items: List, - delegate: AdapterDelegate? -) : BaseRecyclerViewAdapter(items, delegate) { - - companion object { - @Suppress("unused") - private const val TAG = "SPRecyclerViewAdapter" - } - - // MARK: BaseRecyclerViewAdapter - - override fun getItemViewType(position: Int) = 0 - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseRecyclerViewAdapter.ViewHolder { - return ViewHolderName( - LayoutInflater - .from(parent.context) - .inflate(R.layout.list_item_provider, parent, false) - ) - } - - override fun onBindViewHolder(holder: BaseRecyclerViewAdapter.ViewHolder, position: Int) { - holder.bind(getItemAt(position)) - } - - // MARK: ViewHolderName - - inner class ViewHolderName(view: View) : BaseRecyclerViewAdapter.ViewHolder(view) { - private val tvName: TextView? = view.findViewById(R.id.tv_name) - private val tvType: TextView? = view.findViewById(R.id.tv_type) - - override fun bind(item: StatisticsProvider) { - tvName?.text = item.name - tvType?.setText(item.typeName) - itemView.setOnClickListener(this@StatisticsProviderRecyclerViewAdapter) - } - } -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/unit/BowlerUnit.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/unit/BowlerUnit.kt deleted file mode 100644 index cab9c8347..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/unit/BowlerUnit.kt +++ /dev/null @@ -1,55 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.unit - -import android.content.Context -import android.os.Parcel -import ca.josephroque.bowlingcompanion.bowlers.Bowler -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.statistics.StatisticsCategory -import ca.josephroque.bowlingcompanion.statistics.immutable.StatSeries -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 kotlinx.coroutines.experimental.Deferred - -/** - * Copyright (C) 2018 Joseph Roque - * - * A [Bowler] whose statistics can be loaded and displayed. - */ -class BowlerUnit(val bowlerId: Long, bowlerName: String, parcel: Parcel? = null) : StatisticsUnit(parcel) { - - override val name: String = bowlerName - override val excludedCategories: Set = emptySet() - override val excludedStatisticIds: Set = setOf(LeagueNameStatistic.Id, SeriesNameStatistic.Id, GameNameStatistic.Id) - override val canShowGraphs = true - - // MARK: Constructors - - private constructor(p: Parcel): this( - bowlerId = p.readLong(), - bowlerName = p.readString()!!, - parcel = p - ) - - // MARK: StatisticsUnit - - override fun getSeriesForStatistics(context: Context): Deferred> { - return StatSeries.loadSeriesForBowler(context, bowlerId) - } - - // MARK: KParcelable - - override fun writeToParcel(dest: Parcel, flags: Int) = with(dest) { - writeLong(bowlerId) - writeString(name) - writeCacheToParcel(this) - } - - companion object { - @Suppress("unused") - private const val TAG = "BowlerUnit" - - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::BowlerUnit) - } -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/unit/GameUnit.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/unit/GameUnit.kt deleted file mode 100644 index f738f478f..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/unit/GameUnit.kt +++ /dev/null @@ -1,92 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.unit - -import android.content.Context -import android.os.Parcel -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.games.Game -import ca.josephroque.bowlingcompanion.statistics.StatisticsCategory -import ca.josephroque.bowlingcompanion.statistics.immutable.StatSeries -import ca.josephroque.bowlingcompanion.statistics.impl.overall.GameAverageStatistic -import ca.josephroque.bowlingcompanion.statistics.impl.overall.HighSingleStatistic -import ca.josephroque.bowlingcompanion.statistics.impl.overall.NumberOfGamesStatistic -import ca.josephroque.bowlingcompanion.statistics.impl.overall.TotalPinfallStatistic -import ca.josephroque.bowlingcompanion.statistics.impl.pinsleftondeck.AveragePinsLeftStatistic -import ca.josephroque.bowlingcompanion.utils.DateUtils -import kotlinx.coroutines.experimental.CommonPool -import kotlinx.coroutines.experimental.Deferred -import kotlinx.coroutines.experimental.async -import java.util.Date - -/** - * Copyright (C) 2018 Joseph Roque - * - * A [Game] whose statistics can be loaded and displayed. - */ -class GameUnit( - val bowlerName: String, - val leagueName: String, - val seriesDate: Date, - val seriesId: Long, - val gameId: Long, - val gameOrdinal: Int, - parcel: Parcel? = null -) : StatisticsUnit(parcel) { - - val prettySeriesDate = DateUtils.dateToPretty(seriesDate) - - override val name = "Game $gameOrdinal" - override val excludedCategories = setOf(StatisticsCategory.Average, StatisticsCategory.MatchPlay, StatisticsCategory.Series) - override val excludedStatisticIds = setOf(AveragePinsLeftStatistic.Id, GameAverageStatistic.Id, HighSingleStatistic.Id, TotalPinfallStatistic.Id, NumberOfGamesStatistic.Id) - override val canShowGraphs = false - - // MARK: Constructors - - private constructor(p: Parcel): this( - bowlerName = p.readString()!!, - leagueName = p.readString()!!, - seriesDate = p.readDate()!!, - seriesId = p.readLong(), - gameId = p.readLong(), - gameOrdinal = p.readInt(), - parcel = p - ) - - // MARK: StatisticsUnit - - override fun getSeriesForStatistics(context: Context): Deferred> { - return async(CommonPool) { - val seriesList = StatSeries.loadSeriesForSeries(context, seriesId).await() - var series = seriesList.firstOrNull() ?: return@async seriesList - - series = StatSeries( - id = series.id, - games = series.games.filter { it.ordinal == gameOrdinal }, - date = series.date - ) - - return@async listOf(series) - } - } - - // MARK: Parcelable - - override fun writeToParcel(dest: Parcel, flags: Int) = with(dest) { - writeString(bowlerName) - writeString(leagueName) - writeDate(seriesDate) - writeLong(seriesId) - writeLong(gameId) - writeInt(gameOrdinal) - writeCacheToParcel(this) - } - - companion object { - @Suppress("unused") - private const val TAG = "GameUnit" - - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::GameUnit) - } -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/unit/LeagueUnit.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/unit/LeagueUnit.kt deleted file mode 100644 index f59f62bc1..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/unit/LeagueUnit.kt +++ /dev/null @@ -1,58 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.unit - -import android.content.Context -import android.os.Parcel -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.leagues.League -import ca.josephroque.bowlingcompanion.statistics.StatisticsCategory -import ca.josephroque.bowlingcompanion.statistics.immutable.StatSeries -import ca.josephroque.bowlingcompanion.statistics.impl.general.GameNameStatistic -import ca.josephroque.bowlingcompanion.statistics.impl.general.SeriesNameStatistic -import kotlinx.coroutines.experimental.Deferred - -/** - * Copyright (C) 2018 Joseph Roque - * - * A [League] whose statistics can be loaded and displayed. - */ -class LeagueUnit(val bowlerName: String, val leagueId: Long, leagueName: String, parcel: Parcel? = null) : StatisticsUnit(parcel) { - - // MARK: Overrides - - override val name: String = leagueName - override val excludedCategories: Set = emptySet() - override val excludedStatisticIds: Set = setOf(SeriesNameStatistic.Id, GameNameStatistic.Id) - override val canShowGraphs = true - - // MARK: Constructors - - private constructor(p: Parcel): this( - bowlerName = p.readString()!!, - leagueId = p.readLong(), - leagueName = p.readString()!!, - parcel = p - ) - - // MARK: StatisticsUnit - - override fun getSeriesForStatistics(context: Context): Deferred> { - return StatSeries.loadSeriesForLeague(context, leagueId) - } - - // MARK: Parcelable - - override fun writeToParcel(dest: Parcel, flags: Int) = with(dest) { - writeString(bowlerName) - writeLong(leagueId) - writeString(name) - writeCacheToParcel(this) - } - - companion object { - @Suppress("unused") - private const val TAG = "LeagueUnit" - - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::LeagueUnit) - } -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/unit/SeriesUnit.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/unit/SeriesUnit.kt deleted file mode 100644 index a83e7337b..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/unit/SeriesUnit.kt +++ /dev/null @@ -1,70 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.unit - -import android.content.Context -import android.os.Parcel -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.series.Series -import ca.josephroque.bowlingcompanion.statistics.StatisticsCategory -import ca.josephroque.bowlingcompanion.statistics.immutable.StatSeries -import ca.josephroque.bowlingcompanion.statistics.impl.general.GameNameStatistic -import ca.josephroque.bowlingcompanion.utils.DateUtils -import kotlinx.coroutines.experimental.Deferred -import java.util.Date - -/** - * Copyright (C) 2018 Joseph Roque - * - * A [Series] whose statistics can be loaded and displayed. - */ -class SeriesUnit( - val bowlerName: String, - val leagueName: String, - val seriesId: Long, - val seriesDate: Date, - parcel: Parcel? = null -) : StatisticsUnit(parcel) { - - // MARK: Overrides - - override val name: String - get() = DateUtils.dateToPretty(seriesDate) - override val excludedCategories: Set = setOf(StatisticsCategory.Average, StatisticsCategory.Series) - override val excludedStatisticIds: Set = setOf(GameNameStatistic.Id) - override val canShowGraphs = false - - // MARK: Constructors - - private constructor(p: Parcel): this( - bowlerName = p.readString()!!, - leagueName = p.readString()!!, - seriesId = p.readLong(), - seriesDate = p.readDate()!!, - parcel = p - ) - - // MARK: StatisticsUnit - - override fun getSeriesForStatistics(context: Context): Deferred> { - return StatSeries.loadSeriesForSeries(context, seriesId) - } - - // MARK: Parcelable - - override fun writeToParcel(dest: Parcel, flags: Int) = with(dest) { - writeString(bowlerName) - writeString(leagueName) - writeLong(seriesId) - writeDate(seriesDate) - writeCacheToParcel(this) - } - - companion object { - @Suppress("unused") - private const val TAG = "SeriesUnit" - - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::SeriesUnit) - } -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/unit/StatisticsUnit.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/unit/StatisticsUnit.kt deleted file mode 100644 index f18c6fd0d..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/unit/StatisticsUnit.kt +++ /dev/null @@ -1,283 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.unit - -import android.content.Context -import android.os.Parcel -import android.support.v7.preference.PreferenceManager -import ca.josephroque.bowlingcompanion.common.interfaces.KParcelable -import ca.josephroque.bowlingcompanion.common.interfaces.readBoolean -import ca.josephroque.bowlingcompanion.common.interfaces.writeBoolean -import ca.josephroque.bowlingcompanion.statistics.Statistic -import ca.josephroque.bowlingcompanion.statistics.StatisticHelper -import ca.josephroque.bowlingcompanion.statistics.StatisticsCategory -import ca.josephroque.bowlingcompanion.statistics.graph.StatisticsGraphLine -import ca.josephroque.bowlingcompanion.statistics.immutable.StatSeries -import ca.josephroque.bowlingcompanion.statistics.impl.average.PerGameAverageStatistic -import ca.josephroque.bowlingcompanion.statistics.impl.series.HighSeriesStatistic -import ca.josephroque.bowlingcompanion.statistics.list.StatisticListItem -import ca.josephroque.bowlingcompanion.utils.Analytics -import ca.josephroque.bowlingcompanion.utils.DateUtils -import com.github.mikephil.charting.data.Entry -import kotlinx.coroutines.experimental.CommonPool -import kotlinx.coroutines.experimental.Deferred -import kotlinx.coroutines.experimental.async -import java.util.Calendar - -/** - * Copyright (C) 2018 Joseph Roque - * - * A single unit which provides a list of statistics to be displayed. - */ -abstract class StatisticsUnit(initialSeries: List? = null, initialStatistics: MutableList? = null) : KParcelable { - - companion object { - @Suppress("unused") - private const val TAG = "StatisticsUnit" - } - - abstract val name: String - abstract val excludedCategories: Set - abstract val excludedStatisticIds: Set - - private var cachedSeries: List? = initialSeries - private var cachedStatistics: MutableList? = initialStatistics - - abstract val canShowGraphs: Boolean - - private val statisticListItems: MutableList? - get() { - val statistics = cachedStatistics?.toMutableList() ?: return null - val statisticListItems: MutableList = ArrayList(statistics.size + StatisticsCategory.values().size) - - // Add categories in place in the list - var lastCategory: StatisticsCategory? = null - for (statistic in statistics) { - if (statistic.category != lastCategory) { - statisticListItems.add(statistic.category) - lastCategory = statistic.category - } - statisticListItems.add(statistic) - } - - return statisticListItems - } - - // MARK: Constructors - - protected constructor(p: Parcel? = null): this( - initialSeries = if (p != null && p.readBoolean()) { - ArrayList().apply { - val array = p.readParcelableArray(StatSeries::class.java.classLoader)!! - for (i in 0 until array.size) { - add(array[i] as StatSeries) - } - } - } else { - null - }, - initialStatistics = if (p != null && p.readBoolean()) { - ArrayList().apply { - val statisticsSize = p.readInt() - val statisticTypes = IntArray(statisticsSize) - p.readIntArray(statisticTypes) - - for (i in 0 until statisticsSize) { - this.add(StatisticHelper.readParcelable(p, statisticTypes[i])) - } - } - } else { - null - } - ) - - // MARK: StatisticsUnit - - protected abstract fun getSeriesForStatistics(context: Context): Deferred> - - fun clearCache() { - cachedSeries = null - cachedStatistics = null - } - - fun getStatistics(context: Context): Deferred> { - return async(CommonPool) { - if (cachedStatistics == null) { - cachedStatistics = buildStatistics(context).await() - } - - return@async statisticListItems!! - } - } - - fun getStatisticGraphData(context: Context, statisticId: Long, accumulative: Boolean): Deferred, List>> { - return async(CommonPool) { - val graphData: MutableList> = ArrayList() - val graphLabels: MutableList = ArrayList() - - val seriesList = this@StatisticsUnit.cachedSeries ?: getSeriesForStatistics(context).await() - val statistic = StatisticHelper.getStatistic(statisticId) - - if (!statistic.canBeGraphed || seriesList.isEmpty()) { - return@async Pair(emptyList(), graphLabels) - } - - // To determine current week and when to add a new entry to the chart - val calendar = Calendar.getInstance() - calendar.time = seriesList[0].date - var lastDate = seriesList[0].date - var lastYear = calendar.get(Calendar.YEAR) - var lastWeek = calendar.get(Calendar.WEEK_OF_YEAR) - var xPos = 0F - - fun updateGraph() { - addGraphEntries(graphData, xPos, statistic) - graphLabels.add(DateUtils.dateToShort(lastDate)) - if (!accumulative) statistic.zero() - xPos++ - } - - for (series in seriesList) { - calendar.time = series.date - val newDate = series.date - val newYear = calendar.get(Calendar.YEAR) - val newWeek = calendar.get(Calendar.WEEK_OF_YEAR) - - // Either the year or week has incremented, so an entry should be added to the graph - if (newYear > lastYear || newWeek > lastWeek) { - updateGraph() - } - - adjustStatisticBySeries(statistic, series) - - lastDate = newDate - lastYear = newYear - lastWeek = newWeek - } - - // Add the final entry - updateGraph() - - val lines: List = graphData.mapIndexed { index, data -> - val label = when (index) { - 0 -> context.resources.getString(statistic.primaryGraphDataLabelId) - 1 -> context.resources.getString(statistic.secondaryGraphDataLabelId!!) - else -> throw IllegalAccessException("Only up to 2 lines are available for a statistic.") - } - return@mapIndexed StatisticsGraphLine(label, data) - } - - return@async Pair(lines, graphLabels) - } - } - - // MARK: Private functions - - private fun buildStatistics(context: Context): Deferred> { - return async(CommonPool) { - Analytics.trackStatisticsLoaded(Analytics.Companion.EventTime.Begin) - - val seriesList = this@StatisticsUnit.cachedSeries ?: getSeriesForStatistics(context).await() - val statistics = StatisticHelper.getFreshStatistics() - - // Filter out categories which the unit does not accept - for (category in excludedCategories) { - statistics.removeAll { it.category == category } - } - - // Filter out statistics which the unit does not accept - for (statisticId in excludedStatisticIds) { - statistics.removeAll { it.titleId == statisticId } - } - - val preferences = PreferenceManager.getDefaultSharedPreferences(context) - - for (statistic in statistics) { - // Update preferences for statistics - statistic.updatePreferences(preferences) - - // Only allow the [StatisticsUnit] to modify each stat once - if (statistic.isModifiedBy(this@StatisticsUnit)) { - statistic.modify(this@StatisticsUnit) - } - } - - // Parse the remaining statistics and update as per the unit/series/game/frame - for (series in seriesList) { - for (statistic in statistics) { - adjustStatisticBySeries(statistic, series) - } - } - - // Filter invalid statistics - statistics.removeAll { - (it is HighSeriesStatistic && it.value == 0) || - (it is PerGameAverageStatistic && it.total == 0) - } - - Analytics.trackStatisticsLoaded(Analytics.Companion.EventTime.End) - - return@async statistics - } - } - - private fun adjustStatisticBySeries(statistic: Statistic, series: StatSeries) { - if (statistic.isModifiedBy(series)) { - statistic.modify(series) - } - - for (game in series.games) { - // Don't process games with scores of 0 - if (game.score == 0) { - continue - } - - if (statistic.isModifiedBy(game)) { - statistic.modify(game) - } - - // Don't process frames for manual games - if (game.isManual) { - continue - } - - for (frame in game.frames) { - if (frame.isAccessed && statistic.isModifiedBy(frame)) { - statistic.modify(frame) - } - } - } - } - - private fun addGraphEntries(graphData: MutableList>, xPos: Float, statistic: Statistic) { - statistic.primaryGraphY?.let { - if (graphData.size < 1) { graphData.add(ArrayList()) } - graphData[0].add(Entry(xPos, it)) - } - - statistic.secondaryGraphY?.let { - if (graphData.size < 2) { graphData.add(ArrayList()) } - graphData[1].add(Entry(xPos, it)) - } - } - - protected fun writeCacheToParcel(p: Parcel) = with(p) { - val series = cachedSeries - if (series != null) { - writeBoolean(true) - writeParcelableArray(series.toTypedArray(), 0) - } else { - writeBoolean(false) - } - - val statistics = cachedStatistics - if (statistics != null) { - writeBoolean(true) - writeInt(statistics.size) - writeIntArray(statistics.map { it.titleId }.toIntArray()) - for (statistic in statistics) { - writeParcelable(statistic, 0) - } - } else { - writeBoolean(false) - } - } -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/unit/StatisticsUnitDetailsFragment.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/unit/StatisticsUnitDetailsFragment.kt deleted file mode 100644 index 2d1b6ab00..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/unit/StatisticsUnitDetailsFragment.kt +++ /dev/null @@ -1,125 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.unit - -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 ca.josephroque.bowlingcompanion.common.fragments.ListFragment -import ca.josephroque.bowlingcompanion.common.interfaces.IIdentifiable -import ca.josephroque.bowlingcompanion.statistics.Statistic -import ca.josephroque.bowlingcompanion.statistics.StatisticHelper -import ca.josephroque.bowlingcompanion.statistics.graph.StatisticGraphFragment -import ca.josephroque.bowlingcompanion.statistics.list.StatisticsListFragment -import ca.josephroque.bowlingcompanion.utils.Analytics - -/** - * Copyright (C) 2018 Joseph Roque - * - * Display details for a [StatisticsUnit]. - */ -class StatisticsUnitDetailsFragment : BaseFragment(), - ListFragment.ListFragmentDelegate, - StatisticGraphFragment.StatisticGraphDelegate { - - companion object { - @Suppress("unused") - private const val TAG = "StatUnitDetailsFragment" - - private const val ARG_UNIT = "${TAG}_unit" - - fun newInstance(unit: StatisticsUnit): StatisticsUnitDetailsFragment { - return StatisticsUnitDetailsFragment().apply { - arguments = Bundle().apply { putParcelable(ARG_UNIT, unit) } - } - } - } - - private lateinit var unit: StatisticsUnit - - // MARK: Lifecycle functions - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - unit = arguments?.getParcelable(ARG_UNIT)!! - - val view = inflater.inflate(R.layout.fragment_statistics_unit_details, container, false) - - if (savedInstanceState == null) { - val fragment = StatisticsListFragment.newInstance(unit) - childFragmentManager.beginTransaction().apply { - replace(R.id.fragment_container, fragment) - commit() - } - } - - return view - } - - // MARK: BaseFragment - - override fun updateToolbarTitle() { - // Intentionally left blank - } - - override fun popChildFragment(): Boolean { - return childFragmentManager.popBackStackImmediate() - } - - // MARK: StatisticGraphDelegate - - override fun nextStatistic(statisticId: Long) { - val (_, nextStatistic) = StatisticHelper.getAdjacentStatistics(statisticId) - nextStatistic?.let { - val graphFragment = StatisticGraphFragment.newInstance(unit, nextStatistic.id) - showStatisticGraph(graphFragment) - - Analytics.trackViewStatisticsGraph(it.getTitle(resources)) - } - } - - override fun prevStatistic(statisticId: Long) { - val (prevStatistic, _) = StatisticHelper.getAdjacentStatistics(statisticId) - prevStatistic?.let { - val graphFragment = StatisticGraphFragment.newInstance(unit, prevStatistic.id) - showStatisticGraph(graphFragment) - - Analytics.trackViewStatisticsGraph(it.getTitle(resources)) - } - } - - // MARK: ListFragmentDelegate - - override fun onItemSelected(item: IIdentifiable, longPress: Boolean) { - if (!unit.canShowGraphs) { - return - } - - if (item is Statistic) { - if (item.canBeGraphed) { - val graphFragment = StatisticGraphFragment.newInstance(unit, item.id) - showStatisticGraph(graphFragment) - - Analytics.trackViewStatisticsGraph(item.getTitle(resources)) - } - } - } - - override fun onItemDeleted(item: IIdentifiable) { - // Intentionally left blank - } - - // MARK: Private functions - - private fun showStatisticGraph(graphFragment: StatisticGraphFragment) { - childFragmentManager.beginTransaction().apply { - replace(R.id.fragment_container, graphFragment) - - if (childFragmentManager.backStackEntryCount == 0) { - addToBackStack("StatisticsList") - } - - commit() - } - } -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/unit/StatisticsUnitTabbedFragment.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/unit/StatisticsUnitTabbedFragment.kt deleted file mode 100644 index 52ad09adf..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/unit/StatisticsUnitTabbedFragment.kt +++ /dev/null @@ -1,134 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.unit - -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.support.v7.preference.PreferenceManager -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.fragments.TabbedFragment -import ca.josephroque.bowlingcompanion.statistics.provider.StatisticsProvider -import ca.josephroque.bowlingcompanion.utils.Analytics -import ca.josephroque.bowlingcompanion.utils.AppRater -import ca.josephroque.bowlingcompanion.utils.BCError -import kotlinx.android.synthetic.main.fragment_common_tabs.tabbed_fragment_tabs as fragmentTabs - -/** - * Copyright (C) 2018 Joseph Roque - * - * View a list of statistics for each unit in a tabbed layout. - */ -class StatisticsUnitTabbedFragment : TabbedFragment() { - - companion object { - @Suppress("unused") - private const val TAG = "StatUnitTabbedFragment" - - private const val ARG_STATISTICS_PROVIDER_TYPE = "${TAG}_type" - private const val ARG_STATISTICS_PROVIDER = "${TAG}_stats" - private const val TAP_FOR_GRAPH_SHOWN = "${TAG}_tap_for_graph_shown" - - fun newInstance(statisticsProvider: StatisticsProvider): StatisticsUnitTabbedFragment { - val newInstance = StatisticsUnitTabbedFragment() - newInstance.arguments = Bundle().apply { - putInt(ARG_STATISTICS_PROVIDER_TYPE, statisticsProvider.describeContents()) - putParcelable(ARG_STATISTICS_PROVIDER, statisticsProvider) - } - return newInstance - } - } - - private lateinit var statisticsProvider: StatisticsProvider - - // MARK: Lifecycle functions - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - val type = arguments?.getInt(ARG_STATISTICS_PROVIDER_TYPE)!! - statisticsProvider = StatisticsProvider.getParcelable(arguments, ARG_STATISTICS_PROVIDER, type)!! - - // Record analytics only when the view is opened the first time, not restore from state - if (savedInstanceState == null) { - Analytics.trackViewStatisticsList() - } - - return super.onCreateView(inflater, container, savedInstanceState) - } - - override fun onStart() { - super.onStart() - if (statisticsProvider.units.size == 1) { - fragmentTabs.visibility = View.GONE - navigationActivity?.supportActionBar?.elevation = resources.getDimension(R.dimen.base_elevation) - } else { - fragmentTabs.visibility = View.VISIBLE - navigationActivity?.supportActionBar?.elevation = 0F - } - - context?.let { - val preferences = PreferenceManager.getDefaultSharedPreferences(it) - if (statisticsProvider.canShowGraphs && !preferences.getBoolean(TAP_FOR_GRAPH_SHOWN, false)) { - BCError( - title = R.string.did_you_know, - message = R.string.tap_to_show_statistics_graph, - severity = BCError.Severity.Info - ).show(it) { - preferences.edit().putBoolean(TAP_FOR_GRAPH_SHOWN, true).apply() - } - } else { - // Show App Rate dialog if conditions met and tap dialog was not shown - AppRater.show(it) - } - } - } - - // MARK: BaseFragment - - override fun updateToolbarTitle() { - navigationActivity?.setToolbarTitle(resources.getString(R.string.statistics), statisticsProvider.name) - } - - // MARK: TabbedFragment - - override fun buildPagerAdapter(tabCount: Int): FragmentPagerAdapter { - return StatisticsUnitPagerAdapter(childFragmentManager, tabCount, statisticsProvider.units) - } - - override fun addTabs(tabLayout: TabLayout) { - tabLayout.tabMode = TabLayout.MODE_SCROLLABLE - statisticsProvider.units.forEach { unit -> - tabLayout.addTab(tabLayout.newTab().setText(unit.name)) - } - } - - override fun handleTabSwitch(newTab: Int) { - // FIXME: set the tabs to the same scroll point - } - - // MARK: IFloatingActionButtonHandler - - override fun getFabImage(): Int? { - return null - } - - override fun onFabClick() { - // Intentionally left blank - } - - // MARK: StatisticsUnitPagerAdapter - - class StatisticsUnitPagerAdapter( - fragmentManager: FragmentManager, - private val tabCount: Int, - private val statisticsUnits: List - ) : FragmentPagerAdapter(fragmentManager) { - override fun getCount() = tabCount - - override fun getItem(position: Int): Fragment { - return StatisticsUnitDetailsFragment.newInstance(statisticsUnits[position]) - } - } -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/unit/TeamUnit.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/unit/TeamUnit.kt deleted file mode 100644 index 7c12a3505..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/statistics/unit/TeamUnit.kt +++ /dev/null @@ -1,56 +0,0 @@ -package ca.josephroque.bowlingcompanion.statistics.unit - -import android.content.Context -import android.os.Parcel -import ca.josephroque.bowlingcompanion.common.interfaces.parcelableCreator -import ca.josephroque.bowlingcompanion.statistics.StatisticsCategory -import ca.josephroque.bowlingcompanion.statistics.immutable.StatSeries -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.teams.Team -import kotlinx.coroutines.experimental.Deferred - -/** - * Copyright (C) 2018 Joseph Roque - * - * A [Team] whose statistics can be loaded and displayed. - */ -class TeamUnit(val teamId: Long, teamName: String, parcel: Parcel? = null) : StatisticsUnit(parcel) { - - override val name: String = teamName - override val excludedCategories: Set = emptySet() - override val excludedStatisticIds: Set = setOf(BowlerNameStatistic.Id, LeagueNameStatistic.Id, SeriesNameStatistic.Id, GameNameStatistic.Id) - override val canShowGraphs = true - - // MARK: Constructors - - private constructor(p: Parcel): this( - teamId = p.readLong(), - teamName = p.readString()!!, - parcel = p - ) - - // MARK: StatisticsUnit - - override fun getSeriesForStatistics(context: Context): Deferred> { - return StatSeries.loadSeriesForTeam(context, teamId) - } - - // MARK: KParcelable - - override fun writeToParcel(dest: Parcel, flags: Int) = with(dest) { - writeLong(teamId) - writeString(name) - writeCacheToParcel(this) - } - - companion object { - @Suppress("unused") - private const val TAG = "TeamUnit" - - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::TeamUnit) - } -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/teams/Team.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/teams/Team.kt deleted file mode 100644 index 6a3b31116..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/teams/Team.kt +++ /dev/null @@ -1,380 +0,0 @@ -package ca.josephroque.bowlingcompanion.teams - -import android.content.ContentValues -import android.content.Context -import android.database.Cursor -import android.os.Parcel -import android.preference.PreferenceManager -import android.util.Log -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.bowlers.Bowler -import ca.josephroque.bowlingcompanion.common.interfaces.IDeletable -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.database.Annihilator -import ca.josephroque.bowlingcompanion.database.Contract.BowlerEntry -import ca.josephroque.bowlingcompanion.database.Contract.TeamBowlerEntry -import ca.josephroque.bowlingcompanion.database.Contract.TeamEntry -import ca.josephroque.bowlingcompanion.database.DatabaseManager -import ca.josephroque.bowlingcompanion.series.Series -import ca.josephroque.bowlingcompanion.teams.teammember.TeamMember -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 -import kotlin.collections.ArrayList - -/** - * Copyright (C) 2018 Joseph Roque - * - * A single Team, which has a set of bowlers. - */ -class Team( - override val id: Long, - val name: String, - val members: List, - val initialOrder: List? = null -) : KParcelable, IDeletable, IIdentifiable { - - private var _isDeleted: Boolean = false - override val isDeleted: Boolean - get() = _isDeleted - - val membersInOrder: List - get() = members.sortedWith(compareBy { order.indexOf(it.id) }) - - val series: List - get() = membersInOrder.map { it.series!! } - - val order: List = initialOrder?.toMutableList() ?: members.map { it.id } - - // MARK: Constructors - - private constructor(p: Parcel): this( - id = p.readLong(), - name = p.readString()!!, - members = arrayListOf().apply { - val parcelableArray = p.readParcelableArray(TeamMember::class.java.classLoader)!! - this.addAll(parcelableArray.map { - return@map it as TeamMember - }) - }, - initialOrder = arrayListOf().apply { - val size = p.readInt() - val array = LongArray(size) - p.readLongArray(array) - this.addAll(array.toList()) - } - ) - - constructor(team: Team): this( - id = team.id, - name = team.name, - members = team.members, - initialOrder = team.order - ) - - // MARK: Parcelable - - override fun writeToParcel(dest: Parcel, flags: Int) = with(dest) { - writeLong(id) - writeString(name) - writeParcelableArray(members.toTypedArray(), 0) - writeInt(order.size) - writeLongArray(order.toLongArray()) - } - - // MARK: IDeletable - - override fun markForDeletion(): Team { - val newInstance = Team(this) - newInstance._isDeleted = true - return newInstance - } - - override fun cleanDeletion(): Team { - val newInstance = Team(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 = TeamEntry.TABLE_NAME, - whereClause = "${TeamEntry._ID}=?", - whereArgs = arrayOf(id.toString()) - ) - } - } - - // MARK: Team - - fun replaceTeamMember(newMember: TeamMember): Team { - val oldMembers = members.toMutableList() - val replacedMemberIndex = newMember.indexInList(members) - assert(replacedMemberIndex > -1) - oldMembers[replacedMemberIndex] = newMember - - return Team( - id = this.id, - name = this.name, - members = oldMembers, - initialOrder = this.order - ) - } - - companion object { - @Suppress("unused") - private const val TAG = "Team" - - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::Team) - - private val REGEX_NAME = Bowler.REGEX_NAME - - enum class Sort { - Alphabetically, - LastModified; - - companion object { - private val map = Sort.values().associateBy(Sort::ordinal) - fun fromInt(type: Int) = map[type] - } - } - - private fun isTeamNameValid(name: String): Boolean = REGEX_NAME.matches(name) - - private fun isTeamNameUnique(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( - TeamEntry.TABLE_NAME, - arrayOf(TeamEntry.COLUMN_TEAM_NAME), - "${TeamEntry.COLUMN_TEAM_NAME}=? AND ${TeamEntry._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, members: List): Deferred { - return async(CommonPool) { - val errorMessage = R.string.issue_saving_team - val errorTitle: Int? = if (!isTeamNameValid(name)) { - R.string.error_team_name_invalid - } else if (!isTeamNameUnique(context, name, id).await()) { - R.string.error_team_name_in_use - } else if (members.isEmpty()) { - R.string.error_team_has_no_members - } else { - null - } - - return@async if (errorTitle != null) { - BCError(errorTitle, errorMessage, BCError.Severity.Warning) - } else { - null - } - } - } - - fun save(context: Context, id: Long, name: String, members: List): Deferred> { - return if (id < 0) { - createNewAndSave(context, name, members) - } else { - update(context, id, name, members) - } - } - - private fun createNewAndSave(context: Context, name: String, members: List): Deferred> { - return async(CommonPool) { - val error = validateSavePreconditions(context, -1, name, members).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(TeamEntry.COLUMN_TEAM_NAME, name) - put(TeamEntry.COLUMN_DATE_MODIFIED, currentDate) - } - - val teamId: Long - - val database = DatabaseManager.getWritableDatabase(context).await() - database.beginTransaction() - try { - teamId = database.insert(TeamEntry.TABLE_NAME, null, values) - members.forEach { - values = ContentValues().apply { - put(TeamBowlerEntry.COLUMN_BOWLER_ID, it.bowlerId) - put(TeamBowlerEntry.COLUMN_TEAM_ID, teamId) - } - - database.insert(TeamBowlerEntry.TABLE_NAME, null, values) - } - - database.setTransactionSuccessful() - } catch (ex: Exception) { - Log.e(TAG, "Could not create a new team") - return@async Pair( - null, - BCError(R.string.error_saving_team, R.string.error_team_not_saved) - ) - } finally { - database.endTransaction() - } - - Pair(Team(teamId, name, members), null) - } - } - - private fun update(context: Context, id: Long, name: String, members: List): Deferred> { - return async(CommonPool) { - val error = validateSavePreconditions(context, id, name, members).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(TeamEntry.COLUMN_TEAM_NAME, name) - put(TeamEntry.COLUMN_DATE_MODIFIED, currentDate) - } - - val database = DatabaseManager.getWritableDatabase(context).await() - database.beginTransaction() - try { - database.update( - TeamEntry.TABLE_NAME, - values, - "${TeamEntry._ID}=?", - arrayOf(id.toString()) - ) - - database.delete( - TeamBowlerEntry.TABLE_NAME, - "${TeamBowlerEntry.COLUMN_TEAM_ID}=?", - arrayOf(id.toString()) - ) - - members.forEach { - values = ContentValues().apply { - put(TeamBowlerEntry.COLUMN_BOWLER_ID, it.bowlerId) - put(TeamBowlerEntry.COLUMN_TEAM_ID, id) - } - - database.insert(TeamBowlerEntry.TABLE_NAME, null, values) - } - - database.setTransactionSuccessful() - } catch (ex: Exception) { - Log.e(TAG, "Could not update team") - return@async Pair( - null, - BCError(R.string.error_saving_team, R.string.error_team_not_saved) - ) - } finally { - database.endTransaction() - } - - Pair(Team(id, name, members), null) - } - } - - fun fetchAll(context: Context): Deferred> { - return async(CommonPool) { - val teams: MutableList = ArrayList() - - val preferences = PreferenceManager.getDefaultSharedPreferences(context) - val sortBy = Sort.fromInt(preferences.getInt(Preferences.TEAM_SORT_ORDER, Sort.Alphabetically.ordinal)) - val database = DatabaseManager.getReadableDatabase(context).await() - - val orderQueryBy = if (sortBy == Sort.Alphabetically) { - " ORDER BY team." + TeamEntry.COLUMN_TEAM_NAME - } else { - " ORDER BY team." + TeamEntry.COLUMN_DATE_MODIFIED + " DESC" - } - - val rawTeamQuery = ("SELECT " + - "team.${TeamEntry.COLUMN_TEAM_NAME}, " + - "team.${TeamEntry._ID} AS tid, " + - "bowler.${BowlerEntry.COLUMN_BOWLER_NAME}, " + - "bowler.${BowlerEntry._ID} as bid " + - "FROM ${TeamEntry.TABLE_NAME} AS team " + - "JOIN ${TeamBowlerEntry.TABLE_NAME} AS tb " + - "ON team.${TeamEntry._ID}=${TeamBowlerEntry.COLUMN_TEAM_ID} " + - "JOIN ${BowlerEntry.TABLE_NAME} AS bowler " + - "ON tb.${TeamBowlerEntry.COLUMN_BOWLER_ID}=bowler.${BowlerEntry._ID} " + - "$orderQueryBy, " + - "bowler.${BowlerEntry.COLUMN_BOWLER_NAME}") - - var cursor: Cursor? = null - try { - cursor = database.rawQuery(rawTeamQuery, emptyArray()) - if (cursor.moveToFirst()) { - var teamId = cursor.getLong(cursor.getColumnIndex("tid")) - var teamName = cursor.getString(cursor.getColumnIndex(TeamEntry.COLUMN_TEAM_NAME)) - var members: MutableList = ArrayList() - - while (!cursor.isAfterLast) { - val newId = cursor.getLong(cursor.getColumnIndex("tid")) - if (newId != teamId) { - teams.add(Team(teamId, teamName, members)) - - teamId = newId - teamName = cursor.getString(cursor.getColumnIndex(TeamEntry.COLUMN_TEAM_NAME)) - members = ArrayList() - } - - members.add(TeamMember( - teamId = teamId, - bowlerName = cursor.getString(cursor.getColumnIndex(BowlerEntry.COLUMN_BOWLER_NAME)), - bowlerId = cursor.getLong(cursor.getColumnIndex("bid")) - )) - cursor.moveToNext() - } - - teams.add(Team(teamId, teamName, members)) - } - } finally { - cursor?.close() - } - - teams - } - } - } -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/teams/details/TeamDetailsFragment.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/teams/details/TeamDetailsFragment.kt deleted file mode 100644 index bb44c41eb..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/teams/details/TeamDetailsFragment.kt +++ /dev/null @@ -1,265 +0,0 @@ -package ca.josephroque.bowlingcompanion.teams.details - -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.Android -import ca.josephroque.bowlingcompanion.common.fragments.BaseFragment -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.database.DatabaseManager -import ca.josephroque.bowlingcompanion.games.GameControllerFragment -import ca.josephroque.bowlingcompanion.games.SeriesProvider -import ca.josephroque.bowlingcompanion.leagues.League -import ca.josephroque.bowlingcompanion.series.Series -import ca.josephroque.bowlingcompanion.statistics.interfaces.IStatisticsContext -import ca.josephroque.bowlingcompanion.statistics.provider.StatisticsProvider -import ca.josephroque.bowlingcompanion.teams.Team -import ca.josephroque.bowlingcompanion.teams.teammember.TeamMember -import ca.josephroque.bowlingcompanion.teams.teammember.TeamMemberDialog -import ca.josephroque.bowlingcompanion.teams.teammember.TeamMembersListFragment -import ca.josephroque.bowlingcompanion.utils.Analytics -import ca.josephroque.bowlingcompanion.utils.BCError -import ca.josephroque.bowlingcompanion.utils.safeLet -import kotlinx.android.synthetic.main.view_screen_header.view.* -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 the details of a single team and its members. - */ -class TeamDetailsFragment : BaseFragment(), - IFloatingActionButtonHandler, - ListFragment.ListFragmentDelegate, - TeamMemberDialog.TeamMemberDialogDelegate, - TeamMembersListFragment.TeamMemberListFragmentDelegate, - IStatisticsContext { - - companion object { - @Suppress("unused") - private const val TAG = "TeamDetailsFragment" - - private const val ARG_TEAM = "${TAG}_team" - - fun newInstance(team: Team): TeamDetailsFragment { - val fragment = TeamDetailsFragment() - fragment.arguments = Bundle().apply { putParcelable(ARG_TEAM, team) } - return fragment - } - } - - override val statisticsProviders: List by lazy { - val team = team - return@lazy if (team != null) { - arrayListOf(StatisticsProvider.TeamStatistics(team)) - } else { - emptyList() - } - } - - private var team: Team? = null - - private var allTeamMembersReady: Boolean = false - set(value) { - if (field != value) { - field = value - launch(Android) { - fabProvider?.invalidateFab() - } - } - } - - // MARK: Lifecycle functions - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - team = arguments?.getParcelable(ARG_TEAM) - - val view = inflater.inflate(R.layout.fragment_team_details, container, false) - setupHeader(view) - - val team = team - if (savedInstanceState == null && team != null) { - val fragment = TeamMembersListFragment.newInstance(team) - childFragmentManager.beginTransaction().apply { - add(R.id.fragment_container, fragment) - commit() - } - } - - return view - } - - override fun onStart() { - super.onStart() - fabProvider?.invalidateFab() - } - - // MARK: BaseFragment - - override fun updateToolbarTitle() { - team?.let { navigationActivity?.setToolbarTitle(it.name) } - } - - // MARK: IFloatingActionButtonHandler - - override fun getFabImage(): Int? = if (allTeamMembersReady) R.drawable.ic_ball else null - - override fun onFabClick() { - safeLet(context, team) { context, team -> - val needsNewPracticeSeries = team.members.any { it.league?.isPractice == true && it.series == null } - if (needsNewPracticeSeries) { - League.showPracticeGamesPicker(context, ::launchAttemptToBowl) - } else { - launchAttemptToBowl() - } - } - } - - // MARK: ListFragmentDelegate - - override fun onItemSelected(item: IIdentifiable, longPress: Boolean) { - if (item is TeamMember) { - val fragment = TeamMemberDialog.newInstance(item) - fragmentNavigation?.pushDialogFragment(fragment) - } - } - - override fun onItemDeleted(item: IIdentifiable) { - // Intentionally left blank - } - - // MARK: TeamMemberDialogDelegate - - override fun onFinishTeamMember(teamMember: TeamMember) { - childFragmentManager.fragments - .filter { it != null && it.isVisible } - .forEach { - val list = it as? TeamMembersListFragment ?: return - list.refreshList(teamMember) - team = team?.replaceTeamMember(teamMember) - } - } - - // MARK: TeamMemberListFragmentDelegate - - override fun onTeamMembersReadyChanged(ready: Boolean) { - allTeamMembersReady = ready - } - - override fun onTeamMembersReordered(order: List) { - team?.let { team = Team(it.id, it.name, it.members, order) } - - Analytics.trackReorderTeamMembers() - } - - // MARK: Private functions - - private fun setupHeader(rootView: View) { - rootView.tv_header_title.setText(R.string.team_members) - rootView.tv_header_caption.setText(R.string.team_members_select_league) - } - - private fun launchAttemptToBowl(practiceNumberOfGames: Int = 1) { - val context = context ?: return - launch(Android) { - val error = attemptToBowl(practiceNumberOfGames).await() - if (error != null) { - error.show(context) - return@launch - } - - val team = this@TeamDetailsFragment.team - if (team != null) { - val fragment = GameControllerFragment.newInstance(SeriesProvider.TeamSeries(team)) - fragmentNavigation?.pushFragment(fragment) - } else { - BCError().show(context) - } - } - } - - private fun attemptToBowl(practiceNumberOfGames: Int): Deferred { - return async(CommonPool) { - val context = this@TeamDetailsFragment.context ?: return@async BCError() - val team = this@TeamDetailsFragment.team ?: return@async BCError() - - if (allTeamMembersReady) { - allTeamMembersReady = false - val teamMemberSeries: MutableMap = HashMap() - - // Create series in the database for each team member, if one does not exist, - // or retrieve the existing series if it does. - val database = DatabaseManager.getWritableDatabase(context).await() - try { - team.members.forEach { - val league = it.league!! - - when { - league.isEvent -> { - val eventSeries = league.fetchSeries(context).await() - assert(eventSeries.size == 1) - teamMemberSeries[it] = eventSeries.first() - } - it.series == null -> { - if (!database.inTransaction()) { - database.beginTransaction() - } - - val (newSeries, seriesError) = league.createNewSeries( - context = context, - openDatabase = database, - numberOfPracticeGamesOverride = practiceNumberOfGames - ).await() - - if (newSeries != null) { - teamMemberSeries[it] = newSeries - } else if (seriesError != null) { - return@async seriesError - } - } - else -> { - teamMemberSeries[it] = it.series - } - } - } - - if (database.inTransaction()) { - database.setTransactionSuccessful() - } - } finally { - if (database.inTransaction()) { - database.endTransaction() - } - } - - // Replace immutable [TeamMember] instances with updated series - val membersWithSeries = team.members.map { - return@map TeamMember( - teamId = it.teamId, - bowlerName = it.bowlerName, - bowlerId = it.bowlerId, - league = it.league, - series = teamMemberSeries[it] - ) - } - - // Replace team with updated members - this@TeamDetailsFragment.team = Team( - id = team.id, - name = team.name, - members = membersWithSeries, - initialOrder = team.order - ) - } - - return@async null - } - } -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/teams/list/TeamDialog.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/teams/list/TeamDialog.kt deleted file mode 100644 index 3fc78679a..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/teams/list/TeamDialog.kt +++ /dev/null @@ -1,289 +0,0 @@ -package ca.josephroque.bowlingcompanion.teams.list - -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.support.v7.widget.LinearLayoutManager -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 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.adapters.BaseRecyclerViewAdapter -import ca.josephroque.bowlingcompanion.common.adapters.NameAverageRecyclerViewAdapter -import ca.josephroque.bowlingcompanion.common.fragments.BaseDialogFragment -import ca.josephroque.bowlingcompanion.teams.Team -import ca.josephroque.bowlingcompanion.teams.teammember.TeamMember -import ca.josephroque.bowlingcompanion.utils.Analytics -import ca.josephroque.bowlingcompanion.utils.Color -import ca.josephroque.bowlingcompanion.utils.safeLet -import kotlinx.android.synthetic.main.dialog_team.btn_delete as deleteButton -import kotlinx.android.synthetic.main.dialog_team.input_name as nameInput -import kotlinx.android.synthetic.main.dialog_team.list_bowlers as bowlersList -import kotlinx.android.synthetic.main.dialog_team.toolbar_team as teamToolbar -import kotlinx.android.synthetic.main.dialog_team.tv_error_no_bowlers as noBowlersError -import kotlinx.android.synthetic.main.dialog_team.view.* -import kotlinx.coroutines.experimental.launch - -/** - * Copyright (C) 2018 Joseph Roque - * - * Dialog to create a new team. - */ -class TeamDialog : BaseDialogFragment(), - BaseRecyclerViewAdapter.AdapterDelegate { - - companion object { - @Suppress("unused") - private const val TAG = "TeamDialog" - - private const val ARG_TEAM = "${TAG}_TEAM" - - fun newInstance(team: Team?): TeamDialog { - val dialog = TeamDialog() - dialog.arguments = Bundle().apply { team?.let { putParcelable(ARG_TEAM, team) } } - return dialog - } - } - - private var team: Team? = null - private var delegate: TeamDialogDelegate? = null - private lateinit var bowlerAdapter: NameAverageRecyclerViewAdapter - - private val selectedBowlers: List - get() { - val selected = bowlerAdapter.selectedItems - val list: MutableList = ArrayList() - selected.forEach { - list.add(TeamMember( - teamId = team?.id ?: -1, - bowlerName = it.name, - bowlerId = it.id - )) - } - return list - } - - private val onClickListener = View.OnClickListener { - safeLet(context, team) { context, team -> - AlertDialog.Builder(context) - .setTitle(String.format(context.resources.getString(R.string.query_delete_item), team.name)) - .setMessage(R.string.dialog_delete_item_message) - .setPositiveButton(R.string.delete) { _, _ -> - delegate?.onDeleteTeam(team) - 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? { - team = arguments?.getParcelable(ARG_TEAM) - - val rootView = inflater.inflate(R.layout.dialog_team, container, false) - team?.let { resetInputs(it, rootView) } - setupToolbar(rootView) - setupBowlers(rootView) - setupInput(rootView) - return rootView - } - - override fun onAttach(context: Context?) { - super.onAttach(context) - val parent = parentFragment as? TeamDialogDelegate - ?: throw RuntimeException("${parentFragment!!} must implement TeamDialogDelegate") - delegate = parent - } - - override fun onDetach() { - super.onDetach() - delegate = null - } - - override fun onStart() { - super.onStart() - dialog.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT) - - activity?.let { - nameInput.clearFocus() - App.hideSoftKeyBoard(it) - } - - team?.let { deleteButton.visibility = View.VISIBLE } - nameInput.text?.let { nameInput.setSelection(it.length) } - refreshBowlerList() - } - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val dialog = super.onCreateDialog(savedInstanceState) - dialog.requestWindowFeature(Window.FEATURE_NO_TITLE) - return dialog - } - - override fun dismiss() { - activity?.let { - App.hideSoftKeyBoard(it) - it.supportFragmentManager?.popBackStack() - } - - super.dismiss() - } - - // MARK: Private functions - - private fun setupToolbar(rootView: View) { - if (team == null) { - rootView.toolbar_team.setTitle(R.string.new_team) - } else { - rootView.toolbar_team.setTitle(R.string.edit_team) - } - - rootView.toolbar_team.apply { - inflateMenu(R.menu.dialog_team) - setNavigationIcon(R.drawable.ic_dismiss) - setNavigationOnClickListener { - dismiss() - } - setOnMenuItemClickListener { - when (it.itemId) { - R.id.action_save -> { - saveTeam() - true - } - else -> super.onOptionsItemSelected(it) - } - } - } - } - - private fun setupBowlers(rootView: View) { - val context = context ?: return - - bowlerAdapter = NameAverageRecyclerViewAdapter(emptyList(), this) - bowlerAdapter.multiSelect = true - - rootView.list_bowlers.layoutManager = LinearLayoutManager(context) - rootView.list_bowlers.adapter = bowlerAdapter - BaseRecyclerViewAdapter.applyDefaultDivider(rootView.list_bowlers, context) - } - - private fun setupInput(rootView: View) { - rootView.btn_delete.setOnClickListener(onClickListener) - 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(): Boolean { - val name = nameInput.text.toString() - val members = selectedBowlers - - return name.isNotEmpty() && members.isNotEmpty() - } - - private fun updateSaveButton() { - val saveButton = teamToolbar.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 saveTeam() { - launch(Android) { - this@TeamDialog.context?.let { context -> - val name = nameInput.text.toString() - - if (canSave()) { - val oldTeam = team - val (newTeam, error) = if (oldTeam != null) { - Team.save(context, oldTeam.id, name, selectedBowlers).await() - } else { - Team.save(context, -1, name, selectedBowlers).await() - } - - if (error != null) { - error.show(context) - oldTeam?.let { resetInputs(it) } - refreshBowlerList() - } else if (newTeam != null) { - dismiss() - delegate?.onFinishTeam(newTeam) - - if (oldTeam == null) { - Analytics.trackCreateTeam(selectedBowlers.size) - } else { - Analytics.trackEditTeam() - } - } - } - } - } - } - - private fun refreshBowlerList() { - val context = context ?: return - launch(Android) { - val bowlers = Bowler.fetchAll(context).await() - if (bowlers.isEmpty()) { - bowlersList.visibility = View.GONE - noBowlersError.visibility = View.VISIBLE - } else { - bowlersList.visibility = View.VISIBLE - noBowlersError.visibility = View.GONE - } - - val ids: MutableSet = HashSet() - team?.members?.forEach { - ids.add(it.bowlerId) - } - bowlerAdapter.items = bowlers - bowlerAdapter.setSelectedElementsWithIds(ids) - updateSaveButton() - } - } - - private fun resetInputs(team: Team, rootView: View? = null) { - val nameInput = rootView?.input_name ?: this.nameInput - nameInput.setText(team.name) - } - - // MARK: AdapterDelegate - - override fun onItemClick(item: Bowler) { - updateSaveButton() - } - - override fun onItemDelete(item: Bowler) {} - override fun onItemLongClick(item: Bowler) {} - override fun onItemSwipe(item: Bowler) {} - - // MARK: TeamDialogDelegate - - interface TeamDialogDelegate { - fun onFinishTeam(team: Team) - fun onDeleteTeam(team: Team) - } -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/teams/list/TeamListFragment.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/teams/list/TeamListFragment.kt deleted file mode 100644 index f61bd67ee..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/teams/list/TeamListFragment.kt +++ /dev/null @@ -1,106 +0,0 @@ -package ca.josephroque.bowlingcompanion.teams.list - -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.fragments.ListFragment -import ca.josephroque.bowlingcompanion.teams.Team -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 Teams. - */ -class TeamListFragment : ListFragment() { - - companion object { - @Suppress("unused") - private const val TAG = "TeamListFragment" - - fun newInstance(): TeamListFragment { - return TeamListFragment() - } - } - - override val emptyViewImage = R.drawable.empty_view_teams - override val emptyViewText = R.string.empty_view_teams - - // 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_teams, 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 fetchItems(): Deferred> { - context?.let { - return Team.fetchAll(it) - } - - return async(CommonPool) { - mutableListOf() - } - } - - override fun buildAdapter(): TeamRecyclerViewAdapter { - val adapter = TeamRecyclerViewAdapter(emptyList(), this) - adapter.swipeable = true - adapter.longPressable = true - return adapter - } - - // MARK: Private functions - - private fun showSortByDialog() { - context?.let { - AlertDialog.Builder(it) - .setTitle(it.resources.getString(R.string.sort_items)) - .setItems(R.array.team_sort_options) { _, which: Int -> - val order = Bowler.Companion.Sort.fromInt(which) - order?.let { sort -> - PreferenceManager.getDefaultSharedPreferences(context) - .edit() - .putInt(Preferences.TEAM_SORT_ORDER, sort.ordinal) - .apply() - refreshList() - } - } - .show() - } - } -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/teams/list/TeamRecyclerViewAdapter.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/teams/list/TeamRecyclerViewAdapter.kt deleted file mode 100644 index 792480a05..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/teams/list/TeamRecyclerViewAdapter.kt +++ /dev/null @@ -1,122 +0,0 @@ -package ca.josephroque.bowlingcompanion.teams.list - -import android.support.design.chip.Chip -import android.support.design.chip.ChipGroup -import android.support.v4.content.ContextCompat -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.teams.Team - -/** - * Copyright (C) 2018 Joseph Roque - * - * [RecyclerView.Adapter] that can display a [Team] and makes a call to the delegate. - */ -class TeamRecyclerViewAdapter( - items: List, - delegate: BaseRecyclerViewAdapter.AdapterDelegate? -) : BaseRecyclerViewAdapter(items, delegate) { - - companion object { - @Suppress("unused") - private const val TAG = "TeamRecyclerViewAdapter" - - private enum class ViewType { - Active, - Deleted; - - companion object { - private val map = ViewType.values().associateBy(ViewType::ordinal) - fun fromInt(type: Int) = map[type] - } - } - } - - // MARK: BaseRecyclerViewAdapter - - override fun getItemViewType(position: Int): Int { - return if (getItemAt(position).isDeleted) { - ViewType.Deleted.ordinal - } else { - ViewType.Active.ordinal - } - } - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseRecyclerViewAdapter.ViewHolder { - return when (ViewType.fromInt(viewType)) { - ViewType.Active -> { ViewHolderActive(LayoutInflater - .from(parent.context) - .inflate(R.layout.list_item_team, 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: ViewHolderActive - - inner class ViewHolderActive(view: View) : BaseRecyclerViewAdapter.ViewHolder(view) { - private val tvName: TextView? = view.findViewById(R.id.tv_name) - private val chipGroupMembers: ChipGroup? = view.findViewById(R.id.cg_members) - - override fun bind(item: Team) { - val context = itemView.context - tvName?.text = item.name - - chipGroupMembers?.removeAllViews() - item.members.forEach { - val viewId = View.generateViewId() - Chip(context).apply { - id = viewId - isFocusable = false - isClickable = false - text = it.bowlerName - setChipBackgroundColorResource(R.color.colorPrimary) - setTextColor(ContextCompat.getColor(context, R.color.primaryWhiteText)) - chipGroupMembers?.addView(this) - } - } - - itemView.setOnClickListener(this@TeamRecyclerViewAdapter) - itemView.setOnLongClickListener(this@TeamRecyclerViewAdapter) - } - } - - // 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: Team) { - 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/teams/teammember/TeamMember.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/teams/teammember/TeamMember.kt deleted file mode 100644 index 0ed16c49e..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/teams/teammember/TeamMember.kt +++ /dev/null @@ -1,68 +0,0 @@ -package ca.josephroque.bowlingcompanion.teams.teammember - -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.leagues.League -import ca.josephroque.bowlingcompanion.series.Series - -/** - * Copyright (C) 2018 Joseph Roque - * - * Member of a team. - */ -class TeamMember( - val teamId: Long, - val bowlerName: String, - val bowlerId: Long, - val league: League? = null, - val series: Series? = null -) : IIdentifiable, KParcelable { - - companion object { - @Suppress("unused") - private const val TAG = "TeamMember" - - @Suppress("unused") - @JvmField val CREATOR = parcelableCreator(::TeamMember) - - private const val TEAM_ID_SHIFT = 32 - private const val BOWLER_ID_TRIM = 0xFFFFFFFF - } - - /** - * ID is a concatenation of the first 32 bits of the team ID and first 32 bits - * of the bowler ID. - */ - override val id: Long - get() = teamId.shl(TEAM_ID_SHIFT).or(bowlerId.and(BOWLER_ID_TRIM)) - - // MARK: Constructors - - private constructor(p: Parcel): this( - teamId = p.readLong(), - bowlerName = p.readString()!!, - bowlerId = p.readLong(), - league = p.readParcelable(League::class.java.classLoader), - series = p.readParcelable(Series::class.java.classLoader) - ) - - constructor(teamMember: TeamMember): this( - teamId = teamMember.teamId, - bowlerName = teamMember.bowlerName, - bowlerId = teamMember.bowlerId, - league = teamMember.league, - series = teamMember.series - ) - - // MARK: Parcelable - - override fun writeToParcel(dest: Parcel, flags: Int) = with(dest) { - writeLong(teamId) - writeString(bowlerName) - writeLong(bowlerId) - writeParcelable(league, 0) - writeParcelable(series, 0) - } -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/teams/teammember/TeamMemberDialog.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/teams/teammember/TeamMemberDialog.kt deleted file mode 100644 index 02c1a3580..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/teams/teammember/TeamMemberDialog.kt +++ /dev/null @@ -1,225 +0,0 @@ -package ca.josephroque.bowlingcompanion.teams.teammember - -import android.app.Dialog -import android.content.Context -import android.os.Bundle -import android.support.v4.app.DialogFragment -import android.support.v4.app.FragmentManager -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.view.Window -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.Android -import ca.josephroque.bowlingcompanion.common.FabController -import ca.josephroque.bowlingcompanion.common.fragments.BaseDialogFragment -import ca.josephroque.bowlingcompanion.common.fragments.ListFragment -import ca.josephroque.bowlingcompanion.common.interfaces.IIdentifiable -import ca.josephroque.bowlingcompanion.leagues.League -import ca.josephroque.bowlingcompanion.leagues.LeagueListFragment -import ca.josephroque.bowlingcompanion.series.Series -import ca.josephroque.bowlingcompanion.series.SeriesListFragment -import ca.josephroque.bowlingcompanion.utils.safeLet -import kotlinx.android.synthetic.main.dialog_team_member.view.* -import kotlinx.android.synthetic.main.view_screen_header.view.* -import kotlinx.coroutines.experimental.launch - -/** - * Copyright (C) 2018 Joseph Roque - * - * Dialog to select league and series for a team member. - */ -class TeamMemberDialog : BaseDialogFragment(), - ListFragment.ListFragmentDelegate, - FragmentManager.OnBackStackChangedListener { - - companion object { - @Suppress("unused") - private const val TAG = "TeamMemberDialog" - - private const val ARG_TEAM_MEMBER = "${TAG}_team_member" - private const val ARG_SELECTED_LEAGUE = "${TAG}_selected_league" - - fun newInstance(teamMember: TeamMember): TeamMemberDialog { - val dialog = TeamMemberDialog() - dialog.arguments = Bundle().apply { putParcelable(ARG_TEAM_MEMBER, teamMember) } - return dialog - } - } - - private var teamMember: TeamMember? = null - private var selectedLeague: League? = null - private var selectedSeries: Series? = null - private var delegate: TeamMemberDialogDelegate? = null - - private lateinit var fabController: FabController - - // 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? { - teamMember = arguments?.getParcelable(ARG_TEAM_MEMBER) - selectedLeague = savedInstanceState?.getParcelable(ARG_SELECTED_LEAGUE) - - val rootView = inflater.inflate(R.layout.dialog_team_member, container, false) - setupToolbar(rootView) - setupFab(rootView) - prepareActions(rootView, selectedLeague == null) - setupChildFragment(savedInstanceState) - childFragmentManager.addOnBackStackChangedListener(this) - return rootView - } - - override fun onAttach(context: Context?) { - super.onAttach(context) - val parent = parentFragment as? TeamMemberDialogDelegate - ?: throw RuntimeException("${parentFragment!!} must implement TeamMemberDialogDelegate") - delegate = parent - } - - override fun onDetach() { - super.onDetach() - delegate = null - } - - override fun onStart() { - super.onStart() - dialog.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT) - view?.let { prepareActions(it, selectedLeague == null) } - } - - override fun onSaveInstanceState(outState: Bundle) { - super.onSaveInstanceState(outState) - outState.putParcelable(ARG_SELECTED_LEAGUE, selectedLeague) - } - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val dialog = super.onCreateDialog(savedInstanceState) - dialog.requestWindowFeature(Window.FEATURE_NO_TITLE) - return dialog - } - - override fun dismiss() { - activity?.supportFragmentManager?.popBackStack() - super.dismiss() - } - - // MARK: ListFragmentDelegate - - override fun onItemSelected(item: IIdentifiable, longPress: Boolean) { - if (item is League) { - selectedLeague = item - if (item.isEvent) { - saveTeamMember() - } else { - val fragment = SeriesListFragment.newInstance(item, true) - childFragmentManager.beginTransaction().apply { - replace(R.id.fragment_container, fragment) - addToBackStack(resources.getString(R.string.leagues)) - commit() - } - } - } else if (item is Series) { - selectedSeries = item - saveTeamMember() - } - } - - override fun onItemDeleted(item: IIdentifiable) { - // Intentionally left blank - } - - // MARK: OnBackStackChangedListener - - override fun onBackStackChanged() { - view?.let { prepareActions(it, childFragmentManager.backStackEntryCount == 0) } - } - - // MARK: Private functions - - private fun setupToolbar(rootView: View) { - rootView.toolbar_team_member.apply { - setNavigationOnClickListener { - if (selectedLeague == null) { - dismiss() - } else { - selectedLeague = null - childFragmentManager.popBackStack() - } - } - } - } - - private fun setupFab(rootView: View) { - fabController = FabController(rootView.fab, View.OnClickListener { - if (selectedLeague != null) { - saveTeamMember() - } - }) - } - - private fun prepareActions(rootView: View, forLeague: Boolean) { - if (forLeague) { - rootView.tv_header_title.setText(R.string.league) - rootView.tv_header_caption.setText(R.string.team_members_leagues_select_a_league) - fabController.image = null - - rootView.toolbar_team_member.apply { - setNavigationIcon(R.drawable.ic_dismiss) - title = teamMember?.bowlerName - } - } else { - rootView.tv_header_title.setText(R.string.series) - rootView.tv_header_caption.setText(R.string.team_members_series_select_a_series) - fabController.image = R.drawable.ic_add - - rootView.toolbar_team_member.apply { - setNavigationIcon(R.drawable.ic_arrow_back) - title = selectedLeague?.name - } - } - } - - private fun setupChildFragment(savedInstanceState: Bundle?) { - teamMember?.let { - if (savedInstanceState == null) { - val fragment = LeagueListFragment.newInstance( - bowlerId = it.bowlerId, - show = LeagueListFragment.Companion.Show.Both, - singleSelectMode = true - ) - - childFragmentManager.beginTransaction().apply { - add(R.id.fragment_container, fragment) - commit() - } - } - } - } - - private fun saveTeamMember() { - launch(Android) { - safeLet(teamMember, selectedLeague) { teamMember, league -> - val newTeamMember = TeamMember( - teamId = teamMember.teamId, - bowlerName = teamMember.bowlerName, - bowlerId = teamMember.bowlerId, - league = league, - series = selectedSeries - ) - delegate?.onFinishTeamMember(newTeamMember) - dismiss() - } - } - } - - // MARK: TeamMemberDialogDelegate - - interface TeamMemberDialogDelegate { - fun onFinishTeamMember(teamMember: TeamMember) - } -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/teams/teammember/TeamMembersListFragment.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/teams/teammember/TeamMembersListFragment.kt deleted file mode 100644 index a307675c3..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/teams/teammember/TeamMembersListFragment.kt +++ /dev/null @@ -1,119 +0,0 @@ -package ca.josephroque.bowlingcompanion.teams.teammember - -import android.content.Context -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.ListFragment -import ca.josephroque.bowlingcompanion.teams.Team -import kotlinx.coroutines.experimental.CommonPool -import kotlinx.coroutines.experimental.Deferred -import kotlinx.coroutines.experimental.async -import java.util.Collections - -/** - * Copyright (C) 2018 Joseph Roque - * - * A fragment to display a list of team members. - */ -class TeamMembersListFragment : - ListFragment(), - TeamMembersRecyclerViewAdapter.TeamMemberMoveDelegate { - - companion object { - @Suppress("unused") - private const val TAG = "TeamMembersListFragment" - - private const val ARG_TEAM = "${TAG}_team" - - fun newInstance(team: Team): TeamMembersListFragment { - val fragment = TeamMembersListFragment() - fragment.arguments = Bundle().apply { putParcelable(ARG_TEAM, team) } - return fragment - } - } - - private var team: Team? = null - private var teamMemberDelegate: TeamMemberListFragmentDelegate? = null - - private val allTeamMembersReady: Boolean - get() = (adapter?.items?.filter { it.league != null }?.size ?: -1) == (adapter?.items?.size ?: -2) - - override val emptyViewImage = R.drawable.empty_view_teams - override val emptyViewText = R.string.empty_view_team_members - - // MARK: Lifecycle functions - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - team = arguments?.getParcelable(ARG_TEAM) - return super.onCreateView(inflater, container, savedInstanceState) - } - - override fun onAttach(context: Context?) { - super.onAttach(context) - val parent = parentFragment as? TeamMemberListFragmentDelegate ?: throw RuntimeException("${parentFragment!!} must implement TeamMemberListFragmentDelegate") - teamMemberDelegate = parent - } - - override fun onDetach() { - super.onDetach() - teamMemberDelegate = null - } - - // MARK: BaseFragment - - override fun updateToolbarTitle() { - // Intentionally left blank - } - - // MARK: ListFragment - - override fun buildAdapter(): TeamMembersRecyclerViewAdapter { - val teamMembers: List = team?.members ?: emptyList() - val teamMembersOrder: List = team?.order ?: emptyList() - return TeamMembersRecyclerViewAdapter(teamMembers, teamMembersOrder, this, this) - } - - override fun fetchItems(): Deferred> { - return async(CommonPool) { - team?.let { - return@async it.membersInOrder.toMutableList() - } - mutableListOf() - } - } - - override fun listWasRefreshed() { - teamMemberDelegate?.onTeamMembersReadyChanged(allTeamMembersReady) - } - - // MARK: TeamMemberMoveDelegate - - override fun onTeamMemberMoved(from: Int, to: Int) { - val team = team ?: return - val teamMemberOrder = team.order.toMutableList() - Collections.swap(teamMemberOrder, from, to) - - // Update the team with the new order - this@TeamMembersListFragment.team = Team( - id = team.id, - name = team.name, - members = team.members, - initialOrder = teamMemberOrder) - arguments?.putParcelable(ARG_TEAM, this@TeamMembersListFragment.team) - - // Update adapter and delegate - adapter?.itemsOrder = teamMemberOrder - adapter?.notifyItemMoved(from, to) - teamMemberDelegate?.onTeamMembersReordered(teamMemberOrder) - } - - // MARK: TeamMemberListFragmentDelegate - - interface TeamMemberListFragmentDelegate { - fun onTeamMembersReadyChanged(ready: Boolean) - fun onTeamMembersReordered(order: List) - } -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/teams/teammember/TeamMembersRecyclerViewAdapter.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/teams/teammember/TeamMembersRecyclerViewAdapter.kt deleted file mode 100644 index 6aaa9a0d4..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/teams/teammember/TeamMembersRecyclerViewAdapter.kt +++ /dev/null @@ -1,127 +0,0 @@ -package ca.josephroque.bowlingcompanion.teams.teammember - -import android.support.v4.content.ContextCompat -import android.support.v7.widget.RecyclerView -import android.support.v7.widget.helper.ItemTouchHelper -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.ImageView -import android.widget.TextView -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.adapters.BaseRecyclerViewAdapter -import ca.josephroque.bowlingcompanion.teams.Team - -/** - * Copyright (C) 2018 Joseph Roque - * - * [BaseRecyclerViewAdapter] that can display the members of a [Team]. - */ -class TeamMembersRecyclerViewAdapter( - items: List, - var itemsOrder: List, - delegate: BaseRecyclerViewAdapter.AdapterDelegate, - private var moveDelegate: TeamMemberMoveDelegate? -) : BaseRecyclerViewAdapter(items, delegate) { - - companion object { - @Suppress("unused") - private const val TAG = "TeamMembersRecyclerViewAdapter" - } - - // MARK: BaseRecyclerViewAdapter - - override fun getItemAt(position: Int): TeamMember { - return items.first { it.id == itemsOrder[position] } - } - - override fun getPositionOfItem(item: TeamMember): Int { - return itemsOrder.indexOf(item.id) - } - - override fun getItemViewType(position: Int): Int { - return 0 - } - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { - return ViewHolder(LayoutInflater - .from(parent.context) - .inflate(R.layout.list_item_team_member, parent, false) - ) - } - - override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) { - super.onDetachedFromRecyclerView(recyclerView) - moveDelegate = null - } - - override fun onBindViewHolder(holder: BaseRecyclerViewAdapter.ViewHolder, position: Int) { - holder.bind(getItemAt(position)) - } - - override fun buildItemTouchHelper(): ItemTouchHelper.Callback { - return DragCallback() - } - - // MARK: ViewHolder - - inner class ViewHolder(view: View) : BaseRecyclerViewAdapter.ViewHolder(view) { - private val tvBowlerName: TextView = view.findViewById(R.id.tv_team_member_name) - private val tvLeagueName: TextView = view.findViewById(R.id.tv_team_member_league) - private val tvSeriesName: TextView = view.findViewById(R.id.tv_team_member_series) - private val ivIcon: ImageView = view.findViewById(R.id.iv_team_member_icon) - - override fun bind(item: TeamMember) { - val context = itemView.context - ivIcon.setImageResource(R.drawable.ic_menu) - ivIcon.setColorFilter(ContextCompat.getColor(context, R.color.primaryBlackIcon)) - - tvBowlerName.text = item.bowlerName - - if (item.league != null) { - tvLeagueName.text = item.league.name - tvLeagueName.setTextColor(ContextCompat.getColor(context, R.color.primaryBlackText)) - } else { - tvLeagueName.setText(R.string.no_league_selected) - tvLeagueName.setTextColor(ContextCompat.getColor(context, R.color.dangerRed)) - - tvSeriesName.setText(R.string.no_series_selected) - tvSeriesName.setTextColor(ContextCompat.getColor(context, R.color.dangerRed)) - } - - if (item.series != null) { - tvSeriesName.text = item.series.prettyDate - tvSeriesName.setTextColor(ContextCompat.getColor(context, R.color.primaryBlackText)) - } else if (item.league != null) { - tvSeriesName.setText(R.string.create_new_series) - tvSeriesName.setTextColor(ContextCompat.getColor(context, R.color.primaryBlackText)) - } - - itemView.setOnClickListener(this@TeamMembersRecyclerViewAdapter) - } - } - - // MARK: DragCallback - - inner class DragCallback : ItemTouchHelper.Callback() { - override fun isLongPressDragEnabled() = true - override fun isItemViewSwipeEnabled() = false - - override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int { - return makeMovementFlags(ItemTouchHelper.UP or ItemTouchHelper.DOWN, 0) - } - - override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean { - moveDelegate?.onTeamMemberMoved(viewHolder.adapterPosition, target.adapterPosition) - return true - } - - override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {} - } - - // MARK: TeamMemberMoveDelegate - - interface TeamMemberMoveDelegate { - fun onTeamMemberMoved(from: Int, to: Int) - } -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/transfer/BaseTransferDialogFragment.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/transfer/BaseTransferDialogFragment.kt deleted file mode 100644 index a0a6d779e..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/transfer/BaseTransferDialogFragment.kt +++ /dev/null @@ -1,126 +0,0 @@ -package ca.josephroque.bowlingcompanion.transfer - -import android.os.Bundle -import android.support.v4.app.DialogFragment -import android.support.v4.app.FragmentManager -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import ca.josephroque.bowlingcompanion.App -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.fragments.BaseDialogFragment -import ca.josephroque.bowlingcompanion.common.fragments.BaseFragment -import kotlinx.android.synthetic.main.dialog_base_transfer.toolbar_transfer as toolbar -import kotlinx.android.synthetic.main.dialog_base_transfer.view.* - -/** - * Copyright (C) 2018 Joseph Roque - * - * DialogFragment to manage data transfer menu. - */ -class BaseTransferDialogFragment : BaseDialogFragment(), TransferMenuFragment.TransferMenuDelegate { - - companion object { - @Suppress("unused") - private const val TAG = "BaseTransferDialogFragment" - - fun newInstance(): BaseTransferDialogFragment { - return BaseTransferDialogFragment() - } - } - - private val onBackStackChangedListener = FragmentManager.OnBackStackChangedListener { - toolbar.setNavigationIcon(if (childFragmentManager.backStackEntryCount > 0) { - R.drawable.ic_arrow_back - } else { - R.drawable.ic_dismiss - }) - - val transferFragment = childFragmentManager.fragments.last() as? BaseTransferFragment ?: return@OnBackStackChangedListener - transferFragment.toolbarTitle?.let { toolbar.setTitle(it) } - } - - // 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? { - val view = inflater.inflate(R.layout.dialog_base_transfer, container, false) - setupToolbar(view) - - if (savedInstanceState == null) { - val fragment = TransferMenuFragment.newInstance() - childFragmentManager.beginTransaction().apply { - add(R.id.fragment_container, fragment) - commit() - } - - view.toolbar_transfer.setTitle(fragment.toolbarTitle) - } - - return view - } - - override fun onStart() { - super.onStart() - childFragmentManager.addOnBackStackChangedListener(onBackStackChangedListener) - } - - override fun onStop() { - super.onStop() - childFragmentManager.removeOnBackStackChangedListener(onBackStackChangedListener) - } - - override fun dismiss() { - App.hideSoftKeyBoard(activity!!) - activity?.supportFragmentManager?.popBackStack() - super.dismiss() - } - - // MARK: Private functions - - private fun setupToolbar(rootView: View) { - rootView.toolbar_transfer.apply { - setNavigationIcon(R.drawable.ic_dismiss) - setNavigationOnClickListener { _ -> - (childFragmentManager.fragments.lastOrNull() as? BaseTransferFragment)?.let { - if (!it.isBackEnabled) { return@setNavigationOnClickListener } - } - - if (childFragmentManager.backStackEntryCount > 0) { - childFragmentManager.popBackStack() - } else { - dismiss() - } - } - } - } - - private fun pushFragment(fragment: BaseFragment) { - childFragmentManager.beginTransaction().apply { - replace(R.id.fragment_container, fragment) - addToBackStack("BaseTransferMenu") - commit() - } - } - - // MARK: TransferMenuDelegate - - override fun showExportScreen() { - val fragment = TransferExportFragment.newInstance() - pushFragment(fragment) - } - - override fun showImportScreen() { - val fragment = TransferImportFragment.newInstance() - pushFragment(fragment) - } - - override fun showRestoreDeleteScreen() { - val fragment = TransferRestoreDeleteFragment.newInstance() - pushFragment(fragment) - } -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/transfer/BaseTransferFragment.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/transfer/BaseTransferFragment.kt deleted file mode 100644 index 479eb5df8..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/transfer/BaseTransferFragment.kt +++ /dev/null @@ -1,21 +0,0 @@ -package ca.josephroque.bowlingcompanion.transfer - -import ca.josephroque.bowlingcompanion.common.fragments.BaseFragment - -/** - * Copyright (C) 2018 Joseph Roque - * - * Declares values which transfer menu fragments must provide - */ -abstract class BaseTransferFragment : BaseFragment() { - - abstract val toolbarTitle: Int? - - abstract val isBackEnabled: Boolean - - // MARK: BaseFragment - - override fun updateToolbarTitle() { - // Intentionally left blank - } -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/transfer/TransferExportFileProvider.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/transfer/TransferExportFileProvider.kt deleted file mode 100644 index c55a71d94..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/transfer/TransferExportFileProvider.kt +++ /dev/null @@ -1,10 +0,0 @@ -package ca.josephroque.bowlingcompanion.transfer - -import android.support.v4.content.FileProvider - -/** - * Copyright (C) 2020 Joseph Roque - * - * Provider for Game Overview bitmaps. - */ -class TransferExportFileProvider : FileProvider() diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/transfer/TransferExportFragment.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/transfer/TransferExportFragment.kt deleted file mode 100644 index 3016b6054..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/transfer/TransferExportFragment.kt +++ /dev/null @@ -1,86 +0,0 @@ -package ca.josephroque.bowlingcompanion.transfer - -import android.content.Intent -import android.net.Uri -import android.os.Bundle -import android.support.v4.content.FileProvider -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import ca.josephroque.bowlingcompanion.BuildConfig -import ca.josephroque.bowlingcompanion.R -import ca.josephroque.bowlingcompanion.common.Android -import ca.josephroque.bowlingcompanion.utils.BCError -import kotlinx.android.synthetic.main.dialog_transfer_export.view.btn_export -import kotlinx.coroutines.experimental.CommonPool -import kotlinx.coroutines.experimental.launch - -/** - * Copyright (C) 2018 Joseph Roque - * - * A fragment to export user's data. - */ -class TransferExportFragment : BaseTransferFragment() { - - companion object { - @Suppress("unused") - private const val TAG = "TransferExportFragment" - - fun newInstance(): TransferExportFragment { - return TransferExportFragment() - } - } - - private val onClickListener = View.OnClickListener { - when (it.id) { - R.id.btn_export -> { - exportUserData() - } - } - } - - // MARK: BaseTransferFragment - - override val toolbarTitle = R.string.export - override val isBackEnabled = true - - // MARK: Lifecycle functions - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - val view = inflater.inflate(R.layout.dialog_transfer_export, container, false) - - view.btn_export.setOnClickListener(onClickListener) - - return view - } - - // MARK: Private functions - - private fun exportUserData() { - launch(CommonPool) { - val context = this@TransferExportFragment.context ?: return@launch - val userData = UserData(context) - - if (!userData.exportData().await()) { - launch(Android) { - BCError(R.string.export_error, R.string.error_data_export_failed, BCError.Severity.Error).show(context) - } - return@launch - } - - val contentUri = FileProvider.getUriForFile( - context, - "${BuildConfig.APPLICATION_ID}.transfer.TransferExportFileProvider", - userData.exportFile) - - val intent = Intent(Intent.ACTION_SEND) - intent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION - intent.type = "application/octet-stream" - intent.putExtra(Intent.EXTRA_STREAM, contentUri) - - launch(Android) { - startActivity(intent) - } - } - } -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/transfer/TransferImportFragment.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/transfer/TransferImportFragment.kt deleted file mode 100644 index 5c7c94114..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/transfer/TransferImportFragment.kt +++ /dev/null @@ -1,253 +0,0 @@ -package ca.josephroque.bowlingcompanion.transfer - -import android.app.Activity -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.Android -import kotlinx.android.synthetic.main.dialog_transfer_import.import_next_step as importNextStep -import kotlinx.android.synthetic.main.dialog_transfer_import.import_status as importStatus -import kotlinx.android.synthetic.main.dialog_transfer_import.view.* -import kotlinx.coroutines.experimental.Deferred -import kotlinx.coroutines.experimental.launch -import android.content.DialogInterface -import android.content.Intent -import android.support.v7.app.AlertDialog -import android.util.Log -import ca.josephroque.bowlingcompanion.database.DatabaseHelper -import ca.josephroque.bowlingcompanion.utils.Analytics -import ca.josephroque.bowlingcompanion.utils.BCError -import ca.josephroque.bowlingcompanion.utils.Files -import kotlinx.coroutines.experimental.CommonPool -import kotlinx.coroutines.experimental.async -import java.io.BufferedInputStream -import java.io.File -import java.io.FileInputStream -import java.io.IOException -import java.io.InputStream - -/** - * Copyright (C) 2018 Joseph Roque - * - * A fragment to import user's data. - */ -class TransferImportFragment : BaseTransferFragment() { - - companion object { - @Suppress("unused") - private const val TAG = "TransferImportFragment" - private const val DATA_IMPORT_REQUEST = 0 - - fun newInstance(): TransferImportFragment { - return TransferImportFragment() - } - } - - private val sqlDbFileSignature = byteArrayOf( - 0x53.toByte(), 0x51.toByte(), 0x4C.toByte(), 0x69.toByte(), - 0x74.toByte(), 0x65.toByte(), 0x20.toByte(), 0x66.toByte(), - 0x6F.toByte(), 0x72.toByte(), 0x6D.toByte(), 0x61.toByte(), - 0x74.toByte(), 0x20.toByte(), 0x33.toByte(), 0x00.toByte() - ) - - private var fileTask: Deferred? = null - - private val onClickListener = View.OnClickListener { view -> - when (view.id) { - R.id.btn_import -> { - importStatus.visibility = View.GONE - importUserData() - } - } - } - - // MARK: BaseTransferFragment - - override val toolbarTitle = R.string.data_import - override val isBackEnabled = fileTask == null - - // MARK: Lifecycle functions - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - val view = inflater.inflate(R.layout.dialog_transfer_import, container, false) - - view.btn_import.setOnClickListener(onClickListener) - - return view - } - - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - super.onActivityResult(requestCode, resultCode, data) - when(requestCode) { - DATA_IMPORT_REQUEST -> handleImportedData(resultCode, data) - } - } - - // MARK: Private functions - - private fun importSucceeded() { - importNextStep.visibility = View.VISIBLE - importStatus.visibility = View.GONE - } - - private fun importFailed(error: String) { - importNextStep.visibility = View.GONE - importStatus.apply { - text = error - visibility = View.VISIBLE - } - } - - private fun handleImportedData(resultCode: Int, data: Intent?) { - Analytics.trackTransferImport(Analytics.Companion.EventTime.End) - - if (resultCode != Activity.RESULT_OK || data?.data == null) { - importFailed(resources.getString(R.string.error_unknown)) - } - - val uri = data?.data ?: return - val context = this.context ?: return - - launch(CommonPool) { - var error: String? = null - val userData = UserData(context) - - var inputStream: InputStream? = null - try { - inputStream = context.contentResolver.openInputStream(uri) - if (!Files.copyFile(inputStream, userData.importFile).await()) { - error = resources.getString(R.string.error_data_import_failed) - } - } catch (ex: IOException) { - Log.e(TAG, "Failed to open input stream for import", ex) - error = resources.getString(R.string.error_data_import_failed) - } finally { - try { - inputStream?.close() - } catch (ex: IOException) { - Log.e(TAG, "Failed to close input stream for import", ex) - } - } - - if (error == null && !verifyData(userData.importFile)) { - error = resources.getString(R.string.error_data_import_invalid) - } - - launch(Android) { - if (error != null) { - importFailed(error) - } else { - promptUserToOverrideData() - } - } - } - } - - private fun importUserData() { - Analytics.trackTransferImport(Analytics.Companion.EventTime.Begin) - val intent = Intent(Intent.ACTION_GET_CONTENT).apply { - addCategory(Intent.CATEGORY_OPENABLE) - putExtra(Intent.EXTRA_LOCAL_ONLY, true) - type = "*/*" - } - - startActivityForResult(Intent.createChooser(intent, resources.getString(R.string.import_from)), DATA_IMPORT_REQUEST) - } - - private fun overwriteDataWithImport() { - launch(CommonPool) { - val context = this@TransferImportFragment.context ?: return@launch - val userData = UserData(context) - - fileTask = async(CommonPool) { - DatabaseHelper.closeInstance() - - if (!userData.backupData().await()) { - return@async R.string.error_data_backup_failed - } - - if (!userData.overwriteDataWithImport().await()) { - userData.deleteBackup().await() - return@async R.string.error_overwrite_data_failed - } - - userData.deleteImport().await() - - return@async null - } - - val error = fileTask?.await() - fileTask = null - - launch(Android) { - if (error == null) { - importSucceeded() - } else { - BCError(R.string.import_error, error, BCError.Severity.Error).show(context) - } - } - } - } - - private fun deleteImport() { - launch(Android) { - val context = this@TransferImportFragment.context ?: return@launch - val userData = UserData(context) - - fileTask = async(CommonPool) { - userData.deleteImport().await() - return@async null - } - fileTask?.await() - fileTask = null - } - } - - private fun promptUserToOverrideData() { - val onClickListener = DialogInterface.OnClickListener { dialog, which -> - if (which == DialogInterface.BUTTON_POSITIVE) { - overwriteDataWithImport() - } else { - importFailed(resources.getString(R.string.error_data_import_cancelled)) - deleteImport() - } - - dialog.dismiss() - } - - val context = this.context ?: return - AlertDialog.Builder(context) - .setTitle(R.string.overwrite_data_title) - .setMessage(R.string.overwrite_data_message) - .setPositiveButton(R.string.overwrite, onClickListener) - .setNegativeButton(R.string.cancel, onClickListener) - .create() - .show() - } - - private fun verifyData(data: File): Boolean { - if (!data.exists()) { - return false - } - - val fileSignature = ByteArray(16) - var inputStream: BufferedInputStream? = null - try { - inputStream = BufferedInputStream(FileInputStream(data)) - inputStream.read(fileSignature, 0, 16) - } catch (ex: IOException) { - Log.e(TAG, "Failed to verify import data", ex) - return false - } finally { - try { - inputStream?.close() - } catch (ex: IOException) { - Log.e(TAG, "Failed to close input stream verifying import data", ex) - } - } - - return fileSignature.contentEquals(sqlDbFileSignature) - } -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/transfer/TransferMenuFragment.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/transfer/TransferMenuFragment.kt deleted file mode 100644 index 39b4c5a92..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/transfer/TransferMenuFragment.kt +++ /dev/null @@ -1,75 +0,0 @@ -package ca.josephroque.bowlingcompanion.transfer - -import android.content.Context -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import ca.josephroque.bowlingcompanion.R -import kotlinx.android.synthetic.main.dialog_transfer.view.* - -/** - * Copyright (C) 2018 Joseph Roque - * - * A fragment to enable transferring the user's data to a new device, or transferring from another device to their current one. - */ -class TransferMenuFragment : BaseTransferFragment() { - - companion object { - @Suppress("unused") - private const val TAG = "TransferMenuFragment" - - fun newInstance(): TransferMenuFragment { - return TransferMenuFragment() - } - } - - private var delegate: TransferMenuDelegate? = null - - private val onClickListener = View.OnClickListener { - when (it.id) { - R.id.btn_export -> delegate?.showExportScreen() - R.id.btn_import -> delegate?.showImportScreen() - R.id.btn_restore_delete -> delegate?.showRestoreDeleteScreen() - } - } - - // MARK: BaseTransferFragment - - override val toolbarTitle = R.string.transfer_data - override val isBackEnabled = true - - // MARK: Lifecycle functions - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - val view = inflater.inflate(R.layout.dialog_transfer, container, false) - - view.btn_export.setOnClickListener(onClickListener) - view.btn_import.setOnClickListener(onClickListener) - view.btn_restore_delete.setOnClickListener(onClickListener) - - return view - } - - override fun onAttach(context: Context?) { - super.onAttach(context) - delegate = parentFragment as? TransferMenuDelegate ?: throw RuntimeException("${parentFragment!!} must implement TransferMenuDelegate") - } - - override fun onDetach() { - super.onDetach() - delegate = null - } - - override fun updateToolbarTitle() { - // Intentionally left blank - } - - // MARK: TransferMenuDelegate - - interface TransferMenuDelegate { - fun showExportScreen() - fun showImportScreen() - fun showRestoreDeleteScreen() - } -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/transfer/TransferRestoreDeleteFragment.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/transfer/TransferRestoreDeleteFragment.kt deleted file mode 100644 index 0c32b51c6..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/transfer/TransferRestoreDeleteFragment.kt +++ /dev/null @@ -1,168 +0,0 @@ -package ca.josephroque.bowlingcompanion.transfer - -import android.content.DialogInterface -import android.os.Bundle -import android.support.v7.app.AlertDialog -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.utils.Analytics -import ca.josephroque.bowlingcompanion.utils.BCError -import kotlinx.android.synthetic.main.dialog_transfer_restore_delete.tv_no_backup as noBackupText -import kotlinx.android.synthetic.main.dialog_transfer_restore_delete.btn_restore as restoreButton -import kotlinx.android.synthetic.main.dialog_transfer_restore_delete.btn_delete as deleteButton -import kotlinx.android.synthetic.main.dialog_transfer_restore_delete.view.* -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 to restore or delete the user's data. - */ -class TransferRestoreDeleteFragment : BaseTransferFragment() { - - companion object { - @Suppress("unused") - private const val TAG = "TransferResDelFragment" - - fun newInstance(): TransferRestoreDeleteFragment { - return TransferRestoreDeleteFragment() - } - } - - private var fileTask: Deferred? = null - - private val onClickListener = View.OnClickListener { - when (it.id) { - R.id.btn_restore -> { - promptRestoreBackup() - } - R.id.btn_delete -> { - promptDeleteBackup() - } - } - } - - // MARK: BaseTransferFragment - - override val toolbarTitle = R.string.restore_or_delete - override val isBackEnabled = fileTask == null - - // MARK: Lifecycle functions - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - val view = inflater.inflate(R.layout.dialog_transfer_restore_delete, container, false) - - view.btn_restore.setOnClickListener(onClickListener) - view.btn_delete.setOnClickListener(onClickListener) - - return view - } - - override fun onStart() { - super.onStart() - updateButtons() - } - - // MARK: Private functions - - private fun updateButtons() { - val context = context - if (context != null) { - val userData = UserData(context) - if (userData.backupFile.exists()) { - noBackupText.visibility = View.GONE - restoreButton.isEnabled = true - deleteButton.isEnabled = true - return - } - } - - noBackupText.visibility = View.VISIBLE - restoreButton.isEnabled = false - deleteButton.isEnabled = false - } - - private fun promptDeleteBackup() { - val context = context ?: return - val onClickListener = DialogInterface.OnClickListener { dialog, which -> - if (which == DialogInterface.BUTTON_POSITIVE) { - deleteBackup() - } - - dialog.dismiss() - } - - AlertDialog.Builder(context) - .setTitle(R.string.delete_backup_title) - .setMessage(R.string.delete_backup_message) - .setPositiveButton(R.string.delete, onClickListener) - .setNegativeButton(R.string.cancel, onClickListener) - .create() - .show() - } - - private fun deleteBackup() { - val context = context ?: return - Analytics.trackTransferDeleteBackup() - - val userData = UserData(context) - launch(Android) { - fileTask = async(CommonPool) { - return@async if (userData.deleteBackup().await()) null else R.string.error_delete_backup_failed_message - } - val error = fileTask?.await() - fileTask = null - - if (error != null) { - BCError(R.string.error_delete_backup_failed_title, error, BCError.Severity.Error).show(context) - } - - updateButtons() - } - } - - private fun promptRestoreBackup() { - val context = context ?: return - val onClickListener = DialogInterface.OnClickListener { dialog, which -> - if (which == DialogInterface.BUTTON_POSITIVE) { - restoreBackup() - } - - dialog.dismiss() - } - - AlertDialog.Builder(context) - .setTitle(R.string.restore_backup_title) - .setMessage(R.string.restore_backup_message) - .setPositiveButton(R.string.restore, onClickListener) - .setNegativeButton(R.string.cancel, onClickListener) - .create() - .show() - } - - private fun restoreBackup() { - val context = context ?: return - Analytics.trackTransferRestoreBackup() - - val userData = UserData(context) - launch(Android) { - fileTask = async(CommonPool) { - return@async if (userData.restoreBackup().await()) null else R.string.error_restore_backup_failed_message - } - val error = fileTask?.await() - fileTask = null - - if (error != null) { - BCError(R.string.error_restore_backup_failed_title, error, BCError.Severity.Error).show(context) - } - - updateButtons() - } - } -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/transfer/UserData.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/transfer/UserData.kt deleted file mode 100644 index 46e6f521d..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/transfer/UserData.kt +++ /dev/null @@ -1,86 +0,0 @@ -package ca.josephroque.bowlingcompanion.transfer - -import android.content.Context -import ca.josephroque.bowlingcompanion.database.DatabaseHelper -import ca.josephroque.bowlingcompanion.utils.Files -import kotlinx.coroutines.experimental.CommonPool -import kotlinx.coroutines.experimental.Deferred -import kotlinx.coroutines.experimental.async -import java.io.File -import java.lang.ref.WeakReference - -/** - * Copyright (C) 2018 Joseph Roque - * - * User data which can be downloaded or uploaded. - */ -class UserData(context: Context) { - - private val context: WeakReference = WeakReference(context) - - val sourceFile by lazy { File(sourcePath) } - private val sourcePath by lazy { - this@UserData.context.get()?.getDatabasePath(DatabaseHelper.DATABASE_NAME)?.absolutePath - } - - val importFile by lazy { File(importPath) } - private val importPath by lazy { - val cacheDir = this@UserData.context.get()?.cacheDir?.absolutePath - cacheDir?.let { return@lazy "${it}/imports/bowling_db_import.db" } - return@lazy null - } - - val exportFile by lazy { File(exportPath) } - private val exportPath by lazy { - val cacheDir = this@UserData.context.get()?.cacheDir?.absolutePath - cacheDir?.let { return@lazy "${it}/exports/bowling_db_export.db" } - return@lazy null - } - - val backupFile by lazy { File(backupPath) } - private val backupPath by lazy { - val filesDir = this@UserData.context.get()?.filesDir?.absolutePath - filesDir?.let { return@lazy "${it}/bowling_db_backup.db" } - return@lazy null - } - - fun exportData(): Deferred { - return Files.copyFile(sourceFile, exportFile) - } - - fun overwriteDataWithImport(): Deferred { - return async(CommonPool) { - if (importFile.exists()) { - return@async Files.copyFile(importFile, sourceFile).await() - } - - return@async false - } - } - - fun deleteImport(): Deferred { - return async(CommonPool) { - return@async importFile.delete() - } - } - - fun backupData(): Deferred { - return Files.copyFile(sourceFile, backupFile) - } - - fun restoreBackup(): Deferred { - return async(CommonPool) { - if (backupFile.exists()) { - return@async Files.copyFile(backupFile, sourceFile).await() - } - - return@async false - } - } - - fun deleteBackup(): Deferred { - return async(CommonPool) { - return@async backupFile.delete() - } - } -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/transfer/view/ProgressView.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/transfer/view/ProgressView.kt deleted file mode 100644 index 2a877f7bd..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/transfer/view/ProgressView.kt +++ /dev/null @@ -1,37 +0,0 @@ -package ca.josephroque.bowlingcompanion.transfer.view - -import android.content.Context -import android.util.AttributeSet -import android.view.LayoutInflater -import android.widget.LinearLayout -import ca.josephroque.bowlingcompanion.R -import kotlinx.android.synthetic.main.view_progress.view.progress_bar as progressBar -import kotlinx.android.synthetic.main.view_progress.view.progress_status as progressStatus - -/** - * Copyright (C) 2018 Joseph Roque - * - * View to display a progress bar and text. - */ -class ProgressView : LinearLayout { - - companion object { - @Suppress("unused") - private const val TAG = "ProgressView" - } - - 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_progress, this, true) - } - - fun setProgress(progress: Int) { - assert(progress in 0..100) { "Progress can only be between 0% and 100%" } - progressBar.progress = progress - } - - fun setStatus(status: CharSequence?) { - progressStatus.text = status - } -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/utils/Analytics.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/utils/Analytics.kt deleted file mode 100644 index 795a2b1bb..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/utils/Analytics.kt +++ /dev/null @@ -1,184 +0,0 @@ -@file:Suppress("UNUSED_PARAMETER") - -package ca.josephroque.bowlingcompanion.utils - -import android.content.Context -import ca.josephroque.bowlingcompanion.BuildConfig -import ca.josephroque.bowlingcompanion.bowlers.Bowler -import ca.josephroque.bowlingcompanion.leagues.League -import ca.josephroque.bowlingcompanion.series.Series - -/** - * Copyright (C) 2018 Joseph Roque - * - * Analytics engine to record user events. - */ -class Analytics private constructor() { - - // Wrapper for the singleton instance - private object HOLDER { val INSTANCE = Analytics() } - - companion object { - private val instance: Analytics by lazy { - assert(HOLDER.INSTANCE.initialized) { "The Mixpanel instance was accessed before being initialized." } - HOLDER.INSTANCE - } - - private val dangerousInstance: Analytics by lazy { - HOLDER.INSTANCE - } - - fun initialize(context: Context) { - assert(!dangerousInstance.disableTracking) { "You cannot initialize analytics once tracking has been disabled." } - dangerousInstance.initialized = true - } - - @Suppress("unused") - fun disableTracking() { - if (BuildConfig.DEBUG) { - assert(!instance.initialized) { "You must disable tracking before initializing analytics." } - dangerousInstance.disableTracking = true - } - } - - enum class EventTime { - Begin, End - } - - // MARK: Team events - - fun trackSelectTeam() {} - - fun trackCreateTeam(numberOfMembers: Int) {} - - fun trackDeleteTeam() {} - - fun trackEditTeam() {} - - fun trackReorderTeamMembers() {} - - // MARK: Bowler events - - fun trackSelectBowler() {} - - fun trackCreateBowler() {} - - fun trackDeleteBowler() {} - - fun trackEditBowler() {} - - fun trackSortedBowlers(order: Bowler.Companion.Sort) {} - - // MARK: League events - - fun trackSelectLeague(isPractice: Boolean, isEvent: Boolean) {} - - fun trackCreateLeague(isEvent: Boolean, numberOfGames: Int, hasAdditionalInfo: Boolean) {} - - fun trackDeleteLeague() {} - - fun trackEditLeague() {} - - fun trackSortedLeagues(order: League.Companion.Sort) {} - - // MARK: Series events - - fun trackSelectSeries() {} - - fun trackCreateSeries() {} - - fun trackDeleteSeries() {} - - fun trackEditSeries() {} - - fun trackToggledSeriesView(view: Series.Companion.View) {} - - // MARK: Game events - - fun trackChangedGame() {} - - fun trackSetGameManualScore() {} - - fun trackLockGame() {} - - fun trackViewPossibleScore(possibleScore: Int, frame: Int) {} - - fun trackResetGame() {} - - // MARK: Match Play events - - fun trackRecordMatchPlay() {} - - // MARK: Statistics events - - fun trackViewStatisticsList() {} - - fun trackStatisticsLoaded(time: EventTime) {} - - fun trackViewStatisticsGraph(statisticName: String) {} - - // MARK: Transfer events - - fun trackViewTransferMenu() {} - - fun trackTransferExport(time: EventTime) {} - - fun trackTransferImport(time: EventTime) {} - - fun trackTransferRestoreBackup() {} - - fun trackTransferDeleteBackup() {} - - // MARK: Settings - - fun trackViewSettings() {} - - fun trackEnableAutoAdvance() {} - - fun trackDisableAutoAdvance() {} - - fun trackEnableAutoLock() {} - - fun trackDisableAutoLock() {} - - fun trackRate() {} - - fun trackReportBug() {} - - fun trackSendFeedback() {} - - fun trackViewWebsite() {} - - fun trackViewSource() {} - - fun trackViewAttributions() {} - - // MARK: App Rate events - - fun trackViewAppRateDialog() {} - - fun trackAppRateDialogRate() {} - - fun trackAppRateDialogIgnore() {} - - fun trackAppRateDialogDisable() {} - - // MARK: Sharing - - fun trackViewOverview() {} - - fun trackShareImage(numberOfGames: Int) {} - - fun trackSaveImage(numberOfGames: Int) {} - - fun trackSaveImageFailed(numberOfGames: Int) {} - - // MARK: Other - - fun flush() {} - } - - private var initialized: Boolean = false - - private var disableTracking: Boolean = false -} diff --git a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/utils/AppRater.kt b/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/utils/AppRater.kt deleted file mode 100644 index 007924837..000000000 --- a/legacy/app/src/main/java/ca/josephroque/bowlingcompanion/utils/AppRater.kt +++ /dev/null @@ -1,125 +0,0 @@ -package ca.josephroque.bowlingcompanion.utils - -import android.content.Context -import android.support.v7.preference.PreferenceManager -import android.content.Intent -import android.net.Uri -import android.support.v7.app.AlertDialog -import android.view.View -import android.widget.Button -import ca.josephroque.bowlingcompanion.BuildConfig -import ca.josephroque.bowlingcompanion.R - -/** - * Copyright (C) 2018 Joseph Roque - * - * Prompt the user to rate the app in the app store. - */ -object AppRater { - - private const val MINIMUM_DAYS_UNTIL_PROMPT = 14 - private const val MINIMUM_LAUNCHES_UNTIL_PROMPT = 3 - private const val DAYS_BETWEEN_PROMPTS = 14 - - private const val MINIMUM_MILLISECONDS_UNTIL_PROMPT = MINIMUM_DAYS_UNTIL_PROMPT * 24 * 60 * 60 * 1000 - private const val MILLISECONDS_BETWEEN_PROMPTS = DAYS_BETWEEN_PROMPTS * 24 * 60 * 60 * 1000 - - private const val LAUNCH_COUNT = "launch_count" - private const val LAST_PROMPT_TIME = "app_rater_last_prompt_time" - private const val DISABLE_APP_RATER = "disable_app_rater" - private const val TIME_OF_FIRST_LAUNCH = "time_of_first_launch" - private const val CAN_SHOW_APP_RATER = "can_show_app_rater" - - // MARK: AppRater - - fun prepare(context: Context) { - val preferences = PreferenceManager.getDefaultSharedPreferences(context) - - if (preferences.getBoolean(DISABLE_APP_RATER, false)) { - return - } - - val preferencesEditor = preferences.edit() - - val launchCount = preferences.getLong(LAUNCH_COUNT, 0) + 1 - preferencesEditor.putLong(LAUNCH_COUNT, launchCount) - - val timeOfFirstLaunch = if (StartupManager.isFirstLaunch(context)) { - System.currentTimeMillis() - } else { - preferences.getLong(TIME_OF_FIRST_LAUNCH, System.currentTimeMillis()) - } - preferencesEditor.putLong(TIME_OF_FIRST_LAUNCH, System.currentTimeMillis()) - - var dateToWaitFor = timeOfFirstLaunch + MINIMUM_MILLISECONDS_UNTIL_PROMPT - - val lastPromptTime = preferences.getLong(LAST_PROMPT_TIME, 0) - if (lastPromptTime != 0L) { - dateToWaitFor = lastPromptTime + MILLISECONDS_BETWEEN_PROMPTS - } - - if (launchCount > MINIMUM_LAUNCHES_UNTIL_PROMPT && System.currentTimeMillis() > dateToWaitFor) { - preferencesEditor.putBoolean(CAN_SHOW_APP_RATER, true) - } else { - preferencesEditor.putBoolean(CAN_SHOW_APP_RATER, false) - } - - preferencesEditor.apply() - } - - fun show(context: Context) { - val preferences = PreferenceManager.getDefaultSharedPreferences(context) - if (!preferences.getBoolean(CAN_SHOW_APP_RATER, false)) { - return - } - - preferences.edit().putLong(LAST_PROMPT_TIME, System.currentTimeMillis()).apply() - - val dialog = AlertDialog.Builder(context) - val rootView = View.inflate(context, R.layout.dialog_app_rater, null) - dialog.setView(rootView) - val alertDialog = dialog.create() - - val listener = View.OnClickListener { - when (it.id) { - R.id.btn_rate_remind_me -> { - Analytics.trackAppRateDialogIgnore() - } - R.id.btn_rate -> { - Analytics.trackAppRateDialogRate() - try { - context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=${BuildConfig.APPLICATION_ID}"))) - } catch (ex: android.content.ActivityNotFoundException) { - context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("http://play.google.com/store/apps/details?id=${BuildConfig.APPLICATION_ID}"))) - } - - disableAppRater(context) - } - R.id.btn_rate_no -> { - Analytics.trackAppRateDialogDisable() - disableAppRater(context) - } - } - - alertDialog.dismiss() - } - - rootView.findViewById