The #TuentiChallenge5 registration is open!

Publicado el 09/3/2015 por Daniel Pañeda and Alfredo Beaumont, Software Engineers

No more waiting. The fifth edition of the annual programming contest is finally here, Tuenti Challenge. Today we are opening the registration for participants to begin warming up by solving the problems and overcoming the challenges from previous editions.

The contest, like every year, will consist of 2 phases: The first phase, which will last from the 27th of April to the 4th of May, is on-line and the participants will have to solve the 20 problems posed by Tuenti's engineering team. The 50 finalists from this phase will be those that solve the greatest number of posed challenges in the shortest amount of time. The responses from the 50 initial winners will be manually reviewed by Tuenti's engineering team, who will take into account variables such as the chosen algorithm or the clarity and quality of the implemented code.

The second phase which will take place on the 28th and 29th of May at Tuenti's offices in Madrid, the 10 best participants from the first phase will have the opportunity of spending a day working directly with the team and participate in training sessions at the office. Also, they will all receive a prize and some of them will be offered a job.

Therefore, Tuenti Challengers, lets start writing code. Registration will begin today at 13.37 hours (GMT+1) and will remain open until the same hour on the 4th of May, at which moment the first phase of the contest will have been completed. All the information is available on the Tuenti Challenge 5 Website, contest.tuenti.net, as well as the legal rules of the contest.

Creating Apple Watch Apps: App, Glance and Notifications

Publicado el 05/3/2015 por Eduardo González, Mobile Apps Lead

Last Saturday we held another theEvnt workshop at Tuenti Offices. Víctor Corugedo, César Estébanez and Eduardo González (Tuenti iOS Team members) gave a complete workshop about WatchKit SDK and how to develop Apple Watch Apps.

Starting with a brief review about the Apple Watch and the internal device specifications, they continued presenting the Apple Watch development kit, the UI components already available to create apps and the internal framework architecture.

During the second part of the event, they configured a WatchKit project from scratch, dealing with common problems a developer will face once he decides to create a Watch app. Finally, they presented a toy project they were working on and explained how to create an Apple Watch App, a Glance and how to create custom interface notifications supported by the Watch.

If you could not attend for any reason, you can always check the slides and the project they used on the workshop.

We never stop and we are already preparing the next workshop. As usual, you can check all the updates in the workshops web, and follow theEvnt or the Tuenti Engineering accounts on Twitter.

#HMU25 sees integration and improvements to the Tuenti app

Publicado el 27/2/2015 por Engineering Outreach Committee

The 25th edition of Hack Me Up has just come to an end and we couldn’t be prouder of our team! After 24 hours of non-stop programming, this edition of Tuenti’s internal programming contest came to an end with two winning projects, one in the Product category, and another in the Geek category.

Yesterday (Thursday) at 17:00 saw the start of the session, which included movies, popcorn, dinner, table football, video games and music, along with code, lots of code!

As with each HMU, the winning projects were chosen by voting amongst all Tuenti employees. The winning project in the Product category was Tuenti Car, by Alexis S., Antonio A. y Víctor P., a project that presents a new user interface and UX to be able to use Tuenti in a car. 

David P. and David S. lifted the winner’s trophy in the Geek category with DeMemory, a project where memory matters.



Congratulations to both teams! As of today, they’ll join our Hall of Fame in the Tuenti office ;)

Tuenti for Android TV

Publicado el 04/2/2015 por Pedro Vicente Gómez Sánchez, Senior Software Engineer

Less than a year ago, in 2014, Google announced the Google IO event, a really important milestone for the Android Developers Community. Android TV was introduced to the world, a new adaptation of the Android OS based on Android Lollipop for televisions which some manufacturers like Samsung or LG are already using as the OS for their televisions, and which Google is already using as the OS inside its new Nexus Player.


The Tuenti team tries to be always updated and continues learning about new frameworks or technologies. This is the reason why we are going to review some of the most important features of Android TV and provide you with some examples about how to create an awesome application using Android TV features. To provide the best examples we can, we have published a public repository on Github you can fork and use to review some of the concepts explained in this blog post. Every Github star is welcome. In this repository you are going to find all the code needed to create a basic Android TV application. Take into account that all the information shown in this repository is mocked, we just wanted to show how to work over the Android TV UI layer and not how to implement the full application. Here you have a video with the final result:

The first approach to this project was not related to the code, but to the user experience and the user interface. To help me with this, Luis Javier Á., Emanuela M., Juan Manuel J. and Carmen L. came to rescue me and designed the product you can see in the previous video. The key of this development was the “focus” concept. The whole user experience and the user interface are related to this concept because the Android TV devices don’t support touchscreens. Trying to avoid soft keyboard usage and how to use the television background to provide more context to the user without distracting them were also two interesting challenges.


You can review how these ideas are actually present during the login process, where the account used to log in is shown applying a scale up effect when the view gets the focus. The “enter password” user interface has been implemented to use the Nexus Player remote instead of the operative system soft keyboard, in order to improve how the user interacts with the application and to avoid the usage of a small soft keyboard.


Regarding the application development, in this blog post we will review some of the most important details for the Android TV applications development:

  • Configure your project.
  • Create a browse view.
  • Create a detail view.
  • Create a search view.
  • Without Leanback library.

Configure your project:

To be able to create an application for Android TV you’ll have to provide one important dependency, Leanback Android TV library, which depends on V7 App Compat library and V7 RecyclerView library. These dependencies have to be added to your build.gradle file or your pom.xml, depending on the build tool used in your project. Here you have a sample to show how to add these dependencies to a project using Gradle:

dependencies{
    compile 'com.android.support:leanback-v17:21.0.3'
    compile 'com.android.support:appcompat-v7:21.0.3'
    compile 'com.android.support:recyclerview-v7:21.0.3'
}

Once you have your dependencies added to the project, you’ll have to configure one of your activities as the Activity used by the operative system when your application starts, that is to say your launch activity. Using this intent filter you application will be ready to be launched. Here you have a sample that shows how to configure an activity to make it work as the launcher activity for an Android TV project:


 
      
        

        
      
    

Create a browse view:

Browse views are one of the most important views you can use to show content in your application. BrowseFragment is a fragment for creating Leanback browse screens. It is composed of a RowsFragment and a HeadersFragment. A BrowseFragment renders the elements of its object adapter as a set of rows in a vertical list. The elements in this adapter must be subclasses of Row.


To use this Fragment inside your application you can create your own Fragment extending from Leanback BrowseFragment or add an instance of BrowseFragment to your Activity layout and get a reference using Android Fragment Manager. We have created our own Fragment, extending from Leanback BrowseFragment and configuring it to show information related to contacts inside the user interface. To do this, we have used some important methods like: setBrandColor, setSearchAffordanceColor or setBadgeDrawable.

  @Override public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    setBrandColor(getResources().getColor(R.color.primary_color));
    setSearchAffordanceColor(getResources().getColor(R.color.primary_color));
    setBadgeDrawable(getResources().getDrawable(R.drawable.icn_wink));
  }

To show information inside the BrowseFragment you’ll have to use an ArrayObjectAdapter. This Android class is thought to work like a bidimensional matrix where every row is another ArrayObjectAdapter and HeaderItem objects are used as the title for every row. The information shown is rendered using a Presenter instance. In this project you can see different Presenter implementations like ImagePresenter -created to show a simple image without any other visual effect- or IconPresenter -created to show preferences inside MainFragment-. ArrayObjectAdapter class is provided by Leanback Android library.

   @Override public void showMainInformation(List favorites, List conversations, List contacts, List mediaElements, List preferences) {
    ArrayObjectAdapter rowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
    CardPresenter bigCardPresenter = new CardPresenter(CARD_WIDTH_IN_DP, CARD_HEIGHT_IN_DP);
    CardPresenter smallCarPresenter = new CardPresenter();

    addCardInfoElementsToRowsAdapter(R.string.favorites_item_title, favorites, rowsAdapter,
        smallCarPresenter, FAVORITES_ROW);
    addCardInfoElementsToRowsAdapter(R.string.recent_conversation_item_title, conversations,
        rowsAdapter, bigCardPresenter, CONVERSATIONS_ROW);
    addCardInfoElementsToRowsAdapter(R.string.contacts_item_title, contacts, rowsAdapter,
        smallCarPresenter, CONTACTS_ROW);
    addImageInfoElementsToRowAdapter(R.string.media_elements_item_title, mediaElements, rowsAdapter,
        new ImagePresenter(), MEDIA_ROW);
    addIconInfoElementsToRowAdapter(getResources().getString(R.string.preferences), preferences,
        rowsAdapter, new IconPresenter(), PREFERENCES_ROW);

    setAdapter(rowsAdapter);
  }
   private void addIconInfoElementsToRowAdapter(String title, List preferences,ArrayObjectAdapter rowsAdapter, Presenter presenter, int id) {
    ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(presenter);
    for (IconInfo iconInfo : preferences) {
      listRowAdapter.add(iconInfo);
    }
    rowsAdapter.add(new ListRow(new HeaderItem(id, title, ""), listRowAdapter));
  }

Configuring events listeners is also important to be able to react to user movements. For this sample we have configured onItemViewClickedListener and onItemViewSelectedListener.

 private void configureListeners() {

    setOnItemViewSelectedListener(new OnItemViewSelectedListener() {
      @Override public void onItemSelected(Presenter.ViewHolder viewHolder, Object item, RowPresenter.ViewHolder viewHolder1, Row row) {
        if (row.getId() < MEDIA_ROW) {
          presenter.onCardInfoSelected((CardInfo) item);
        } else if (row.getId() == MEDIA_ROW) {
          presenter.onImageInfoSelected((ImageInfo) item);
        } else if (row.getId() == PREFERENCES_ROW) {
          presenter.onPreferencesSelected();
        }
      }
    });

    setOnItemViewClickedListener(new OnItemViewClickedListener() {
      @Override public void onItemClicked(Presenter.ViewHolder viewHolder, Object item,
          RowPresenter.ViewHolder viewHolder1, Row row) {
        if (row.getId() == PREFERENCES_ROW) {
          int id = ((IconInfo) item).getIconId();
          switch (id) {
            case R.drawable.icn_settings_log_out:
              presenter.logout();
              break;
            default:
          }
        } else if (row.getId() == MEDIA_ROW) {
          presenter.onImageInfoClicked((ImageInfo) item);
        } else if (row.getId() < MEDIA_ROW) {
          presenter.onCardInfoClicked((CardInfo) item);
        }
      }
    });
  }

Create a detail view:

The detailed view has been one of the key elements of the user interface for this project. We need to show detailed information about user’s contacts and some actions related to the content.


To be able to show detailed information we have created a custom Fragment extending from Leanback DetailsFragment with the same strategy we have used to create our BrowseFragment extension. To show information inside this fragment you can also use an ArrayObjectAdapter and different presenters as we have already done with the previous fragment. However, to show the detailed view you have to create a DetailsOverviewRow and a ClassPresenterSelector used to fill the ArrayObjectAdapter used inside the DetailsFragment. You can add more information to the ArrayObjectAdapter used and show a row full of data like the one shown in the previous sample.

private void showDetailInformation(Bitmap bitmap, CardInfo cardInfo){
        DetailsOverviewRow detailRow = configureDetailsOverviewRow(cardInfo, bitmap);
        ClassPresenterSelector presenterSelector = new ClassPresenterSelector();
        DetailsOverviewRowPresenter dorPresenter =
            new DetailsOverviewRowPresenter(new DetailsDescriptionPresenter());
        dorPresenter.setBackgroundColor(getResources().getColor(R.color.primary_color));
        dorPresenter.setStyleLarge(false);
        presenterSelector.addClassPresenter(DetailsOverviewRow.class, dorPresenter);
        presenterSelector.addClassPresenter(ListRow.class, new ListRowPresenter());
        adapter = new ArrayObjectAdapter(presenterSelector);
        adapter.add(detailRow);
        setAdapter(adapter);
  }

To show actions inside the detail view you can add Action objects to your DetailsOverviewRow.

  private DetailsOverviewRow configureDetailsOverviewRow(CardInfo cardInfo, Bitmap bitmap) {
    final DetailsOverviewRow row = new DetailsOverviewRow(cardInfo);
    row.setImageBitmap(getActivity(), bitmap);
    row.addAction(new Action(VD_CALL_ACTION_ID, getString(R.string.vd_call_action_title)));
    row.addAction(new Action(CALL_ACTION_ID, getString(R.string.call_action_title)));
    row.addAction(new Action(CHAT_ACTION_ID, getString(R.string.chat_action_title)));
    row.addAction(new Action(EDIT_ACTION_ID, getString(R.string.edit_action_title)));
    return row;
  }

Another important element to use in your detail views is the background. Remember that creating your custom Picasso Target and using Picasso as the main image management library you can perform background changes easily.

 private void configureBackground() {
    metrics = new DisplayMetrics();
    getActivity().getWindowManager().getDefaultDisplay().getMetrics(metrics);
    BackgroundManager backgroundManager = BackgroundManager.getInstance(getActivity());
    backgroundManager.attach(getActivity().getWindow());
    backgroundTarget = new PicassoBackgroundManagerTarget(backgroundManager);
  }
  
  @Override public void showBackground(String backgroundUrl) {
    Picasso.with(getActivity())
        .load(backgroundUrl)
        .resize(metrics.widthPixels, metrics.heightPixels)
        .error(R.drawable.fragment_default_background)
        .centerCrop()
        .into(backgroundTarget);
  }

Create a search view:

In order to show a user interface that performs searches in your application using a widget based on Leanback library, you’ll have to follow an approach similar to the previous Fragment usage. Create your own Fragment and extend it from Leanback SearchFragment.


To be able to react when the user updates the search edit text you’ll have to provide a SearchResultProvider implementation and implement two methods: onQueryTextChange and onQueryTextSubmit. Implementing this method you’ll be able to perform your custom searches using a String search term.

  @Override public boolean onQueryTextChange(String newQuery) {
    Log.d(TAG, String.format("Search Query Text Change %s", newQuery));
    queryByWords(newQuery);
    return true;
  }

  @Override public boolean onQueryTextSubmit(String query) {
    Log.d(TAG, String.format("Search Query Text Submit %s", query));
    queryByWords(query);
    return true;
  }

As in the previous sample, how we can show information inside this Fragment is based on the usage of one ArrayObjectAdapter object, HeaderItems and Presenter instances.

  @Override public void showAllContacts(List contacts) {
    ArrayObjectAdapter arrayObjectAdapter = new ArrayObjectAdapter(new CardPresenter());
    for (Contact contact : contacts) {
      arrayObjectAdapter.add(contact);
    }
    HeaderItem headerItem = new HeaderItem(getString(R.string.contacts_item_title), "");
    rowsAdapter.add(new ListRow(headerItem, arrayObjectAdapter));
  }

  @Override public void showSearchResultContacts(String query, List contacts) {
    ArrayObjectAdapter arrayObjectAdapter = new ArrayObjectAdapter(new CardPresenter());
    for (Contact contact : contacts) {
      arrayObjectAdapter.add(contact);
    }
    String resultSearchTitle = getString(R.string.search_result, query);
    HeaderItem headerItem = new HeaderItem(resultSearchTitle, "");
    rowsAdapter.add(new ListRow(headerItem, arrayObjectAdapter));
  }

If your user interface has to react to user clicks you’ll have to provide a OnItemViewClickedListener implementation to the DetailsFragment.

  @Override public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setSearchResultProvider(this);
    rowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
    setOnItemViewClickedListener(new OnItemViewClickedListener() {
      @Override public void onItemClicked(Presenter.ViewHolder viewHolder, Object item, RowPresenter.ViewHolder presenterViewHolder, Row row){
        presenter.onContactClicked((Contact) item);
      }
    });
  }

Without Leanback library:

If your user interface has to be built on top of non so common user interface widgets you’ll have to review some important concepts:

  • Focus usage is going to be fundamental, learn how to use nextFocusX attributes in your layout widgets and how to use <requestFocus/> label.

 

    

  


  • State List Animators are going to help you to highlight some of your widgets when a widget is selected.


  
    
      
      
      
      
    
  
...

  • getCurrentFocus()” method will give you the UI widget selected by the user and is going to be really useful if you have to change your UI programmatically. OnFocusChangedListener is also really interesting to provide dynamic UI changes.
  @Override public void hidePreviousPasswordElements() {
    View currentViewWithFocus = getCurrentFocus();
    ViewGroup parent = (ViewGroup) currentViewWithFocus.getParent();
    for (int i = 0; i < parent.getChildCount(); i++) {
      View ib_element_item = parent.getChildAt(i);
      if (currentViewWithFocus.equals(ib_element_item)) {
        ImageButton previousFocusedElement = (ImageButton) parent.getChildAt(i - 1);
        updatePasswordElementWithAsterisk(previousFocusedElement, 0);
      }
    }
  }

    @OnFocusChange(R.id.iv_app_logo) void onFocusChanged(boolean focused) {
    if (!focused) {
      iv_app_logo.setFocusable(false);
    }
  }
  • Displaying recommended content using the Android Notifications API will help you to show important content to your users. It’s as easy as displaying an old Android notification.
    Notification notification = new NotificationCompat.BigPictureStyle(
        new NotificationCompat.Builder(context).setContentTitle(title)
            .setContentText(description)
            .setPriority(priority)
            .setLocalOnly(true)
            .setOngoing(true)
            .setColor(context.getResources().getColor(R.color.primary_color))
            .setCategory(Notification.CATEGORY_RECOMMENDATION)
            .setLargeIcon(image)
            .setSmallIcon(smallIcon)
            .setContentIntent(pendingIntent)
            .setExtras(extras)).build();

    notificationManager.notify(id, notification);

Conclusions:

Create a sample application based on the Tuenti product has been a really interesting challenge, not just from the coding point of view, but an interesting challenge from the UX, UI and Product point of view. User interaction with this new set of devices is really different, and how you have to base the UI state in the application focus is a different way to think in an Android device. The usage of the Activity/Fragment background to provide more context to the user and use scale up/down animations to guide the user navigation through the application was really funny. From the development side, to code for Android TV is really easy if your UI has to use Leanback base components, but if you want to provide a different user experience based on a different user interface you’ll have to do your best and start using state list animators and other new features from API 21. Review also some base components of Android TV Leanback library could be interesting to provide a customized UX but based on the original components. Review RowsFragment and HeadersFragment if needed. Explaining in this blog post how to use the full Leanback library to build applications for Android TV would be impossible, so I recommend you to take a look at the Github repository we have prepared for you and to review the usage of some non familiar elements like ArrayObjectAdapter, HeaderItem objects or other minor details like how to configure the application icon for Android TV.

Developing browser extensions: why and how

Publicado el 03/2/2015 por Joaquín Engelmo, Software Engineer

New year, new theEvnt workshop at Tuenti. It took place in our office last Saturday. This time Otogami’s CTO Rock Star Jerónimo López, a.k.a. @jerolba on Twitter, talked about developing browser extensions.

First, he reviewed browser extensions history and what options we do have when we face this challenge in the existing browsers. After that master class, we got our hands dirty creating a Chrome extension following official documentation and Jero’s tips and tricks. You can check the slides and the code and create your first awesome extension. Finally, Jero showed us Otogami’s extension for Chrome and Firefox, PowerUp, which simplifies the way users find cheaper games in their searches for the latest ones.

As in previous editions, we really appreciate you all attending the 4 hour workshop and your positive energy spending a #SaturdayCoding ;) As for us, we’re already preparing the next workshop related to iOS with our great engineering team. You can check all the updates in the workshops web, and following theEvnt or the Tuenti Engineering accounts on Twitter.

Pages