diff --git a/app/src/main/java/net/pokeranalytics/android/ui/activity/NewDataMenuActivity.kt b/app/src/main/java/net/pokeranalytics/android/ui/activity/NewDataMenuActivity.kt index 3daab915..be5e8cc8 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/activity/NewDataMenuActivity.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/activity/NewDataMenuActivity.kt @@ -79,6 +79,10 @@ class NewDataMenuActivity : BaseActivity() { finishWithResult(2) } + newHandHistory.setOnClickListener { + finishWithResult(3) + } + container.setOnClickListener { hideMenu() } diff --git a/app/src/main/java/net/pokeranalytics/android/ui/extensions/UIExtensions.kt b/app/src/main/java/net/pokeranalytics/android/ui/extensions/UIExtensions.kt index 897bfc97..70445b27 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/extensions/UIExtensions.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/extensions/UIExtensions.kt @@ -11,6 +11,7 @@ import android.graphics.Color import android.net.Uri import android.util.TypedValue import android.view.View +import android.widget.ImageView import android.widget.LinearLayout import android.widget.Toast import androidx.appcompat.app.AlertDialog @@ -19,6 +20,7 @@ import androidx.appcompat.widget.SearchView import androidx.browser.customtabs.CustomTabsIntent import androidx.core.content.ContextCompat import androidx.core.content.FileProvider +import androidx.core.graphics.drawable.toBitmap import androidx.core.view.isVisible import androidx.fragment.app.Fragment import net.pokeranalytics.android.BuildConfig @@ -178,20 +180,28 @@ fun SearchView.removeMargins() { } fun View.toByteArray() : ByteArray { - - val bitmap = this.convertToBitmap() - val stream = ByteArrayOutputStream() - bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream) - return stream.toByteArray() + return this.convertToBitmap().toByteArray() } fun View.convertToBitmap(): Bitmap { - val measureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) - measure(measureSpec, measureSpec) - layout(0, 0, measuredWidth, measuredHeight) - val r = Bitmap.createBitmap(measuredWidth, measuredHeight, Bitmap.Config.ARGB_8888) - r.eraseColor(Color.TRANSPARENT) - val canvas = Canvas(r) - draw(canvas) - return r -} \ No newline at end of file + val b = Bitmap.createBitmap(this.width, this.height, Bitmap.Config.ARGB_8888) + val canvas = Canvas(b) + val background = this.background + this.background?.let { + background.draw(canvas) + } ?: run { + canvas.drawColor(Color.WHITE) + } + this.draw(canvas) + return b +} + +fun ImageView.toByteArray() : ByteArray { + return this.drawable.toBitmap().toByteArray() +} + +private fun Bitmap.toByteArray() : ByteArray { + val baos = ByteArrayOutputStream() + this.compress(Bitmap.CompressFormat.PNG, 100, baos) + return baos.toByteArray() +} diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/FeedFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/FeedFragment.kt index 31ec10c2..a725a701 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/FeedFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/FeedFragment.kt @@ -26,14 +26,18 @@ import net.pokeranalytics.android.ui.activity.components.RequestCode import net.pokeranalytics.android.ui.adapter.FeedSessionRowRepresentableAdapter import net.pokeranalytics.android.ui.adapter.FeedTransactionRowRepresentableAdapter import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate +import net.pokeranalytics.android.ui.extensions.toByteArray import net.pokeranalytics.android.ui.fragment.components.FilterableFragment import net.pokeranalytics.android.ui.interfaces.FilterActivityRequestCode import net.pokeranalytics.android.ui.interfaces.FilterableType import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.SmoothScrollLinearLayoutManager +import net.pokeranalytics.android.ui.view.handhistory.VideoView import net.pokeranalytics.android.util.Preferences import net.pokeranalytics.android.util.billing.AppGuard import net.pokeranalytics.android.util.extensions.count +import net.pokeranalytics.android.util.video.MMediaMuxer +import timber.log.Timber import java.text.SimpleDateFormat import java.util.* @@ -70,9 +74,13 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { private var selectedTransaction: Transaction? = null private var selectedTransactionPosition: Int = -1 - override val observedEntities: List> = listOf(Session::class.java, Transaction::class.java) + override val observedEntities: List> = + listOf(Session::class.java, Transaction::class.java) - override fun entitiesChanged(clazz: Class, results: RealmResults) { + override fun entitiesChanged( + clazz: Class, + results: RealmResults + ) { super.entitiesChanged(clazz, results) when (clazz.kotlin) { @@ -88,12 +96,20 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { super.onCreateView(inflater, container, savedInstanceState) return inflater.inflate(R.layout.fragment_feed, container, false) } - override fun onCreateContextMenu(menu: ContextMenu?, v: View?, menuInfo: ContextMenu.ContextMenuInfo?) { + override fun onCreateContextMenu( + menu: ContextMenu?, + v: View?, + menuInfo: ContextMenu.ContextMenuInfo? + ) { super.onCreateContextMenu(menu, v, menuInfo) if (v?.id == R.id.menuRecyclerView) { @@ -168,6 +184,7 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { 0 -> createNewSession(false) 1 -> createNewSession(true) 2 -> createNewTransaction() + 3 -> createNewHandHistory() } } else if (requestCode == RequestCode.FEED_TRANSACTION_DETAILS.value && resultCode == RESULT_OK && data != null) { if (data.getStringExtra(DataListActivity.IntentKey.ITEM_DELETED.keyName) != null) { @@ -175,7 +192,10 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { } } else if (requestCode == FilterActivityRequestCode.CREATE_FILTER.ordinal && resultCode == RESULT_OK) { data?.let { - this.saveFilter(this.requireContext(), it.getStringExtra(FiltersActivity.IntentKey.FILTER_ID.keyName)) + this.saveFilter( + this.requireContext(), + it.getStringExtra(FiltersActivity.IntentKey.FILTER_ID.keyName) + ) } } else if (requestCode == RequestCode.NEW_TRANSACTION.value && resultCode == RESULT_OK) { this.selectTab(Tab.TRANSACTIONS) @@ -192,7 +212,10 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) { when (row) { - is Session -> SessionActivity.newInstance(requireContext(), sessionId = (row as Editable).id) + is Session -> SessionActivity.newInstance( + requireContext(), + sessionId = (row as Editable).id + ) is Transaction -> { selectedTransaction = row selectedTransactionPosition = position @@ -215,7 +238,8 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { registerForContextMenu(this.menuRecyclerView) - val messageToShow: Preferences.FeedMessage? = Preferences.feedMessageToShow(requireContext()) + val messageToShow: Preferences.FeedMessage? = + Preferences.feedMessageToShow(requireContext()) if (messageToShow != null) { messageBox.isVisible = true @@ -322,7 +346,8 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { } // Transactions - this.realmTransactions = transactionFilter?.results() ?: run { getRealm().where().findAll() } + this.realmTransactions = + transactionFilter?.results() ?: run { getRealm().where().findAll() } this.realmTransactions = this.realmTransactions.sort("date", Sort.DESCENDING) var distinctDateTransactions = transactionFilter?.results("year", "month") ?: run { @@ -330,14 +355,22 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { } distinctDateTransactions = distinctDateTransactions.sort("date", Sort.DESCENDING) this.feedTransactionAdapter = - FeedTransactionRowRepresentableAdapter(this, realmTransactions, distinctDateTransactions) + FeedTransactionRowRepresentableAdapter( + this, + realmTransactions, + distinctDateTransactions + ) } /** * Create a new cash game */ - private fun createNewSession(isTournament: Boolean, sessionId: String? = null, duplicate: Boolean = false) { + private fun createNewSession( + isTournament: Boolean, + sessionId: String? = null, + duplicate: Boolean = false + ) { val sessionCount = getRealm().count(Session::class.java) if (!AppGuard.isProUser && sessionCount >= AppGuard.MAX_SESSIONS_BEFORE_REQUESTING_SUBSCRIPTION) { // && !BuildConfig.DEBUG @@ -374,7 +407,56 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { } } - EditableDataActivity.newInstanceForResult(this, LiveData.TRANSACTION, null, RequestCode.NEW_TRANSACTION.value) + EditableDataActivity.newInstanceForResult( + this, + LiveData.TRANSACTION, + null, + RequestCode.NEW_TRANSACTION.value + ) + } + + /*** + * Create a new hand history + */ + private fun createNewHandHistory() { + + Timber.d("**** Start video test") + + + val testView = View(requireContext()) + testView.setBackgroundColor(requireContext().getColor(R.color.blue)) + testView.layoutParams = ViewGroup.LayoutParams(480, 480) + + val videoView = VideoView(requireContext()) + + videoView.let { + + val muxer = MMediaMuxer() + + Timber.d("width = ${it.width}, height = ${it.height}") + + val width = (it.width / 2) * 2 + val height= (it.height / 2) * 2 + + muxer.Init(requireActivity(), width, height, "hhVideo", "YES!") + + Timber.d("**** Adds frames") + for (i in 0..50) { + + try { + val byteArray = it.toByteArray() + muxer.AddFrame(byteArray) + } catch (e: Exception) { + Timber.e("error = ${e.message}") + } + } + Timber.d("**** Create video") + muxer.CreateVideo() + + val path = muxer.GetPath() + Timber.d("**** Video path = $path") + } + } /** @@ -399,7 +481,7 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { ).show() } - // Filter Handler +// Filter Handler override fun applyFilter() { super.applyFilter() diff --git a/app/src/main/java/net/pokeranalytics/android/ui/view/handhistory/Circle.kt b/app/src/main/java/net/pokeranalytics/android/ui/view/handhistory/Circle.kt new file mode 100644 index 00000000..1853296f --- /dev/null +++ b/app/src/main/java/net/pokeranalytics/android/ui/view/handhistory/Circle.kt @@ -0,0 +1,35 @@ +package net.pokeranalytics.android.ui.view.handhistory + +import android.content.Context +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.RectF +import android.util.AttributeSet +import android.view.View +import net.pokeranalytics.android.R + +class Circle(context: Context, attrs: AttributeSet) : View(context, attrs) { + + companion object { + private val START_ANGLE_POINT = 270 + private val STROKE_WIDTH = 40 + } + + private val paint: Paint = Paint() + private lateinit var rect: RectF + + var angle: Float = 180.0f + + init { + paint.isAntiAlias = true + paint.style = Paint.Style.STROKE + paint.strokeWidth = STROKE_WIDTH.toFloat() + paint.color = getContext().getColor(R.color.chart_selected_bar) + } + + override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + canvas.drawArc(rect, START_ANGLE_POINT.toFloat(), angle, false, paint) + } + +} \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/ui/view/handhistory/VideoView.kt b/app/src/main/java/net/pokeranalytics/android/ui/view/handhistory/VideoView.kt new file mode 100644 index 00000000..8d48d9eb --- /dev/null +++ b/app/src/main/java/net/pokeranalytics/android/ui/view/handhistory/VideoView.kt @@ -0,0 +1,53 @@ +package net.pokeranalytics.android.ui.view.handhistory + +import android.content.Context +import android.util.AttributeSet +import android.view.LayoutInflater +import android.widget.FrameLayout +import net.pokeranalytics.android.R +import timber.log.Timber + +class VideoView : FrameLayout { + + /** + * Constructors + */ + constructor(context: Context) : super(context) { + init() + } + + constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) { + init() + } + + constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { + init() + } + + open fun getResourceLayout(): Int { + return R.layout.view_video + } + + private lateinit var frameLayout: FrameLayout + + /** + * Init + */ + private fun init() { + + this.setBackgroundColor(context.getColor(R.color.blue)) + + val layoutInflater = LayoutInflater.from(context) + frameLayout = layoutInflater.inflate(getResourceLayout(), this, false) as FrameLayout +// val layoutParams = LayoutParams(480, 480) + + val width = frameLayout.layoutParams.width + val height = frameLayout.layoutParams.height + Timber.d("W = $width, H = $height") + + this.layoutParams = frameLayout.layoutParams + addView(frameLayout, frameLayout.layoutParams) + + } + +} \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/util/video/MMediaMuxer.kt b/app/src/main/java/net/pokeranalytics/android/util/video/MMediaMuxer.kt index d131a478..4c781376 100644 --- a/app/src/main/java/net/pokeranalytics/android/util/video/MMediaMuxer.kt +++ b/app/src/main/java/net/pokeranalytics/android/util/video/MMediaMuxer.kt @@ -9,6 +9,7 @@ import android.media.MediaCodecInfo.CodecCapabilities import android.os.Environment import android.os.Handler import android.util.Log +import timber.log.Timber import java.io.File import java.io.IOException import java.text.DateFormat @@ -203,7 +204,7 @@ class MMediaMuxer { DateFormat.getDateTimeInstance().format(Date()) outputPath = File( Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES), - "pixel$currentDateTimeString.mp4" + "pixel.mp4" ).toString() mediaMuxer = MediaMuxer(outputPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4) } catch (ioe: IOException) { @@ -278,7 +279,7 @@ class MMediaMuxer { mediaMuxer!!.stop() mediaMuxer!!.release() mediaMuxer = null - Logd("RELEASE MUXER") + Logd("RELEASE MUXER, path = ${outputPath}") } } @@ -310,21 +311,22 @@ class MMediaMuxer { var index = 0 for (j in 0 until height) { for (i in 0 until width) { + a = argb[index] and -0x1000000 shr 24 // a is not used obviously R = argb[index] and 0xff0000 shr 16 G = argb[index] and 0xff00 shr 8 B = argb[index] and 0xff shr 0 + Y = (66 * R + 129 * G + 25 * B + 128 shr 8) + 16 U = (-38 * R - 74 * G + 112 * B + 128 shr 8) + 128 V = (112 * R - 94 * G - 18 * B + 128 shr 8) + 128 - yuv420sp[yIndex++] = - (if (Y < 0) 0 else if (Y > 255) 255 else Y).toByte() + + yuv420sp[yIndex++] = (if (Y < 0) 0 else if (Y > 255) 255 else Y).toByte() if (j % 2 == 0 && index % 2 == 0) { - yuv420sp[uvIndex++] = - (if (U < 0) 0 else if (U > 255) 255 else U).toByte() - yuv420sp[uvIndex++] = - (if (V < 0) 0 else if (V > 255) 255 else V).toByte() + yuv420sp[uvIndex++] = (if (U < 0) 0 else if (U > 255) 255 else U).toByte() + yuv420sp[uvIndex++] = (if (V < 0) 0 else if (V > 255) 255 else V).toByte() } + index++ } } @@ -411,13 +413,15 @@ class MMediaMuxer { } private fun Logd(Mess: String) { - if (DEBUG) { - Log.d(TAG, Mess) - } + Timber.d("$Mess") +// if (DEBUG) { +// Log.d(TAG, Mess) +// } } private fun Loge(Mess: String?) { - Log.e(TAG, Mess) + Timber.e("$Mess") +// Log.e(TAG, Mess) } } } \ No newline at end of file diff --git a/app/src/main/res/layout/activity_new_data.xml b/app/src/main/res/layout/activity_new_data.xml index e1cdc09b..db71888c 100644 --- a/app/src/main/res/layout/activity_new_data.xml +++ b/app/src/main/res/layout/activity_new_data.xml @@ -104,7 +104,6 @@ android:orientation="horizontal" android:paddingStart="16dp" android:paddingEnd="16dp" - app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/newTournament"> @@ -127,6 +126,39 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/view_video.xml b/app/src/main/res/layout/view_video.xml new file mode 100644 index 00000000..f2fae02c --- /dev/null +++ b/app/src/main/res/layout/view_video.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file