Saturday, 13 May 2017

The Interest Points Tracker app

I was approached by a dear friend of mine with an interesting problem. She is a chef and, as most of chefs do, likes to go around and try new food in new places all the time. However, she usually picks her favorite places from home, perhaps checking reviews on Google or other popular websites. The problem is that, when she happens to be around a particular place she had intention to go visit, she totally forgets about it and perhaps drive past it without noticing.

So, she asked me to make a simple app that allows her to pick places from the map and tracks her once she leaves her house and, most importantly, that notifies her whenever she is close to a placed she saved.

And that's what this application does. In addition, the app will display a place details, such as business contacts, photos and more, if required.

Here are a few screenshots:









Some information about it:

- For the sake of experimenting, I used 2 different ways to obtain the user location. To locate the user position while interacting with the app, I used the Google Location API, the so called fused location provider. This method is apparently more efficient than the classic Android SDK with the Location Manager. Nonetheless, the latter was used in the background service, in order to track the user while the app is not on foreground.

- To pick a place from the map I used the Google Place Picker, which I believe is a great tool considering that it is specifically designed to do exactly that.

- In order to retrieve information about a place, I made use of the Google Web Services API. After performing an HTTP request, the server will send information back in JSON format. All I had to do was manipulating the data and display it to the user. With this method, however, only 5 reviews and 10 photos for each places are sent back, which for this particular application is enough.

- To obtain pictures a similar process was used: from the previous step, part of the JSON response is a list of references for the pictures. Once obtained, these reference can be used to make additional requests to get the actual images (more on this here).

- The activity that displays all the detail about a selected place was another experiment, specifically with the new UI tools such as Coordinator Layout and custom behaviors. It was both challenging and rewarding getting all those views to do what I had in mind.

- To display the pictures to full screen and to add zoom in/out features I opted for a quick and easy solution: this amazing library by Chris Banes fixed all my problems in no time.

The application is downloadable at this link.

Thursday, 13 April 2017

Custom behavior for Coordinator Layouts

Using the new CoordinatorLayout for your Android app is a great way to create interesting interactions between your views in the activity.

According to the official documentation, it is a super powered FrameLayout.

It allows the developer to create dependancies among the various view that populate the layout, creating dynamic effects which ultimately result in a more immersive user experience.

Recently, I'm developing an application, which I will post on this blog, and I wanted to experiment with this tool and I ended up creating a custom behavior for my views. Creating behaviors will enable you to, quite literally, do whatever you want with your views in response to other views.

Before I post any code, this is the effect I was trying to achieve:


Let's have a look at the code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class PlaceDetailsCardDisapperBehaviour extends CoordinatorLayout.Behavior<CardView> {

   float maximumDifference = 0;

    public PlaceDetailsCardDisapperBehaviour(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, CardView child, View dependency) {

        return dependency instanceof NestedScrollView;
    }

    @TargetApi(Build.VERSION_CODES.M)
    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, CardView child, View dependency) {

        int targetLoc[] = new int[2];
        dependency.getLocationInWindow(targetLoc);

        int[] childLocation = new int[2];
        child.getLocationInWindow(childLocation);

        float YThreshold = GlobalVariables.DpToPx(100);

        if(maximumDifference==0)
            maximumDifference = childLocation[1] - YThreshold;

        float currentDifference = childLocation[1] - YThreshold;

        float perc = currentDifference / maximumDifference;

        if(perc>1.0f)
            perc = 1.0f;
        else if(perc<0)
            perc = 0;

        child.setScaleX(perc);
        child.setScaleY(perc);

        return false;
    }

}

We create a class that extends CoordinatorLayout.Behavior<CardView>, and we pass in CardView as that is the type of view we are writing the behavior for.

Although we do not call the constructor directly it is important to implement it as it is invoked automatically when the layout is being inflated.

We then override the method layoutDependsOn, which is a method that is called every time something changes in the layout and we must return the class of the view our behavior will react in response to. In our case, the NestedScrollView.

When this method returns true, OnDependentViewChanged is called immediately and this is where we write the reaction. In our case, we simply check the distance of the nested scroll being dragged from the top of the screen, we get a percentage  of the current position and set it as size X and Y for our CardView.

In order to apply our behavior to the view we simply need to add a single line in the xml layout file:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<android.support.v7.widget.CardView
        android:id="@+id/placeDetailsCard"
        app:layout_behavior="com.blogspot.androidcanteen.interestpoints.PlaceDetailsCardDisapperBehaviour"
        app:layout_anchor="@+id/appbar"

        app:layout_anchorGravity="bottom|center"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="20dp">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical"
            android:paddingLeft="6dp"
            android:paddingRight="6dp">

    <me.grantland.widget.AutofitTextView
        android:text="Title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"

        android:id="@+id/theTitle"
        android:textAppearance="@style/Base.TextAppearance.AppCompat.Large"
        android:gravity="center_horizontal"
        android:textStyle="normal|bold"
        tools:textSize="18sp"
        android:layout_weight="0.5"
        android:padding="2dp"
       />

            <me.grantland.widget.AutofitTextView
                android:text=""
                android:layout_width="match_parent"
                android:layout_height="wrap_content"

                android:id="@+id/theType"
                android:textAppearance="@style/Base.TextAppearance.AppCompat.Small"
                android:gravity="center_horizontal"
                android:textStyle="italic"
                tools:textSize="12sp"
                android:layout_weight="0.5"
                android:padding="2dp"
                 />

        </LinearLayout>


    </android.support.v7.widget.CardView>

The behavior is applied on line 3, directly to the CardView. It is very important to remember that every behavior we write must be applied to a direct child of the Coordinator Layout. For example, this would not work if we applied the custom behavior to the Linear Layout inside the CardView, regardless whether we modify the Java code accordingly.

Tuesday, 7 February 2017

The Stocktake app

My latest project.

This application serves as an inventory manager and it is currently used by some friends of mine who works in bars. As they need to perform a stock count every week of the current amount of drinks and food in the premise, I provided them with this simple piece of software that facilitates their job.

The project if fully downloadable on this post, at the bottom of the page.

Description:

The app will allow the user to create multiple databases which can contain multiple inventories. These databases are exportable (as ".stock" files) and can be shared. Of course, the app will allow users to import databases with that extension. There is also one separate databases which contains all the items that are put in in order to let the user pick from the database whenever possible rather then type each and every time.

In order to quickly pre load the database of items, it is also possible to load all the information using a .txt file, which you will be able to also download and try out.

Once all the items have been put int the inventory, the application will calculate the total amount of each item for each location. For example, if we have 2 ltrs of Coke in the Restaurant and 3 in the Bar the app will display a total of 5 ltrs of Coke in the venue, and also show the correct amount for each location.

All this information is visible within the app or, more usefully, as a pdf file. The app will allow to export all the information regarding the inventory as a pdf file, with name of the person who performed the task, date and time and all the items listed alphabetically.

Also, a calculation of the amount of drinks to order is also displayed, based on the Top Stock value of each item.

Finally, since I wanted to experiment with cloud computing, I added an extra feature for authorization checking: once the app is started, an id is sent to an external server (I used Amazon Web Services) in order to check if the current user has permission to use the app. This is just something I wanted to try, and it's currently implemented in the downloadable version on this post.

I modified the logos which I replaced with the default android icon.

Here are some screenshots:









Conclusion

The application is downloadable for free here and you will also find a txt file which contains a list of items that can be used for the inventory.
Please report any bug or problems.Also, if interested, I'll gladly create tutorial posts on how to create the app itself, let me know.