jenkins-bot has submitted this change and it was merged. (
https://gerrit.wikimedia.org/r/367894 )
Change subject: Compilations: close the loop.
......................................................................
Compilations: close the loop.
This implements downloading of compilations.
Some architecture notes:
This introduces an abstract Fragment that becomes an observer of the
system DownloadManager, by polling it at a predefined interval.
So then, the other fragments that show lists of compilations (or a single
compilation) will automatically become subscribers to the polling of the
DownloadManager. Upon every poll event, we'll update each compilation
item with the appropriate state that the DownloadManager reports.
If a compilation item is in the middle of being downloaded, we show the
DownloadControlView component inline with the item.
TODO:
- Implement deleting a downloaded compilation from disk.
- Implement more robust behavior upon successful download completion.
Bug: T167523
Bug: T164764
Bug: T167527
Change-Id: I2741c14894d990c59c30bd685546c2dd61c5cab3
---
M app/src/main/java/org/wikipedia/gallery/MediaDownloadReceiver.java
M app/src/main/java/org/wikipedia/history/HistoryFragment.java
M app/src/main/java/org/wikipedia/offline/Compilation.java
M app/src/main/java/org/wikipedia/offline/CompilationDetailFragment.java
M app/src/main/java/org/wikipedia/offline/CompilationDownloadControlView.java
A app/src/main/java/org/wikipedia/offline/DownloadManagerItem.java
A app/src/main/java/org/wikipedia/offline/DownloadManagerObserver.java
A app/src/main/java/org/wikipedia/offline/DownloadObserverFragment.java
M app/src/main/java/org/wikipedia/offline/LocalCompilationsFragment.java
M app/src/main/java/org/wikipedia/offline/OfflineManager.java
M app/src/main/java/org/wikipedia/offline/RemoteCompilationsFragment.java
M app/src/main/java/org/wikipedia/readinglist/ReadingListFragment.java
M app/src/main/java/org/wikipedia/views/PageItemView.java
M app/src/main/res/layout/fragment_compilation_detail.xml
M app/src/main/res/layout/item_page_list_entry.xml
M app/src/main/res/layout/view_compilation_download_widget.xml
A app/src/main/res/menu/menu_remote_compilation_item.xml
M app/src/main/res/values-qq/strings.xml
M app/src/main/res/values/dimens.xml
M app/src/main/res/values/strings.xml
20 files changed, 572 insertions(+), 97 deletions(-)
Approvals:
jenkins-bot: Verified
Mholloway: Looks good to me, approved
diff --git a/app/src/main/java/org/wikipedia/gallery/MediaDownloadReceiver.java
b/app/src/main/java/org/wikipedia/gallery/MediaDownloadReceiver.java
index bc4ae04..c667b47 100644
--- a/app/src/main/java/org/wikipedia/gallery/MediaDownloadReceiver.java
+++ b/app/src/main/java/org/wikipedia/gallery/MediaDownloadReceiver.java
@@ -15,6 +15,7 @@
import org.wikipedia.R;
import org.wikipedia.WikipediaApp;
import org.wikipedia.feed.image.FeaturedImage;
+import org.wikipedia.offline.Compilation;
import org.wikipedia.util.FileUtil;
import java.io.File;
@@ -32,13 +33,19 @@
this.callback = callback;
}
- public void download(@NonNull Context context, @NonNull FeaturedImage
featuredImage) {
+ public static void download(@NonNull Context context, @NonNull Compilation
compilation) {
+ String filename =
FileUtil.sanitizeFileName(compilation.uri().getLastPathSegment());
+ String targetDirectory = Environment.DIRECTORY_DOWNLOADS;
+ performDownloadRequest(context, compilation.uri(), targetDirectory,
filename, Compilation.MIME_TYPE);
+ }
+
+ public static void download(@NonNull Context context, @NonNull
FeaturedImage featuredImage) {
String filename = FileUtil.sanitizeFileName(featuredImage.title());
String targetDirectory = Environment.DIRECTORY_PICTURES;
performDownloadRequest(context, featuredImage.image().source(),
targetDirectory, filename, null);
}
- public void download(@NonNull Context context, @NonNull GalleryItem
galleryItem) {
+ public static void download(@NonNull Context context, @NonNull GalleryItem
galleryItem) {
String saveFilename =
FileUtil.sanitizeFileName(trimFileNamespace(galleryItem.getName()));
String targetDirectoryType;
if (FileUtil.isVideo(galleryItem.getMimeType())) {
@@ -54,7 +61,7 @@
saveFilename, galleryItem.getMimeType());
}
- private void performDownloadRequest(@NonNull Context context, @NonNull Uri
uri,
+ private static void performDownloadRequest(@NonNull Context context,
@NonNull Uri uri,
@NonNull String targetDirectoryType,
@NonNull String targetFileName,
@Nullable String mimeType) {
final String targetSubfolderName =
WikipediaApp.getInstance().getString(R.string.app_name);
@@ -104,7 +111,7 @@
}
}
- @NonNull private String trimFileNamespace(@NonNull String filename) {
+ @NonNull private static String trimFileNamespace(@NonNull String filename)
{
return filename.startsWith(FILE_NAMESPACE) ?
filename.substring(FILE_NAMESPACE.length()) : filename;
}
diff --git a/app/src/main/java/org/wikipedia/history/HistoryFragment.java
b/app/src/main/java/org/wikipedia/history/HistoryFragment.java
index 625957b..8d2fd26 100644
--- a/app/src/main/java/org/wikipedia/history/HistoryFragment.java
+++ b/app/src/main/java/org/wikipedia/history/HistoryFragment.java
@@ -469,10 +469,10 @@
}
@Override
- public void onActionClick(@Nullable IndexedHistoryEntry entry,
@NonNull PageItemView view) {
+ public void onActionClick(@Nullable IndexedHistoryEntry entry,
@NonNull View view) {
}
@Override
- public void onSecondaryActionClick(@Nullable IndexedHistoryEntry
entry, @NonNull PageItemView view) {
+ public void onSecondaryActionClick(@Nullable IndexedHistoryEntry
entry, @NonNull View view) {
}
}
diff --git a/app/src/main/java/org/wikipedia/offline/Compilation.java
b/app/src/main/java/org/wikipedia/offline/Compilation.java
index 60911dc..5db5665 100644
--- a/app/src/main/java/org/wikipedia/offline/Compilation.java
+++ b/app/src/main/java/org/wikipedia/offline/Compilation.java
@@ -23,6 +23,7 @@
import static org.apache.commons.lang3.StringUtils.defaultString;
public class Compilation {
+ public static final String MIME_TYPE = "application/zim";
private static final int COMPRESSION_DICT_SIZE = 2 * 1024 * 1024;
@Nullable private String name;
@@ -91,6 +92,20 @@
timestamp = other.timestamp();
}
+ public boolean pathNameMatchesUri(@Nullable Uri otherUri) {
+ if (file == null || otherUri == null) {
+ return false;
+ }
+ return file.getName().equals(otherUri.getLastPathSegment());
+ }
+
+ public boolean uriNameMatchesUri(@Nullable Uri otherUri) {
+ if (uri == null || otherUri == null) {
+ return false;
+ }
+ return uri.getLastPathSegment().equals(otherUri.getLastPathSegment());
+ }
+
public void close() {
try {
if (reader != null) {
@@ -116,6 +131,10 @@
return file != null ? file.getAbsolutePath() : defaultString(path);
}
+ public boolean existsOnDisk() {
+ return !TextUtils.isEmpty(path());
+ }
+
public long size() {
return file != null && size == 0 ? file.length() : size;
}
diff --git
a/app/src/main/java/org/wikipedia/offline/CompilationDetailFragment.java
b/app/src/main/java/org/wikipedia/offline/CompilationDetailFragment.java
index bf3c4a0..83505bf 100644
--- a/app/src/main/java/org/wikipedia/offline/CompilationDetailFragment.java
+++ b/app/src/main/java/org/wikipedia/offline/CompilationDetailFragment.java
@@ -5,7 +5,6 @@
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
-import android.support.v4.app.Fragment;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
@@ -16,6 +15,7 @@
import android.widget.TextView;
import org.wikipedia.R;
+import org.wikipedia.gallery.MediaDownloadReceiver;
import org.wikipedia.json.GsonMarshaller;
import org.wikipedia.json.GsonUnmarshaller;
import org.wikipedia.util.DimenUtil;
@@ -25,6 +25,7 @@
import butterknife.BindView;
import butterknife.ButterKnife;
+import butterknife.OnClick;
import butterknife.Unbinder;
import static
org.wikipedia.offline.CompilationDetailActivity.EXTRA_COMPILATION;
@@ -32,9 +33,9 @@
import static org.wikipedia.util.DimenUtil.leadImageHeightForDevice;
import static org.wikipedia.util.FileUtil.bytesToGB;
-public class CompilationDetailFragment extends Fragment {
+public class CompilationDetailFragment extends DownloadObserverFragment {
@BindView(R.id.compilation_detail_toolbar) Toolbar toolbar;
- @BindView(R.id.compilation_detail_header_container) View containerView;
+ @BindView(R.id.compilation_detail_header_container) View
headerContainerView;
@BindView(R.id.compilation_detail_header_image)
FaceAndColorDetectImageView imageView;
@BindView(R.id.compilation_detail_header_gradient) View gradientView;
@BindView(R.id.view_compilation_info_title) TextView nameView;
@@ -42,9 +43,12 @@
@BindView(R.id.view_compilation_info_summary) TextView summaryView;
@BindView(R.id.view_compilation_info_description) TextView descriptionView;
@BindView(R.id.button_compilation_detail_download) TextView downloadButton;
+ @BindView(R.id.compilation_detail_downloaded_buttons_container) View
downloadedContainerView;
@BindView(R.id.view_compilation_detail_download_control)
CompilationDownloadControlView controls;
private Unbinder unbinder;
+ private Compilation compilation;
+ private boolean downloadPending;
@NonNull
public static CompilationDetailFragment newInstance(@NonNull Compilation
info) {
@@ -66,37 +70,97 @@
getAppCompatActivity().getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getAppCompatActivity().getSupportActionBar().setTitle("");
- Compilation comp = GsonUnmarshaller.unmarshal(Compilation.class,
+ compilation = GsonUnmarshaller.unmarshal(Compilation.class,
getActivity().getIntent().getStringExtra(EXTRA_COMPILATION));
- Uri featureImageUri = comp.featureImageUri();
+ Uri featureImageUri = compilation.featureImageUri();
int height = featureImageUri == null ?
DimenUtil.getContentTopOffsetPx(getContext()) : leadImageHeightForDevice();
if (featureImageUri == null) {
toolbar.setBackgroundColor(ContextCompat.getColor(getContext(),
R.color.main_toolbar_background));
}
- DimenUtil.setViewHeight(containerView, height);
+ DimenUtil.setViewHeight(headerContainerView, height);
ViewUtil.setBackgroundDrawable(gradientView,
GradientUtil.getCubicGradient(Color.BLACK, Gravity.TOP));
imageView.loadImage(featureImageUri);
- nameView.setText(comp.name());
+ nameView.setText(compilation.name());
dateSizeView.setText(String.format(getString(R.string.offline_compilation_detail_date_size),
- getShortDateString(comp.timestamp()), bytesToGB(comp.size())));
- summaryView.setText(comp.summary());
- descriptionView.setText(comp.description());
+ getShortDateString(compilation.timestamp()),
bytesToGB(compilation.size())));
+ summaryView.setText(compilation.summary());
+ descriptionView.setText(compilation.description());
downloadButton.setText(String.format(getString(R.string.offline_compilation_detail_button_download),
- bytesToGB(comp.size())));
- controls.setCompilation(comp);
+ bytesToGB(compilation.size())));
+ controls.setCallback(new CompilationDownloadControlView.Callback() {
+ @Override
+ public void onCancel() {
+ getDownloadObserver().remove(compilation);
+ }
+ });
+
+ updateDownloadState(true, null);
return view;
}
- @Override public void onDestroyView() {
+ @Override
+ public void onDestroyView() {
unbinder.unbind();
unbinder = null;
super.onDestroyView();
}
+ @OnClick(R.id.button_compilation_detail_download) void onDownloadClick() {
+ if (!getDownloadObserver().isDownloading(compilation)) {
+ MediaDownloadReceiver.download(getContext(), compilation);
+ downloadPending = true;
+ updateDownloadState(true, null);
+ }
+ }
+
+ @OnClick(R.id.button_compilation_detail_view_compilations) void
onMyCompilationsClick() {
+ getAppCompatActivity().finish();
+ }
+
+ @OnClick(R.id.button_compilation_detail_remove) void onRemoveClick() {
+ }
+
private AppCompatActivity getAppCompatActivity() {
return (AppCompatActivity) getActivity();
}
+
+ private void updateDownloadState(boolean indeterminate, @Nullable
DownloadManagerItem item) {
+ if (indeterminate) {
+ downloadButton.setVisibility(View.VISIBLE);
+ downloadButton.setEnabled(false);
+ downloadedContainerView.setVisibility(View.GONE);
+ controls.setVisibility(View.GONE);
+ return;
+ }
+ if (item == null && !compilation.existsOnDisk()) {
+ downloadButton.setVisibility(View.VISIBLE);
+ downloadButton.setEnabled(!downloadPending);
+ downloadedContainerView.setVisibility(View.GONE);
+ controls.setVisibility(View.GONE);
+ } else if (CompilationDownloadControlView.shouldShowControls(item)) {
+ downloadButton.setVisibility(View.GONE);
+ downloadedContainerView.setVisibility(View.GONE);
+ controls.setVisibility(View.VISIBLE);
+ controls.update(item);
+ } else {
+ downloadButton.setVisibility(View.GONE);
+ downloadedContainerView.setVisibility(View.VISIBLE);
+ controls.setVisibility(View.GONE);
+ }
+ }
+
+ @Override
+ protected void onPollDownloads() {
+ for (DownloadManagerItem item : getCurrentDownloads()) {
+ if (item.is(compilation)) {
+ downloadPending = false;
+ updateDownloadState(false, item);
+ return;
+ }
+ }
+ updateDownloadState(false, null);
+ }
}
diff --git
a/app/src/main/java/org/wikipedia/offline/CompilationDownloadControlView.java
b/app/src/main/java/org/wikipedia/offline/CompilationDownloadControlView.java
index 9a764b9..d3ab33c 100644
---
a/app/src/main/java/org/wikipedia/offline/CompilationDownloadControlView.java
+++
b/app/src/main/java/org/wikipedia/offline/CompilationDownloadControlView.java
@@ -1,19 +1,23 @@
package org.wikipedia.offline;
import android.annotation.TargetApi;
+import android.app.DownloadManager;
import android.content.Context;
-import android.graphics.drawable.Drawable;
+import android.content.DialogInterface;
import android.os.Build;
-import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
-import android.support.v4.content.ContextCompat;
+import android.support.v7.app.AlertDialog;
import android.util.AttributeSet;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
+import org.wikipedia.Constants;
import org.wikipedia.R;
+import org.wikipedia.util.FeedbackUtil;
+
+import java.util.concurrent.TimeUnit;
import butterknife.BindView;
import butterknife.ButterKnife;
@@ -25,10 +29,19 @@
@BindView(R.id.compilation_download_widget_progress_text) TextView
progressText;
@BindView(R.id.compilation_download_widget_progress_time_remaining)
TextView timeRemainingText;
@BindView(R.id.compilation_download_progress) ProgressBar progressBar;
- @BindView(R.id.compilation_download_widget_button_pause_resume) ImageView
pauseResumeButton;
+ @BindView(R.id.compilation_download_widget_button_cancel) ImageView
cancelButton;
- private Compilation comp;
- private boolean downloading;
+ @Nullable private Callback callback;
+
+ public interface Callback {
+ void onCancel();
+ }
+
+ public static boolean shouldShowControls(@Nullable DownloadManagerItem
item) {
+ return item != null && (item.status() == DownloadManager.STATUS_PENDING
+ || item.status() == DownloadManager.STATUS_RUNNING
+ || item.status() == DownloadManager.STATUS_PAUSED);
+ }
public CompilationDownloadControlView(Context context) {
super(context);
@@ -51,54 +64,54 @@
init();
}
- void setCompilation(@NonNull Compilation comp) {
- this.comp = comp;
- updateViews(0f, 0);
+ public void setCallback(@Nullable Callback callback) {
+ this.callback = callback;
}
- private void updateViews(float amtDownloaded, int minsRemaining) {
+ public void update(@Nullable DownloadManagerItem item) {
+ if (item == null) {
+ return;
+ }
+ if (item.status() == DownloadManager.STATUS_RUNNING) {
+ progressBar.setIndeterminate(false);
+ progressBar.setProgress((int) (item.bytesDownloaded()
+ * Constants.PROGRESS_BAR_MAX_VALUE / item.bytesTotal()));
+ timeRemainingText.setVisibility(VISIBLE);
+ } else {
+ progressBar.setIndeterminate(true);
+ timeRemainingText.setVisibility(GONE);
+ }
progressText.setText(getString(R.string.offline_compilation_download_progress_text,
- amtDownloaded, bytesToGB(comp.size())));
-
timeRemainingText.setText(getQuantityString(R.plurals.offline_compilation_download_time_remaining,
- minsRemaining, minsRemaining));
- }
-
- @OnClick(R.id.compilation_download_widget_button_pause_resume)
- void onPlayPauseToggleClicked() {
- togglePlayPause();
+ bytesToGB(item.bytesDownloaded()),
bytesToGB(item.bytesTotal())));
+ long bytesPerMin = item.bytesPerSec() * TimeUnit.MINUTES.toSeconds(1);
+ if (bytesPerMin >= 0) {
+ long minsRemaining = (item.bytesTotal() - item.bytesDownloaded())
/ bytesPerMin;
+
timeRemainingText.setText(getQuantityString(R.plurals.offline_compilation_download_time_remaining,
+ (int) minsRemaining, minsRemaining));
+ }
}
@OnClick(R.id.compilation_download_widget_button_cancel)
void onCancelClicked() {
- // cancelDownload();
- }
-
- private void togglePlayPause() {
- if (downloading) {
- // pauseDownload();
- } else {
- // resumeDownload();
- }
- downloading = !downloading;
- pauseResumeButton.setImageDrawable(downloading ? getPauseIcon() :
getResumeIcon());
- updateProgressBar();
+ new AlertDialog.Builder(getContext())
+ .setMessage(R.string.compilation_download_cancel_confirm)
+ .setPositiveButton(R.string.yes, new
DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialogInterface, int
i) {
+ if (callback != null) {
+ callback.onCancel();
+ }
+ }
+ })
+ .setNegativeButton(R.string.no, null)
+ .show();
}
private void init() {
inflate(getContext(), R.layout.view_compilation_download_widget, this);
ButterKnife.bind(this);
- }
-
- private void updateProgressBar() {
- progressBar.setIndeterminate(downloading);
- }
-
- private Drawable getPauseIcon() {
- return ContextCompat.getDrawable(getContext(),
R.drawable.ic_pause_white_24px);
- }
-
- private Drawable getResumeIcon() {
- return ContextCompat.getDrawable(getContext(),
R.drawable.ic_play_arrow_white_24px);
+ progressBar.setMax(Constants.PROGRESS_BAR_MAX_VALUE);
+ FeedbackUtil.setToolbarButtonLongPressToast(cancelButton);
}
private String getQuantityString(int id, int amt, Object... formatArgs) {
diff --git a/app/src/main/java/org/wikipedia/offline/DownloadManagerItem.java
b/app/src/main/java/org/wikipedia/offline/DownloadManagerItem.java
new file mode 100644
index 0000000..e2794c5
--- /dev/null
+++ b/app/src/main/java/org/wikipedia/offline/DownloadManagerItem.java
@@ -0,0 +1,60 @@
+package org.wikipedia.offline;
+
+import android.net.Uri;
+import android.support.annotation.NonNull;
+import android.text.TextUtils;
+
+import java.io.File;
+
+public class DownloadManagerItem {
+ private int id;
+ @NonNull private Uri uri;
+ private int status;
+ private long bytesDownloaded;
+ private long bytesTotal;
+ private long bytesPerSec;
+
+ public DownloadManagerItem(int id, @NonNull Uri uri, int status, long
bytesDownloaded,
+ long bytesTotal, long bytesPerSec) {
+ this.id = id;
+ this.uri = uri;
+ this.status = status;
+ this.bytesDownloaded = bytesDownloaded;
+ this.bytesTotal = bytesTotal;
+ this.bytesPerSec = bytesPerSec;
+ }
+
+ public int id() {
+ return id;
+ }
+
+ @NonNull
+ public Uri uri() {
+ return uri;
+ }
+
+ public int status() {
+ return status;
+ }
+
+ public long bytesDownloaded() {
+ return bytesDownloaded;
+ }
+
+ public long bytesTotal() {
+ return bytesTotal;
+ }
+
+ public long bytesPerSec() {
+ return bytesPerSec;
+ }
+
+ public boolean is(@NonNull Compilation compilation) {
+ if (!TextUtils.isEmpty(compilation.path())) {
+ return uri.getLastPathSegment().equals(new
File(compilation.path()).getName());
+ } else if (compilation.uri() != null) {
+ return
uri.getLastPathSegment().equals(compilation.uri().getLastPathSegment());
+ }
+ return false;
+ }
+}
diff --git
a/app/src/main/java/org/wikipedia/offline/DownloadManagerObserver.java
b/app/src/main/java/org/wikipedia/offline/DownloadManagerObserver.java
new file mode 100644
index 0000000..94ae235
--- /dev/null
+++ b/app/src/main/java/org/wikipedia/offline/DownloadManagerObserver.java
@@ -0,0 +1,125 @@
+package org.wikipedia.offline;
+
+import android.app.DownloadManager;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Handler;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.util.SparseArrayCompat;
+import android.text.TextUtils;
+
+import org.wikipedia.WikipediaApp;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+public class DownloadManagerObserver {
+ private static final int POLL_INTERVAL_MS = 500;
+
+ public interface Callback {
+ void onDownloadStatus(@NonNull List<DownloadManagerItem> items);
+ }
+
+ @Nullable private DownloadManager downloadManager;
+ @Nullable private Callback callback;
+ @Nullable private Handler handler;
+ private PollRunnable pollRunnable = new PollRunnable();
+
+ private SparseArrayCompat<Long> downloadAmountCache = new
SparseArrayCompat<>();
+ private SparseArrayCompat<Long> downloadTimeMillis = new
SparseArrayCompat<>();
+
+ public void register(@NonNull Callback callback) {
+ downloadManager = (DownloadManager)
WikipediaApp.getInstance().getSystemService(Context.DOWNLOAD_SERVICE);
+ handler = new Handler(WikipediaApp.getInstance().getMainLooper());
+ this.callback = callback;
+ handler.postDelayed(pollRunnable, POLL_INTERVAL_MS);
+ }
+
+ public void unregister() {
+ downloadManager = null;
+ callback = null;
+ handler = null;
+ }
+
+ public boolean isDownloading(@NonNull Compilation compilation) {
+ for (DownloadManagerItem item : queryCurrentDownloads()) {
+ if (item.is(compilation)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void remove(@NonNull Compilation compilation) {
+ if (downloadManager == null) {
+ return;
+ }
+ for (DownloadManagerItem item : queryCurrentDownloads()) {
+ if (item.is(compilation)) {
+ downloadManager.remove(item.id());
+ }
+ }
+ }
+
+ private class PollRunnable implements Runnable {
+ @Override
+ public void run() {
+ if (handler == null) {
+ return;
+ }
+ List<DownloadManagerItem> items = queryCurrentDownloads();
+ if (callback != null) {
+ callback.onDownloadStatus(items);
+ }
+
+ handler.postDelayed(pollRunnable, POLL_INTERVAL_MS);
+ }
+ }
+
+ @NonNull
+ private List<DownloadManagerItem> queryCurrentDownloads() {
+ List<DownloadManagerItem> items = new ArrayList<>();
+ if (downloadManager == null) {
+ return items;
+ }
+ DownloadManager.Query query = new DownloadManager.Query();
+ Cursor cursor = downloadManager.query(query);
+ while (cursor.moveToNext()) {
+ String mimeType =
cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_MEDIA_TYPE));
+ if (!Compilation.MIME_TYPE.equals(mimeType)) {
+ continue;
+ }
+
+ int id =
cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_ID));
+ String uri =
cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
+ int status =
cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));
+ long bytesDownloaded =
cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
+ long bytesTotal =
cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
+
+ long bytesPerSec = -1;
+
+ if (downloadAmountCache.get(id) == null) {
+ downloadAmountCache.put(id, bytesDownloaded);
+ downloadTimeMillis.put(id, System.currentTimeMillis());
+ }
+
+ if (System.currentTimeMillis() > downloadTimeMillis.get(id)
+ && bytesDownloaded > downloadAmountCache.get(id)) {
+ bytesPerSec = (bytesDownloaded - downloadAmountCache.get(id))
+ * TimeUnit.SECONDS.toMillis(1)
+ / (System.currentTimeMillis() -
downloadTimeMillis.get(id));
+ downloadAmountCache.put(id, bytesDownloaded);
+ downloadTimeMillis.put(id, System.currentTimeMillis());
+ }
+ if (!TextUtils.isEmpty(uri)) {
+ items.add(new DownloadManagerItem(id, Uri.parse(uri), status,
bytesDownloaded,
+ bytesTotal, bytesPerSec));
+ }
+ }
+ cursor.close();
+ return items;
+ }
+}
diff --git
a/app/src/main/java/org/wikipedia/offline/DownloadObserverFragment.java
b/app/src/main/java/org/wikipedia/offline/DownloadObserverFragment.java
new file mode 100644
index 0000000..de53b53
--- /dev/null
+++ b/app/src/main/java/org/wikipedia/offline/DownloadObserverFragment.java
@@ -0,0 +1,45 @@
+package org.wikipedia.offline;
+
+import android.support.annotation.NonNull;
+import android.support.v4.app.Fragment;
+
+import java.util.Collections;
+import java.util.List;
+
+public abstract class DownloadObserverFragment extends Fragment {
+
+ private DownloadManagerObserver downloadObserver = new
DownloadManagerObserver();
+ private DownloadObserverCallback downloadObserverCallback = new
DownloadObserverCallback();
+ @NonNull private List<DownloadManagerItem> currentDownloads =
Collections.emptyList();
+
+ @Override
+ public void onPause() {
+ downloadObserver.unregister();
+ super.onPause();
+ }
+
+ @Override
+ public void onResume() {
+ downloadObserver.register(downloadObserverCallback);
+ super.onResume();
+ }
+
+ protected abstract void onPollDownloads();
+
+ protected DownloadManagerObserver getDownloadObserver() {
+ return downloadObserver;
+ }
+
+ @NonNull
+ protected List<DownloadManagerItem> getCurrentDownloads() {
+ return currentDownloads;
+ }
+
+ private class DownloadObserverCallback implements
DownloadManagerObserver.Callback {
+ @Override
+ public void onDownloadStatus(@NonNull List<DownloadManagerItem> items)
{
+ currentDownloads = items;
+ onPollDownloads();
+ }
+ }
+}
diff --git
a/app/src/main/java/org/wikipedia/offline/LocalCompilationsFragment.java
b/app/src/main/java/org/wikipedia/offline/LocalCompilationsFragment.java
index 8ab4d20..d72dda4 100644
--- a/app/src/main/java/org/wikipedia/offline/LocalCompilationsFragment.java
+++ b/app/src/main/java/org/wikipedia/offline/LocalCompilationsFragment.java
@@ -3,11 +3,11 @@
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
-import android.support.v4.app.Fragment;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.view.ActionMode;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.SimpleItemAnimator;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -21,6 +21,7 @@
import org.wikipedia.R;
import org.wikipedia.activity.FragmentUtil;
import org.wikipedia.history.SearchActionModeCallback;
+import org.wikipedia.util.DimenUtil;
import org.wikipedia.util.ResourceUtil;
import org.wikipedia.views.DefaultViewHolder;
import org.wikipedia.views.DrawableItemDecoration;
@@ -36,7 +37,7 @@
import butterknife.OnClick;
import butterknife.Unbinder;
-public class LocalCompilationsFragment extends Fragment {
+public class LocalCompilationsFragment extends DownloadObserverFragment {
@BindView(R.id.compilation_list_container) View listContainer;
@BindView(R.id.compilation_list) RecyclerView recyclerView;
@BindView(R.id.search_empty_view) SearchEmptyView searchEmptyView;
@@ -76,6 +77,7 @@
recyclerView.setAdapter(adapter);
recyclerView.addItemDecoration(new DrawableItemDecoration(getContext(),
ResourceUtil.getThemedAttributeId(getContext(),
R.attr.list_separator_drawable), true));
+ ((SimpleItemAnimator)
recyclerView.getItemAnimator()).setSupportsChangeAnimations(false);
errorView.setBackClickListener(new View.OnClickListener() {
@Override
@@ -84,7 +86,6 @@
}
});
- beginUpdate();
return view;
}
@@ -96,6 +97,7 @@
@Override
public void onResume() {
+ beginUpdate();
adapter.notifyDataSetChanged();
super.onResume();
}
@@ -133,6 +135,11 @@
startActivity(RemoteCompilationsActivity.newIntent(getContext()));
}
+ @Override
+ protected void onPollDownloads() {
+ adapter.notifyItemRangeChanged(0, adapter.getItemCount());
+ }
+
public void onCompilationsRefreshed() {
updating = false;
lastError = null;
@@ -143,6 +150,15 @@
updating = false;
lastError = t;
update();
+ }
+
+ private void postBeginUpdate() {
+ listContainer.post(new Runnable() {
+ @Override
+ public void run() {
+ beginUpdate();
+ }
+ });
}
private void beginUpdate() {
@@ -203,12 +219,50 @@
private class CompilationItemHolder extends
DefaultViewHolder<PageItemView<Compilation>> {
private Compilation compilation;
+ private CompilationDownloadControlView controlView;
+ private boolean wasDownloading;
CompilationItemHolder(PageItemView<Compilation> itemView) {
super(itemView);
+ controlView = new
CompilationDownloadControlView(itemView.getContext());
+ itemView.addFooter(controlView);
+
controlView.setPadding(DimenUtil.roundedDpToPx(DimenUtil.getDimension(R.dimen.activity_horizontal_margin)),
+
DimenUtil.roundedDpToPx(DimenUtil.getDimension(R.dimen.list_item_footer_padding)),
+
DimenUtil.roundedDpToPx(DimenUtil.getDimension(R.dimen.activity_horizontal_margin)),
+
DimenUtil.roundedDpToPx(DimenUtil.getDimension(R.dimen.list_item_footer_padding)));
+
controlView.setBackgroundColor(ResourceUtil.getThemedColor(getContext(),
R.attr.inline_onboarding_background_color));
+ controlView.setCallback(new
CompilationDownloadControlView.Callback() {
+ @Override
+ public void onCancel() {
+ getDownloadObserver().remove(compilation);
+ }
+ });
}
void bindItem(Compilation compilation) {
+ DownloadManagerItem myItem = null;
+ for (DownloadManagerItem item : getCurrentDownloads()) {
+ if (item.is(compilation)) {
+ myItem = item;
+ break;
+ }
+ }
+ if (CompilationDownloadControlView.shouldShowControls(myItem)) {
+ controlView.setVisibility(View.VISIBLE);
+ controlView.update(myItem);
+ } else {
+ controlView.setVisibility(View.GONE);
+ }
+ if (myItem == null && wasDownloading) {
+ postBeginUpdate();
+ wasDownloading = false;
+ } else if (myItem != null) {
+ wasDownloading = true;
+ }
+ if (compilation == this.compilation) {
+ return;
+ }
+ wasDownloading = false;
this.compilation = compilation;
getView().setItem(compilation);
getView().setTitle(compilation.name());
@@ -265,11 +319,11 @@
}
@Override
- public void onActionClick(@Nullable Compilation item, @NonNull
PageItemView view) {
+ public void onActionClick(@Nullable Compilation item, @NonNull View
view) {
}
@Override
- public void onSecondaryActionClick(@Nullable Compilation item,
@NonNull PageItemView view) {
+ public void onSecondaryActionClick(@Nullable Compilation item,
@NonNull View view) {
}
}
diff --git a/app/src/main/java/org/wikipedia/offline/OfflineManager.java
b/app/src/main/java/org/wikipedia/offline/OfflineManager.java
index aeba0a7..e51c91d 100644
--- a/app/src/main/java/org/wikipedia/offline/OfflineManager.java
+++ b/app/src/main/java/org/wikipedia/offline/OfflineManager.java
@@ -11,6 +11,7 @@
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;
@@ -20,6 +21,7 @@
@Nullable private CompilationSearchTask searchTask;
private long lastSearchTime;
@NonNull private List<Compilation> compilations = new ArrayList<>();
+ @NonNull private List<Compilation> remoteCompilationCache =
Collections.emptyList();
public interface Callback {
void onCompilationsFound(@NonNull List<Compilation> compilations);
@@ -65,6 +67,13 @@
}
c.close();
}
+ for (Compilation result : results) {
+ for (Compilation remote : remoteCompilationCache) {
+ if (result.pathNameMatchesUri(remote.uri())) {
+ result.copyMetadataFrom(remote);
+ }
+ }
+ }
compilations.clear();
compilations.addAll(results);
Prefs.setCompilationCache(compilations);
@@ -80,10 +89,10 @@
}
void updateFromRemoteMetadata(@NonNull List<Compilation>
remoteCompilations) {
- for (Compilation remoteCompilation : remoteCompilations) {
+ remoteCompilationCache = remoteCompilations;
+ for (Compilation remoteCompilation : remoteCompilationCache) {
for (Compilation localCompilation : compilations) {
- if (remoteCompilation.uri() != null
- && new
File(localCompilation.path()).getName().equals(remoteCompilation.uri().getLastPathSegment()))
{
+ if
(localCompilation.pathNameMatchesUri(remoteCompilation.uri())) {
localCompilation.copyMetadataFrom(remoteCompilation);
}
}
diff --git
a/app/src/main/java/org/wikipedia/offline/RemoteCompilationsFragment.java
b/app/src/main/java/org/wikipedia/offline/RemoteCompilationsFragment.java
index d94851a..94115e2 100644
--- a/app/src/main/java/org/wikipedia/offline/RemoteCompilationsFragment.java
+++ b/app/src/main/java/org/wikipedia/offline/RemoteCompilationsFragment.java
@@ -6,11 +6,12 @@
import android.support.annotation.Nullable;
import android.support.design.widget.AppBarLayout;
import android.support.design.widget.CollapsingToolbarLayout;
-import android.support.v4.app.Fragment;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.view.ActionMode;
import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.PopupMenu;
import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.SimpleItemAnimator;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.view.LayoutInflater;
@@ -23,7 +24,9 @@
import org.wikipedia.R;
import org.wikipedia.WikipediaApp;
+import org.wikipedia.gallery.MediaDownloadReceiver;
import org.wikipedia.history.SearchActionModeCallback;
+import org.wikipedia.util.DimenUtil;
import org.wikipedia.util.ResourceUtil;
import org.wikipedia.views.DefaultViewHolder;
import org.wikipedia.views.DrawableItemDecoration;
@@ -38,7 +41,7 @@
import butterknife.ButterKnife;
import butterknife.Unbinder;
-public class RemoteCompilationsFragment extends Fragment {
+public class RemoteCompilationsFragment extends DownloadObserverFragment {
@BindView(R.id.compilation_list_toolbar_container) CollapsingToolbarLayout
toolbarLayout;
@BindView(R.id.compilation_list_app_bar) AppBarLayout appBarLayout;
@BindView(R.id.compilation_list_toolbar) Toolbar toolbar;
@@ -83,6 +86,7 @@
recyclerView.setAdapter(adapter);
recyclerView.addItemDecoration(new DrawableItemDecoration(getContext(),
ResourceUtil.getThemedAttributeId(getContext(),
R.attr.list_separator_drawable), true));
+ ((SimpleItemAnimator)
recyclerView.getItemAnimator()).setSupportsChangeAnimations(false);
errorView.setRetryClickListener(new View.OnClickListener() {
@Override
@@ -99,7 +103,6 @@
});
beginUpdate();
-
return view;
}
@@ -179,14 +182,49 @@
}
}
+ @Override
+ protected void onPollDownloads() {
+ adapter.notifyItemRangeChanged(0, adapter.getItemCount());
+ }
+
private class CompilationItemHolder extends
DefaultViewHolder<PageItemView<Compilation>> {
private Compilation compilation;
+ private CompilationDownloadControlView controlView;
CompilationItemHolder(PageItemView<Compilation> itemView) {
super(itemView);
+ controlView = new
CompilationDownloadControlView(itemView.getContext());
+ itemView.addFooter(controlView);
+
controlView.setPadding(DimenUtil.roundedDpToPx(DimenUtil.getDimension(R.dimen.activity_horizontal_margin)),
+
DimenUtil.roundedDpToPx(DimenUtil.getDimension(R.dimen.list_item_footer_padding)),
+
DimenUtil.roundedDpToPx(DimenUtil.getDimension(R.dimen.activity_horizontal_margin)),
+
DimenUtil.roundedDpToPx(DimenUtil.getDimension(R.dimen.list_item_footer_padding)));
+
controlView.setBackgroundColor(ResourceUtil.getThemedColor(getContext(),
R.attr.inline_onboarding_background_color));
+ controlView.setCallback(new
CompilationDownloadControlView.Callback() {
+ @Override
+ public void onCancel() {
+ getDownloadObserver().remove(compilation);
+ }
+ });
}
void bindItem(Compilation compilation) {
+ DownloadManagerItem myItem = null;
+ for (DownloadManagerItem item : getCurrentDownloads()) {
+ if (item.is(compilation)) {
+ myItem = item;
+ break;
+ }
+ }
+ if (CompilationDownloadControlView.shouldShowControls(myItem)) {
+ controlView.setVisibility(View.VISIBLE);
+ controlView.update(myItem);
+ } else {
+ controlView.setVisibility(View.GONE);
+ }
+ if (compilation == this.compilation) {
+ return;
+ }
this.compilation = compilation;
getView().setItem(compilation);
getView().setTitle(compilation.name());
@@ -243,11 +281,14 @@
}
@Override
- public void onActionClick(@Nullable Compilation item, @NonNull
PageItemView view) {
+ public void onActionClick(@Nullable Compilation item, @NonNull View
view) {
+ if (item != null) {
+ showCompilationOverflowMenu(item, view);
+ }
}
@Override
- public void onSecondaryActionClick(@Nullable Compilation item,
@NonNull PageItemView view) {
+ public void onSecondaryActionClick(@Nullable Compilation item,
@NonNull View view) {
}
}
@@ -278,9 +319,23 @@
private class CompilationCallback implements CompilationClient.Callback {
@Override
public void success(@NonNull List<Compilation> compilations) {
- allItems = compilations;
- updating = false;
OfflineManager.instance().updateFromRemoteMetadata(compilations);
+
+ allItems.clear();
+ for (Compilation remote : compilations) {
+ boolean haveLocal = false;
+ for (Compilation local :
OfflineManager.instance().compilations()) {
+ if (local.pathNameMatchesUri(remote.uri())) {
+ haveLocal = true;
+ allItems.add(local);
+ }
+ }
+ if (!haveLocal) {
+ allItems.add(remote);
+ }
+ }
+
+ updating = false;
setSearchQuery(currentSearchQuery);
}
@@ -292,6 +347,24 @@
}
}
+ private void showCompilationOverflowMenu(@NonNull final Compilation
compilation, @NonNull View anchorView) {
+ PopupMenu menu = new PopupMenu(anchorView.getContext(), anchorView);
+ menu.getMenuInflater().inflate(R.menu.menu_remote_compilation_item,
menu.getMenu());
+ menu.setOnMenuItemClickListener(new
PopupMenu.OnMenuItemClickListener() {
+ @Override
+ public boolean onMenuItemClick(MenuItem menuItem) {
+ switch (menuItem.getItemId()) {
+ case R.id.menu_compilation_download:
+ MediaDownloadReceiver.download(getContext(),
compilation);
+ return false;
+ default:
+ return false;
+ }
+ }
+ });
+ menu.show();
+ }
+
private AppCompatActivity getAppCompatActivity() {
return (AppCompatActivity) getActivity();
}
diff --git
a/app/src/main/java/org/wikipedia/readinglist/ReadingListFragment.java
b/app/src/main/java/org/wikipedia/readinglist/ReadingListFragment.java
index 4140b7f..4b2723a 100644
--- a/app/src/main/java/org/wikipedia/readinglist/ReadingListFragment.java
+++ b/app/src/main/java/org/wikipedia/readinglist/ReadingListFragment.java
@@ -708,7 +708,7 @@
}
@Override
- public void onActionClick(@Nullable ReadingListPage page, @NonNull
PageItemView view) {
+ public void onActionClick(@Nullable ReadingListPage page, @NonNull
View view) {
if (page == null || readingList == null) {
return;
}
@@ -717,7 +717,7 @@
}
@Override
- public void onSecondaryActionClick(@Nullable ReadingListPage page,
@NonNull PageItemView view) {
+ public void onSecondaryActionClick(@Nullable ReadingListPage page,
@NonNull View view) {
if (page != null) {
if (page.isSaving()) {
Toast.makeText(getContext(),
R.string.reading_list_article_save_in_progress, Toast.LENGTH_LONG).show();
diff --git a/app/src/main/java/org/wikipedia/views/PageItemView.java
b/app/src/main/java/org/wikipedia/views/PageItemView.java
index 1b76a42..80ac1fe 100644
--- a/app/src/main/java/org/wikipedia/views/PageItemView.java
+++ b/app/src/main/java/org/wikipedia/views/PageItemView.java
@@ -32,10 +32,11 @@
void onClick(@Nullable T item);
boolean onLongClick(@Nullable T item);
void onThumbClick(@Nullable T item);
- void onActionClick(@Nullable T item, @NonNull PageItemView view);
- void onSecondaryActionClick(@Nullable T item, @NonNull PageItemView
view);
+ void onActionClick(@Nullable T item, @NonNull View view);
+ void onSecondaryActionClick(@Nullable T item, @NonNull View view);
}
+ @BindView(R.id.page_list_item_container) ViewGroup containerView;
@BindView(R.id.page_list_item_title) TextView titleView;
@BindView(R.id.page_list_item_description) TextView descriptionView;
@BindView(R.id.page_list_item_image) SimpleDraweeView imageView;
@@ -104,6 +105,10 @@
}
}
+ public void addFooter(@NonNull View view) {
+ containerView.addView(view);
+ }
+
@OnClick(R.id.page_list_item_container) void onClick() {
if (callback != null) {
callback.onClick(item);
@@ -123,9 +128,9 @@
}
}
- @OnClick(R.id.page_list_item_action_primary) void onActionClick() {
+ @OnClick(R.id.page_list_item_action_primary) void onActionClick(View v) {
if (callback != null) {
- callback.onActionClick(item, this);
+ callback.onActionClick(item, v);
}
}
diff --git a/app/src/main/res/layout/fragment_compilation_detail.xml
b/app/src/main/res/layout/fragment_compilation_detail.xml
index ae21bc3..e96bf37 100644
--- a/app/src/main/res/layout/fragment_compilation_detail.xml
+++ b/app/src/main/res/layout/fragment_compilation_detail.xml
@@ -44,8 +44,7 @@
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
style="@style/App.Button.TransparentBlue"
- android:text="@string/offline_compilation_detail_button_download"
- android:visibility="gone"/>
+ android:text="@string/offline_compilation_detail_button_download"/>
<!-- Compilation download controls for when download is in progress -->
<org.wikipedia.offline.CompilationDownloadControlView
diff --git a/app/src/main/res/layout/item_page_list_entry.xml
b/app/src/main/res/layout/item_page_list_entry.xml
index dd28a69..d0a1edb 100644
--- a/app/src/main/res/layout/item_page_list_entry.xml
+++ b/app/src/main/res/layout/item_page_list_entry.xml
@@ -9,7 +9,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/list_item_vertical_padding"
- android:paddingBottom="@dimen/list_item_vertical_padding"
android:background="?attr/selectableItemBackground"
android:gravity="top">
@@ -33,7 +32,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/list_item_horizontal_padding"
- android:layout_marginLeft="@dimen/list_item_horizontal_padding">
+ android:layout_marginLeft="@dimen/list_item_horizontal_padding"
+ android:layout_marginBottom="@dimen/list_item_vertical_padding">
<FrameLayout
android:id="@+id/page_list_item_image_container"
diff --git a/app/src/main/res/layout/view_compilation_download_widget.xml
b/app/src/main/res/layout/view_compilation_download_widget.xml
index c18ba95..897cbe1 100644
--- a/app/src/main/res/layout/view_compilation_download_widget.xml
+++ b/app/src/main/res/layout/view_compilation_download_widget.xml
@@ -47,22 +47,13 @@
</LinearLayout>
<ImageView
- android:id="@+id/compilation_download_widget_button_pause_resume"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
- android:padding="2dp"
- android:tint="?attr/feed_text_secondary_color"
- app:srcCompat="@drawable/ic_pause_white_24px"
-
android:contentDescription="@string/compilation_download_widget_button_pause_resume_hint"/>
-
- <ImageView
android:id="@+id/compilation_download_widget_button_cancel"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:padding="2dp"
android:tint="?attr/feed_text_secondary_color"
app:srcCompat="@drawable/ic_clear_white_24px"
+ android:clickable="true"
+ android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/compilation_download_widget_button_cancel_hint"/>
</merge>
\ No newline at end of file
diff --git a/app/src/main/res/menu/menu_remote_compilation_item.xml
b/app/src/main/res/menu/menu_remote_compilation_item.xml
new file mode 100644
index 0000000..c1d932d
--- /dev/null
+++ b/app/src/main/res/menu/menu_remote_compilation_item.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@+id/menu_compilation_download"
+ android:title="@string/remote_compilation_download"/>
+</menu>
diff --git a/app/src/main/res/values-qq/strings.xml
b/app/src/main/res/values-qq/strings.xml
index 67311f7..dca2e0e 100644
--- a/app/src/main/res/values-qq/strings.xml
+++ b/app/src/main/res/values-qq/strings.xml
@@ -418,6 +418,7 @@
<string name="offline_compilation_detail_date_size">Information about the
date and size of a compilation. The %1$s and %2$.2f symbols will be replaced
with the date and size. Only the \"GB\" unit needs to be translated (see
storage_size_gb).</string>
<string name="offline_compilation_detail_button_download">Label for a button
for downloading an offline compilation. The %.2f symbol is replaced with the
compilation size, in gigabytes (GB).</string>
<string name="offline_compilation_detail_button_remove">Label for a button
for removing an offline compilation from storage on the user\'s device.</string>
+ <string name="remote_compilation_download">Menu label for downloading a
selected compilation.</string>
<string name="storage_size_format">Format specifier where %.2f represents a
decimal number with two significant figures to the right of the decimal
point.</string>
<string name="storage_size_gb">Abbreviated form for the unit of a
gigabyte.</string>
<string name="storage_size_free">Label that says how much free space is
available on the device. The %.2f symbol is replaced with the number in
gigabytes.</string>
@@ -430,6 +431,7 @@
</plurals>
<string
name="compilation_download_widget_button_pause_resume_hint">Accessibility hint
text for a button to pause or resume an offline compilation file
download</string>
<string name="compilation_download_widget_button_cancel_hint">Accessibility
hint text for a button to cancel an offline compilation file download</string>
+ <string name="compilation_download_cancel_confirm">Confirmation message
asking if the user really wants to cancel the download.</string>
<string name="onboarding_skip">Button label to skip the current onboarding
or tutorial screen.\n{{Identical|Skip}}</string>
<string name="onboarding_continue">Button label to continue to the next
onboarding or tutorial screen.\n{{Identical|Continue}}</string>
<string name="onboarding_get_started">Button label to finish the current
onboarding or tutorial workflow.\n{{Identical|Get started}}</string>
diff --git a/app/src/main/res/values/dimens.xml
b/app/src/main/res/values/dimens.xml
index f272f7c..9be6da9 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -18,6 +18,7 @@
<dimen name="list_item_vertical_padding">16dp</dimen>
<dimen name="list_item_horizontal_padding">16dp</dimen>
+ <dimen name="list_item_footer_padding">8dp</dimen>
<!-- Default font size for WebView text based on system settings -->
<dimen name="textSize">16sp</dimen>
diff --git a/app/src/main/res/values/strings.xml
b/app/src/main/res/values/strings.xml
index 68726c7..3e16fd5 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -472,6 +472,7 @@
<string name="offline_compilation_detail_date_size">%1$s · %2$.2f
GB</string>
<string name="offline_compilation_detail_button_download">Download · %.2f
GB</string>
<string name="offline_compilation_detail_button_remove">Remove</string>
+ <string name="remote_compilation_download">Download</string>
<string name="storage_size_format">%.2f</string>
<string name="storage_size_gb">GB</string>
<string name="storage_size_free">%.2f GB free</string>
@@ -484,6 +485,7 @@
</plurals>
<string name="compilation_download_widget_button_pause_resume_hint">Pause
or resume download</string>
<string name="compilation_download_widget_button_cancel_hint">Cancel
download</string>
+ <string name="compilation_download_cancel_confirm">Are you sure you want
to cancel downloading this compilation?</string>
<!-- /Offline -->
<!-- Onboarding -->
--
To view, visit https://gerrit.wikimedia.org/r/367894
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I2741c14894d990c59c30bd685546c2dd61c5cab3
Gerrit-PatchSet: 12
Gerrit-Project: apps/android/wikipedia
Gerrit-Branch: master
Gerrit-Owner: Dbrant <[email protected]>
Gerrit-Reviewer: Brion VIBBER <[email protected]>
Gerrit-Reviewer: Dbrant <[email protected]>
Gerrit-Reviewer: Mholloway <[email protected]>
Gerrit-Reviewer: jenkins-bot <>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits