Home Development for Android Declarative programming of client-server applications on android.Part 2

Declarative programming of client-server applications on android.Part 2

by admin

In previous article briefly showed the advantages of declarative versus imperative programming of client-server applications on android.
Now we will write a small, but sufficient project to evaluate the effectiveness of the DePro library. It is part of one of the tutorials examples of the library The design of all the screens we describe are shown in the following figures :
Declarative programming of client-server applications on android.Part 2

 DRAWER Screen CATALOG screen PRODUCT_LIST screen 

 CATALOG_a DESCRIPT screen Screen CHARACTERISTIC 

Declarative programming of client-server applications on android.Part 2

 FITNESS Screen FITNESS_a Screen Exit Screen 

From these screens, their functionality is generally clear. The side menu contains two items "Catalog" and "Fitness" when you select an item, the corresponding screen is shown. The CATALOG screen contains a horizontal "Novelties" list. Clicking on any "novelty" displays a screen with a description of that product. The description consists of two tabs: "Description" and "Characteristics". The CATALOG screen also has a "Catalog" drop-down list. When you click on the down arrow, the catalog opens (closes). Clicking on the full line shows the PRODUCT_LIST screen with a list of products by the selected item in the catalog.
When you click on "Fitness", the FITNESS screen is shown with a list of services. The list depends on the club selected in the spinner. If you try to exit the application, a warning dialog is shown.
The server sends the data to the application in json format. The data structure for each screen is described below. In DePro applications only the URL is used from the API, the data structure is only needed to correctly specify the name (id) of the view, because binding is done by name. Note that the data is an incomplete slice of real data. Therefore, there may be inconsistencies on the names, images. In particular, there are only 20 images of products. Therefore, one and the same picture can be at many products.
API for example screens CATALOG screen for horizontal list (News) URL depro/cron/news, GET method. When reading novelty data, pagination is used. The pagination parameters are passed in the header.
Reply

{"product_id":4610, "catalog_id":15984, "product_name":"Heat Shrinkable Tube 20mm set of 6 colors (pack 1m*20pcs) APRO", "catalog_code":"ZRG-20kit", "picture":"depro/cronimg/picture_1.jpeg", "bar_code":"4824041010653", "oem":"", "price":175.98, "brand":"APRO", "product_code":"032578", "gift":0, "bonus":0, "new_product":1, "quantity":5}, . . .

Category list for an undisclosed URL depro/cron/catalog.
For dropdown lists URL depro/cron/catalog_ex with parameter catalog_id as a value which uses the id of the category that is being dropped down.
The answer for all queries is the same :

[{"catalog_id":15510, "parent_id":0, "catalog_name":"Body"}, {"catalog_id":15584, "parent_id":0, "catalog_name":"Engine"}, . . .]

The PRODUCT_LIST URL depro/cron/product_list screen with the parameters: expandedLevel and catalog_id.
Here expandedLevel is the level of catalog exposures (0, 1 or 2), catalog_id is the id of the selected category.
For categories level 0 and 1, you define a list of all categories that are included in the selected category, and then use that list to define a list of products that are included in any category on that list.
GET method
The structure of the response data is the same as for the horizontal list (Novelties)
The URL depro/cron/product_barcode with
parameter barcode_scanner.
The URL depro/cron/product_search with the product_name parameter is used to select products by search string. Selection is performed by LIKE condition. Product_name can contain one or two words.
The same query is used for the voice search text.
DESCRIPT screen
Product description : URL depro/cron/product_id with parameter product_id. GET method.
Reply

{"product_id":2942, "catalog_id":15594, "product_name":"Temperature sensor VAZ 2110, 2111, 2112, ZAZ Sens 'Sens' AUTOPRIBOR ", "catalog_code":"23.3828", "picture":"depro/cronimg/picture_16.jpeg", "bar_code":"2000000148472", "oem":"2112-3851010", "price":103.02, "brand":"Avtopribor, Kaluga, Russia", "product_code":"027729", "gift":1, "bonus":0, "new_product":0, "quantity":16}

Analogs : URL depro/cron/product_analog with parameter product_id. GET method.
Reply

[{"product_id":561, "catalog_id":15587, "product_name":"Water pump VAZ 2110, 2111, 2112 (pump for 16 claps. motor) AURORA", "catalog_code":"WP-LA2112", "picture":"depro/cronimg/picture_12.jpeg", "bar_code":"2900011680711", "oem":"2112-1307010", "price":188.16, "brand":"AURORA, Poland", "product_code":"016807", "gift":0, "bonus":1, "new_product":0, "quantity":15}, . . .]

The screen of the CHARACTERISTIC URL depro/cron/product_charact with the product_id parameter. The GET method.
Reply

[{"prop_id":2764, "product_id":2942, "name":"Manufacturer", "value":"Avtopribor, Kaluga, Russia"}, {"prop_id":2765, "product_id":2942, "name":"Car Brands for them", "value":"VAZ, ZAZ"}, . . .]

Now let’s take a look at our steps in writing the application.
1. In the studio, create a new project. You can name it as you see fit.
2. Resource Deployment. As mentioned in the first article, the resource (XML) files are used normally. So, to avoid wasting time on known things, let’s just download all the resources you need
Unzip the res_example.zip file. In the studio, delete all the contents of the res folder.
Let’s move the contents of the unzipped folder res to the res folder of the project. After that, taking into account the peculiarities of Android Studio, it might be necessary to clear the project and / or run the command Invalid Caches/Restart.
3. Connecting the library
In the dependencies section of the module’s build.gradle file, you must specify :

implementation 'github.com/deprosystem/depro:compon:3.0.1'

In the android section of the module’s build.gradle file, you must specify :

packagingOptions {exclude 'META-INF/DEPENDENCIES'}compileOptions {sourceCompatibility JavaVersion.VERSION_1_8targetCompatibility JavaVersion.VERSION_1_8}

When selecting the minSdkVersion attribute, please note that the library supports minSdkVersion = 17. After changing the build.gradle you need to synchronize the project.
4. Creating the necessary classes (files). When working with the library only 4 classes are used: MyDeclareScreens – describe all the screens; MyParams – set the necessary parameters for the application; MyApp – initiates the DePro library; MainActivity – the start activity. You can use your own class names.
We create the class MyDeclareScreens.java. Its contents are as follows :

public class MyDeclareScreens extends DeclareScreens {public final static StringMAIN = "main", DRAWER = "DRAWER", CATALOG = "CATALOG", DESCRIPT = "DESCRIPT", CHARACTERISTIC = "CHARACTERISTIC", PRODUCT_LIST = "PRODUCT_LIST", PRODUCT_DESCRIPT = "PRODUCT_DESCRIPT", FITNESS = "FITNESS";@Overridepublic void declare() {activity(MAIN, R.layout.activity_main).navigator(finishDialog(R.string.attention, R.string.finishOk)).drawer(R.id.drawer, R.id.content_frame, R.id.left_drawer, null, DRAWER);fragment(DRAWER, R.layout.fragment_drawer).menu(model(menu), view(R.id.recycler));fragment(CATALOG, R.layout.fragment_catalog).navigator(handler(R.id.back, VH.OPEN_DRAWER)).component(TC.RECYCLER_HORIZONTAL, model(Api.NEWS_PROD).pagination().progress(R.id.progr), view(R.id.recycler_news, R.layout.item_news_prod), navigator(start(PRODUCT_DESCRIPT))).component(TC.RECYCLER, model(Api.CATALOG), view(R.id.recycler, "expandedLevel", new int[]{R.layout.item_catalog_type_1, R.layout.item_catalog_type_2, R.layout.item_catalog_type_3}).expanded(R.id.expand, R.id.expand, model(Api.CATALOG_EX, "catalog_id")), navigator(start(PRODUCT_LIST)));activity(PRODUCT_LIST, R.layout.activity_product_list, "%1$s", "catalog_name").animate(AS.RL).navigator(back(R.id.back)).component(TC.RECYCLER, model(Api.PRODUCT_LIST, "expandedLevel, catalog_id"), view(R.id.recycler, R.layout.item_product_list).visibilityManager(visibility(R.id.bonus_i, "bonus"), visibility(R.id.gift_i, "gift"), visibility(R.id.newT, "new_product")), navigator(start(PRODUCT_DESCRIPT)));activity(PRODUCT_DESCRIPT, R.layout.activity_product_descript, "%1$s", "catalog_name").animate(AS.RL).navigator(back(R.id.back)).setValue(item(R.id.product_name, TS.PARAM, "product_name")).component(TC.PAGER_F, view(R.id.pager, DESCRIPT, CHARACTERISTIC).setTab(R.id.tabs, R.array.descript_tab_name));fragment(DESCRIPT, R.layout.fragment_descript).component(TC.PANEL, model(Api.PRODUCT_ID, "product_id"), view(R.id.panel).visibilityManager(visibility(R.id.bonus, "bonus"))).component(TC.RECYCLER, model(Api.ANALOG_ID_PRODUCT, "product_id"), view(R.id.recycler, R.layout.item_product_list).noDataView(R.id.not_analog).visibilityManager(visibility(R.id.bonus_i, "bonus"), visibility(R.id.gift_i, "gift"), visibility(R.id.newT, "new_product")), navigator(start(0, PRODUCT_DESCRIPT, PS.RECORD), handler(0, VH.BACK))) ;fragment(CHARACTERISTIC, R.layout.fragment_characteristic).component(TC.RECYCLER, model(Api.CHARACT_ID_PRODUCT, "product_id"), view(R.id.recycler, "2", new int[] {R.layout.item_property, R.layout.item_property_1}));fragment(FITNESS, R.layout.fragment_fitness).navigator(handler(R.id.back, VH.OPEN_DRAWER)).component(TC.SPINNER, model(JSON, getString(R.string.clubs)), view(R.id.spinner, R.layout.item_spin_drop, R.layout.item_spin_hider)).component(TC.RECYCLER, model(Api.FITNESS, "clubId"), view(R.id.recycler, R.layout.item_fitness), null).eventFrom(R.id.spinner);}Menu menu = new Menu().item(R.drawable.list, R.string.m_catalog, CATALOG, true).divider().item(R.drawable.ic_aura, R.string.fitness, FITNESS);}

Later we will describe all DePro constructs used. Use alt+enter to connect the import. The Api class will also be highlighted in red, in which the addresses for all requests are set. Its contents will be listed below.
Let’s create a class MyParams.java. In most cases the default value of parameters is enough. In our case, we’ll only set a basic URL.

public class MyParams extends AppParams {@Overridepublic void setParams() {baseUrl = "https://deprosystem.com/";}}

Change the contents of the studio-created MainActivity class to the following :

public class MainActivity extends BaseActivity {@Overridepublic String getNameScreen() {return MyDeclareScreens.MAIN;}}

You can specify portrait orientation in the manifest for MainActivity.In principle the library supports screen rotation, but in this version the portrait orientation is prescribed for activity() screens in addition to the starting orientation.
Create MyApp class:

public class MyApp extends Application {private static MyApp instance;private Context context;public static MyApp getInstance() {if (instance == null) {instance = new MyApp();}return instance;}@Overridepublic void onCreate() {super.onCreate();instance = this;context = getApplicationContext();DeclareParam.build(context).setAppParams(new MyParams()).setDeclareScreens(new MyDeclareScreens());}}

This is a normal singleton with MyParams and MyDeclareScreens set in the onCreate method.
Don’t forget to describe MyApp in the manifest.
To finish, let’s create an Api class:

public class Api {public static final String CATALOG = "depro/cron/catalog", NEWS_PROD = "depro/cron/news_prod", PRODUCT_LIST = "depro/cron/product_list", PRODUCT_ID = "depro/cron/product_id", ANALOG_ID_PRODUCT = "depro/cron/product_analog", CHARACT_ID_PRODUCT = "depro/cron/product_charact", FITNESS = "depro/cron/fitness", CATALOG_EX = "depro/cron/catalog_ex";}

5. You can now run the application for execution.
If everything is entered correctly, all of the specified screens will be displayed (and work) when the application starts.
As you can see we have only five simple classes for the whole application. And four of them are auxiliary. Their contents do not depend on the number of screens. The description of the screens, which are not very trivial, also takes a bit of space (less than 80 lines). Now let’s show that the description is not only not too big, but also not too complicated. To do this, let’s describe the work of the used library components.
As mentioned in the first article, the screens in the library can be either an activity or a fragment, which is given a screen name (string) and layout. Screens are referenced by their names. Selecting which screen type to use is done in the usual way.
MAINscreen
So, from the design you can see that the start screen has a side menu and a snippet container. The R.layout.activity_main is set to the standard DrawerLayout. So in the MAIN screen description, we also define a drawer component, to which we pass the id of the DrawerLayout itself and its sidebar and fragment containers. We also specify the name of the screen (DRAWER) that will be displayed in the sidebar. The finishDialog navigator indicates that a confirmation dialog must be issued before exiting the application.
DRAWER screen
It contains only one component menu, which has a variable of type Menu in its model and id of markup element of type RecyclerView in its view, which will display the menu. The menu itself says that the menu item "CATALOG" will be displayed in a fragment container when the menu starts.
CATALOGscreen
Contains a single horizontal "Novelties" list. Its model uses pagination when retrieving data from the server, and the progress is displayed in R.id.progr. If you have a fast Internet you may not notice the panel with the progress. If you want to see it, you can switch to slower internet or change background color of R.id.progr. In this case you will at least see it blinking. The pagination uses the default parameters set in AppParams. The view is set to R.id.recycler_news with RecyclerView and layout for the items. Clicking on any item in the list starts the PRODUCT_DESCRIPT screen.
The "Catalog" list is a drop-down list. The expanded level is defined in the "expandedLevel" field. If it is not passed with the initial data it’s OK, the library will deal with it itself. The same parameter sets and which layout from the list should be used at each level of disclosure. The fact that the list is expandable serves the expanded(…) functionality, in which the model for getting data from the server for the next levels is returned. The model specifies the Api.CATALOG_EX query address and the name of the query parameter "catalog_id". In the model you should also specify R.id.expand – element of markup in the entities, on clicking on which will be disclosed the list, and the id of the element which will be animated to rotate by 180 gradusov at disclosure – closing of the list. And finally the navigator says that clicking on the list item will bring up the PRODUCT_LIST screen.
The navigator relating to the entire screen states that clicking on the element R.id.back will open the sidebar.
PRODUCT_LISTscreen
When setting the screen it is specified that the header will show the actual value of the parameter "catalog_name" and the output animation will be from right to left (AS.RL). The description of the list is already familiar to us. The only thing that is specified for the view is the visibilityManager, which controls the visibility of the markup elements depending on the values of the corresponding data. In the navigator that refers to the whole screen it is specified that clicking on element R.id.back will take you back to the previous screen.
PRODUCT_DESCRIPT screen
New to us is a component of type PAGER_F. It is connected with the usual ViewPager. It has a list of screens (slices) that will be shown and a TabLayout (setTab) is connected.
PRODUCT_DESCRIPT screen is shown in the "DESCRIPT" tab.
A component of type PANEL displays the data obtained from the model. The "Analogues" list displays a list of similar products, if there is one, or with the noDataView() functionality a "No Analogues" message.
The CHARACTERISTIC screen
Displays a list of product features.
FITNESS screen
A component of SPINNER type shows a list of clubs with selectable clubs. The list of clubs is specified in the model as a json string.
The list of class categories is normal. The eventFrom(…) functionality indicates that when a component associated with R.id.spinner (in our case a spinner) changes, the data must be updated.
The code for the application discussed in this article can be seen at Github
The article is an introductory one. You can read more about the features of DePro library at documentation

You may also like