Showing posts with label quick tips. Show all posts
Showing posts with label quick tips. Show all posts

16 February 2017

:facepalm:

I just spent an hour debugging an issue that should have been a non-issue at all.

A thing about working on a huge product like Domain with such a small team is that there are some bits that tend to get left behind. Lately I have been playing with a bunch of libraries to find out how the modern ones do image pan and zoom. This is important for us because those crazy expensive houses have pretty high-res photos.

Look at this!
Click to embiggen
One of the libraries I was looking at was Subsampling Scale Image View. I have heard good things about it, but was too lazy to figure it out. Today will be the day, I said. Today I will stop being lazy and figure it out.

After about an hour of furious Googling, I ended up finding a bunch of code that I managed to cobble together that theoretically should work. Maybe at this point I should mention that images and graphics are not my strong suit. There's too much stuff going on and my I can't wrap my head around it.

Anyway, I need to get the Bitmap from a file downloaded by Glide. Someone on the internet said this should work:
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inJustDecodeBounds = true;
Bitmap bitmap = BitmapFactory.decodeFile(downloadedFile.getAbsolutePath(), opts);


I fired up my app and nothing is loading. All the pages in the whole of my image gallery are empty. Maybe I'm using the library wrong? Maybe this is not how I should apply BitmapRegionDecoder? Maybe it's not actually downloading the file?

I ended putting breakpoints all over the place:

And it's still not working. It's not crashing.. so at least I got that going for me.

I was getting frustrated, so I was like, "What does BitmapFactory.decodeFile and the options do anyway??" I looked for the JavaDoc.

inJustDecodeBounds

boolean inJustDecodeBounds
If set to true, the decoder will return null (no bitmap), but the out... fields will still be set, allowing the caller to query the bitmap without having to allocate the memory for its pixels.

Ah. FML.

What have we learned today? Read the docs, kids.


20 January 2017

Troubleshooting autoVerify

So you implement app links and you are 300% sure you have implemented everything correctly. The important thing to remember here is that verification is all or nothing. From the docs:
For app link verification to succeed, the system must be able to verify your app with all of the websites that you specify in your app’s intent filters, and that meet the criteria for app links. 
Google has outlined several steps on how to test your implementation and they have provided several samples as well. As with life, however, things can go awry no matter how hard you try.

If automatic link handling does not work, chances are one of the hosts declared in your Manifest failed verification. If you have a bunch (subdomains are considered unique and separate), it can be hard to figure out which one is failing. Thankfully, there is a way for us to see which is causing the failure.

Fire up your terminal, start logcat, install your APK (adb install <path-to-apk>) and keep an eye out for the verifier logs. Here is a sample output:
I SingleHostAsyncVerifier: Verification result: checking for a statement with source a <
I SingleHostAsyncVerifier:   a: "https://domain.com.au"
I SingleHostAsyncVerifier: >
I SingleHostAsyncVerifier: , relation delegate_permission/common.handle_all_urls, and target b <
I SingleHostAsyncVerifier:   a: "com.fairfax.domain"
I SingleHostAsyncVerifier:   b <
I SingleHostAsyncVerifier:     a: "AA:B4:3F:0F:A7:49:F8:90:F3:D6:64:30:FB:5E:69:54:7B:BA:EB:85:7D:9D:04:57:83:5F:FD:58:E7:B9:70:6A"
I SingleHostAsyncVerifier:   >
I SingleHostAsyncVerifier: >
I SingleHostAsyncVerifier:  --> true.
I SingleHostAsyncVerifier: Verification result: checking for a statement with source a <
I SingleHostAsyncVerifier:   a: "https://www.domain.com.au"
I SingleHostAsyncVerifier: >
I SingleHostAsyncVerifier: , relation delegate_permission/common.handle_all_urls, and target b <
I SingleHostAsyncVerifier:   a: "com.fairfax.domain"
I SingleHostAsyncVerifier:   b <
I SingleHostAsyncVerifier:     a: "AA:B4:3F:0F:A7:49:F8:90:F3:D6:64:30:FB:5E:69:54:7B:BA:EB:85:7D:9D:04:57:83:5F:FD:58:E7:B9:70:6A"
I SingleHostAsyncVerifier:   >
I SingleHostAsyncVerifier: >
I SingleHostAsyncVerifier:  --> true.
I SingleHostAsyncVerifier: Verification result: checking for a statement with source a <
I SingleHostAsyncVerifier:   a: "https://m.domain.com.au"
I SingleHostAsyncVerifier: >
I SingleHostAsyncVerifier: , relation delegate_permission/common.handle_all_urls, and target b <
I SingleHostAsyncVerifier:   a: "com.fairfax.domain"
I SingleHostAsyncVerifier:   b <
I SingleHostAsyncVerifier:     a: "AA:B4:3F:0F:A7:49:F8:90:F3:D6:64:30:FB:5E:69:54:7B:BA:EB:85:7D:9D:04:57:83:5F:FD:58:E7:B9:70:6A"
I SingleHostAsyncVerifier:   >
I SingleHostAsyncVerifier: >
I SingleHostAsyncVerifier:  --> false.
I IntentFilterIntentSvc: Verification 6 complete. Success:false. Failed hosts:m.domain.com.au.

In this case one host (https://m.domain.com.au) fails, which means automatic link handling will not work at all. If this happens, hit up your friendly web developers and go through the troubleshooting steps outlined in the developer guide.

Once successful, the last tine in the verification process should say Success:true:
I IntentFilterIntentSvc: Verification 7 complete. Success:true. Failed hosts:.
H/T to Wojtek KaliciƄski for the tip!

15 July 2016

Quick tip: Enabling data binding

Say you have a simple layout file:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  android:layout_width="match_parent"
                  android:layout_height="match_parent"
                  android:orientation="vertical">

        <TextView
            android:id="@+id/title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

        <TextView
            android:id="@+id/description"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

</LinearLayout>
And say you want to finally give data binding a try. So you watch Yigit's I/O talk on data binding and follow the steps on implementation.

You enable it in Gradle:
android {
    dataBinding {
        enabled = true
    }
}

You wrap your existing layout in <layout> tags.
<layout>
      <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  android:layout_width="match_parent"
                  android:layout_height="match_parent"
                  android:orientation="vertical">

        ...

      </LinearLayout>
<layout>

Then life started sucking. Your app won't compile. Well, we can fix it in two easy steps!

Step 1: Add namespace declaration to your root tag:
<layout xmlns:android="http://schemas.android.com/apk/res/android">

This still won't compile! If you look at the logs you will see somewhere there: "Error:(7) Error parsing XML: duplicate attribute"

Step 2: Remove xmlns declaration in the now-non-root layout.

And that's it. Add your variables and happy data binding!


Here's a complete layout file for your perusal:
<layout xmlns:android="http://schemas.android.com/apk/res/android">
      <data>
        <variable
            name="listing"
            type="com.zdominguez.blog.samples.Listing"/>
    </data>

      <LinearLayout 
                  android:layout_width="match_parent"
                  android:layout_height="match_parent"
                  android:orientation="vertical">

        <TextView
            android:id="@+id/title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{listing.address}" />

        <TextView
            android:id="@+id/description"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" 
            android:text="@{listing.agency}" />

      </LinearLayout>
<layout>


14 May 2015

Stringy strings


While we are on the subject of strings, here are more ways of dealing with them in Android Studio. We all know that we should not hardcode strings in code, right? But sometimes, we forget and tend to do code first before defining them in strings.xml.

There are a couple of ways that Android Studio/IntelliJ makes this easy for us. The gif below (which took me a while to figure out how to do, by the way), shows how to deal with:
1. Moving a hardcoded string into strings.xml
2. Giving a previously undefined string ID a value in strings.xml

(Click to embiggen)

Move hardcoded string into XML:
This is probably the more common scenario. You happily put in texts into your TextViews, and now you have to copy and paste them into strings.xml. Don't! There is a shortcut for that.

Put your cursor somewhere in the string, press ALT+Enter to bring up the context menu, choose "Extract string resource" and give your string resource a name. This will create a new <string name="my_string_name">My string value</string> in strings.xml.

Studio magically also calls getString(R.string.xxxxx) for you. Neat, huh?


Make new string from ID:
This is for when you suddenly remember that you need a new string and want to sort of try to do it correctly so you type in the string ID. Only it hasn't been defined yet, so Studio complains. But it's fine. There is a shortcut for that.

Put your cursor somewhere in the as of yet undefined ID, press ALT+Enter to bring up the context menu, choose "Create string value for resource my_string_id", and type in the actual string value you want. Again, this will create a new <string name="my_string_id">My other string value</string> in strings.xml.

Remember, in both of these cases, Studio will create the new strings in strings.xml, but you can modify it to put in whatever variant you want it to be in (for localisations, screen size support, etc).

08 June 2014

Quick Tip: Understanding Alternate Resources

Trying to support as many devices as possible the best way possible is a very daunting task indeed. You will usually need to provide a lot of different layouts, strings, or dimensions (among others) to make your app look great whatever the user's device is. And then you start chaining resource qualifiers and testing which resource is being loaded by the OS can become a nightmare very quickly.

Here's a trick I started using which seemed to work quite well. Create a string (the app name works well if you have an Action Bar) that you can display on-screen (or just throw into a Log or a Toast) that will quickly let you know from which of those very many /res folders your resources are being pulled out of.

For this demo, I have the following /res/values-xxxxx folders:

As you can see, there are strings.xml files in each of the configurations I want to support. Each of these files will contain whatever description we want to see on the device or in the Logs. In my case, I have custom strings for each form factor and orientation, which allows me to validate things easier. But this is particularly useful if the changes per form factor and orientation is subtle, such as dimensions.

Here's what it looks like for phones:


And here are the outputs for tablets:

Each of the strings.xml files contains just these two strings (sample taken from /res/values-sw800dp):


    MultiDeviceSupport - Tablet Portrait
    Hello world on a tablet!


Have fun debugging!

13 May 2014

Quick Tip: Updating the location update frequency

When using Google Play's Location Services and you want to change the frequency of the updates, make sure to do these in order:

stopLocationUpdates();
// This method should implement mLocationClient.removeLocationUpdates()

// Set the new frequency in your location client
updateLocationClient(frequency);

startLocationUpdates();
// This method should implement mLocationClient.requestLocationUpdates()
// which means it should check for isConnected() as well!

If you do not remove the updates before updating the frequency, it looks like the old frequency update is still active BUT a new one is started.

23 April 2013

Quick Tip: Checking for emptiness

Always using if(myString != null && myString.length() > 0)? Use !TextUtils.isEmpty(myString) instead.

Check out the class for more utility methods that you won't have to implement yourself.

03 November 2012

Quick Tip: Pulling an SQLite db file from device

I have always thought that you would need root access to pull an SQLite file from a non-rooted Android device. Turns out I thought wrong! Here's how you do it:
$ adb -d shell
$ run-as your.package.name
$ cat /data/data/your.package.name/databases/yourdatabasename  >/sdcard/yourdatabasename
This will copy your app's SQLite db file to the SD card's root directory. From there, you can copy the file to your computer any way you like.

Props to this SO answer!

19 July 2012

Save Logcat contents to file

Note to self: to save the contents of Logcat to a text file:
  1. Navigate to the SDK installation directory.
  2. Go to the /platform-tools folder.
  3. adb logcat -d > my_logcat_dump.txt
If there is more than one device connected to adb, specify which device's log to dump:
adb -s emulator-5558 logcat -d > my_logcat_dump.txt

13 April 2012

Adding a float value to your resources

Earlier today, I was trying to figure out how to add a float value to constants file. What I used to do was add a string value in my strings.xml, retrieve the string value, and convert it to float.
float floatFromFile = Float.valueOf(getResources().getString(R.string.my_float));
I was trying out something new but it wasn't working, so I decided to look for a more accepted solution. Google led me to this StackOverflow question. I was on the right track after all! I think the accepted answer is incomplete, or not clear enough for my purposes. I ended up having this entry in my dimensions.xml file:
<item name="face_feature_marker_size" type="vals" format="float">2.0</item>
And then in my code, I retrieve the value as:
TypedValue tempVal = new TypedValue();
getResources().getValue(R.vals.face_feature_marker_size, tempVal, true);
float markerSize = testVal.getFloat();
I ended up having more lines of code with no idea if this is more optimized. Let me know what you think!

10 April 2012

Quick Tip: git cloning

A user-friendly way of cloning a git repo is through the eGit plug-in in Eclipse. But sometimes, especially on Windows machines, Eclipse has trouble cleaning up after itself after completing a clone operation. The best workaround for this is to clone the repo from git bash and then import the repo in Eclipse.
default@ZDOMINGUEZ-T420 ~
$ git clone git@github.com:<your git repo> <local folder to check out to>
When git finishes cloning your repo, import it to Eclipse.
Browse to the folder you checked out to, click OK, and the newly-cloned repo should now appear on the Git Repositories view.