Friday, 26 September 2014

Android Studio doesn't preview custom View

The Android Studio Layout Preview can be a powerful tool, but it still has his problems. Everything works perfectly fine while you stick to the default Android widgets, but with a custom widget you will likely incur in some kind of problem. In fact, sometimes your View is working perfectly at runtime, but it won't show in the preview.
You will get a stack trace like this one:

android.content.res.Resources$NotFoundException: Could not find attr resource matching value 0x7FFF0004 (resolved name: bar_thickness) in current configuration.

This could be caused by many factors, here are the two of them that I've found more annoying:
  • Using an OvalShape in your custom View
  • Using a resource in your View constructor
While the first one is currently an open bug, and not much can be done about it, the second one can be addressed with a workaround.

The Android View object provides a method to check if the View is being rendered in a preview tool, like the Android Studio one or like the eclipse plugin: isInEditMode(). This simply returns a boolean that states if the View is in a preview tool or not.

If you're using one or more resources in your View constructor you should replace them with fixed values when you're in edit mode.

For example this:

params.setMargins(getResources()
                    .getDimensionPixelSize(R.dimen.floating_label_left_margin), 0, 0, 0);


should become this:

 if (isInEditMode()) {
      params.setMargins(5, 0, 0, 0);

} else {
      params.setMargins(getResources()
              .getDimensionPixelSize(R.dimen.floating_label_left_margin), 0, 0, 0);

}


Tuesday, 23 September 2014

Android Menu showAsAction Ignored

While working on a menu definition in Android you have the option to specify if the menu items should be visible as icons or should be hidden in the overflow menu (the three dots at the end of the action bar). This can be achieved with the showAsAction attribute in the menu XML definition. As a default the menu items are hidden in the overflow menu.

Today this attribute was simply ignored by my Android app. This is what my menu item definition looked like:

<item
        android:title="Search"
        android:id="@+id/menu_search"
        android:icon="@drawable/ic_action_content_copy"
        custom:showAsAction="always|collapseActionView"
        android:actionViewClass="android.support.v7.widget.SearchView"/>


But the item was not shown as an icon on the action bar. As you can see I'm using a custom namespace for the showAsAction attribute. The namespace is defined as:

xmlns:custom="http://schemas.android.com/apk/res-auto"

This is required if you are working with the compat library. What I didn't know, that was causing the issue, is that when you use the showAsAction with the custom namespace your Activity must extend the support ActionBarActivity, otherwise the attribute will just be ignored.

Android Google+ Sign In Problem

Last week I was working to implement Google+ authentication in an Android app. The process is well documented on Android Developers and Google provides also a sample app on GitHub.

As stated in the quickstart, before start coding you should create a new project into the Google Developer Console, in order to register your app on the Google APIs. You have to provide your Android certificate SHA1 fingerprint and your app package name, then everything should work.

Clearly, it didn't.

While trying to login with the sample app I got back on the log an error saying:

ConnectionResult.getErrorCode() = 4

According to the documentation for ConnectionResult code 4 stands for: SIGN_IN_REQUIRED, but no other explanation is given. After many hours of searches and tests, I discovered that I was simply missing the definition of the sign-in screen in the Google Developer Console.



In the lateral menu of the Developer Console, select APIs & auth and the Consent screen fill in the required fields, save, give it a couple of minutes and you should be OK.


Hope it helps someone.

Saturday, 6 September 2014

Chrome Blurry on Windows 8

Today I updated Google Chrome to version 38 at 64 bit on my Windows 8 machine. Everything became bigger and blurry. I'm using the desktop version of the browser, not the Windows 8 mode.

If you get this issue try this:

  • Right click the Google Chrome link you're using
  • Select "Properties"
  • Go to the tab "Shortcut"
  • At the end of the field "Target" add this line: " /high-dpi-support=1 /force-device-scale-factor=1"
You should get something like this:


Now restart Chrome and the problem should be solved.

Friday, 28 March 2014

Customize AlertDialog buttons inside DialogFragment

When working with alert dialogs I was used to customize the positive and negative buttons appearance by retrieving them directly from the dialog after calling the show() method. (If you try to retrieve the buttons before the dialog is shown they will be null)
Just like this:

// Create the dialog
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setPositiveButton(R.string.btn_confirm_all, null);
builder.setNegativeButton(R.string.btn_cancel, null);
builder.setCancelable(false);
AlertDialog dialog = builder.create();
dialog.show();
// Retrieve the button
Button button = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
// Customize the button
button.setTextColor(Color.WHITE);
button.setTypeface(Typeface.DEFAULT_BOLD);

Well, lately I started to use DialogFragment to embedd dialog lifecycle, the problem is that I didn't have direct access to the dialog anymore, I was just overriding the onCreateDialog() method:

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    // Create the dialog
    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    builder.setPositiveButton(R.string.btn_confirm_all, null);
    builder.setNegativeButton(R.string.btn_cancel, null);
    builder.setCancelable(false);
    AlertDialog dialog = builder.create();
    return dialog;
}

The solution I found to customize the dialog button after show() was to store the just created dialog object in a global variable and then to retrieve it in the onResume() method of the DialogFragment:

@Override
public void onResume() {
     super.onResume();
     // Retrieve the button
     Button button = this.dialog.getButton(DialogInterface.BUTTON_POSITIVE);
     // Customize the button
     button.setTextColor(Color.WHITE);
     button.setTypeface(Typeface.DEFAULT_BOLD);
}

Hope it helps somebody!

Tuesday, 14 January 2014

Programmatically enable USB tethering on Android

Android USB tethering lets you share your phone Internet connection to your PC via an USB cable. The big advantage over the Wi-Fi tether option is in the lower battery consumption.

User can manually enable USB tethering from the phone settings, but what if your application wants to help the user enable it or force it to start?

Show the user tethering settings page

From within your application you can call activities defined by other apps, even the system ones, and the settings manager is not an exception. So, if you want the user to enable USB tethering on your device you can redirect him to the right settings page. This can be done simply by using an Intent:

 Intent intent = new Intent();  
 intent.setClassName("com.android.settings", "com.android.settings.TetherSettings");  
 startActivity(intent);  

On some phones, the ones without a mobile data capability I suppose, this settings section is "hidden" and not reachable by the user. Using this Intent will show it but the "Enable USB tethering" option could behave in strange ways.

Programmatically enable USB tethering

On ROOTED phones there's also a way to enable programmatically the USB tethering option (NOTE: This method seems to work on Android < 4.0). I haven't found a way to do it on a non rooted phone.

First of all add the following permissions to your AndroidManifest.xml:

 <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />  
 <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />   

Then using Java Reflection we can call the method to enable USB tethering:

 public void switchOnTethering() {  
   Object systemService = getSystemService(Context.CONNECTIVITY_SERVICE);  
   for (Method method : systemService.getClass().getDeclaredMethods()) {  
     if (method.getName().equals("tether")) {  
       try {  
         method.invoke(systemService, "usb0");  
       } catch (IllegalArgumentException e) {  
         e.printStackTrace();  
       } catch (IllegalAccessException e) {  
         e.printStackTrace();  
       } catch (InvocationTargetException e) {  
         e.printStackTrace();  
       }  
     }  
   }  
 }