ECn Categories > Non-Existent Result Notification (NRN) > Non-existent Notification of Problem when Performing an Action (NNPPA) > Non-existent Notification of an Issue while Downloading Content (NNDC)
The app does not show anything indicating that the required content is being downloaded. It seems that nothing happened.
Amount of Issues | App List |
---|---|
7 | (A4) Galaxy Zoo, (A5) Fanfiction reader, (A10) OsmAnd, (A12) XOWA, (A20) MAPS.ME |
Examples
Stepik
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, as it can be seen in the following snippet and knowing that it is in the adapter class, the following “onClickListener” is called everytime a new download is started. As it can be seen, this method calls the “onClickLoad” method.
app/src/main/java/org/stepic/droid/ui/adapters/SectionAdapter.java
loadButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
372. onClickLoad(getAdapterPosition());
}
});
This method after some validations about permissions, make a validation to know if the click is starting a download, stoping it or deleting the content. For this specific case the content is being downloaded, so as is shown at line 231, after the dialog that allows the user to select the video quality for the download is shown and a option is selected the flow event lands in the “setOnLoadPositionListener”.
app/src/main/java/org/stepic/droid/ui/adapters/SectionAdapter.java
public void onClickLoad(int adapterPosition) {
...
} else {
if (sharedPreferenceHelper.isNeedToShowVideoQualityExplanation()) {
VideoQualityDetailedDialog dialogFragment = VideoQualityDetailedDialog.Companion.newInstan(adapterPosition);
231. dialogFragment.setOnLoadPositionListener(this);
if (!dialogFragment.isAdded()) {
FragmentTransaction ft = activity.getSupportFragmentManager().beginTransaction();
ft.add(dialogFragment, null);
ft.commitAllowingStateLoss();
}
} else {
loadSection(adapterPosition);
}
}
That listener in the dialog is assigned to “onNeedLoadPosition”, and as is shown that method calls “loadSection” method.
app/src/main/java/org/stepic/droid/ui/adapters/SectionAdapter.java
public void onNeedLoadPosition(int adapterPosition) {
277. loadSection(adapterPosition);
}
Therefore when reviewing the code of the “loadSection”, after a validation of the parameter the “checkOnLoading” method from “downloadingInteractionPresenter” is called
app/src/main/java/org/stepic/droid/ui/adapters/SectionAdapter.java
private void loadSection(int adapterPosition) {
int sectionPosition = adapterPosition - PRE_SECTION_LIST_DELTA;
if (sectionPosition >= 0 && sectionPosition < sections.size()) {
248. downloadingInteractionPresenter.checkOnLoading(adapterPosition);
}
}
Second, when the event flow gets into “DownloadinInteractionPresenter”, the method checkOnLoading calls the “checkOnLoadingExclusive”method after a validation.
app/src/main/java/org/stepic/droid/core/presenters/DownloadingInteractionPresenter.kt
fun checkOnLoading(position: Int) {
if (isHandling.compareAndSet(false, true)) {
threadPoolExecutor.execute {
try {
27. checkOnLoadingExclusive(position)
} finally {
isHandling.set(false)
}
}
}
}
However, this method based on the current state of the connection calls a different method, but as it can be seen in the video the issue appears with wifi connection, therefore at line 40 the case for wifi calls the “onLoadingAccepted” method.
app/src/main/java/org/stepic/droid/core/presenters/DownloadingInteractionPresenter.kt
private fun checkOnLoadingExclusive(position: Int) {
val networkType = networkTypeDeterminer.determineNetworkType()
with(mainHandler) {
when (networkType) {
40. NetworkType.wifi -> post { view?.onLoadingAccepted(position) }
NetworkType.none -> post { view?.onShowInternetIsNotAvailableRetry(position) }
NetworkType.onlyMobile -> {
if (userPreferences.isNetworkMobileAllowed) {
post { view?.onLoadingAccepted(position) }
} else {
post { view?.onShowPreferenceSuggestion() }
}
}
}
}
}
Nevertheless, the “onLoadingAccepted” method only calls the “loadAfterDetermineNetworkState” method.
app/src/main/java/org/stepic/droid/ui/fragments/SectionsFragment.java
public void onLoadingAccepted(int position) {
878. adapter.loadAfterDetermineNetworkState(position);
}
Third, the event flow arrives again into the “SectionAdapter” class, in this case the “loadAfterDetermineNetworkState” method save some statistics and changes some state for the section in order to know that will be downloaded. After that at line 264 inside a new thread the download request is made using the “downloadSection” method from the “SectionDownloader”.
app/src/main/java/org/stepic/droid/ui/adapters/SectionAdapter.java
public void loadAfterDetermineNetworkState(int adapterPosition) {
int sectionPosition = adapterPosition - PRE_SECTION_LIST_DELTA;
if (sectionPosition >= 0 && sectionPosition < sections.size()) {
final Section section = sections.get(sectionPosition);
analytic.reportEvent(Analytic.Interaction.CLICK_CACHE_SECTION, section.getId() + "");
section.setCached(false);
section.setLoading(true);
downloadingPresenter.onStateChanged(section.getId(), section.isLoading());
threadPoolExecutor.execute(new Runnable() {
@Override
public void run() {
databaseFacade.updateOnlyCachedLoadingSection(section);
264. sectionDownloader.downloadSection(section.getId());
}
});
notifyItemChanged(adapterPosition);
}
}
However, the “downloadSection” method creates a new task to a “threadPool” and executes the “addSection” method from the “DownloadManager” class
app/src/main/java/org/stepic/droid/storage/SectionDownloaderImpl.kt
override fun downloadSection(sectionId: Long) {
threadPoolExecutor.execute {
val section = databaseFacade.getSectionById(sectionId)
cancelSniffer.removeSectionIdCancel(sectionId)
19. downloadManager.addSection(section)
}
}
Fourth, when the “addSection” method is called, a new intent is create for LoadService class at the line 27.
app/src/main/java/org/stepic/droid/storage/DownloadManagerImpl.java
public void addSection(Section section) {
27. Intent loadIntent = new Intent(App.Companion.getAppContext(), LoadService.class);
loadIntent.putExtra(AppConstants.KEY_LOAD_TYPE, LoadService.LoadTypeKey.Section);
loadIntent.putExtra(AppConstants.KEY_SECTION_BUNDLE, (Serializable) section);
App.Companion.getAppContext().startService(loadIntent);
}
As the class represent a service the method “onHandleIntent” is called when an intent is started to the class, and as it can be seen at line 98 knowing that the previous method send the extra that represents the type, the method called when a session is being tried to download.
app/src/main/java/org/stepic/droid/services/LoadService.java
protected void onHandleIntent(Intent intent) {
LoadTypeKey type = (LoadTypeKey) intent.getSerializableExtra(AppConstants.KEY_LOAD_TYPE);
try {
switch (type) {
case Section:
Section section = (Section) intent.getSerializableExtra(AppConstants.KEY_SECTION_BUNDLE);
addSection(section);
break;
Finally, in “addSection” method of the “LoadService” the request is made, the snippet shows all the process made by the method. Between lines 318 and 328 the method make an iteration to get all the videos that belong to the section, and it can be seen that there is no progress storing to let the user know the download is in progress
app/src/main/java/org/stepic/droid/services/LoadService.java
private void addSection(Section sectionOut) {
//if user click to removeSection, then section already in database.
Section section = databaseFacade.getSectionById(sectionOut.getId());//make copy of section.
if (section != null) {
try {
boolean responseIsSuccess = true;
final List<Unit> units = new ArrayList<>();
long[] unitIds = section.getUnits();
if (unitIds == null) {
responseIsSuccess = false;
}
int pointer = 0;
while (responseIsSuccess && pointer < unitIds.length) {
int lastExclusive = Math.min(unitIds.length, pointer + AppConstants.DEFAULT_NUMBER_IDS_IN_QUERY);
long[] subArrayForLoading = Arrays.copyOfRange(unitIds, pointer, lastExclusive);
Response<UnitMetaResponse> unitResponse = api.getUnits(subArrayForLoading).execute();
if (!unitResponse.isSuccessful()) {
responseIsSuccess = false;
} else {
units.addAll(unitResponse.body().getUnits());
pointer = lastExclusive;
}
}