Abrir la navegación en la app de Google Maps con un Intent

La documentación se puede consultar en la página Google Maps Intents.

public static void navigateExternalTo(Context context, double latitude, double longitude) {
    String uri = String.format(Locale.ENGLISH, "google.navigation:q=%1$f,%2$f", latitude, longitude);
    Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(uri));
    intent.setPackage("com.google.android.apps.maps");
    context.startActivity(intent);
}

Forzar mayúsculas con NavigationView

En el fichero de estilos, definir un nuevo estilo:

<style name="navigation_view_text_style">
    <item name="android:textAllCaps">true</item>
</style>

A la hora de aplicarlo, tenemos dos opciones. Si queremos que aplique a todos los TextView incluyendo los de la vista “header”:

<android.support.design.widget.NavigationView
    ...
    app:theme="@style/navigation_view_text_style" />

Si sólo queremos que aplique a los items del listado:

<android.support.design.widget.NavigationView
    ...
    app:itemTextAppearance="@style/navigation_view_text_style" />

Saludos

Tamaños de texto Material Design

    <dimen name="abc_text_size_body_1_material">14sp</dimen>
    <dimen name="abc_text_size_body_2_material">14sp</dimen>
    <dimen name="abc_text_size_button_material">14sp</dimen>
    <dimen name="abc_text_size_caption_material">12sp</dimen>
    <dimen name="abc_text_size_display_1_material">34sp</dimen>
    <dimen name="abc_text_size_display_2_material">45sp</dimen>
    <dimen name="abc_text_size_display_3_material">56sp</dimen>
    <dimen name="abc_text_size_display_4_material">112sp</dimen>
    <dimen name="abc_text_size_headline_material">24sp</dimen>
    <dimen name="abc_text_size_large_material">22sp</dimen>
    <dimen name="abc_text_size_medium_material">18sp</dimen>
    <dimen name="abc_text_size_menu_material">16sp</dimen>
    <dimen name="abc_text_size_small_material">14sp</dimen>
    <dimen name="abc_text_size_subhead_material">16sp</dimen>
    <dimen name="abc_text_size_subtitle_material_toolbar">16dp</dimen>
    <dimen name="abc_text_size_title_material">20sp</dimen>
    <dimen name="abc_text_size_title_material_toolbar">20dp</dimen>

Añadir iconos de Material Design directamente desde Android Studio

Existe un asistente en Android Studio, desde el cual podemos añadir una imagen vectorial (Vector Drawable) a nuestro proyecto. Para mostrarlo, en la carpeta “res” de nuestro proyecto, haremos click con el botón derecho y seleccionaremos “New > Vector Asset”.

Screen Shot 2016-05-30 at 13.09.48

Este asistente permite, además de importar un svg a nuestro proyecto, utilizar los iconos de Material Design.

Screen Shot 2016-05-30 at 13.13.08

De esta manera no es necesario descargarlos de la web cada vez (que es lo que venía haciendo).

Saludos

Forzar que una petición HTTP se envíe por la WiFi seleccionada

En uno de los proyectos en los que trabajo, nos conectamos a un pequeño dispositivo para leer / escribir datos en el.

La primera vez que se arrranca el dispositivo, este lo hace en un modo “Access Point” que crea una WiFi virtual, desde la que nos podemos conectar al dispositivo directamente para configurarle la red WiFi a la que se tiene que conectar. Este punto de acceso, evidentemente no da salida a Internet.

En Android Marshmallow, cuando el dispositivo está en punto de acceso y, al no ofrecer una conexión que el sistema Android considere de “calidad”, este decide enviar las peticiones siempre por la red 4g/3g. Haciendo imposible la configuración del dispositivo desde esta versión del sistema.

El siguiente código inicializa OkHttpClient para que siempre envie la petición por la primera wifi que tiene configurada (la que tienes seleccionada) en lugar de la que ofrezca más calidad.

Pongo aquí el código por si a alguien le sirviese de utilidad. Básicamente consiste en sobreescribir la clase OkHttpClient añadiéndole un interceptor.

public class DeviceHttpClient extends OkHttpClient {

    private static final String TAG = "DeviceHttpClient";

    public DeviceHttpClient(final Context context) {

        // Configure Timeouts
        setConnectTimeout(Constants.CONNECT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);

        try {
            // Configure Retry policy and Prioritize Wifi
            interceptors().add(
            new Interceptor() {
                   @Override
                   public Response intercept(Chain chain) throws IOException {
                       try {
                           boundRequestToWifi(context);
                           Request request = chain.request();
                           return chain.proceed(request);
                       } finally {
                           unBoundRequestToWifi(context);
                       }
                   }
               }
            );
        } catch (Exception e) {
            Log.e(TAG, "Error while initializing http client", e);
        }
    }

    @SuppressWarnings("deprecation")
    private void boundRequestToWifi(Context context) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);

            Network[] networks = connectivityManager.getAllNetworks();
            if (networks != null && networks.length > 0) {
                for (Network network : networks) {
                    NetworkInfo networkInfo = connectivityManager.getNetworkInfo(network);

                    if (networkInfo != null && networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                            connectivityManager.bindProcessToNetwork(network);
                        }
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP &&
                            Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
                            ConnectivityManager.setProcessDefaultNetwork(network);
                        }
                        break;
                    }
                }
            }
        }
    }

    @SuppressWarnings("deprecation")
    private void unBoundRequestToWifi(Context context) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                connectivityManager.bindProcessToNetwork(null);
            }
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP &&
                Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
                ConnectivityManager.setProcessDefaultNetwork(null);
            }
        }
    }
}

Evitar warning Nullable en findViewById (Android 23)

Si utilizas appcompat 23.3.0 han añadido la anotación @Nullable al método findViewById en AppCompatActivity y también en el framework base. Lo cual es muy molesto, ya que provoca que el código de las actividades aparezca lleno de warnings.

Mientras llega la solución utilizo esta “chapuzilla”:

public class BaseActivity extends AppCompatActivity {
    @NonNull
    @Override
    public View findViewById(@IdRes int id) {
        //noinspection ConstantConditions
        return super.findViewById(id);
    }
}