Lost Text (LT)

ECn Categories > Lost Content (LC) > Lost Text (LT)

A text section is not available because there is no connection.

Amount of Issues App List
11 (A22) Hubble Gallery, (A31) OpenWeather, (A36) Stepik, (A39) Habitica, (A44) Materialistic, (A46) Open Food, (A50) KickStarter

Examples

Habitica

Category: Productivity
v 1.1.6
Scenario: To access the information in the shop section without internet connection
This video shows an example of a lost text. At 2nd second the user selects the "Shops" item from the side menu, this shows an activity where there are 3 tabs. The activity starts at "Market" tab, that doenst display any content. After navigating over all the tabs the user turn on the connection at 12th second, When the connection is stablished the user goes to "avatar" item from side menu in order to when access again to "Shops" item, the content is refreshed. But in this case the "Market" tab shows some content.

The following code snippets show the classes and files that are involved in the generation of the previuos issue.

In order to see how this error is generated, we need to review all the flow event. First, we have the creation of the variable “shopIdentifier” that will store the value of the selected tab.

src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/shops/ShopFragment.kt

30. var shopIdentifier: String? = null
    var user: User? = null
    var shop: Shop? = null

Second, on the “OnViewCreate” knowing that the shop element has not benn initialized, the condition evaluated at line 87 is true, so the “loadShopInventory” method is called.

src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/shops/ShopFragment.kt

55. override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    ...
87.     if (shop == null) {
88.         loadShopInventory()
        } else {
            adapter?.setShop(shop, configManager.shopSpriteSuffix())
        }

Third, when the “loadShopInventory” method is called the value of the “shopIdentifier” variable is used in order to know which value must be used to retrive the information, so at line 108 knowing that the user has selected the Market tab the value assigned to the variable “shopUrl” is “market”. Now with this value the a new event is created in order to retrieve the information at line 114 where the “retrieveShopInventory” method is called.

src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/shops/ShopFragment.kt

    private fun loadShopInventory() {
        val shopUrl = when (this.shopIdentifier) {
108.        Shop.MARKET -> "market"
            Shop.QUEST_SHOP -> "quests"
            Shop.TIME_TRAVELERS_SHOP -> "time-travelers"
            Shop.SEASONAL_SHOP -> "seasonal"
            else -> ""
        }
114.    this.inventoryRepository.retrieveShopInventory(shopUrl)
                .map { shop1 ->

Nevertheless, this method belongs to a interface called “InventoryRepository”. The method signature is shown in the following snippet.

src/main/java/com/habitrpg/android/habitica/data/InventoryRepository.java

26. public interface InventoryRepository extends ContentRepository {
    ...
78.     Observable<Shop> retrieveShopInventory(String identifier);

However, the class “InventoryRepositoryImpl” implements all the methods defined in the previous interface file. So, between lines 305 and 308, the method “retrieveShopInventory” is implemented and as it can be seen at line 307 the method “retrieveShopIventory” from the object “apiClient” handles the event.

src/main/java/com/habitrpg/android/habitica/data/implementation/InventoryRepositoryImpl.java

32. public class InventoryRepositoryImpl extends ContentRepositoryImpl<InventoryLocalRepository> implements InventoryRepository {
    ...
        @Override
        public Observable<Shop> retrieveShopInventory(String identifier) {
307.        return apiClient.retrieveShopIventory(identifier);
        }

But, just as “InventoryRepository” the “ApiClient” is an interface, and at line 191 the method “retrieveShopIventory” is defined.

src/main/java/com/habitrpg/android/habitica/data/ApiClient.java

43. public interface ApiClient {
    ...
191.    Observable<Shop> retrieveShopIventory(String identifier);

In despite of that, there is also a class that implements all the methods from “ApiClient” called “ApiClientImpl” that between lines 852 and 855 has the implementation of the “retrieveShopInventory”. As we can see at line 854 the method “retrieveShopInventory” from “apiService” object handles the event.

src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.java

133. public class ApiClientImpl implements Action1<Throwable>, ApiClient {
     ...
        @Override
        public Observable<Shop> retrieveShopIventory(String identifier) {
            return apiService.retrieveShopInventory(identifier).compose(configureApiCallObserver());
        }

Nevertheless, in this case the “apiService” object is initialized at line 217 using “retrofit” library, then the class of the instance will only be a interface and the implementation will be in charge of retrofit. However, knowing that the user has no connection the library returns either a blank result or an error, that will lead the map done at “ShopFragment” class to fail.

src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.java

144. private final ApiService apiService;
     ...
     retrofitAdapter = new Retrofit.Builder()
         .client(client)
         .baseUrl(server.toString())
         .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
         .addConverterFactory(gsonConverter)
         .build();

217. this.apiService = retrofitAdapter.create(ApiService.class);