New Place, No Cash: Why I (probably) won’t be attending OHM 2013 or Burning Man

The issues with the neighbours and the landlord have recently come to a head.  I’m not going to go too heavy into details, but we’re moving to a newer, much larger place in the next few days.  While the new place is awesome, the rent is double what I am paying now, so my plans for heading off to OHM 2013 are on hold.

OHM2013 is the next in a series of Dutch Hacker Camps such as What The Hack and Hacking At Random.  HAR2009 is the one that I went to, and I remember camping on a hill next to the GSM tent, mixing Maple Syrup with Club Mate and realizing that this is a bad idea, and haggling with Julian Assange over the cost my white Wikileaks T-Shirt.  OHM2013 is a fun time and is similar to the Chaos Communications Camp and Toorcamp in the US.

The big downside of OHM2013 is that it is located in the Netherlands, which is expensive to go to.  Unlike Toorcamp, where I was able to just hop in my car, drive across the border and take a ferry, OHM is going to require plain, train and probably a strange car to get into, and will require some European Backpacking, which gets expensive very quickly.  Unlike in Germany, where things are relatively inexpensive, the only thing that seems cheap in the Netherlands is the 3G Data Plans (no showing a passport, and only 50 EUR for unilmited!)

I also won’t be going to Burning Man, mostly because it’s too expensive to rent an RV to head to Burning Man, and my lease expires in September, and I need to be back in town for that in case I have to move again.

The good news is that it frees up my vacation time.  So, I have to decide what to do with this vacation time.  It’s pretty clear that the vacation will probably have these constraints:

  • Camping > Hotel – Camping is cheaper and more fun than staying at a hotel.  I stay at hotels enough for work, so staying at a hotel isn’t a huge vacation.  Hotels are also more expensive.
  • No Flying – I have a kid, and she’s not old enough to go through airport security
  • Kid-Friendly – Most trips should be kid-friendly
  • Be somewhere where I won’t go during the weekend – So, anything on Highway 99 is out, because I will probably do weekend camping trips there

This basically limits me to how far I’m willing to drive.  This also were the constraints of my parents when they went on vacation.  The main difference here is that I’m willing to cross the border with my kid in the car.

So, right now the options are as follows:

  • Yukon trip: Go way up north because we can – This trip does have some serious highlights, such as Liard River Hot Springs, but there’s also a few major downsides to this trip as well, such as 8 hours plus in the car to get from campground to campground.  Of course, since this is the summer, there will be more hours of daylight the further north we go, which is a plus.
  • Drumheller: My kid loves Dinosaurs.  The Royal Tyrell Museum was awesome when I was a kid and it’d be interesting to go there.  The downside is that this would mean booking a hotel in Drumheller while we are there.
  • Kootenay trip: Go through the Kootenays and check out various hot springs, waterfalls and other things.  This is the winner so far, since the distance is the shortest, and there’s lots of camping opportunities
  • Yellowstone: Go through the US to Yellowstone Park.  Traditional US Road Trip, similar to the road trip I did on US 101 last year.  Gas is cheaper in the US, so this may be a real contender as well.

I know that I could just stay home, but that would be boring.  Ideally, I’m looking for a family vacation where I can get the biggest bang for my buck, but also go on an adventure.  The big thing about road trips is that nobody really does this anymore, and you get to find some really weird and interesting things on the highway.  Also, not being bound to a conference or an event schedule like with festivals is a nice touch as well.  I’ll have to narrow it down and figure out when to go, but this should be interesting.

Self-Respect Matters

Yesterday, I had the misfortune of entering a discussion with Asher Wolf right before she quit CryptoParty.  Here’s the backstory that I was operating with when I checked in on something on Twitter.

A few days ago, Asher was tweeting about how she was unable to attend #29c3 because her talk was rejected.  I heard claims that the CCC was sexist, and having known people who are female, feminists, and parents who have talked at past congresses and camps, I knew that this couldn’t be the case unless things went way off the rails.  I posted an open tweet because this just seemed bizzare, and I got some feedback.  I came to the conclusion that there was something else wrong with the submission, and that things were lost in translation and forgot about it.  I’m fiercely loyal to the CCC, since they flew me to Camp back in 2011 and allowed me to talk about the RCMP and State Surveillance, and since they have a history of doing extremely important work.

Then, earlier today, I saw yet another tweet by Asher about CCC being sexist.  She shared a link to the popcccorn.de blog, which seems to be documentation of the sexism that happens at CCC.  I feel that this is disingenuous since it just shows that sexism happened.  Even though I’m a white male, I still believe that sexism happens everywhere, and that the world and even the CCC isn’t a vacuum.  The question should be how does the CCC handle the sexism, namely how is the policy enforced.  The CCC has an Anti-Harassment Policy, and this Anti-Harassment Policy can also extend to the web by sending an e-mail to CCC.  This isn’t the be all and end all, but given that people have fought for that Anti-Harassment Policy, it’s important to actually give the conference a fighting chance to enforce this policy to see if it’s not just bullshit, otherwise what is the point of having this policy to begin with.

At any rate, I suggested these solutions, and of course I was then told by Asher that it wasn’t her problem.  I personally believe that it’s everyone’s department to deal with harassment, especially if you’re the party that is offended.  I’m irritated that some of the attendees of #29c3 sunk to the level that DEFCON. However, to just throw mud at the CCC instead of actually sending a e-mail that takes a minute so that the administrators could lock a wiki article is stupid.  If they do nothing, than by all means.

Of course, since I didn’t know the massive backstory about how shitty the CryptoParty organizers were to Asher, and how bullying and bullshit were what really caused Asher to not make it to #29c3, not the strength of the presentation.  The thing is that if I knew this earlier, I wouldn’t have called Asher petty for what I thought was just someone smearing the CCC because their talk wasn’t accepted.  It’s very easy for something to sound like sour grapes without knowing all of the facts, and I apologize for calling that out when that wasn’t the case.  However, I still stand behind my statement that harassment should be reported.

Now, as for my thoughts on Asher Wolf leaving Cryptoparty, I think it’s a good thing.  The reason I believe it’s a good thing is because it’s a wake up call as to who is being served by having these CryptoParties.  I’ve been at two CryptoParties so far, and it so often feels like preaching to the converted.  I don’t like teaching GPG/PGP because it sucks and is hard for people to use, and often the talks diverge to non-practical solutions, such as BitCoin.

My main motivation to host CryptoParty comes from the what got me spied on by the RCMP, which is teaching activists cryptographic tools to allow them to resist the state.  These activists recently do things like participate in #IdleNoMore, fight the Enbridge Pipeline, or engage in other actions that directly confront the state.  However, I personally don’t think that I’ve helped anyone in these causes by sitting in the Vancouver Hack Space and talking about Wikileaks or Anonymous or BitCoin.  The fact is that there are real people on the ground who need these tools, and they don’t have them, and based on this, I consider CryptoParty a failed model for these people to get these tools, because they have to want to go to CryptoParty and have to deal with people who care more about Silk Road than about what happens when the cops come knocking on the door.  I may be wrong and the next CryptoParty may be better, but so far from my experience this isn’t the case.

But the reason I think it’s good that Asher is leaving is that she’s finally standing up to all the bullies.  While I disagree with her being in control of something decentralized, I still think that it’s shitty that she was bullied out of going to #29c3 by people who helped write a talk and that people don’t think that she is qualified to talk about CryptoParty, since they seemed to have missed the point as to what CryptoParty was supposed to be about in the first place.  I just wish I knew who more of these people were, but I can understand the reluctance to name names.

tldr; everyone needs to step up and say that this behaviour isn’t acceptable.  We’re  adults, and we should all act like it.

Chrome Content View: Compilation, Comparison and Thoughts

One thing that we get asked a lot is whether Cordova should bring along its own WebKit when running on Android. The reason for this is pretty obvious to everyone at this point, especially those poor users stuck on Android 2.x. If you’re an Android 2.x user and you went to html5test.com, you would see something like this:

Yeah, that’s pretty bad, and it doesn’t get a lot better when we go to Android 4.1.

Of course, those are the WebViews that we have access to. If you compare them to Chrome:

Or you compare it with Silk on the Amazon Kindle Fire HD:

It just gets depressing. We know that it’s possible to have Webkit not suck on Android, but since the Android Team neglected the browser, we have to get desperate and do crazy things like build our own. The most reasonable candidate for us is to hack on Chromium, and the only thing that I can get building was the Chromium content view. So, I took my personal Ubuntu 12.10 machine, followed these instructions here, and after fixing a header I got this installed on my phone.

Now, that’s a good score. Sadly, this is a debug build so the performance on it is rather slow, but it does show what’s possible on mobile.  It’s actually comparable to what’s on a desktop version of Chrome minus WebGL according to this test.

However, the reality of the matter is something different.  Currently if you took the Chrome ContentView to html.adobe.com and tried to use any of the Adobe examples, the application would currently crash.

As far as us getting Cordova working on it, that’s going to take a lot of time, since the ContentView is structured very differently than the Android SDK’s WebView, and it’s not ready for primetime. Anything that we do will be a hack, and not suitable for the Android Market or anywhere else right now. Hopefully in a few month’s time, we can get something more solid and be able to have better WebViews on Android.

Advanced Tutorial: Using CordovaWebView on Android

Back on the lead-up to Cordova 2.0, we pushed for the implementation of Cordova WebView.  While this broke a lot of things, and irritated a lot of people, it seems that people are actually starting to use this new functionality.  Unfortunately, we also accidentally made a change to it in Cordova 2.2.0. In this post we step through an example that I wrote up using the existing example of the Action Bar from the Android SDK. It should be noted that this guide assumes that you are comfortable with working with Java.

You can find the example of how to use this on a GitHub repository: https://github.com/infil00p/CordovaActionView

Even though CordovaWebView does its best to try to encapsulate all the methods that are required for a working PhoneGap project, there’s still a few things that the activity that holds it has to maintain.  This is why we created the CordovaInterface. In this example, we take the default example of the ActionBar application, and we implement CordovaInterface on it.  When we start dissecting it, we see the following:

public class MainActivity extends ActionBarActivity implements CordovaInterface{
    private final ExecutorService threadPool = Executors.newCachedThreadPool();

This has to be here for the plugins to actually work. Following this, we have our typical variables both from the app, and what you’d normally find in a Cordova Activity:

    private boolean mAlternateTitle = false;
    private boolean bound;
    private boolean volumeupBound;
    private boolean volumedownBound;

    String TAG = "MainActivity-ActionBarTest";
    private CordovaPlugin activityResultCallback;
    private Object activityResultKeepRunning;
    private Object keepRunning;

    CordovaWebView mainView;

This is primarily to maintain the view, then in the create method, we basically do exactly what the old Android WebView example code does, and we find the view, and load our app:

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        mainView =  (CordovaWebView) findViewById(R.id.mainView);
        mainView.loadUrl("file:///android_asset/www/index.html");
    }

Meanwhile, if you look at the XML file, you will see the FrameLayout for the main application. The ActionBar in this case is handled separately by the Android Application:


<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <org.apache.cordova.CordovaWebView 
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" 
        android:id = "@+id/mainView"/>
</FrameLayout>

When using custom widgets, you have to include the full name of the object that you want to include. It should be noted that I made a mistake here and that “match_parent” should be used instead of “fill_parent”, since “fill_parent” will be deprecated. Back in the code, we add the following methods to implement the CordovaInterface.  The first that we add is getActivity.  This is expected to get the activity that actually contains the CordovaWebView so that we can handle intents that are passed to the application.  This is pretty self-explanatory:

    @Override
    public Activity getActivity() {
        return this;
    }

After this, we implement onMessage, which deals when a message is sent from a plugin to the activity itself.  This currently exists in DroidGap, and facilitates the app plugin functionality, such as the splashscreen, the spinner, and whether the application exists.  This is not entirely necessary to implement, so in this example we simply ignore everything but exit.

    /**
     * Called when a message is sent to plugin.
     *
     * @param id            The message id
     * @param data          The message data
     * @return              Object or null
     */
    public Object onMessage(String id, Object data) {
        LOG.d(TAG, "onMessage(" + id + "," + data + ")");
        if ("exit".equals(id)) {
            super.finish();
        }
        return null;
    }

Next is the most important, which is the setActivityResultCallback code and startActivityForResult. This facilitates the passing of information from activities such as the Gallery and the Camera back to Cordova. Without this the capture API will not work, nor will any plugin that relies on another activity to start. This does the majority of the Android housekeeping:

    @Override
    public void setActivityResultCallback(CordovaPlugin plugin) {
        this.activityResultCallback = plugin;        
    }
    /**
     * Launch an activity for which you would like a result when it finished. When this activity exits, 
     * your onActivityResult() method will be called.
     *
     * @param command           The command object
     * @param intent            The intent to start
     * @param requestCode       The request code that is passed to callback to identify the activity
     */
    public void startActivityForResult(CordovaPlugin command, Intent intent, int requestCode) {
        this.activityResultCallback = command;
        this.activityResultKeepRunning = this.keepRunning;

        // If multitasking turned on, then disable it for activities that return results
        if (command != null) {
            this.keepRunning = false;
        }

        // Start activity
        super.startActivityForResult(intent, requestCode);
    }

Next is cancelLoadUrl, which was once supposed to cancel the loading of a URL. We decided to get rid of this functionality, but this operation still exists on the CordovaInterface, and will hopefully be deprecated soon. However, we still have to have it.

    @Override
    public void cancelLoadUrl() {
        // This is a no-op.
    }

The last required method are the following two at the bottom. This is the thread pool and getContext(). Both of these are expected to exist for plugins to function, however one is deprecated, and this is getContext(). Initially the CordovaInterface was once the DroidGap activity, and since an Activity inherits the properties of the context, people would just use the activity, since we can’t assume that we’re using a DroidGap activity, we need to provide an Activity. We do this with getActivity, but some people still use the old getContext() API, which is why this is still here. Eventually we will be dropping this because having two methods that return the same thing is silly.

    @Override
    public ExecutorService getThreadPool() {
        return threadPool;
    }

    @Override
    @Deprecated
    public Context getContext() {
        return this;
    }

Android Lifecycle and Results:

Now, back to actually handling the result that comes back. This is where we use the stored callback and send the result to the proper plugin. This is pretty self-explanatory as well when you look at this code. The thing is that this isn’t a part of the CordovaInterface, but instead is a part of Android. This has to be implemented if you want your Camera, Gallery and other Activity-based plugins to work.

    @Override
    /**
     * Called when an activity you launched exits, giving you the requestCode you started it with,
     * the resultCode it returned, and any additional data from it.
     *
     * @param requestCode       The request code originally supplied to startActivityForResult(),
     *                          allowing you to identify who this result came from.
     * @param resultCode        The integer result code returned by the child activity through its setResult().
     * @param data              An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
     */
    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
        super.onActivityResult(requestCode, resultCode, intent);
        CordovaPlugin callback = this.activityResultCallback;
        if (callback != null) {
            callback.onActivityResult(requestCode, resultCode, intent);
        }
    }

The other big thing is pause and resume. One of the things that we have to keep in mind is what happens to Cordova and the plugins when they pause and resume. Many of them will save their current state, and these events need to be handled. This is the most barebones code to deal with pause and resume. This does not pause any WebKit timers, so your JS will still run in the background thread, but this will handle Java states.

    @Override
    /**
     * Called when the system is about to start resuming a previous activity.
     */
    protected void onPause() {
        super.onPause();

         // Send pause event to JavaScript
        this.mainView.loadUrl("javascript:try{cordova.fireDocumentEvent('pause');}catch(e){console.log('exception firing pause event from native');};");

        // Forward to plugins
        if (this.mainView.pluginManager != null) {
            this.mainView.pluginManager.onPause(true);
        }
    }

    @Override
    /**
     * Called when the activity will start interacting with the user.
     */
    protected void onResume() {
        super.onResume();

        if (this.mainView == null) {
            return;
        }

        // Send resume event to JavaScript
        this.mainView.loadUrl("javascript:try{cordova.fireDocumentEvent('resume');}catch(e){console.log('exception firing resume event from native');};");

        // Forward to plugins
        if (this.mainView.pluginManager != null) {
            this.mainView.pluginManager.onResume(true);
        }

    }

    @Override
    /**
     * The final call you receive before your activity is destroyed.
     */
    public void onDestroy() {
        LOG.d(TAG, "onDestroy()");
        super.onDestroy();

        if (this.mainView != null) {

            // Send destroy event to JavaScript
            this.mainView.loadUrl("javascript:try{cordova.require('cordova/channel').onDestroy.fire();}catch(e){console.log('exception firing destroy event from native');};");

            // Load blank page so that JavaScript onunload is called
            this.mainView.loadUrl("about:blank");
            mainView.handleDestroy();
        }
    }

Finally, it’s important to handle any new event that appears to the application, which is what we do here with onNewIntent:

    @Override
    /**
     * Called when the activity receives a new intent
     **/
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);

        //Forward to plugins
        if ((this.mainView != null) &amp;&amp; (this.mainView.pluginManager != null)) {
            this.mainView.pluginManager.onNewIntent(intent);
        }
    }

The final result of all this is the following UI that works on Gingerbread, Honeycomb, ICS and Jellybean.

At this point, you could use fragments to have multiple CordovaWebViews, or add plugin code to manipulate the CordovaWebView so that it runs Javascript through a plugin. The reason you would want to do this is for further integration with the platform. This is clearly not for the average Cordova user, but instead for people who are comfortable with Java but still want the APIs that Cordova offers.