lucasr is currently certified at Master level.

Name: Lucas Rocha
Member since: 2002-11-28 14:12:04
Last Login: 2010-02-20 20:32:45

FOAF RDF Share This

Homepage: http://lucasr.org

Projects

Recent blog entries by lucasr

Syndication: RSS 2.0

The new TwoWayView

What if writing custom view recycling layouts was a lot simpler? This question stuck in my mind since I started writing Android apps a few years ago.

The lack of proper extension hooks in the AbsListView API has been one of my biggest pain points on Android. The community has come up with different layout implementations that were largely based on AbsListView‘s code but none of them really solved the framework problem.

So a few months ago, I finally set to work on a new API for TwoWayView that would provide a framework for custom view recycling layouts. I had made some good progress but then Google announced RecyclerView at I/O and everything changed.

At first sight, RecyclerView seemed to be an exact overlap with the new TwoWayView API. After some digging though, it became clear that RecyclerView was a superset of what I was working on. So I decided to embrace RecyclerView and rebuild TwoWayView on top of it.

The new TwoWayView is functional enough now. Time to get some early feedback. This post covers the upcoming API and the general-purpose layout managers that will ship with it.

Creating your own layouts

RecyclerView itself doesn’t actually do much. It implements the fundamental state handling around child views, touch events and adapter changes, then delegates the actual behaviour to separate components—LayoutManager, ItemDecoration, ItemAnimator, etc. This means that you still have to write some non-trivial code to create your own layouts.

LayoutManager is a low-level API. It simply gives you extension points to handle scrolling and layout. For most layouts, the general structure of a LayoutManager implementation is going to be very similar—recycle views out of parent bounds, add new views as the user scrolls, layout scrap list items, etc.

Wouldn’t it be nice if you could implement LayoutManagers with a higher-level API that was more focused on the layout itself? Enter the new TwoWayView API.

TWAbsLayoutManagercode is a simple API on top of LayoutManager that does all the laborious work for you so that you can focus on how the child views are measured, placed, and detached from the RecyclerView.

To get a better idea of what the API looks like, have a look at these sample layouts: SimpleListLayout is a list layout and GridAndListLayout is a more complex example where the first N items are laid out as a grid and the remaining ones behave like a list. As you can see you only need to override a couple of simple methods to create your own layouts.

Built-in layouts

The new API is pretty nice but I also wanted to create a space for collaboration around general-purpose layout managers. So far, Google has only provided LinearLayoutManager. They might end up releasing a few more layouts later this year but, for now, that is all we got.

layouts

The new TwoWayView ships with a collection of four built-in layouts: List, Grid, Staggered Grid, and Spannable Grid.

These layouts support all RecyclerView features: item animations, decorations, scroll to position, smooth scroll to position, view state saving, etc. They can all be scrolled vertically and horizontally—this is the TwoWayView project after all ;-)

You probably know how the List and Grid layouts work. Staggered Grid arranges items with variable heights or widths into different columns or rows according to its orientation.

Spannable Grid is a grid layout with fixed-size cells that allows items to span multiple columns and rows. You can define the column and row spans as attributes in the child views as shown below.

<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:colSpan="2"
    app:rowSpan="3">
    ...

Utilities

The new TwoWayView API will ship with a convenience view (TWView) that can take a layoutManager XML attribute that points to a layout manager class.

<org.lucasr.twowayview.TWView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layoutManager="TWListLayoutManager"/>

This way you can leverage the resource system to set layout manager depending on device features and configuration via styles.

You can also use TWItemClickListener to use ListView-style item (long) click listeners. You can easily plug-in support for those in any RecyclerView (see sample).

I’m also planning to create pluggable item decorations for dividers, item spacing, list selectors, and more.


That’s all for now! The API is still in flux and will probably go through a few more iterations. The built-in layouts definitely need more testing.

You can help by filing (and fixing) bugs and giving feedback on the API. Maybe try using the built-in layouts in your apps and see what happens?

I hope TwoWayView becomes a productive collaboration space for RecyclerView extensions and layouts. Contributions are very welcome!

Syndicated 2014-07-31 11:33:17 from Lucas Rocha

Custom Layouts on Android

If you ever built an Android app, you have definitely used some of the built-in layouts available in the platform—RelativeLayout, LinearLayout, FrameLayout, etc. They are our bread and butter for building Android UIs.

The built-in layouts combined provide a powerful toolbox for implementing complex UIs. But there will still be cases where the design of your app will require you to implement custom layouts.

There are two main reasons to go custom. First, to make your UI more efficient—by reducing the number of views and/or making faster layout traversals. Second, when you are building UIs that are not naturally possible to implement with stock widgets.

In this post, I will demonstrate four different ways of implementing custom layouts and discuss their respective pros and cons: composite view, custom composite view, flat custom view, and async custom views.

The code samples are available in my android-layout-samples repo. This app implements the same UI with each technique discussed here and uses Picasso for image loading. The UI is a simplified version of Twitter app’s stream—no interactions, just the layouts.

Ok, let’s start with the most common type of custom layout: composite view.

Composite View

This is usually your starting point. Composite views (also known as compound views) are the easiest way of combining multiple views into a reusable UI component. They are very simple to implement:

  1. Subclass one of the built-in layouts.
  2. Inflate a merge layout in the constructor.
  3. Initialize members to point to inner views with findViewById().
  4. Add your own APIs to query and update the view state.

TweetCompositeViewcode is a composite view. It subclasses RelativeLayout, inflates tweet_composite_layout.xmlcode into it, and exposes an update() method to refresh its state in the adaptercode. Simple stuff.

Custom Composite View

TweetCompositeView will likely perform fairly well in most situations. But, for sake of argument, suppose you want to reduce the number of child views and make layout traversals a bit more efficient.

Although composite views are simple to implement, using general-purpose layouts has a cost—especially with complex containers like LinearLayout and RelativeLayout. As platform building blocks, they have to handle tons of layout combinations and might have to measure child views multiple times in a single traversal—LinearLayout‘s layout_weight being a common example.

You can greatly optimize your UI by implementing a custom logic for measuring and positioning child views that is specially tailored for your app. This is what I like to call a custom composite view.

A custom composite view is simply a composite view that overrides onMeasure() and onLayout(). So, instead of subclassing an existing container like RelativeLayout, you will be subclassing the more general ViewGroup.

TweetLayoutViewcode implements this technique. Note how it gets rid of the LinearLayout from TweetComposiveView and avoids the use of layout_weight altogethercode.

The laborious work of figuring out what MeasureSpec to use on each child view is done by the ViewGroup’s handy measureChildWithMargins() method—and getChildMeasureSpec() under the hood.

TweetLayoutView probably doesn’t handle all possible layout combinations correctly but it doesn’t have to. It is absolutely fine to optimize custom layouts for your specific needs. This allows you to write both simpler and more efficient layout code for your app.

Flat Custom View

As you can see, custom composite views are fairly simple to write using ViewGroup APIs. Most of the time, they will give you the performance your app needs.

However, you might want to go further and optimize your layouts even more on critical parts of your UI that are very dynamic e.g. ListViews, ViewPager, etc. What about merging all TweetLayoutView child views into a single custom view to rule them all? That is what flat custom views are about—see image below.

Custom Composite View (left) and Flat Custom View (right)

Custom Composite View (left) and Flat Custom View (right)

A flat custom view is a fully custom view that measures, arranges, and draws its inner elements. So you will be subclassing View instead of ViewGroup.

If you are looking for real-world examples, enable the “show layout bounds” developer option in your device and have a look at apps like Twitter, GMail, and Pocket. They all use flat custom views in their listing UIs.

The main benefit from using flat custom views is the great potential for flattening your view hierarchy, resulting in faster traversals and, potentially, a reduced memory footprint.

Flat custom views give you maximum freedom as they are literally a blank canvas. But this comes at a price: you won’t be able to use the feature-packed stock widgets such as TextView and ImageView. Yes, it is simple to draw text on a Canvas but what about ellipsizing? Yes, you can easily draw a bitmap but what about scaling modes? Same applies to touch events, accessibility, keyboard navigation, etc.

The bottom line is: with flat custom views,you will likely have to re-implement features that you would get for free from the platform. So you should only consider using them on core parts of your UI. For all other cases, just lean on the platform with composite views, custom or not.

TweetElementViewcode is a flat custom view. To make it easier to implement it, I created a little custom view framework called UIElement. You will find it in the canvascode package.

The UIElement framework provides a measure/layout API which is analogous to Android’s. It contains headless versions of TextView and ImageView with only the necessary features for this demo—see TextElementcode and ImageElementcode respectively. It also has its own inflatercode to instantiate UIElements from layout resource filescode.

Probably worth noting: the UIElement framework is in a very early development stage. Consider it a very rough sketch of something that might actually become useful in the future.

You have probably noticed how simple TweetElementView looks. This is because the real code is all in TweetElementcode—with TweetElementView just acting as a hostcode.

The layout code in TweetElement is pretty much analogous to TweetLayoutView‘s. It handles Picasso requests differentlycode because it doesn’t use ImageViews.

Async Custom View

As we all know, the Android UI framework is single-threaded. And this is for a good reason: UI toolkits are not your simplest piece of code. Making them thread-safe and asynchronous would be an unworthy herculean effort.

This single-threaded nature leads to some fundamental limitations. It means, for example, that you can’t do layout traversals off main thread at all—something that would be useful in very complex and dynamic UIs.

For example, if your app has complex items in a ListView (like most social apps do), you will probably end up skipping frames while scrolling because ListView has to measurecode and layoutcode each child view to account for their new content as they become visible. The same issue applies to GridViews, ViewPagers, and the like.

Wouldn’t it be nice if we could do a layout traversal on the child views that are not visible yet without blocking the main thread? This way, the measure() and layout() calls on child views would take no time when needed in the UI thread.

Enter async custom view, an experiment to allow layout passes to happen off main thread. This is inspired by the async node framework developed by the Paper team at Facebook.

Given that we can never ever touch the UI toolkit off main thread, we need an API that is able to measure/layout the contents of a view without being directly coupled to it. This is exactly what the UIElement framework provides us.

AsyncTweetViewcode is an async custom view. It uses a thread-safe AsyncTweetElementcode factorycode to define its contents. Off-screen AsyncTweetElement instances are created, pre-measured, and cached in memory from a background thread using a Smoothie item loadercode.

I had to compromise the async behaviour a bit because there’s no sane way of showing layout placeholders on list items with arbitrary heights i.e. you end up resizing them once the layout gets delivered asynchronously. So whenever an AsyncTweetView is about to be displayed and it doesn’t find a matching AsyncTweetElement in memory, it will force its creation in the UI threadcode.

Furthermore, both the preloading logic and the memory cache expiration would need to be a lot smarter to ensure more layout cache hits in the UI thread. For instance, using a LRU cachecode here doesn’t seem ideal.

Despite these limitations, the preliminary results from async custom views look very promising. I’ll continue the explorations in this area by refining the UIElement framework and using it in other kinds of UIs. Let’s see where it goes.

Wrapping up

When it comes to layouts, the more custom you go, the less you’ll be able to lean on the platform’s proven components. So avoid premature optimization and only go fully custom on areas that will actually affect the perceived quality and performance of your app.

This is not a black-and-white decision though. Between stock widgets and fully custom views there’s a wide spectrum of solutions—from simple composite views to the more complex async views. In practice, you’ll usually end up combining more than one of the techniques demonstrated here.

Syndicated 2014-05-12 12:42:06 from Lucas Rocha

How Android transitions work

One of the biggest highlights of the Android KitKat release was the new Transitions framework which provides a very convenient API to animate UIs between different states.

The Transitions framework got me curious about how it orchestrates layout rounds and animations between UI states. This post documents my understanding of how transitions are implemented in Android after skimming through the source code for a bit. I’ve sprinkled a bunch of source code links throughout the post to make it easier to follow.

Although this post does contain a few development tips, this is not a tutorial on how to use transitions in your apps. If that’s what you’re looking for, I recommend reading Mark Allison’s tutorials on the topic.

With that said, let’s get started.

The framework

Android’s Transitions framework is essentially a mechanism for animating layout changes e.g. adding, removing, moving, resizing, showing, or hiding views.

The framework is built around three core entities: scene root, scenes, and transitions. A scene root is an ordinary ViewGroup that defines the piece of the UI on which the transitions will run. A scene is a thin wrapper around a ViewGroup representing a specific layout state of the scene root.

Finally, and most importantly, a transition is the component responsible for capturing layout differences and generating animators to switch UI states. The execution of any transition always follows these steps:

  1. Capture start state
  2. Perform layout changes
  3. Capture end state
  4. Run animators

The process as a whole is managed by the TransitionManager but most of the steps above (except for step 2) are performed by the transition. Step 2 might be either a scene switch or an arbitrary layout change.

How it works

Let’s take the simplest possible way of triggering a transition and go through what happens under the hood. So, here’s a little code sample:

TransitionManager.beginDelayedTransition(sceneRoot);

View child = sceneRoot.findViewById(R.id.child);
LayoutParams params = child.getLayoutParams();
params.width = 150;
params.height = 25;
child.setLayoutParams(params);

This code triggers an AutoTransition on the given scene root animating child to its new size.

The first thing the TransitionManager will do in beingDelayedTransition() is checking if there is a pending delayed transition on the same scene root and just bail if there is onecode. This means only the first beingDelayedTransition() call within the same rendering frame will take effect.

Next, it will reuse a static AutoTransition instancecode. You could also provide your own transition using a variant of the same method. In any case, it will always clonecode the given transition instance to ensure a fresh startcode—consequently allowing you to safely reuse Transition instances across beingDelayedTransition() calls.

It then moves on to capturing the start statecode. If you set target view IDs on your transition, it will only capture values for thosecode. Otherwise it will capture the start state recursively for all views under the scene rootcode. So, please, set target views on all your transitions, especially if your scene root is a deep container with tons of children.

An interesting detail here: the state capturing code in Transition has especial treatment for ListViews using adapters with stable IDscode. It will mark the ListView children as having transient state to avoid them to be recycled during the transition. This means you can very easily perform transitions when adding or removing items to/from a ListView. Just call beginDelayedTransition() before updating your adapter and an AutoTransition will do the magic for you—see this gist for a quick sample.

The state of each view taking part in a transition is stored in TransitionValues instances which are essentially a Map with an associated Viewcode. This is one part of the API that feels a bit hand wavy. Maybe TransitionValues should have been better encapsulated?

Transition subclasses fill the TransitionValues instances with the bits of the View state that they’re interested in. The ChangeBounds transition, for example, will capture the view bounds (left, top, right, bottom) and location on screencode.

Once the start state is fully captured, beginDelayedTransition() will exit whatever previous scene was set in the scene rootcode, set current scene to nullcode (as this is not a scene switch), and finally wait for the next rendering framecode.

TransitionManager waits for the next rendering frame by adding an OnPreDrawListenercode which is invoked once all views have been properly measured and laid out, and are ready to be drawn on screen (Step 2). In other words, when the OnPreDrawListener is triggered, all the views involved in the transition have their target sizes and positions in the layout. This means it’s time to capture the end state (Step 3) for all of themcode—following the same logic than the start state capturing described before.

With both the start and end states for all views, the transition now has enough data to animate the views aroundcode. It will first update or cancel any running transitions for the same viewscode and then create new animators with the new TransitionValuescode (Step 4).

The transitions will use the start state for each view to “reset” the UI to its original state before animating them to their end state. This is only possible because this code runs just before the next rendering frame is drawn i.e. inside an OnPreDrawListener.

Finally, the animators are startedcode in the defined order (together or sequentially) and magic happens on screen.

Switching scenes

The code path for switching scenes is very similar to beginDelayedTransition()—the main difference being in how the layout changes take place.

Calling go() or transitionTo() only differ in how they get their transition instance. The former will just use an AutoTransition and the latter will get the transition defined by the TransitionManager e.g. toScene and fromScene attributes.

Maybe the most relevant of aspect of scene transitions is that they effectively replace the contents of the scene root. When a new scene is entered, it will remove all views from the scene root and then add itself to itcode.

So make sure you update any class members (in your Activity, Fragment, custom View, etc.) holding view references when you switch to a new scene. You’ll also have to re-establish any dynamic state held by the previous scene. For example, if you loaded an image from the cloud into an ImageView in the previous scene, you’ll have to transfer this state to the new scene somehow.

Some extras

Here are some curious details in how certain transitions are implemented that are worth mentioning.

The ChangeBounds transition is interesting in that it animates, as the name implies, the view bounds. But how does it do this without triggering layouts between rendering frames? It animates the view frame (left, top, right, and bottom) which triggers size changes as you’d expect. But the view frame is reset on every layout() call which could make the transition unreliable. ChangeBounds avoids this problem by suppressing layout changes on the scene root while the transition is runningcode.

The Fade transition fades views in or out according to their presence and visibility between layout or scene changes. Among other things, it fades out the views that have been removed from the scene root, which is an interesting detail because those views will not be in the tree anymore on the next rendering frame. Fade temporarily adds the removed views to the scene root‘s overlay to animate them out during the transitioncode.

Wrapping up

The overall architecture of the Transitions framework is fairly simple—most of the complexity is in the Transition subclasses to handle all types of layout changes and edge cases.

The trick of capturing start and end states before and after an OnPreDrawListener can be easily applied outside the Transitions framework—within the limitations of not having access to certain private APIs such as ViewGroup‘s supressLayout()code.

As a quick exercise, I sketched a LinearLayout that animates layout changes using the same technique—it’s just a quick hack, don’t use it in production! The same idea could be applied to implement transitions in a backwards compatible way for pre-KitKat devices, among other things.

That’s all for now. I hope you enjoyed it!

Syndicated 2014-03-13 14:15:26 from Lucas Rocha

Reconnecting

I’m just back from a well deserved 2-week vacation in Salvador—the city where I was born and raised. While in Brazil, I managed to stay (mostly) offline. I intentionally left all my gadgetry at home. No laptop, no tablet. Only an old-ish smartphone with no data connection.

Offline breaks are very refreshing. They give you perspective. I feel we, from tech circles, are always too distracted, too busy, too close to these things.

Stepping back is important. Idle time is as important as productive time. The world already has enough bullshit. Let’s not make it worse.

Here’s for a more thoughtful, mindful, and meaningful online experience for all of us. Happy new year!

Syndicated 2014-01-01 22:09:43 from Lucas Rocha

Firefox for Android in 2013

Since our big rewrite last year, we’ve released new features of all sizes and shapes in Firefox for Android—as well as tons of bug fixes, of course. The feedback has been amazingly positive.

This was a year of consolidation for us, and I think we’ve succeeded in getting Firefox for Android in a much better place in the mobile browser space. We’ve gone from an (embarrassing) 3.5 average rating on Google Play to a solid 4.4 in just over a year (!). And we’re wrapping up 2013 as a pre-installed browser in a few devices—hopefully the first of many!

We’ve just released Firefox for Android 26 today, our last release this year. This is my favourite release by a mile. Besides bringing a much better UX, the new Home screen lays the ground for some of the most exciting stuff we’ll be releasing next year.

A lot of what we do in Firefox for Android is so incremental that it’s sometimes hard to see how all the releases add up. If you haven’t tried Firefox for Android yet, here is my personal list of things that I believe sets it apart from the crowd.

All your stuff, one tap

The new Home in Firefox for Android 26 gives you instant access to all your data (history, bookmarks, reading list, top sites) through a fluid set of swipable pages. They are easily accessible at any time—when the app starts, when you create a new tab, or when you tap on the location bar.

You can always search your browsing data by tapping on the location bar. As an extra help, we also show search suggestions from your default search engine as well as auto-completing domains you’ve visited before. You’ll usually find what you’re looking for by just typing a couple of letters.

Top Sites, History, and Search.

Top Sites, History, and Search.

Great for reading

Firefox for Android does a couple of special things for readers. Every time you access a page with long-form content—such as a news article or an essay—we offer you an option to switch to Reader Mode.

Reader Mode removes all the visual clutter from the original page and presents the content in a distraction-free UI—where you can set your own text size and color scheme for comfortable reading. This is especially useful on mobile browsers as there are still many websites that don’t provide a mobile-friendly layout.

Reader Mode in Firefox for Android

Reader Mode in Firefox for Android

Secondly, we bundle nice default fonts for web content. This makes a subtle yet noticeable difference on a lot of websites.

Last but not least, we make it very easy to save content to read later—either by adding pages to Firefox’s reading list or by using our quickshare feature to save it to your favourite app, such as Pocket or Evernote.

Make it yours

Add-ons are big in desktop Firefox. And we want Firefox for Android to be no different. We provide several JavaScript APIs that allow developers to extend the browser with new features. As a user, you can benefit from add-ons like Adblock Plus and Lastpass.

If you’re into blingy UIs, you can install some lightweight themes. Furthermore, you can install and use any web search engine of your choice.

Lightweight theme, Add-ons, and Search Engines

Lightweight theme, Add-ons, and Search Engines

Smooth panning and zooming

An all-new panning and zooming framework was built as part of the big native rewrite last year. The main focus areas were performance and reliability. The (mobile) graphics team has released major improvements since then and some of this framework is going to be shared across most (if not all) platforms soon.

From a user perspective, this means you get consistently smooth panning and zooming in Firefox for Android.

Fast-paced development

We develop Firefox for Android through a series of fast-paced 6-week development cycles. In each cycle, we try to keep a balance between general housekeeping (bug fixes and polishing) and new features. This means you get a better browser every 6 weeks.

Open and transparent

Firefox for Android is the only truly open-source mobile browser. There, I said it. We’re a community of paid staff and volunteers. We’re always mentoring new contributors. Our roadmap is public. Everything we’re working on is being proposed, reviewed, and discussed in Bugzilla and our mailing list. Let us know if you’d like to get involved by the way :-)


That’s it. I hope this post got you curious enough to try Firefox for Android today. Do we still have work to do? Hell yeah. While 2013 was a year of consolidation, I expect 2014 to be the year of excitement and expansion for Firefox on Android. This means we’ll have to set an even higher bar in terms of quality and, at the same time, make sure we’re always working on features our users actually care about.

2014 will be awesome. Can’t wait! In the meantime, install Firefox for Android and let us know what you think!

Syndicated 2013-12-10 17:15:43 from Lucas Rocha

281 older entries...

 

lucasr certified others as follows:

  • lucasr certified lucasr as Journeyer
  • lucasr certified terceiro as Journeyer
  • lucasr certified gman as Master
  • lucasr certified andersca as Master
  • lucasr certified jamesh as Master
  • lucasr certified jdahlin as Master
  • lucasr certified kiko as Journeyer
  • lucasr certified Uraeus as Master
  • lucasr certified kov as Journeyer
  • lucasr certified thomasvs as Master
  • lucasr certified jdub as Master
  • lucasr certified rbultje as Master
  • lucasr certified company as Master
  • lucasr certified hub as Master
  • lucasr certified DV as Master
  • lucasr certified behdad as Master
  • lucasr certified pbor as Journeyer
  • lucasr certified ebassi as Journeyer
  • lucasr certified gicmo as Master
  • lucasr certified seb128 as Master
  • lucasr certified murrayc as Master
  • lucasr certified garnacho as Journeyer
  • lucasr certified miguel as Master
  • lucasr certified federico as Master
  • lucasr certified gpoo as Journeyer
  • lucasr certified menthos as Master
  • lucasr certified roozbeh as Master
  • lucasr certified Jody as Master
  • lucasr certified hp as Master
  • lucasr certified aurium as Journeyer
  • lucasr certified calum as Master
  • lucasr certified clarkbw as Journeyer
  • lucasr certified kristian as Journeyer
  • lucasr certified valessio as Apprentice
  • lucasr certified tigert as Master
  • lucasr certified garrett as Master
  • lucasr certified campd as Master
  • lucasr certified jimmac as Master
  • lucasr certified dobey as Master
  • lucasr certified dsandras as Master
  • lucasr certified Iain as Master
  • lucasr certified hadess as Master
  • lucasr certified chipx86 as Journeyer
  • lucasr certified wingo as Master
  • lucasr certified cinamod as Master
  • lucasr certified pvanhoof as Journeyer
  • lucasr certified Jimbob as Journeyer
  • lucasr certified timg as Journeyer
  • lucasr certified mjs as Master
  • lucasr certified timj as Master
  • lucasr certified rms as Master
  • lucasr certified jpr as Master
  • lucasr certified rodrigo as Master
  • lucasr certified mathieu as Master
  • lucasr certified kmaraas as Master
  • lucasr certified lewing as Master
  • lucasr certified notzed as Master
  • lucasr certified jrb as Master
  • lucasr certified vicious as Master
  • lucasr certified anderson as Journeyer
  • lucasr certified jvic as Apprentice
  • lucasr certified csv as Journeyer
  • lucasr certified RossBurton as Master
  • lucasr certified AlanHorkan as Journeyer
  • lucasr certified faw as Apprentice
  • lucasr certified zwnj as Journeyer
  • lucasr certified bilboed as Journeyer
  • lucasr certified orph as Master
  • lucasr certified caio1982 as Apprentice
  • lucasr certified sdodji as Master
  • lucasr certified paolo as Master
  • lucasr certified blizzard as Master
  • lucasr certified bratsche as Journeyer
  • lucasr certified snorp as Journeyer
  • lucasr certified sopwith as Master
  • lucasr certified otavio as Master

Others have certified lucasr as follows:

  • lucasr certified lucasr as Journeyer
  • terceiro certified lucasr as Apprentice
  • Lobster certified lucasr as Journeyer
  • tiagovaz certified lucasr as Apprentice
  • nikole certified lucasr as Journeyer
  • jvic certified lucasr as Journeyer
  • valessio certified lucasr as Master
  • aurium certified lucasr as Master
  • pbor certified lucasr as Journeyer
  • gicmo certified lucasr as Master
  • cinamod certified lucasr as Master
  • faw certified lucasr as Journeyer
  • zwnj certified lucasr as Journeyer
  • gpoo certified lucasr as Journeyer
  • company certified lucasr as Master
  • eopadoan certified lucasr as Master
  • csv certified lucasr as Master
  • phplev certified lucasr as Master

[ Certification disabled because you're not logged in. ]

New Advogato Features

New HTML Parser: The long-awaited libxml2 based HTML parser code is live. It needs further work but already handles most markup better than the original parser.

Keep up with the latest Advogato features by reading the Advogato status blog.

If you're a C programmer with some spare time, take a look at the mod_virgule project page and help us with one of the tasks on the ToDo list!

X
Share this page