Tuesday, July 8, 2014

Taco trucks

About a month ago I was browsing Reddit's SF Bay Area sub, and noticed a link to maps of taco trucks in the East Bay. Taco trucks take me back to my early days in Silly and my first job (in Mountain View), and a cow-orker who introduced me to the joys of a lengua taco dripping onto a paper plate. The redditor who made the map seemed interested in a web version, so I built TacoTacoTruck. However, he has yet to follow up on whether he liked it, hated it, or just didn't have time to deal. So although the map itself is not my own work, the web version is mine and could be fed any user-made Google map.

Thursday, July 3, 2014

Finally published Napkin Ideas on Play.

Well, finally.

This was actually done months ago, but I thought it would be really nice to have an ability to edit the background. So I started creating a wizard to do that, and for whatever reason, I just could not get it working the way I wanted, and got kind of depressed about that. So I dropped that bit out of the code and decided to publish as it was before.

Well, almost as it was before, with one notable exception: I took out the advertising. It's annoying to look at, and probably not going to generate any significant amount of revenue. In its place, I have put in what I'm calling a beg bar: give me a donation, the little yellow bar goes away.

Monday, April 14, 2014

Vertigo: so that's what it's like

My mom has gotten vertigo spells for the past 20 years or more. As she is incredibly detailed when it comes to her personal health, she has described to me every single episode and what might have caused it to happen. Quite often it's a sudden head turn, one of those unconscious moves you make when driving; sometimes it's having your head at an odd angle for an extended period of time, which happens when she gets her hair washed in the salon. Whenever it happens she usually goes to the hospital because she's old and falling has serious consequences, plus she is often unsure if she's having a heart attack. (To which I often say, Mom, if you had enough time to contemplate if you're having a heart attack, you probably aren't.)

So this morning I got to experience a bit of the joy of vertigo. And because I'm my mother's daughter, I'll describe it in loving detail.

Having been on the receiving end of these stories, I almost instantly understood what had happened. I was lying in bed, having just woken up, rolled over to kiss my sweetie, and suddenly felt a bit odd inside my right ear. Almost an itchy feeling, definitely deeper than the canal, and above it, and as I am now looking at pictures of the inner ear, in the region of the semicircular canal.

I opened my eyes and things were moving like an animated GIF loop, fortunately not too fast. I sat up and it didn't go away, and that's when I said "Oh crap, I'm having vertigo like my mom gets." I felt clammy and slightly nauseated. After a few minutes holding still (except for my mouth, which was cussing out my mother for passing this weakness on to me), the spin decreased to a twitch. My guts were wanting to get rid of anything offensive, and did, fortunately in the down direction.

Staring straight ahead was okay. Almost any other direction and the reaction was surprisingly immediate: clamminess and nausea. And it would go away almost as fast when I looked in a "safe" direction. But this was not a gut problem, it was neurological. Looking at the tablet seemed okay, but I couldn't use the computer -- a little too much head tilt there, I think.

I'd researched the Epley maneuver for mom in the past, but was feeling too trembly in the guts to want to try it. When you're feeling this way, the last thing you want to do is something with high potential to make a terrific mess to clean up.

Sitting up in bed seemed to be the best thing for staying un-dizzy and mitigating the chills. My mom had been advised to sit still and stare at a fixed point, so I focused on the opposite wall.  Slowly and gently I started tracking left and right, trying to increase the safe zone. It seemed to work. I pointed my gaze a foot or two higher on the wall and did the same. Repeat, raising a foot or two each time. My toes felt a little achy and I spent some time massaging them. I eventually graduated to turning my head, too. I don't know how long I spent doing this, but somewhere near the end of it, Best Boyfriend Ever came by to check on me, offered to take me to the hospital, and when I said I'd be okay, left for work. Out of everyone I've ever known intimately enough, BBE takes the second-longest time to get ready for work: two hours is average. (My mom is the first.)

As you might imagine from all this back and forth, I was also getting very sleepy.


I was pretty sure I was sleeping sitting up, in between tracking. We had had a normal amount of sleep but I was just wiped out. I carefully lay flat, not sure if this would be troublesome. It wasn't.

I sacked out for several hours. When I woke up things were 100% better. I'm taking it easy and slow this afternoon but I seem to be able to point in most any direction safely. I still itch a little bit in the ear, but I think the crystals are by and large back where they belong.

Tuesday, January 14, 2014

Done with the paint app

There was a trip to Germany for the boyfriend to attend a conference, and Christmas, and New Year's, so not a whole lot got done between the 15th and the 2nd. But since then it's been chin-rubbing and poking and growling, and finally the app was finished. I still need code review but right now it does just about everything I want it to.

The sticky bit was adding text. I'd originally set up the button to open an alert dialog where you could enter your text, and select a font size via a slider. That was visually jarring and functionally awkward because you couldn't really tell how large to make your text in relation to the drawing.  You would only know after placing it, so you'd have to undo and try again if you found it the wrong size.

So I found a bit of code that would include an actionable graphic (an image of an X to clear the text) in an EditText. From that starting point I made the EditText so it would be placed directly on the drawing surface, and it could be resized by dragging at the corner graphic. Clicking the checkmark graphic at the top cements the text in place. It's much smoother than having a dialog box.

After that, some code to save the drawing in JSON so it can be retrieved later.

For the next trick I want to put advertising in it. I'd like have a free-with-ads version, and a paid no-ads version. Don't hurt me, please. I'm not crazy about doing it either.

So to distract you from that last paragraph, here's the code for an edittext that can be moved around and the font resized by dragging.  Both the drawing and the EditText are children of a Viewgroup.

public class MovableEditText extends EditText {

    // 2 drawables: an OK and a resize handle
    private Drawable closeImg = getResources().getDrawable(R.drawable.text_ok);
    private Drawable resizeImg = getResources().getDrawable(R.drawable.text_resize);
    private long timeFingerDown;
    public InputMethodManager imm;
    private App app;
    private boolean resizing;
    private TextCompletedListener textListener;
    private boolean finished = false;
    private float startDeltaLeft;
    private float startDeltaTop;

    float leftMargin = 100;
    float topMargin = 100;

    public MovableEditText(Context context) {
        super(context);
        app = (App) context.getApplicationContext();
        imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
        init();
    }

    @Override
    public void scrollTo(int x, int y) {
        super.scrollTo(0, 0);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                timeFingerDown = event.getEventTime();
                // Remembers where you grabbed the text
                startDeltaLeft = event.getX();
                startDeltaTop = event.getY();
                setCursorVisible(true);
                return true;

            case MotionEvent.ACTION_UP:
                resizing = false;
                // clicking on the OK checkbox
                if (event.getY() < getPaddingTop() + closeImg.getIntrinsicHeight()) {
                    MovableEditText.this.okButtonClick();
                }
                // otherwise, act like a proper edittext
                if (event.getEventTime() - timeFingerDown < 200 && !finished) {
                    super.onTouchEvent(event);
                }
                return false;

            case MotionEvent.ACTION_MOVE:
                if (event.getX() > getWidth() - getPaddingRight() - resizeImg.getIntrinsicWidth()) {
                    resizeBox(event);
                    // we touched the arrow, so we are resizing the box (i.e. increasing font size)
                    resizing = true;
                    return true;
                }
                if (event.getEventTime() - timeFingerDown > 200 && !resizing) {
                    // we are moving the box; close the keyboard
                    imm.hideSoftInputFromWindow(getWindowToken(), 0);
                    moveBox(event);
                }
                return true;

            case MotionEvent.ACTION_CANCEL:
                resizing = false;
                return false;
        }
        return true;
    }

    public void setTextCompletedListener(TextCompletedListener l) {
        textListener = l;
    }

    private void init() {
        setMinimumHeight(closeImg.getIntrinsicHeight()*2);
        setMinimumWidth(closeImg.getIntrinsicWidth()+resizeImg.getIntrinsicWidth() *2);
        // Set bounds of the Clear button so it will look ok
        closeImg.setBounds(0, 0, closeImg.getIntrinsicWidth(), closeImg.getIntrinsicHeight());
        resizeImg.setBounds(0, 0, resizeImg.getIntrinsicWidth(), resizeImg.getIntrinsicHeight());
        // There may be initial text in the field, so we may need to display the  button
        showOkButton();
        //if text changes, take care of the button
        this.addTextChangedListener(new TextWatcher() {
            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                MovableEditText.this.showOkButton();
            }

            @Override
            public void afterTextChanged(Editable arg0) {
            }

            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            }
        });
    }

    private void moveBox(MotionEvent event) {
        leftMargin += (int) (event.getX() - startDeltaLeft);
        topMargin += (int) (event.getY() - startDeltaTop);
        requestLayout();
    }

    // the following is to be used by drawview when resizing and panning
    public void externalMove(float dragX, float dragY) {
        leftMargin = (int) (leftMargin + dragX);
        topMargin = (int) (topMargin + dragY);
        requestLayout();
    }

    private void resizeBox(MotionEvent event) {
        setWidth((int) event.getX());
        setHeight((int) event.getY());
        resizeText();
    }

    // change the text size to whatever will fit in the box
    private void resizeText() {
        String[] multiline = this.getText().toString().split("\n");
        float adjustedHeight = (getMeasuredHeight()/multiline.length - getCompoundPaddingBottom() - getCompoundPaddingTop())/2;
        setTextSize(adjustedHeight);
    }

    // if there is text, add the OK button
    void showOkButton() {
        if (this.getText().toString().equals("")) {
            // remove the clear button
            this.setCompoundDrawables(this.getCompoundDrawables()[0],null, resizeImg, this.getCompoundDrawables()[3] );
        } else {
            //add clear button
            this.setCompoundDrawables(this.getCompoundDrawables()[0], closeImg, resizeImg, this.getCompoundDrawables()[3]);
        }
    }

    void okButtonClick() {
        MyViewGroup viewGroup = (MyViewGroup) getParent();
        // get the text, get the size, get the position, create a Stroke from it
        // locate this view in the viewgroup's coordinate system
        // the screen coordinates minus the location of the MET
        Float deltaL = getLeft() - getScrollX() - viewGroup.dv.getCenterX();
        Float deltaB = getTop() - getScrollY() + getBaseline() - viewGroup.dv.getCenterY() ;
        // viewcenterxy are the canvas coordinates
        Float dvViewCenterX = viewGroup.dv.getViewCenterX();
        Float dvViewCenterY = viewGroup.dv.getViewCenterY();
        Float scaleFactor = viewGroup.dv.getScaleFactor();
        // translate that delta to the canvas's coordinates
        Float translatedL = dvViewCenterX + (deltaL/scaleFactor);
        Float translatedB = dvViewCenterY + (deltaB/scaleFactor);

        if (this.getText().toString().length()>0) {
            app.setText(getText().toString());
            app.setTextSize(getTextSize()/2);  // i do not know why it keeps doubling the size
            app.strokeInProgress = new TextStrokeBuilder(app, translatedL, translatedB, scaleFactor);
            app.finishStroke();
        }
        // tell viewgroup to delete the EditText
        textListener.onTextCompleted();
        finished = true;
        imm.hideSoftInputFromWindow(getWindowToken(),0);
    }

    public void setLeftMargin(float left) {
        leftMargin = left;
    }
    public void setTopMargin (float top) {
        topMargin = top;
    }
}