jenkins-bot has submitted this change and it was merged. ( https://gerrit.wikimedia.org/r/388269 )
Change subject: Allow users to cancel/pause/resume the downloading process when syncing - Use BroadcastReceiver to receive the actions from notification bar - For < API19, use regular activity to receive the cancel action from notification bar - When login / syncing, ...................................................................... Allow users to cancel/pause/resume the downloading process when syncing - Use BroadcastReceiver to receive the actions from notification bar - For < API19, use regular activity to receive the cancel action from notification bar - When login / syncing, the notification bar will be displayed. Please follow the QA steps on Phabricator Bug: T177518 Change-Id: If53a948051a30fe76e3a164b03c1167271fa7f1a --- M app/src/main/AndroidManifest.xml M app/src/main/java/org/wikipedia/Constants.java M app/src/main/java/org/wikipedia/activity/BaseActivity.java M app/src/main/java/org/wikipedia/feed/FeedFragment.java M app/src/main/java/org/wikipedia/login/LoginActivity.java M app/src/main/java/org/wikipedia/readinglist/database/ReadingListTable.java M app/src/main/java/org/wikipedia/readinglist/page/ReadingListPage.java M app/src/main/java/org/wikipedia/readinglist/page/database/ReadingListPageDao.java M app/src/main/java/org/wikipedia/readinglist/sync/ReadingListSynchronizer.java A app/src/main/java/org/wikipedia/savedpages/SavedPageSyncBroadcastReceiver.java A app/src/main/java/org/wikipedia/savedpages/SavedPageSyncNotification.java M app/src/main/java/org/wikipedia/savedpages/SavedPageSyncService.java M app/src/main/java/org/wikipedia/util/MathUtil.java A app/src/main/res/drawable/ic_cancel_black_24dp.xml A app/src/main/res/drawable/ic_file_download_white_24dp.xml A app/src/main/res/drawable/ic_pause_black_24dp.xml A app/src/main/res/drawable/ic_play_arrow_black_24dp.xml M app/src/main/res/values-qq/strings.xml M app/src/main/res/values/strings.xml 19 files changed, 454 insertions(+), 23 deletions(-) Approvals: Dbrant: Looks good to me, approved jenkins-bot: Verified diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 02b4856..e01cd5d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -346,5 +346,9 @@ android:name=".savedpages.SavedPageSyncService" android:permission="android.permission.BIND_JOB_SERVICE" /> + <!-- TODO: Investigate the addAction buttons with same Intent class issue --> + <receiver android:name=".savedpages.SavedPageSyncBroadcastReceiver$SyncCancelReceiver" /> + <receiver android:name=".savedpages.SavedPageSyncBroadcastReceiver$SyncPauseReceiver" /> + </application> </manifest> diff --git a/app/src/main/java/org/wikipedia/Constants.java b/app/src/main/java/org/wikipedia/Constants.java index 8d42d6e..8f26db9 100644 --- a/app/src/main/java/org/wikipedia/Constants.java +++ b/app/src/main/java/org/wikipedia/Constants.java @@ -36,6 +36,9 @@ public static final String INTENT_EXTRA_REVERT_QNUMBER = "revertQNumber"; public static final String INTENT_EXTRA_DELETE_READING_LIST = "deleteReadingList"; + public static final String INTENT_EXTRA_NOTIFICATION_SYNC_PAUSE = "syncPause"; + public static final String INTENT_EXTRA_NOTIFICATION_SYNC_CANCEL = "syncCancel"; + public static final int PROGRESS_BAR_MAX_VALUE = 10_000; public static final int MAX_SUGGESTION_RESULTS = 3; diff --git a/app/src/main/java/org/wikipedia/activity/BaseActivity.java b/app/src/main/java/org/wikipedia/activity/BaseActivity.java index 0e805cf..1b0a2e9 100644 --- a/app/src/main/java/org/wikipedia/activity/BaseActivity.java +++ b/app/src/main/java/org/wikipedia/activity/BaseActivity.java @@ -198,7 +198,7 @@ public void onReceive(Context context, Intent intent) { if (DeviceUtil.isOnline()) { onGoOnline(); - ReadingListSynchronizer.instance().syncSavedPages(); + ReadingListSynchronizer.instance().syncSavedPages(true); } else { onGoOffline(); } @@ -220,7 +220,7 @@ } @Subscribe public void on(NetworkConnectEvent event) { - ReadingListSynchronizer.instance().syncSavedPages(); + ReadingListSynchronizer.instance().syncSavedPages(false); } @Subscribe public void on(ThemeChangeEvent event) { diff --git a/app/src/main/java/org/wikipedia/feed/FeedFragment.java b/app/src/main/java/org/wikipedia/feed/FeedFragment.java index afacfa9..e4ba419 100644 --- a/app/src/main/java/org/wikipedia/feed/FeedFragment.java +++ b/app/src/main/java/org/wikipedia/feed/FeedFragment.java @@ -154,7 +154,7 @@ getCallback().updateToolbarElevation(shouldElevateToolbar()); } - ReadingListSynchronizer.instance().sync(); + ReadingListSynchronizer.instance().sync(true); return view; } diff --git a/app/src/main/java/org/wikipedia/login/LoginActivity.java b/app/src/main/java/org/wikipedia/login/LoginActivity.java index 5960daa..dc0c012 100644 --- a/app/src/main/java/org/wikipedia/login/LoginActivity.java +++ b/app/src/main/java/org/wikipedia/login/LoginActivity.java @@ -242,7 +242,7 @@ hideSoftKeyboard(LoginActivity.this); setResult(RESULT_LOGIN_SUCCESS); - ReadingListSynchronizer.instance().sync(); + ReadingListSynchronizer.instance().sync(true); finish(); } else if (result.fail()) { String message = result.getMessage(); diff --git a/app/src/main/java/org/wikipedia/readinglist/database/ReadingListTable.java b/app/src/main/java/org/wikipedia/readinglist/database/ReadingListTable.java index 0dd886b..dda7fbd 100644 --- a/app/src/main/java/org/wikipedia/readinglist/database/ReadingListTable.java +++ b/app/src/main/java/org/wikipedia/readinglist/database/ReadingListTable.java @@ -157,7 +157,7 @@ } finally { c.close(); } - ReadingListSynchronizer.instance().syncSavedPages(); + ReadingListSynchronizer.instance().syncSavedPages(true); return null; } }, null); diff --git a/app/src/main/java/org/wikipedia/readinglist/page/ReadingListPage.java b/app/src/main/java/org/wikipedia/readinglist/page/ReadingListPage.java index ccbefa1..1de9372 100644 --- a/app/src/main/java/org/wikipedia/readinglist/page/ReadingListPage.java +++ b/app/src/main/java/org/wikipedia/readinglist/page/ReadingListPage.java @@ -20,6 +20,13 @@ .build(); } + public static ReadingListPage fromDiskRow(@NonNull ReadingListPageDiskRow diskRow) { + return builder() + .copy(diskRow.dat()) + .diskStatus(diskRow.status()) + .build(); + } + public static Builder builder() { return new Builder(); } diff --git a/app/src/main/java/org/wikipedia/readinglist/page/database/ReadingListPageDao.java b/app/src/main/java/org/wikipedia/readinglist/page/database/ReadingListPageDao.java index d9f7f15..f3e29aa 100644 --- a/app/src/main/java/org/wikipedia/readinglist/page/database/ReadingListPageDao.java +++ b/app/src/main/java/org/wikipedia/readinglist/page/database/ReadingListPageDao.java @@ -124,8 +124,19 @@ diskDao.markOutdated(row); } + @NonNull public synchronized Collection<ReadingListPageDiskRow> collectPausedDiskTransactions() { + Collection<ReadingListPageDiskRow> rows = queryPausedDiskTransactions(); + return rows; + } + @NonNull public synchronized Collection<ReadingListPageDiskRow> startDiskTransaction() { Collection<ReadingListPageDiskRow> rows = queryPendingDiskTransactions(); + diskDao.startTransaction(rows); + return rows; + } + + @NonNull public synchronized Collection<ReadingListPageDiskRow> startPausedDiskTransaction() { + Collection<ReadingListPageDiskRow> rows = queryPausedDiskTransactions(); diskDao.startTransaction(rows); return rows; } @@ -176,6 +187,25 @@ return rows; } + @NonNull private Collection<ReadingListPageDiskRow> queryPausedDiskTransactions() { + Uri uri = ReadingListPageContract.DiskWithPage.URI; + String selection = Sql.SELECT_ROWS_PAUSED_DISK_TRANSACTION; + final String[] selectionArgs = null; + final String order = null; + Cursor cursor = client().select(uri, selection, + selectionArgs, order); + + Collection<ReadingListPageDiskRow> rows = new ArrayList<>(); + try { + while (cursor.moveToNext()) { + rows.add(ReadingListPageDiskRow.fromCursor(cursor)); + } + } finally { + cursor.close(); + } + return rows; + } + // TODO: expose HTTP DAO methods. private ReadingListPageDao() { @@ -206,5 +236,9 @@ private static String SELECT_ROWS_PENDING_DISK_TRANSACTION = ":transactionIdCol == :noTransactionId" .replaceAll(":transactionIdCol", ReadingListPageContract.DiskWithPage.DISK_TRANSACTION_ID.qualifiedName()) .replaceAll(":noTransactionId", String.valueOf(AsyncConstant.NO_TRANSACTION_ID)); + + private static String SELECT_ROWS_PAUSED_DISK_TRANSACTION = ":diskStatusCol == :diskStatus" + .replaceAll(":diskStatusCol", ReadingListPageContract.DiskWithPage.DISK_STATUS.qualifiedName()) + .replaceAll(":diskStatus", String.valueOf(DiskStatus.OUTDATED.code())); } } diff --git a/app/src/main/java/org/wikipedia/readinglist/sync/ReadingListSynchronizer.java b/app/src/main/java/org/wikipedia/readinglist/sync/ReadingListSynchronizer.java index d559bc8..5bf25ea 100644 --- a/app/src/main/java/org/wikipedia/readinglist/sync/ReadingListSynchronizer.java +++ b/app/src/main/java/org/wikipedia/readinglist/sync/ReadingListSynchronizer.java @@ -39,6 +39,7 @@ public class ReadingListSynchronizer { private static final String READING_LISTS_SYNC_OPTION = "userjs-reading-lists-v1"; private static final ReadingListSynchronizer INSTANCE = new ReadingListSynchronizer(); + private boolean showNotification = false; private final Handler syncHandler = new Handler(WikipediaApp.getInstance().getMainLooper()); private final SyncRunnable syncRunnable = new SyncRunnable(); @@ -56,31 +57,38 @@ syncHandler.postDelayed(syncRunnable, TimeUnit.SECONDS.toMillis(1)); } + public void sync(@NonNull boolean showNotification) { + this.showNotification = showNotification; + sync(); + } + public void sync() { if (!ReleaseUtil.isPreBetaRelease() // TODO: remove when ready for beta/production || !AccountUtil.isLoggedIn() || !(isReadingListSyncEnabled() || isReadingListsRemoteDeletePending())) { - syncSavedPages(); + syncSavedPages(showNotification); + showNotification = false; L.d("Skipped sync of reading lists."); return; } - UserOptionDataClientSingleton.instance().get(new UserOptionDataClient.UserInfoCallback() { - @Override - public void success(@NonNull final UserInfo info) { - CallbackTask.execute(new CallbackTask.Task<Void>() { - @Override public Void execute() throws Throwable { + UserOptionDataClientSingleton.instance().get((UserInfo info) -> + CallbackTask.execute(() -> { syncFromRemote(info); - syncSavedPages(); + syncSavedPages(showNotification); + showNotification = false; return null; - } - }); - } - }); + }) + ); + } + + public void syncSavedPages(@NonNull boolean showNotification) { + SavedPageSyncService.enqueueService(WikipediaApp.getInstance(), showNotification); } public void syncSavedPages() { - SavedPageSyncService.enqueueService(WikipediaApp.getInstance()); + SavedPageSyncService.enqueueService(WikipediaApp.getInstance(), false); } + private synchronized void syncFromRemote(@NonNull UserInfo info) { long localRev = Prefs.getReadingListSyncRev(); @@ -229,7 +237,7 @@ .mtime(now) .atime(now) .description(remoteList.desc()) - .pages(new ArrayList<ReadingListPage>()) + .pages(new ArrayList<>()) .build(); ReadingList.DAO.addList(localList); localLists.add(localList); diff --git a/app/src/main/java/org/wikipedia/savedpages/SavedPageSyncBroadcastReceiver.java b/app/src/main/java/org/wikipedia/savedpages/SavedPageSyncBroadcastReceiver.java new file mode 100644 index 0000000..f5a0492 --- /dev/null +++ b/app/src/main/java/org/wikipedia/savedpages/SavedPageSyncBroadcastReceiver.java @@ -0,0 +1,33 @@ +package org.wikipedia.savedpages; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +import org.wikipedia.Constants; + +public class SavedPageSyncBroadcastReceiver{ + + + public static class SyncCancelReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + + if (intent.getBooleanExtra(Constants.INTENT_EXTRA_NOTIFICATION_SYNC_CANCEL, false)) { + // cancel sync service + SavedPageSyncNotification.getInstance().setCancelSyncDownload(); + } + } + } + + public static class SyncPauseReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + + if (intent.getBooleanExtra(Constants.INTENT_EXTRA_NOTIFICATION_SYNC_PAUSE, false)) { + // pause or resume sync service + SavedPageSyncNotification.getInstance().setPauseSyncDownload(); + } + } + } +} diff --git a/app/src/main/java/org/wikipedia/savedpages/SavedPageSyncNotification.java b/app/src/main/java/org/wikipedia/savedpages/SavedPageSyncNotification.java new file mode 100644 index 0000000..aafcfe5 --- /dev/null +++ b/app/src/main/java/org/wikipedia/savedpages/SavedPageSyncNotification.java @@ -0,0 +1,219 @@ +package org.wikipedia.savedpages; + +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.graphics.BitmapFactory; +import android.os.Build; +import android.support.annotation.NonNull; +import android.support.v4.app.NotificationCompat; + +import org.wikipedia.Constants; +import org.wikipedia.R; +import org.wikipedia.WikipediaApp; +import org.wikipedia.util.MathUtil; + + +public final class SavedPageSyncNotification{ + + private static final SavedPageSyncNotification INSTANCE = new SavedPageSyncNotification(); + private NotificationCompat.Builder mBuilder; + private NotificationManager mNotificationManager; + private Context mContext; + private static final String CHANNEL_ID = "SYNCING_CHANNEL"; + private static final int NOTIFICATION_ID_FOR_SYNCING = 1001; + private boolean syncCanceled; + private boolean syncPaused; + private boolean syncNotificationVisible; + private int queueSize; + private int syncCount; + + private SavedPageSyncNotification() { + mContext = WikipediaApp.getInstance(); + mBuilder = new NotificationCompat.Builder(mContext, CHANNEL_ID); + mNotificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); + } + + public static SavedPageSyncNotification getInstance() { + return INSTANCE; + } + + + public void setCancelSyncDownload() { + syncCanceled = true; + doCancel(); + SavedPageSyncService.cancelService(mContext); + } + + public void clearAll() { + queueSize = 0; + syncCount = 0; + syncCanceled = false; + syncPaused = false; + } + + public void setPauseSyncDownload() { + + if (isSyncPaused()) { + syncPaused = false; + SavedPageSyncService.resumeService(mContext); + } else { + syncPaused = true; + getInstance().show(false); + } + } + + public void setQueueSize(int queueSize) { + this.queueSize = queueSize; + } + + public void setVisible(boolean visible) { + syncNotificationVisible = visible; + } + + public boolean isVisible() { + return syncNotificationVisible; + } + + public boolean isSyncCanceled() { + return syncCanceled; + } + + public boolean isSyncPaused() { + return syncPaused; + } + + + public void setup() { + + if (queueSize > 0) { + int notificationIcon; + String notificationTitle; + String notificationDescription; + String notificationInfo; + + // Notification channel ( >= API 26 ) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + CharSequence name = mContext.getString(R.string.notification_channel_name); + String description = mContext.getString(R.string.notification_channel_description); + int importance = NotificationManager.IMPORTANCE_LOW; + NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID, name, importance); + mChannel.setDescription(description); + mChannel.setSound(null, null); + mNotificationManager.createNotificationChannel(mChannel); + } + + // setup notification content + mBuilder = new NotificationCompat.Builder(mContext, CHANNEL_ID); + + // build action buttons + int pauseButton = R.string.notification_syncing_pause_button; + int pauseButtonIcon = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? R.drawable.ic_pause_black_24dp : android.R.color.transparent; + if (getInstance().isSyncPaused()) { + pauseButton = R.string.notification_syncing_resume_button; + pauseButtonIcon = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? R.drawable.ic_play_arrow_black_24dp : android.R.color.transparent; + } + + // The reason why registering two receivers because the "addAction" has issues when the target classes are the same. + // If the target classes are e.g.: "MainActivity.java", and we have put different data with different key, + // The MainActivity can only receive the last data we have assigned. + // e.g. assign PAUSE and then CANCEL. the action of both buttons will be assigned as CANCEL + NotificationCompat.Action actionPause = actionBuilder( + SavedPageSyncBroadcastReceiver.SyncPauseReceiver.class, + Constants.INTENT_EXTRA_NOTIFICATION_SYNC_PAUSE, + pauseButtonIcon, + pauseButton); + + + NotificationCompat.Action actionCancel = actionBuilder( + SavedPageSyncBroadcastReceiver.SyncCancelReceiver.class, + Constants.INTENT_EXTRA_NOTIFICATION_SYNC_CANCEL, + Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? R.drawable.ic_cancel_black_24dp : android.R.color.transparent, + R.string.notification_syncing_cancel_button); + + + notificationIcon = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? R.drawable.ic_file_download_white_24dp : R.mipmap.launcher; + notificationTitle = String.format(mContext.getString(R.string.notification_syncing_title), queueSize); + notificationInfo = MathUtil.percentage(syncCount, queueSize) + "%"; + notificationDescription = String.format(mContext.getString(R.string.notification_syncing_description), queueSize - syncCount); + + + mBuilder.setSmallIcon(notificationIcon) + .setLargeIcon(BitmapFactory.decodeResource(mContext.getResources(), notificationIcon)) + .setStyle(new NotificationCompat.BigTextStyle().bigText(notificationDescription)) + .setContentTitle(notificationTitle) + .setContentText(notificationDescription) + .setPriority(NotificationCompat.PRIORITY_DEFAULT) + .setSound(null) + .setContentInfo(notificationInfo) + .setOngoing(true) + .addAction(actionPause) + .addAction(actionCancel); + + // Reference: https://developer.android.com/reference/android/app/Notification.Builder.html#setContentInfo(java.lang.CharSequence) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + mBuilder.setSubText(notificationInfo); + } + } + } + + + public void show(boolean init) { + if (queueSize > 0) { + if (init && !isSyncCanceled() && !isSyncPaused()) { + syncCount = 0; + setup(); + mBuilder.setProgress(queueSize, syncCount, true); + doNotify(); + syncCount++; + } else { + setup(); + if (!isSyncPaused()) { + syncCount++; + } + mBuilder.setProgress(queueSize, syncCount, false); + doNotify(); + if (queueSize <= syncCount) { + doCancel(); + } + } + } + } + + private void doCancel() { + mNotificationManager.cancel(NOTIFICATION_ID_FOR_SYNCING); + } + + private void doNotify() { + if (isVisible() && !isSyncCanceled()) { + mNotificationManager.notify(NOTIFICATION_ID_FOR_SYNCING, mBuilder.build()); + } + } + + + @NonNull private NotificationCompat.Action actionBuilder(@NonNull Class<?> targetClass, + @NonNull String intentExtra, + @NonNull int buttonDrawable, + @NonNull int buttonText) { + return new NotificationCompat.Action.Builder(buttonDrawable, mContext.getString(buttonText), pendingIntentBuilder(targetClass, intentExtra, true)).build(); + } + + @NonNull private PendingIntent pendingIntentBuilder(@NonNull Class<?> targetClass, + @NonNull String intentExtra, + boolean isBroadcast) { + Intent resultIntent = new Intent(mContext, targetClass); + resultIntent.putExtra(intentExtra, true); + + if (!isBroadcast) { + resultIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + } + + PendingIntent resultPendingIntent = isBroadcast + ? PendingIntent.getBroadcast(mContext, 0, resultIntent, PendingIntent.FLAG_UPDATE_CURRENT) + : PendingIntent.getActivity(mContext, 0, resultIntent, PendingIntent.FLAG_UPDATE_CURRENT); + + return resultPendingIntent; + } +} diff --git a/app/src/main/java/org/wikipedia/savedpages/SavedPageSyncService.java b/app/src/main/java/org/wikipedia/savedpages/SavedPageSyncService.java index fbc4237..63f52f8 100644 --- a/app/src/main/java/org/wikipedia/savedpages/SavedPageSyncService.java +++ b/app/src/main/java/org/wikipedia/savedpages/SavedPageSyncService.java @@ -54,6 +54,9 @@ public class SavedPageSyncService extends JobIntentService { // Unique job ID for this service (do not duplicate). private static final int JOB_ID = 1000; + private static final String RESUME_OR_PAUSE_SYNCING = "RESUME_OR_PAUSE_SYNCING"; + private static final String CANCEL_SYNCING = "CANCEL_SYNCING"; + private static final String SHOW_NOTIFICATION_WHEN_SYNCING = "SHOW_NOTIFICATION_WHEN_SYNCING"; @NonNull private ReadingListPageDao dao; @NonNull private final CacheDelegate cacheDelegate = new CacheDelegate(SAVE_CACHE); @@ -61,19 +64,57 @@ = new PageImageUrlParser(new ImageTagParser(), new PixelDensityDescriptorParser()); private long blockSize; + private SavedPageSyncNotification savedPageSyncNotification; + public SavedPageSyncService() { dao = ReadingListPageDao.instance(); blockSize = FileUtil.blockSize(cacheDelegate.diskLruCache().getDirectory()); + savedPageSyncNotification = SavedPageSyncNotification.getInstance(); } - public static void enqueueService(@NonNull Context context) { - enqueueWork(context, SavedPageSyncService.class, JOB_ID, - new Intent(context, SavedPageSyncService.class)); + + public static void enqueueService(@NonNull Context context, @NonNull boolean showNotification) { + Intent intent = new Intent(context, SavedPageSyncService.class); + intent.putExtra(RESUME_OR_PAUSE_SYNCING, false); + intent.putExtra(CANCEL_SYNCING, false); + intent.putExtra(SHOW_NOTIFICATION_WHEN_SYNCING, showNotification); + enqueueWork(context, SavedPageSyncService.class, JOB_ID, intent); + } + + public static void resumeService(@NonNull Context context) { + Intent intent = new Intent(context, SavedPageSyncService.class); + intent.putExtra(RESUME_OR_PAUSE_SYNCING, true); + intent.putExtra(CANCEL_SYNCING, false); + intent.putExtra(SHOW_NOTIFICATION_WHEN_SYNCING, true); + enqueueWork(context, SavedPageSyncService.class, JOB_ID, intent); + } + + public static void cancelService(@NonNull Context context) { + Intent intent = new Intent(context, SavedPageSyncService.class); + intent.putExtra(RESUME_OR_PAUSE_SYNCING, false); + intent.putExtra(CANCEL_SYNCING, true); + intent.putExtra(SHOW_NOTIFICATION_WHEN_SYNCING, true); + enqueueWork(context, SavedPageSyncService.class, JOB_ID, intent); } @Override protected void onHandleWork(@NonNull Intent intent) { + savedPageSyncNotification.setVisible(intent.getBooleanExtra(SHOW_NOTIFICATION_WHEN_SYNCING, false)); + if (!intent.getBooleanExtra(CANCEL_SYNCING, false)) { + setupSyncEvent(intent.getBooleanExtra(RESUME_OR_PAUSE_SYNCING, false)); + } else { + setupCancelSyncEvent(); + } + } + + private void setupSyncEvent(boolean resumeTransaction) { List<ReadingListPageDiskRow> queue = new ArrayList<>(); - Collection<ReadingListPageDiskRow> rows = dao.startDiskTransaction(); + Collection<ReadingListPageDiskRow> rows; + + if (!resumeTransaction) { + rows = dao.startDiskTransaction(); + } else { + rows = dao.startPausedDiskTransaction(); + } for (ReadingListPageDiskRow row : rows) { switch (row.status()) { @@ -94,7 +135,23 @@ + row.status().name()); } } + + savedPageSyncNotification.setQueueSize(queue.size()); saveNewEntries(queue); + } + + private void setupCancelSyncEvent() { + Collection<ReadingListPageDiskRow> rows = dao.collectPausedDiskTransactions(); + + // not sure this is a better way to "actually" delete the cache and turn the status into ONLINE rather than just change the disk status + for (ReadingListPageDiskRow row : rows) { + ReadingListPage tempPage = ReadingListPage.fromDiskRow(row); + if (tempPage != null) { + ReadingListData.instance().setPageOffline(tempPage, false); + } + } + + savedPageSyncNotification.clearAll(); } private void sendSyncEvent() { @@ -102,6 +159,9 @@ // received on the main thread. WikipediaApp.getInstance().getBus().post(new ReadingListSyncEvent()); } + + + private void deleteRow(@NonNull ReadingListPageDiskRow row) { ReadingListPageRow dat = row.dat(); @@ -139,7 +199,8 @@ private void saveNewEntries(List<ReadingListPageDiskRow> queue) { sendSyncEvent(); - while (!queue.isEmpty()) { + savedPageSyncNotification.show(true); + while (!queue.isEmpty() && !savedPageSyncNotification.isSyncCanceled() && !savedPageSyncNotification.isSyncPaused()) { // Pick off the DB row that we'll be working on... ReadingListPageDiskRow tempRow = queue.remove(0); @@ -190,6 +251,7 @@ if (success) { dao.completeDiskTransaction(updatedRow); sendSyncEvent(); + savedPageSyncNotification.show(false); } else { dao.failDiskTransaction(updatedRow); } diff --git a/app/src/main/java/org/wikipedia/util/MathUtil.java b/app/src/main/java/org/wikipedia/util/MathUtil.java index 04e5002..99edcc4 100644 --- a/app/src/main/java/org/wikipedia/util/MathUtil.java +++ b/app/src/main/java/org/wikipedia/util/MathUtil.java @@ -1,6 +1,10 @@ package org.wikipedia.util; +import android.support.annotation.NonNull; + public final class MathUtil { + + private static final int PERCENTAGE_BASE = 100; public static float constrain(float f, float min, float max) { return Math.min(Math.max(min, f), max); @@ -25,6 +29,10 @@ } } + public static int percentage(@NonNull float numerator, @NonNull float denominator) { + return (int) (numerator / denominator * PERCENTAGE_BASE); + } + private MathUtil() { } } diff --git a/app/src/main/res/drawable/ic_cancel_black_24dp.xml b/app/src/main/res/drawable/ic_cancel_black_24dp.xml new file mode 100644 index 0000000..ede4b71 --- /dev/null +++ b/app/src/main/res/drawable/ic_cancel_black_24dp.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="#FF000000" + android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/> +</vector> diff --git a/app/src/main/res/drawable/ic_file_download_white_24dp.xml b/app/src/main/res/drawable/ic_file_download_white_24dp.xml new file mode 100644 index 0000000..e43b864 --- /dev/null +++ b/app/src/main/res/drawable/ic_file_download_white_24dp.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M19,9h-4V3H9v6H5l7,7 7,-7zM5,18v2h14v-2H5z"/> +</vector> diff --git a/app/src/main/res/drawable/ic_pause_black_24dp.xml b/app/src/main/res/drawable/ic_pause_black_24dp.xml new file mode 100644 index 0000000..bb28a6c --- /dev/null +++ b/app/src/main/res/drawable/ic_pause_black_24dp.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="#FF000000" + android:pathData="M6,19h4L10,5L6,5v14zM14,5v14h4L18,5h-4z"/> +</vector> diff --git a/app/src/main/res/drawable/ic_play_arrow_black_24dp.xml b/app/src/main/res/drawable/ic_play_arrow_black_24dp.xml new file mode 100644 index 0000000..bf9b895 --- /dev/null +++ b/app/src/main/res/drawable/ic_play_arrow_black_24dp.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="#FF000000" + android:pathData="M8,5v14l11,-7z"/> +</vector> diff --git a/app/src/main/res/values-qq/strings.xml b/app/src/main/res/values-qq/strings.xml index 755b5fc..f813fd4 100644 --- a/app/src/main/res/values-qq/strings.xml +++ b/app/src/main/res/values-qq/strings.xml @@ -371,6 +371,14 @@ <string name="notification_reverted">Text of notification when an edit made by the user is reverted. The \"%1$s\" symbol corresponds to the user who reverted the edit, and the \"%2$s\" symbol is the title of the page that was edited.</string> <string name="notification_thanks_title">Title for notification when the user receives a Thanks from another user.</string> <string name="notification_thanks">Text of notification when the user receives a Thanks from another user. The \"%1$s\" symbol corresponds to the user who sent the thanks, and the \"%2$s\" symbol is the title of the page for which the user was thanked.</string> + <string name="notification_channel_name">Name of the notification channel</string> + <string name="notification_channel_description">Description of the notification channel</string> + <string name="notification_syncing_title">Title for notification when the syncing process begins. The \"%1$d\" symbol corresponds to the amount of articles.</string> + <string name="notification_syncing_description">Text of notification shows the remaining articles. The \"%1$d\" symbol corresponds to the amount of remaining articles.</string> + <string name="notification_syncing_pause_button">Text of button on notification bar to pause the syncing process</string> + <string name="notification_syncing_resume_button">Text of button on notification bar to resume the syncing process</string> + <string name="notification_syncing_cancel_button">Text of button on notification bar to cancel the syncing process</string> + <string name="view_continue_reading_card_title">Label for card in the feed that reminds the user to continue reading an article from their browsing history.</string> <string name="view_because_you_read_card_title">Label for card in the feed that gives the user reading suggestions based on an article from their browsing history.</string> <string name="view_random_card_title">Title of a card that the user can click to view a randomly selected Wikipedia article</string> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8fd8c41..9877e8d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -402,6 +402,13 @@ <string name="notification_reverted"><![CDATA[Your edit to the article <strong>%2$s</strong> was reverted by <em>%1$s</em>.]]></string> <string name="notification_thanks_title">You\'ve been thanked!</string> <string name="notification_thanks"><![CDATA[<em>%1$s</em> thanked you for your edit on the page <strong>%2$s</strong>]]></string> + <string name="notification_channel_name">Syncing</string> + <string name="notification_channel_description">Syncing progress</string> + <string name="notification_syncing_title">Syncing %1$d articles…</string> + <string name="notification_syncing_description">%1$d articles left</string> + <string name="notification_syncing_pause_button">PAUSE</string> + <string name="notification_syncing_resume_button">RESUME</string> + <string name="notification_syncing_cancel_button">CANCEL</string> <!-- /Notifications --> <!-- The Feed --> @@ -575,4 +582,6 @@ <item quantity="one">Last year</item> <item quantity="other">%d years ago</item> </plurals> + <!-- /On This Day --> + </resources> -- To view, visit https://gerrit.wikimedia.org/r/388269 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: If53a948051a30fe76e3a164b03c1167271fa7f1a Gerrit-PatchSet: 13 Gerrit-Project: apps/android/wikipedia Gerrit-Branch: master Gerrit-Owner: Cooltey <cf...@wikimedia.org> Gerrit-Reviewer: Brion VIBBER <br...@wikimedia.org> Gerrit-Reviewer: Cooltey <cf...@wikimedia.org> Gerrit-Reviewer: Dbrant <dbr...@wikimedia.org> Gerrit-Reviewer: Sharvaniharan <sha...@wikimedia.org> Gerrit-Reviewer: jenkins-bot <> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits