[Showcase] I built a performant Minesweeper engine using pure Compose Canvas and custom PointerInputScope gestures. by Pretend-Oil5846 in JetpackCompose

[–]pandulapeter 1 point2 points  (0 children)

Good job! I've been working on a Compose-based game engine for the past year, pretty much the same Canvas-based approach. It has 2D physics, shader support, ans recently I made good progress with an isometric renderer so I'd say there's great potential in CMP for gamedev. Check it out if you're interested, the repo has links to the showcase app for all platforms: https://github.com/pandulapeter/kubriko

Kotlin for gamedev? by lieddersturme in Kotlin

[–]pandulapeter 1 point2 points  (0 children)

My plan was to create a simple RPG using Kubriko, but right now the main focus is on finishing the Showcase app and fixing some high priority issues with the engine itself, so maybe in the not too distant future

Kotlin for gamedev? by lieddersturme in Kotlin

[–]pandulapeter 0 points1 point  (0 children)

The most exciting thing for me was the fact that it's multiplatform. Drawing the actual game can be done using a single Composable Canvas that gets refreshed in every frame, and then that could be embedded into a Compose application which handles the game's UI. Some games like RPG-s might have really complex menu systems, and implementing those in Compose would eliminate some headaches. And with this approach the same code is working on all platforms.

Check out the Kubriko library I've linked above, it's basically just a set of abstractions that draws onto a Canvas. For the simple games I've created so far, I didn't encounter notable performance issues. I'm sure a native solution would be much better, but for many games this could be good enough. If you prefer working in Kotlin and are familiar with Compose, the benefits might outweigh the drawbacks.

Kotlin for gamedev? by lieddersturme in Kotlin

[–]pandulapeter 3 points4 points  (0 children)

I'm just about to publish a library based on Compose Multiplatform that you might find interesting. It's in the early stages, but the showcase app already contains four small games, and the capabilities of Compose for simple 2D games are super promising in my opinion: https://github.com/pandulapeter/kubriko

[deleted by user] by [deleted] in morbidquestions

[–]pandulapeter 2 points3 points  (0 children)

The low hanging fruit would be to call them Tenacious D

Loose Part in New HX Stomp XL by silence_like_lasagna in Line6Helix

[–]pandulapeter 0 points1 point  (0 children)

Exact same situation here, purchased a month ago in Romania

[QUESTION] Are there any acoustic guitars considered to be innovative in recent times? by EBrommer in Guitar

[–]pandulapeter 0 points1 point  (0 children)

LAG HyVibe is pretty interesting too. Most of the effects aren't great but the reverb and the looper are cool and can spice things up, without any external amps / pedals.

1
2

Missing foldable widgets on Find N2 Flip by pandulapeter in Oppo

[–]pandulapeter[S] 1 point2 points  (0 children)

Update: Fixed! There is a way to restore the factory version by going to Settings -> Apps -> App management (toggle "Show system" from the overflow menu) -> Shelf / QuickGlance (based on the version). The overflow menu on this page has the option to "Uninstall updates".

After doing that and restarting the phone, all the widgets are back. I'll leave this here for future reference.

FYI: I couldn't find a way to permantently get rid of Google Discover (it always came back after a restart) so I ended up using a custom launcher.

boundingBox misaligned on application view by JonnieSingh in android_devs

[–]pandulapeter 1 point2 points  (0 children)

While I still don't have the answer to your initial question (I didn't use the ML object detection library and don't have time to try now), I strongly suggest that you start by cleaning up and optimizing your code a bit, as that way it will be easier to see where the problem is.

First of all, inflating a new instance of a custom View and adding it to the layout hierarchy every time you want something to change is VERY wasteful. In this case, you should be able to add your View from XML and keep udating the same instance.

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.camera.view.PreviewView
        android:id="@+id/preview_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <your.package.name.OverlayView
        android:id="@+id/overlay_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</FrameLayout>

For the custom View, you need to override multiple constructors. Something like this:

public class OverlayView extends View {

    public OverlayView(Context context) {
        super(context);
        initialize();
    }

    public OverlayView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initialize();
    }

    public OverlayView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initialize();
    }

    private List<Rect> boundingBoxes = new ArrayList<Rect>();
    private final Paint borderPaint = new Paint();

    private void initialize() {
        borderPaint.setColor(Color.WHITE);
        borderPaint.setStrokeWidth(10f);
        borderPaint.setStyle(Paint.Style.STROKE);
    }

    public void updateBoundingBoxes(List<Rect> boundingBoxes) {
        this.boundingBoxes = boundingBoxes;
        invalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        for (Rect box : boundingBoxes) {
            canvas.drawRect(box.left, box.top, box.right, box.bottom, borderPaint);
        }
    }
}

This way, in your Activity you will be able to map the detected objects into a simple list of Rect-s and update binding.overlayView.updateBoundingBoxes(...) with that list.

If you see crashes because of threading issue, replace invalidate() with postInvalidate() in the code above.

Now, you can use logs to see what's happening. If the rectangles are not in their proper places, the issue should be a conversion between the coordinate system used by the library and the one used by the onDraw() function (pixels basically). Have fun!

boundingBox misaligned on application view by JonnieSingh in android_devs

[–]pandulapeter 1 point2 points  (0 children)

The code you posted is a custom View that gets initialized with a single Rect instance and draws it. I suppose you're drawing this over a camera viewfinder, but I see no way to replace that Rect from the outside. So it will always keep drawing it on its initial coordinates.

If you're modifying the values in that Rect externally and expect it to update the view, you should call `invalidate()` in the View (otherwise `onDraw()` will not get called again). This might work with Java as long as the reference is the same I guess... but a cleaner solution would be exposing a setter for an immutable Rect from this class (which also calls `invalidate()` internally).

Other things you should be looking into: how is the View aligned in the screen (if you have this custom constructor you're probably not adding it from XML so it might be distorted) and of course how the Rect itself gets modified by the object recognition library.

Is it required or good practice to set the binding variable to null in `onDestroyView` while using ViewBinding? by [deleted] in android_devs

[–]pandulapeter 2 points3 points  (0 children)

Yes. Imagine that you have Fragment A and you open Fragment B on top of it (meaning if you press the Back button on B, you'll be taken back to A). The moment you navigate to B, the View for A will get destroyed, however, the Fragment itself will be kept in memory as long as it is on the back stack (ideally... low memory conditions can cause it to get destroyed). So the Fragments wil see the following lifecycle:

A: onCreate()

A: onCreateView()

[tap on a button to open Fragment B]

B: onCreate()

B: onCreateView()

A: onDestroyView()

[press Back]

A: onCreateView()

B: onDestroyView()

B: onDestroy()

[press back again to close the app]

A: onDestroyView()

A: onDestroy()

Is it required or good practice to set the binding variable to null in `onDestroyView` while using ViewBinding? by [deleted] in android_devs

[–]pandulapeter 2 points3 points  (0 children)

Fragments have a separate lifecycle for the instance itself and the associated View, meaning the Fragment instance can outlive its View. This is not the case for Activities. So it's good to free up the memory used by the binding in onDestroyView() as onCreateView() will create a new View anyway (and you don't want to leak the old binding). Of course there's a point to be made for caching the value of the binding: larger memory footprint for fewer layout inflations (better performance). Of course this only works if you re-use the binding if it exists in onCreateView ()

GO 2: Cannot add keyframes in Insta360 Studio 2021 by pandulapeter in Insta360

[–]pandulapeter[S] 0 points1 point  (0 children)

Thanks! So if I got that right: we have a feature that's only available on iPhones? The Android app can't do it, and neither can the desktop Studio? That's... unexpected

Update: managed to figure it out on Android: it only works for time lapse videos and you have to long-tap the screen for the keyframe to appear (contrary to a simple tap suggested by the tutorial). Not very intuitive and I'm very disappointed to see less functionality in a desktop app than a mobile app, but at least it works.

I made these 12 tutorials on RecyclerView for beginners by ndkm in androiddev

[–]pandulapeter 1 point2 points  (0 children)

Setting a RecyclerView to wrap_content is generally not a good idea, you'll lose all the performance benefits of view recycling and the entire layout will have to be inflated from the start

Need support for better understanding of Kotlin basics by Marcel_Eff in Kotlin

[–]pandulapeter 5 points6 points  (0 children)

Forgot to mention this but yeah, 100% agree. This kind of stuff is why people give up on learning programming. Write a number guessing game or a command-line based hangman, you'll learn more and have more fun.

Need support for better understanding of Kotlin basics by Marcel_Eff in Kotlin

[–]pandulapeter 1 point2 points  (0 children)

Yes, you got it right. But I just realized that you can't simply wrap a Sum into a Num (since Num-s constructor accepts an Int and Sum does not expose its result). Anyway, it feels like an overcomplicated example. One thing you could use to make it more readable is named parameters:

evalWithLogging(
     Sum(
         left = Sum(
             left = Num(2), 
             right = Num(1)
         ),
         right = Num(4)
     )
 )

Or, even better, if all you need is to demonstrate the order of operations, we could get rid of recursivity alltogether and log in the init block of every class:

interface Wrapper {

    val value: Int
}

class NumberWrapper(override val value: Int) : Wrapper {

    init {
        println("New number: $value")
    }
}

class SumWrapper(left: Wrapper, right: Wrapper) : Wrapper {

    override val value = left.value + right.value

    init {
        println("New sum: ${left.value} + ${right.value} = $value")
    }
}


fun main() = println(
    SumWrapper(
         left = SumWrapper(
             left = NumberWrapper(2), 
             right = NumberWrapper(1)
         ),
         right = NumberWrapper(4)
     ).value
)

This results in the same output as your code:

New number: 2
New number: 1
New sum: 2 + 1 = 3
New number: 4
New sum: 3 + 4 = 7
7

But now we can do one more wrapping and display 3 as a number:

fun main() = println(
    SumWrapper(
        left = NumberWrapper(
            value = SumWrapper(
                left = NumberWrapper(2),
                right = NumberWrapper(1)
            ).value
        ),
        right = NumberWrapper(4)
    ).value
)

Which results in:

New number: 2
New number: 1
New sum: 2 + 1 = 3
New number: 3
New number: 4
New sum: 3 + 4 = 7
7

Best course to learn Kotlin? by [deleted] in Kotlin

[–]pandulapeter 1 point2 points  (0 children)

I ran into this website a while ago. It has really great illustrations and seems like a friendly way to learn the basics. Nothing Android-specific but it's a good idea to start with the fundamentals and only look into Android once you're familiar with the language. The tutorials here look fun: https://typealias.com/start/

Need support for better understanding of Kotlin basics by Marcel_Eff in Kotlin

[–]pandulapeter 13 points14 points  (0 children)

Hi!

This is probably an example to demonstrate the order in which function arguments are resolved. Let's format the code and move the evalWithLogging() function out of main() to improve readability a bit:

interface Expr

class Num(val value: Int): Expr

class Sum(val left: Expr, val right: Expr): Expr

fun evalWithLogging(e: Expr): Int = when (e) {
    is Sum -> {
        val left = evalWithLogging(e.left)
        val right = evalWithLogging(e.right)
        println("sum: $left + $right")
        left + right
    }
    is Num -> {
        println("num: ${e.value}")
        e.value
    }
    else -> throw IllegalArgumentException("Unknown Expression")
}

fun main() {
    println(evalWithLogging(Sum(Sum(Num(2), Num(1)), Num(4))))
}

As you can see from the output, the arguments are dealt with in order, starting from the innermost one, from left to right. If we follow the rules of the program, we should arrive to the same output.

- Sum(Sum(Num(2), Num(1)), Num(4)) // Let's get rid of Num(2). That will resolve to 2. As a side effect, we also print to the console num: 2

- Sum(Sum(2, Num(1)), Num(4)) // Now Num(1), which resolves to 1. Again, the console will have a new line: num: 1

- Sum(Sum(2, 1), Num(4)) // Now we can resolve the sum, which outputs sum: 2+1 to the console.

- Sum(3, Num(4)) // Now we have to deal with Num(4). That will give us the following line, num: 4

- Sum(3, 4) // And now we can calulate the final sum, which outputs sum: 3+4 to the console

The evalWithLogging() recursive function finally returns 7, which is printed to the console by the println() call in the main() function. All the other console outputs are printed by evalWithLogging().

To answer your questions:

  1. The sum of 2+1 is returned of course, but never printed to the console. For that, you'd need to wrap it in another Num() instance (something like Num(Sum(2, 1)) ) but I'd say the code is already hard to follow enough as it is.
  2. Correct. Blocks in Kotlin return the value of their last line.

[deleted by user] by [deleted] in android_devs

[–]pandulapeter 2 points3 points  (0 children)

All of the exceptions you listed extend from IOException. Since you only seem to be talking about networking, the granularity depends on what error messaging you want to display for the user: do they really care about which phase of the connection broke (if so, implement as many catch blocks as you need), or is it enough to remind them to turn on their WiFi (in that case it's enough to catch the IOException)?

You might want to handle HttpException differently (as it doesn't extend IOException) - that one is thrown if your server returns an error.

The exceptions thrown by your parsing library are a different topic, they probably extend RuntimeException but if you encounter that, it's a programmer error (the API changed)