diff --git a/app/build.gradle b/app/build.gradle index e633f19c..ab1a65b2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -72,7 +72,7 @@ dependencies { // Android implementation 'androidx.appcompat:appcompat:1.0.2' - implementation 'androidx.core:core-ktx:1.1.0-alpha05' + implementation 'androidx.core:core-ktx:1.2.0-alpha01' implementation 'com.google.android.material:material:1.0.0' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0' @@ -88,13 +88,15 @@ dependencies { implementation 'com.google.android.libraries.places:places:1.1.0' // Billing / Subscriptions + // WARNING FOR 2.0: https://developer.android.com/google/play/billing/billing_library_releases_notes + // Purchases MUST BE ACKNOWLEDGED implementation 'com.android.billingclient:billing:1.2.2' // Firebase - implementation 'com.google.firebase:firebase-core:16.0.8' + implementation 'com.google.firebase:firebase-core:16.0.9' // Crashlytics - implementation 'com.crashlytics.sdk.android:crashlytics:2.9.9' + implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1' // Logs implementation 'com.jakewharton.timber:timber:4.7.1' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index b068e33e..de2ea637 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -22,6 +22,7 @@ @@ -30,11 +31,6 @@ - - - @@ -49,6 +45,13 @@ + + + + ().equalTo("name", name).findFirst() - return if (bankroll != null) { - bankroll + val br = realm.where().equalTo("name", name).findFirst() + return if (br != null) { + br } else { val bankroll = Bankroll() bankroll.name = name + bankroll.live = live val currency = Currency() currency.code = currencyCode currency.rate = currencyRate diff --git a/app/src/main/java/net/pokeranalytics/android/model/utils/SessionSetManager.kt b/app/src/main/java/net/pokeranalytics/android/model/utils/SessionSetManager.kt index 95fea7f9..76cce72c 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/utils/SessionSetManager.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/utils/SessionSetManager.kt @@ -7,9 +7,7 @@ import net.pokeranalytics.android.model.realm.Session import net.pokeranalytics.android.model.realm.SessionSet import kotlin.math.max -class CorruptSessionSetException(message: String) : Exception(message) { - -} +class CorruptSessionSetException(message: String) : Exception(message) /** * The manager is in charge of updating the abstract concept of timeline, diff --git a/app/src/main/java/net/pokeranalytics/android/ui/activity/HomeActivity.kt b/app/src/main/java/net/pokeranalytics/android/ui/activity/HomeActivity.kt index 6bfbf3a5..d58ad551 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/activity/HomeActivity.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/activity/HomeActivity.kt @@ -3,6 +3,7 @@ package net.pokeranalytics.android.ui.activity import android.app.KeyguardManager import android.content.Context import android.content.Intent +import android.net.Uri import android.os.Build import android.os.Bundle import com.google.android.material.bottomnavigation.BottomNavigationView @@ -13,7 +14,9 @@ import net.pokeranalytics.android.R import net.pokeranalytics.android.model.realm.Currency import net.pokeranalytics.android.ui.activity.components.PokerAnalyticsActivity import net.pokeranalytics.android.ui.adapter.HomePagerAdapter +import net.pokeranalytics.android.ui.extensions.showAlertDialog import net.pokeranalytics.android.util.billing.AppGuard +import timber.log.Timber class HomeActivity : PokerAnalyticsActivity() { @@ -75,6 +78,30 @@ class HomeActivity : PokerAnalyticsActivity() { } + override fun onNewIntent(intent: Intent?) { + super.onNewIntent(intent) + + intent?.let { + + when (intent.action) { + "android.intent.action.VIEW" -> { // import + val data = it.data + if (data != null) { + this.requestImportConfirmation(data) + } else { + throw IllegalStateException("URI null on import") + } + } + else -> { + Timber.d("Intent ${intent.action} unmanaged") + } + } + + } + + + } + private fun observeRealmObjects() { val realm = getRealm() @@ -125,4 +152,14 @@ class HomeActivity : PokerAnalyticsActivity() { viewPager.setCurrentItem(index, false) } + // Import + + fun requestImportConfirmation(uri: Uri) { + + showAlertDialog(this, R.string.import_confirmation, showCancelButton = true, positiveAction = { + ImportActivity.newInstance(this, uri) + }) + + } + } diff --git a/app/src/main/java/net/pokeranalytics/android/ui/activity/ImportActivity.kt b/app/src/main/java/net/pokeranalytics/android/ui/activity/ImportActivity.kt index fc86d727..55b8f7f9 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/activity/ImportActivity.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/activity/ImportActivity.kt @@ -1,12 +1,9 @@ package net.pokeranalytics.android.ui.activity -import android.Manifest import android.content.Context import android.content.Intent -import android.content.pm.PackageManager +import android.net.Uri import android.os.Bundle -import android.os.PersistableBundle -import androidx.core.app.ActivityCompat import io.realm.Realm import net.pokeranalytics.android.R import net.pokeranalytics.android.ui.activity.components.PokerAnalyticsActivity @@ -15,8 +12,10 @@ import timber.log.Timber class ImportActivity : PokerAnalyticsActivity() { + private lateinit var fileURI: Uri + enum class IntentKey(val keyName: String) { - FILE_PATH("filepath") + URI("uri") } companion object { @@ -24,25 +23,24 @@ class ImportActivity : PokerAnalyticsActivity() { /** * Create a new instance for result */ - fun newInstance(context: Context, filePath: String) { - context.startActivity(getIntent(context, filePath)) + fun newInstance(context: Context, uri: Uri) { + context.startActivity(getIntent(context, uri)) } - private fun getIntent(context: Context, filePath: String) : Intent { + private fun getIntent(context: Context, uri: Uri) : Intent { val intent = Intent(context, ImportActivity::class.java) - intent.putExtra(ImportActivity.IntentKey.FILE_PATH.keyName, filePath) + intent.putExtra(ImportActivity.IntentKey.URI.keyName, uri) return intent } } + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) - override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) { - super.onCreate(savedInstanceState, persistentState) + this.fileURI = intent.getParcelableExtra(ImportActivity.IntentKey.URI.keyName) setContentView(R.layout.activity_import) - initUI() - } override fun onStop() { @@ -59,32 +57,27 @@ class ImportActivity : PokerAnalyticsActivity() { val fragmentTransaction = supportFragmentManager.beginTransaction() val fragment = ImportFragment() - val filePath = intent.getStringExtra(ImportActivity.IntentKey.FILE_PATH.keyName) - fragment.setData(filePath) + val fis = contentResolver.openInputStream(fileURI) + Timber.d("Load fragment data with: $fis") + fis?.let { + fragment.setData(it) + } fragmentTransaction.add(R.id.container, fragment) fragmentTransaction.commit() - } - private fun requestPermission() { - if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) { - ActivityCompat.requestPermissions( - this, arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), PERMISSION_REQUEST_ACCESS_FINE_LOCATION - ) - } } - override fun onNewIntent(intent: Intent?) { - super.onNewIntent(intent) - - val f = intent?.data - val uri = f.toString() - Timber.d("!!!Intent!!! uri = $uri") - - } - - override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults) - } +// private fun requestPermission() { +// if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) { +// ActivityCompat.requestPermissions( +// this, arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), PERMISSION_REQUEST_ACCESS_FINE_LOCATION +// ) +// } +// } +// +// override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { +// super.onRequestPermissionsResult(requestCode, permissions, grantResults) +// } } \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/ImportFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/ImportFragment.kt index fc084b97..177cd9e9 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/ImportFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/ImportFragment.kt @@ -12,6 +12,7 @@ import net.pokeranalytics.android.R import net.pokeranalytics.android.ui.fragment.components.RealmFragment import net.pokeranalytics.android.util.csv.CSVImporter import timber.log.Timber +import java.io.InputStream import java.util.* import kotlin.coroutines.CoroutineContext @@ -21,11 +22,17 @@ class ImportFragment : RealmFragment() { get() = Dispatchers.Main private lateinit var filePath: String + private lateinit var inputStream: InputStream fun setData(path: String) { this.filePath = path } + fun setData(inputStream: InputStream) { + Timber.d("> setData with IS...") + this.inputStream = inputStream + } + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { super.onCreateView(inflater, container, savedInstanceState) return inflater.inflate(R.layout.fragment_import, container, false) @@ -33,6 +40,8 @@ class ImportFragment : RealmFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + + Timber.d("> onViewCreated...") this.startImport() } @@ -42,9 +51,9 @@ class ImportFragment : RealmFragment() { val test = GlobalScope.async { val s = Date() - Timber.d(">>> start...") + Timber.d(">>> Start Import...") - val csv = CSVImporter(filePath) + val csv = CSVImporter(inputStream) csv.start() val e = Date() diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/data/DataManagerFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/data/DataManagerFragment.kt index 686671d8..aeb41345 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/data/DataManagerFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/data/DataManagerFragment.kt @@ -95,7 +95,7 @@ open class DataManagerFragment : RealmFragment() { this.getRealm().executeTransaction { val managedItem = it.copyToRealmOrUpdate(this.item) if (managedItem is Savable) { - val uniqueIdentifier = (managedItem as Savable).id + val uniqueIdentifier = managedItem.id finishActivityWithResult(uniqueIdentifier) } } diff --git a/app/src/main/java/net/pokeranalytics/android/util/csv/CSVImporter.kt b/app/src/main/java/net/pokeranalytics/android/util/csv/CSVImporter.kt index ef18a235..b988a824 100644 --- a/app/src/main/java/net/pokeranalytics/android/util/csv/CSVImporter.kt +++ b/app/src/main/java/net/pokeranalytics/android/util/csv/CSVImporter.kt @@ -5,8 +5,24 @@ import org.apache.commons.csv.CSVFormat import org.apache.commons.csv.CSVRecord import timber.log.Timber import java.io.FileReader +import java.io.InputStream +import java.io.InputStreamReader +import java.io.Reader -open class CSVImporter(var path: String) { +open class CSVImporter { + + private val COMMIT_FREQUENCY = 100 + + var path: String? = null + var inputStream: InputStream? = null + + constructor(istream: InputStream) { + inputStream = istream + } + + constructor(filePath: String) { + path = filePath + } private var usedDescriptors: MutableList = mutableListOf() private var currentDescriptor: CSVDescriptor? = null @@ -15,40 +31,52 @@ open class CSVImporter(var path: String) { val realm = Realm.getDefaultInstance() - val reader = FileReader(this.path) + var reader: Reader? = null + if (this.path != null) { + reader = FileReader(this.path) + } + if (this.inputStream != null) { + reader = InputStreamReader(this.inputStream) + } val parser = CSVFormat.DEFAULT.withAllowMissingColumnNames().parse(reader) Timber.d("Starting import...") - realm.executeTransaction { + realm.beginTransaction() - parser.forEachIndexed { index, record -> + parser.forEachIndexed { index, record -> - Timber.d("line $index") + Timber.d("line $index") - if (currentDescriptor == null) { // find descriptor - this.findDescriptor(record) - } else { + if (currentDescriptor == null) { // find descriptor + this.findDescriptor(record) + } else { - currentDescriptor?.let { - if (record.size() == 0) { - this.usedDescriptors.add(it) - this.currentDescriptor = null // reset descriptor when encountering an empty line (multiple descriptors can be found in a single file) - } else { - it.parse(realm, record) - } - } ?: run { - throw IllegalStateException("CSVDescriptor should never be null here") - } + if ((index + 1) % COMMIT_FREQUENCY == 0) { + Timber.d("****** committing at ${index} sessions...") + realm.commitTransaction() + realm.beginTransaction() + } + currentDescriptor?.let { + if (record.size() == 0) { + this.usedDescriptors.add(it) + this.currentDescriptor = + null // reset descriptor when encountering an empty line (multiple descriptors can be found in a single file) + } else { + it.parse(realm, record) + } + } ?: run { + throw IllegalStateException("CSVDescriptor should never be null here") } + } } - Timber.d("Ending import...") + realm.commitTransaction() -// this.save(realm) + Timber.d("Ending import...") realm.close() } diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 372267b6..e6436138 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -379,7 +379,7 @@ Vérifiez que la transaction possède un type et un montant Il faut choisir un nom! Types de transactions - Transaction + Transactions Autres Choisissez un code PIN pour\n protéger l\'accès à l\'application.\nUn code PIN ne peut pas être retrouvé.\nSoyez attentif lors de l\'activation\n de cette fonction. Code PIN diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2daa133f..8e860c65 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -31,6 +31,7 @@ Launch Report Progress Save Report + Do you want to proceed with the file import? Update %s Comparison chart The filter cannot be deleted because it is currently selected.