Profile

Cover photo
Sergey Povzner
396 followers|509,207 views
AboutPostsPhotosVideos

Stream

 
 
Q: So, why is Silicon Valley studded with an implausibly large number of abandoned barns, shacks, and other things that don't look like they belong here?

A: Because this all used to be orchards. This all made sense, fifty years ago.

Q: Right, but shouldn't they have, like, torn them down by now?

A: No. Abandoned barns in Silicon Valley are a better investment than historical stock market returns.

Q: Wouldn't they be an even better investment if there were, like, usable buildings on the land?

A: No.

Q: You have got to be shitting me.

A: Proposition 13 makes abandoned barns an enormously lucrative investment. You see, the assessed value of a piece of property is capped at a rate well below the rate of increase in property values out here. So you can just get the price of the land reevaluated every year and take tax-free loans against the increase in equity. This is a huge amount of untaxable money. Especially if you're an abandoned shed that's worth seven figures, right smack dab in the middle of the Google campus.

Q: Right, but that's just about low taxes, right?

A: Yeah. Well. Uh. I was going to get to the "except" part.

Q: And what's that? 

A: The increase is capped except upon the sale or the completion of new improvements. Like an apartment building, for instance, to partially solve the housing crisis out here. Or a new building on the Google campus. This means that if the value of the improvement is less than the compounded increase in the value of the property absent the improvement, then it doesn't make any sense to actually build anything.

Q: That's horrible. Why aren't people, like, vandalizing abandoned buildings to get rid of them?

A: The only way you can vandalize anything in California that solves the problem is to literally build a new improvement on the property without the landowner finding out. That resets the tax basis and gives them an incentive to stop holding the land off the market.

Q: That's... double horrible.

A: Yeeeeeeah.
1
OC Crush's profile photo
 
boo Hoo ... bay area problems.
Add a comment...
 
 
Android App Polishing: Show UI State Through Icons

With the release of Capitaine Train for Android[0], several people recently asked me how we implemented some of the tips & tricks available in the application (some of these tricks are barely visible but remember, I love details :p). In order to showcase some of the most interesting application bits of code, I thought it could be helpful to Android developers to start a small series of posts. Feel free to comment this article if you want me to describe something you found nice in the Capitaine Train application.

As some of you may have noticed, we put a lot of attention to the search form in the Capitaine Train Android app. As I explained in my previous article[1], I'm not a huge fan of long texts. While often offering a great explanation, text requires a lot of parsing time. Icons are often a great alternative to text because they allow user to understand what's going on more rapidly.

When creating the search form, we thought about leveraging icons to help user better understand the current "form state". As a consequence, the form heavily relies on icons:

  • The "To" icon changes whether the search is round trip or not
  • Mandatory fields (i.e "From", "To" and "Passengers") icons turn red when missing
  • A red icon turns grey in edit mode while animating back to red if the field is not set when exiting the edit mode
  • The "Passengers" icon changes depending on the current number of selected passengers (see screencast)

As you can see in the screencast, the change is done as smoothly as possible simply fading between the different states. This can be done fairly easily thanks to a LevelListDrawable[2]. Here is the XML we use in Capitaine Train Android for the "Passengers" icon.

<?xml version="1.0" encoding="utf-8"?>
<level-list xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- The error icon -->
    <item
        android:drawable="@drawable/ic_search_passengers_0"
        android:maxLevel="0"
        />

    <item
        android:drawable="@drawable/ic_search_passengers_1"
        android:maxLevel="1"
        />

    <item
        android:drawable="@drawable/ic_search_passengers_2"
        android:maxLevel="2"
        />

    <item
        android:drawable="@drawable/ic_search_passengers_several"
        android:maxLevel="10000"
        />

</level-list>

Starting API 11, you can animate changes in a LevelListDrawable or more globally in any DrawableContainer. Unfortunately, the framework only exposes the XML attributes in <selector /> (I really don't know why it has not being exposed to all DrawableContainer :s). Fortunately for us, you can tweak the fading duration values in your Java code with the set[Enter|Exit]FadeDuration(int) methods (yep I also don't know why the duration is an 'int' rather than a 'long').

In our code, we simply update the icon whenever there is an impacting change (an error occurred, the number of selected passenger changed, etc.). Here are some notes about the following snippet of code:

  • mFieldErrorState is a bit mask gathering all of the fields that are currently in error
  • mEditMode contains the current "edit mode" ("Passengers", "From", "To", etc.).
  • LEVEL_ERROR is 0 & LEVEL_NORMAL is 1

private void updateIcons(boolean animated) {
    final int animationDuration = (int) ANIMATION_DURATION;

    // Update the "From" icon...

    // Update the "To" icon...

    final Drawable passengersIcon = mPassengersView.getCompoundDrawables()[TextViewAdditions.INDEX_LEFT];
    if (passengersIcon != null) {
        if (passengersIcon instanceof LevelListDrawable) {
            ((LevelListDrawable) passengersIcon).setEnterFadeDuration(animationDuration);
            ((LevelListDrawable) passengersIcon).setExitFadeDuration(animationDuration);
        }
        int level = LEVEL_ERROR;
        if ((mFieldErrorState & ERROR_PASSENGERS) == 0 || mEditMode == EDIT_MODE_PASSENGERS) {
            level = Math.max(LEVEL_NORMAL, mPassengerIds.size());
        }
        passengersIcon.setLevel(level);
        if (!animated) {
            passengersIcon.jumpToCurrentState();
        }
    }
}

[0]: https://play.google.com/store/apps/details?id=com.capitainetrain.android
[1]: https://plus.google.com/118417777153109946393/posts/FABaJhRMCuy
[2]: http://developer.android.com/reference/android/graphics/drawable/LevelListDrawable.html
1
Add a comment...
 
 
Android App Polishing: Adding Subtle Attention Seekers

With the release of Capitaine Train for Android[0], several people recently asked me how we implemented some of the tips & tricks available in the application (some of these tricks are barely visible but remember, I love details :p). In order to showcase some of the most interesting application bits of code, I thought it could be helpful to Android developers to start a small series of posts. Feel free to comment this article if you want me to describe something you found nice in the Capitaine Train application.

During the entire development of the Capitaine Train Android app, we kept one important thing in mind: be subtle but still understandable. Although this rule is easy to understand it is always hard to implement correctly.

Some great examples of adding subtle but yet understandable visual tricks are the search form and the passenger detail screen. Indeed, when launching a new train search with some missing required fields, the field animates to indicate the user something is wrong. An equivalent is the "Add a passenger" ActionBar button than wobble when the user is tapping the empty screen indicating she/he has no registered passengers yet. This wobbling button is like saying "Hey, guy, tap me to create a new passenger!". The gif attached to this post demonstrates the animation but let's be honest I'm not a gif expert :s I highly encourage you to have a look at the Capitaine Train and try it out directly ;).

When used appropriately, attention seekers, are way better than these pretty ugly Toasts displayed right in the middle of the screen. Why? Mostly because they are subtle and require less time to be scanned/parsed than a long and difficult to read text.

But how to implement such an animation? It generally consists on several "back and forth"... We could have obviously implemented this using a sequence of ObjectAnimators or a pretty weird TimeInterpolator but that would have been quite painful. The trick is simply to leverage the animation framework by using the Keyframe[1] & PropertyValuesHolder[2] objects.

A Keyframe is an object that holds a time/value pair describing a particular state in an animation. For instance Keyframe.ofInt(.5f, Color.RED) indicates the color should be Color.RED at 50% of the animation duration.

A PropertyValuesHolder is a class that associates a property to some values. It is particularly useful whenever you want to animate several properties at once with a single ValueAnimator.

When developing the Capitaine Train application we created several attention seekers and tried them all. We finally ended up using only 2 different animations:
  
  • The "nope" animation: used whenever a field is invalid (this is the case when trying to start a search with some missing mandatory fields)
  • The "tada" animation: used to indicate the user the action she/he is looking for is probably a tap-away from this button (this is the case when tapping the empty state with no passenger and/or when tapping the "no cards" cell on the passenger detail screen)


public static ObjectAnimator tada(View view) {
    return tada(view, 1f);
}

public static ObjectAnimator tada(View view, float shakeFactor) {

    PropertyValuesHolder pvhScaleX = PropertyValuesHolder.ofKeyframe(View.SCALE_X,
            Keyframe.ofFloat(0f, 1f),
            Keyframe.ofFloat(.1f, .9f),
            Keyframe.ofFloat(.2f, .9f),
            Keyframe.ofFloat(.3f, 1.1f),
            Keyframe.ofFloat(.4f, 1.1f),
            Keyframe.ofFloat(.5f, 1.1f),
            Keyframe.ofFloat(.6f, 1.1f),
            Keyframe.ofFloat(.7f, 1.1f),
            Keyframe.ofFloat(.8f, 1.1f),
            Keyframe.ofFloat(.9f, 1.1f),
            Keyframe.ofFloat(1f, 1f)
    );

    PropertyValuesHolder pvhScaleY = PropertyValuesHolder.ofKeyframe(View.SCALE_Y,
            Keyframe.ofFloat(0f, 1f),
            Keyframe.ofFloat(.1f, .9f),
            Keyframe.ofFloat(.2f, .9f),
            Keyframe.ofFloat(.3f, 1.1f),
            Keyframe.ofFloat(.4f, 1.1f),
            Keyframe.ofFloat(.5f, 1.1f),
            Keyframe.ofFloat(.6f, 1.1f),
            Keyframe.ofFloat(.7f, 1.1f),
            Keyframe.ofFloat(.8f, 1.1f),
            Keyframe.ofFloat(.9f, 1.1f),
            Keyframe.ofFloat(1f, 1f)
    );

    PropertyValuesHolder pvhRotate = PropertyValuesHolder.ofKeyframe(View.ROTATION,
            Keyframe.ofFloat(0f, 0f),
            Keyframe.ofFloat(.1f, -3f * shakeFactor),
            Keyframe.ofFloat(.2f, -3f * shakeFactor),
            Keyframe.ofFloat(.3f, 3f * shakeFactor),
            Keyframe.ofFloat(.4f, -3f * shakeFactor),
            Keyframe.ofFloat(.5f, 3f * shakeFactor),
            Keyframe.ofFloat(.6f, -3f * shakeFactor),
            Keyframe.ofFloat(.7f, 3f * shakeFactor),
            Keyframe.ofFloat(.8f, -3f * shakeFactor),
            Keyframe.ofFloat(.9f, 3f * shakeFactor),
            Keyframe.ofFloat(1f, 0)
    );

    return ObjectAnimator.ofPropertyValuesHolder(view, pvhScaleX, pvhScaleY, pvhRotate).
            setDuration(1000);
}

public static ObjectAnimator nope(View view) {
    int delta = view.getResources().getDimensionPixelOffset(R.dimen.spacing_medium);

    PropertyValuesHolder pvhTranslateX = PropertyValuesHolder.ofKeyframe(View.TRANSLATION_X,
            Keyframe.ofFloat(0f, 0),
            Keyframe.ofFloat(.10f, -delta),
            Keyframe.ofFloat(.26f, delta),
            Keyframe.ofFloat(.42f, -delta),
            Keyframe.ofFloat(.58f, delta),
            Keyframe.ofFloat(.74f, -delta),
            Keyframe.ofFloat(.90f, delta),
            Keyframe.ofFloat(1f, 0f)
    );

    return ObjectAnimator.ofPropertyValuesHolder(view, pvhTranslateX).
            setDuration(500);
}

Once again, attention seekers are great but they should be used with parsimony. The less you use them, the greater their influence is.

#gde #android

[0]: https://play.google.com/store/apps/details?id=com.capitainetrain.android
[1]: http://developer.android.com/reference/android/animation/Keyframe.html
[2]: http://developer.android.com/reference/android/animation/PropertyValuesHolder.html
1
Add a comment...

Sergey Povzner

Shared publicly  - 
 
 
Issue #2 - Weekly Wearable News

The second issue of my weekly newsletter on wearables is out now. - http://tmblr.co/ZSc06o1BQwhiF 
1
Add a comment...

Sergey Povzner

Shared publicly  - 
 
 
So Android has an official pull-to-refresh widget. #AndroidDev

I woke up this morning to many tweets and G+ notifications telling me so. It's up to you which you use. Just be aware that I haven't updated my lib in ~3 months as I've been busy, and I knew this was coming.

Also, I haven't contributed to this class.

On a side note, I really need to spend some time on my libraries._
1
Add a comment...
Have him in circles
396 people
Neli Brod's profile photo

Sergey Povzner

Shared publicly  - 
 
 
The world's coolest water slide, Sicily, Italy.

Please Follow: +Interesting Things 
1
Add a comment...

Sergey Povzner

Shared publicly  - 
 
 
Did you know you can toggle visibility of the ActionBar?  #androiddev methods of the day: ActionBar.hide()/show().
1
Add a comment...
 
 
Rounded up the first two weeks of #androiddev tips in a blog post.
1
Add a comment...

Sergey Povzner

Shared publicly  - 
 
 
Subscribers now have access to the latest release of The Busy Coder’s Guide to Android Development, known as Version 5.7, in all formats. Just log into your Warescription page and download away, or set up an account and subscribe!
1
Add a comment...
 
 
Dear Google: The "Want Quick Suggestions" feature of the Google Play Store is decimating the hard-earned ratings of developers because its users are not aware that they are actually rating apps.  Please correct or remove this feature.

While a user may think that this rating box is informing the Play Store only of their preference to see similar apps, it is in fact submitting an actual review of the app that affects its overall rating score.  Users are giving 1-star ratings to their favorite apps because they are happy with them and are not looking for a replacement.

The ratings of SystemPanel, WebSharing, FX, and the FX root add-on are superimposed in the adjacent plot.   Each app's rating has fallen sharply at an identical rate since this feature was introduced in December 2013.  The ratings of these apps were built over four years of hard work, maintenance, and support, and that has now been marginalized by the "Want Quick Suggestions" feature.  I've seen the total number one-star ratings double in the past three months compared to the last four years.  The ratings of competing apps are clearly suffering the same fate.

Older apps, including my own, are actually least affected: they have a large pool of reviews from before this change keeping overall ratings reasonably high.  New apps have to contend with this inaccurate rating system from day one, and are thus placed at an extreme disadvantage.

Google, please do one of the following:

* Remove this feature.

* Correctly and thoroughly explain to users what this feature actually does.

* Change this feature so that it behaves according to the current description (i.e., uses the ratings only for purposes of showing a user more of fewer similar applications to the one rated).

Until this issue is corrected, you're introducing an enormous amount of noise to the Play Store rating system, destroying years of hard work, and preventing new developers from competing with established applications on a level playing field.

AndroidPolice has a more detailed review of this issue, which was first spotted weeks after its introduction.  It's now been three months.  Their write-up is here: http://www.androidpolice.com/2013/12/30/multiple-developers-seeing-influx-of-unexplained-1-star-ratings-since-launch-of-play-stores-new-quick-rating-widget/

Please fix this.
1
1
Ivo Encarnação's profile photo
Add a comment...
People
Have him in circles
396 people
Neli Brod's profile photo
Basic Information
Gender
Male