Add delete history option to contextual action bar

This commit is contained in:
Abdul Wadood 2018-06-30 06:28:09 +05:30 committed by Isaac Hutt
parent 55c232d79c
commit 1f28683e5f
21 changed files with 242 additions and 74 deletions

View File

@ -1,5 +1,9 @@
package org.kiwix.kiwixmobile.data;
import org.kiwix.kiwixmobile.di.qualifiers.Computation;
import org.kiwix.kiwixmobile.di.qualifiers.IO;
import org.kiwix.kiwixmobile.di.qualifiers.MainThread;
import javax.inject.Singleton;
import dagger.Module;

View File

@ -22,5 +22,7 @@ public interface DataSource {
Single<List<History>> getDateCategorizedHistory(boolean showHistoryCurrentBook);
void saveHistory(String file, String favicon, String url, String title, long timeStamp);
Completable saveHistory(String file, String favicon, String url, String title, long timeStamp);
Completable deleteHistory(List<History> historyList);
}

View File

@ -4,6 +4,8 @@ import org.kiwix.kiwixmobile.data.local.dao.BookDao;
import org.kiwix.kiwixmobile.data.local.dao.HistoryDao;
import org.kiwix.kiwixmobile.data.local.entity.History;
import org.kiwix.kiwixmobile.data.local.dao.NetworkLanguageDao;
import org.kiwix.kiwixmobile.di.qualifiers.IO;
import org.kiwix.kiwixmobile.di.qualifiers.MainThread;
import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity;
import org.kiwix.kiwixmobile.models.Language;
@ -108,11 +110,20 @@ public class Repository implements DataSource {
history = histories.get(position);
}
return histories;
});
})
.subscribeOn(io)
.observeOn(mainThread);
}
@Override
public void saveHistory(String file, String favicon, String url, String title, long timeStamp) {
historyDao.saveHistory(file, favicon, url, title, timeStamp);
public Completable saveHistory(String file, String favicon, String url, String title, long timeStamp) {
return Completable.fromAction(() -> historyDao.saveHistory(file, favicon, url, title, timeStamp))
.subscribeOn(io);
}
@Override
public Completable deleteHistory(List<History> historyList) {
return Completable.fromAction(() -> historyDao.deleteHistory(historyList))
.subscribeOn(io);
}
}

View File

@ -8,6 +8,7 @@ import org.kiwix.kiwixmobile.data.local.KiwixDatabase;
import org.kiwix.kiwixmobile.data.local.entity.History;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
@ -24,7 +25,7 @@ public class HistoryDao {
.setHistoryUrl(url).setHistoryTitle(title).setTimeStamp(timeStamp));
}
public ArrayList<History> getHistoryList(boolean showHistoryCurrentBook) {
public List<History> getHistoryList(boolean showHistoryCurrentBook) {
ArrayList<History> histories = new ArrayList<>();
Query query = Query.select();
if (showHistoryCurrentBook) {
@ -44,4 +45,10 @@ public class HistoryDao {
}
return histories;
}
public void deleteHistory(List<History> historyList) {
for (History history : historyList) {
kiwixDatabase.deleteWhere(History.class, History.TIME_STAMP.eq(history.getTimeStamp()));
}
}
}

View File

@ -1,4 +1,4 @@
package org.kiwix.kiwixmobile.data;
package org.kiwix.kiwixmobile.di.qualifiers;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

View File

@ -1,4 +1,4 @@
package org.kiwix.kiwixmobile.data;
package org.kiwix.kiwixmobile.di.qualifiers;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

View File

@ -1,4 +1,4 @@
package org.kiwix.kiwixmobile.data;
package org.kiwix.kiwixmobile.di.qualifiers;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

View File

@ -4,12 +4,15 @@ import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.view.ActionMode;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.SearchView;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.ImageView;
import org.kiwix.kiwixmobile.R;
import org.kiwix.kiwixmobile.base.BaseActivity;
@ -25,11 +28,16 @@ import javax.inject.Inject;
import butterknife.BindView;
import static org.kiwix.kiwixmobile.library.LibraryAdapter.createBitmapFromEncodedString;
import static org.kiwix.kiwixmobile.utils.Constants.EXTRA_CHOSE_X_URL;
public class HistoryActivity extends BaseActivity implements HistoryContract.View,
HistoryAdapter.OnItemClickListener {
private final List<History> historyList = new ArrayList<>();
private final List<History> fullHistory = new ArrayList<>();
private final List<History> deleteList = new ArrayList<>();
@BindView(R.id.activity_history_toolbar)
Toolbar toolbar;
@BindView(R.id.activity_history_recycler_view)
@ -37,9 +45,62 @@ public class HistoryActivity extends BaseActivity implements HistoryContract.Vie
@Inject
HistoryContract.Presenter presenter;
private boolean refreshAdapter = true;
private HistoryAdapter historyAdapter;
private List<History> historyList = new ArrayList<>();
private List<History> fullHistory = new ArrayList<>();
private ActionMode actionMode;
private final ActionMode.Callback actionModeCallback = new ActionMode.Callback() {
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
mode.getMenuInflater().inflate(R.menu.menu_context_history, menu);
return true;
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
refreshAdapter = false;
switch (item.getItemId()) {
case R.id.menu_context_history_delete:
fullHistory.removeAll(deleteList);
for (History history : deleteList) {
int position = historyList.indexOf(history);
/*
Delete the current category header if there are no items after the current one or
if the item being removed is between two category headers.
*/
if (position - 1 >= 0 && historyList.get(position - 1) == null &&
(position + 1 >= historyList.size() ||
(position + 1 < historyList.size() && historyList.get(position + 1) == null))) {
historyList.remove(position - 1);
historyAdapter.notifyItemRemoved(position - 1);
}
position = historyList.indexOf(history);
historyList.remove(history);
historyAdapter.notifyItemRemoved(position);
historyAdapter.notifyItemRangeChanged(position, historyAdapter.getItemCount());
}
mode.finish();
return true;
}
return false;
}
@Override
public void onDestroyActionMode(ActionMode mode) {
if (deleteList.size() != 0) {
presenter.deleteHistory(new ArrayList<>(deleteList));
deleteList.clear();
}
actionMode = null;
if (refreshAdapter) {
historyAdapter.notifyDataSetChanged();
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -52,7 +113,7 @@ public class HistoryActivity extends BaseActivity implements HistoryContract.Vie
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
}
historyAdapter = new HistoryAdapter(this);
historyAdapter = new HistoryAdapter(historyList, deleteList, this);
recyclerView.setAdapter(historyAdapter);
}
@ -80,6 +141,10 @@ public class HistoryActivity extends BaseActivity implements HistoryContract.Vie
public boolean onQueryTextChange(String newText) {
historyList.clear();
historyList.addAll(fullHistory);
if ("".equals(newText)) {
historyAdapter.notifyDataSetChanged();
return true;
}
presenter.filterHistory(historyList, newText);
return true;
}
@ -110,30 +175,60 @@ public class HistoryActivity extends BaseActivity implements HistoryContract.Vie
@Override
public void updateHistoryList(List<History> historyList) {
this.historyList = historyList;
fullHistory.clear();
fullHistory.addAll(historyList);
notifyHistoryListFiltered(this.historyList);
notifyHistoryListFiltered(historyList);
}
@Override
public void notifyHistoryListFiltered(List<History> historyList) {
historyAdapter.setHistoryList(historyList);
this.historyList.clear();
this.historyList.addAll(historyList);
historyAdapter.notifyDataSetChanged();
}
@Override
public void openHistoryUrl(String url, String zimFile) {
Intent intent = new Intent(this, MainActivity.class);
intent.putExtra(EXTRA_CHOSE_X_URL, url);
if (!zimFile.equals(ZimContentProvider.getZimFile())) {
intent.setData(Uri.fromFile(new File(zimFile)));
}
if (Settings.System.getInt(getContentResolver(), Settings.Global.ALWAYS_FINISH_ACTIVITIES, 0) == 1) {
startActivity(intent);
finish();
public void onItemClick(ImageView favicon, History history) {
if (actionMode == null) {
Intent intent = new Intent(this, MainActivity.class);
intent.putExtra(EXTRA_CHOSE_X_URL, history.getHistoryUrl());
if (!history.getZimFile().equals(ZimContentProvider.getZimFile())) {
intent.setData(Uri.fromFile(new File(history.getZimFile())));
}
if (Settings.System.getInt(getContentResolver(), Settings.Global.ALWAYS_FINISH_ACTIVITIES, 0) == 1) {
startActivity(intent);
finish();
} else {
setResult(RESULT_OK, intent);
finish();
}
} else {
setResult(RESULT_OK, intent);
finish();
toggleSelection(favicon, history);
}
}
@Override
public boolean onItemLongClick(ImageView favicon, History history) {
if (actionMode != null) {
return false;
}
actionMode = startSupportActionMode(actionModeCallback);
refreshAdapter = true;
toggleSelection(favicon, history);
return true;
}
private void toggleSelection(ImageView favicon, History history) {
if (deleteList.remove(history)) {
favicon.setImageBitmap(createBitmapFromEncodedString(history.getFavicon(), this));
} else {
favicon.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_check_circle_blue_24dp));
deleteList.add(history);
}
actionMode.setTitle(getString(R.string.selected_items, deleteList.size()));
if (deleteList.size() == 0) {
actionMode.finish();
}
}
}

View File

@ -1,6 +1,7 @@
package org.kiwix.kiwixmobile.history;
import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
@ -10,10 +11,8 @@ import android.widget.TextView;
import org.kiwix.kiwixmobile.R;
import org.kiwix.kiwixmobile.data.local.entity.History;
import org.kiwix.kiwixmobile.library.LibraryAdapter;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
@ -21,12 +20,17 @@ import java.util.Locale;
import butterknife.BindView;
import butterknife.ButterKnife;
class HistoryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static int TYPE_ITEM = 1;
private List<History> historyList = new ArrayList<>();
private OnItemClickListener itemClickListener;
import static org.kiwix.kiwixmobile.library.LibraryAdapter.createBitmapFromEncodedString;
HistoryAdapter(OnItemClickListener itemClickListener) {
class HistoryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int TYPE_ITEM = 1;
private final List<History> historyList;
private final OnItemClickListener itemClickListener;
private final List<History> deleteList;
HistoryAdapter(List<History> historyList, List<History> deleteList, OnItemClickListener itemClickListener) {
this.historyList = historyList;
this.deleteList = deleteList;
this.itemClickListener = itemClickListener;
}
@ -48,10 +52,16 @@ class HistoryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
History history = historyList.get(position);
Item item = (Item) holder;
item.title.setText(history.getHistoryTitle());
item.favicon.setImageBitmap(LibraryAdapter.createBitmapFromEncodedString(history.getFavicon(),
item.favicon.getContext()));
item.itemView.setOnClickListener(v -> itemClickListener
.openHistoryUrl(history.getHistoryUrl(), history.getZimFile()));
if (deleteList.contains(history)) {
item.favicon.setImageDrawable(ContextCompat.getDrawable(item.favicon.getContext(),
R.drawable.ic_check_circle_blue_24dp));
} else {
item.favicon.setImageBitmap(createBitmapFromEncodedString(history.getFavicon(),
item.favicon.getContext()));
}
item.itemView.setOnClickListener(v -> itemClickListener.onItemClick(item.favicon, history));
item.itemView.setOnLongClickListener(v ->
itemClickListener.onItemLongClick(item.favicon, history));
} else {
DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.getDefault());
((Category) holder).date.setText(dateFormat.format(new Date(historyList.get(position + 1)
@ -69,13 +79,10 @@ class HistoryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
return historyList.size();
}
public void setHistoryList(List<History> historyList) {
this.historyList = historyList;
notifyDataSetChanged();
}
interface OnItemClickListener {
void openHistoryUrl(String historyUrl, String zimFile);
void onItemClick(ImageView favicon, History history);
boolean onItemLongClick(ImageView favicon, History history);
}
class Item extends RecyclerView.ViewHolder {

View File

@ -16,5 +16,7 @@ interface HistoryContract {
void loadHistory(boolean showHistoryCurrentBook);
void filterHistory(List<History> historyList, String newText);
void deleteHistory(List<History> deleteList);
}
}

View File

@ -3,18 +3,18 @@ package org.kiwix.kiwixmobile.history;
import android.util.Log;
import org.kiwix.kiwixmobile.base.BasePresenter;
import org.kiwix.kiwixmobile.data.Computation;
import org.kiwix.kiwixmobile.data.DataSource;
import org.kiwix.kiwixmobile.data.IO;
import org.kiwix.kiwixmobile.data.MainThread;
import org.kiwix.kiwixmobile.data.local.entity.History;
import org.kiwix.kiwixmobile.di.PerActivity;
import org.kiwix.kiwixmobile.di.qualifiers.Computation;
import org.kiwix.kiwixmobile.di.qualifiers.MainThread;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import io.reactivex.CompletableObserver;
import io.reactivex.Observable;
import io.reactivex.Scheduler;
import io.reactivex.SingleObserver;
@ -23,16 +23,15 @@ import io.reactivex.disposables.Disposable;
@PerActivity
class HistoryPresenter extends BasePresenter<HistoryContract.View> implements HistoryContract.Presenter {
private DataSource dataSource;
private Scheduler io;
private Scheduler mainThread;
private Scheduler computation;
private final DataSource dataSource;
private final Scheduler mainThread;
private final Scheduler computation;
private Disposable disposable;
@Inject
HistoryPresenter(DataSource dataSource, @IO Scheduler io, @MainThread Scheduler mainThread,
HistoryPresenter(DataSource dataSource, @MainThread Scheduler mainThread,
@Computation Scheduler computation) {
this.dataSource = dataSource;
this.io = io;
this.mainThread = mainThread;
this.computation = computation;
}
@ -40,11 +39,13 @@ class HistoryPresenter extends BasePresenter<HistoryContract.View> implements Hi
@Override
public void loadHistory(boolean showHistoryCurrentBook) {
dataSource.getDateCategorizedHistory(showHistoryCurrentBook)
.subscribeOn(io)
.observeOn(mainThread)
.subscribe(new SingleObserver<List<History>>() {
@Override
public void onSubscribe(Disposable d) {
if (disposable != null && !disposable.isDisposed()) {
disposable.dispose();
}
disposable = d;
compositeDisposable.add(d);
}
@ -93,4 +94,25 @@ class HistoryPresenter extends BasePresenter<HistoryContract.View> implements Hi
}
});
}
@Override
public void deleteHistory(List<History> deleteList) {
dataSource.deleteHistory(deleteList)
.subscribe(new CompletableObserver() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onComplete() {
}
@Override
public void onError(Throwable e) {
Log.e("HistoryPresenter", e.toString());
}
});
}
}

View File

@ -4,7 +4,6 @@ import android.util.Log;
import org.kiwix.kiwixmobile.base.BasePresenter;
import org.kiwix.kiwixmobile.data.DataSource;
import org.kiwix.kiwixmobile.data.IO;
import org.kiwix.kiwixmobile.di.PerActivity;
import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity;
@ -12,9 +11,7 @@ import java.util.List;
import javax.inject.Inject;
import io.reactivex.Completable;
import io.reactivex.CompletableObserver;
import io.reactivex.Scheduler;
import io.reactivex.SingleObserver;
import io.reactivex.disposables.Disposable;
@ -26,12 +23,10 @@ import io.reactivex.disposables.Disposable;
class MainPresenter extends BasePresenter<MainContract.View> implements MainContract.Presenter {
private DataSource dataSource;
private Scheduler io;
@Inject
MainPresenter(DataSource dataSource, @IO Scheduler io) {
MainPresenter(DataSource dataSource) {
this.dataSource = dataSource;
this.io = io;
}
@Override
@ -63,8 +58,7 @@ class MainPresenter extends BasePresenter<MainContract.View> implements MainCont
@Override
public void saveHistory(String file, String favicon, String url, String title, long timeStamp) {
Completable.fromAction(() -> dataSource.saveHistory(file, favicon, url, title, timeStamp))
.subscribeOn(io)
dataSource.saveHistory(file, favicon, url, title, timeStamp)
.subscribe(new CompletableObserver() {
@Override
public void onSubscribe(Disposable d) {

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#4285F4"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM10,17l-5,-5 1.41,-1.41L10,14.17l7.59,-7.59L19,8l-9,9z" />
</vector>

View File

@ -1,9 +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="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z" />
</vector>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/menu_context_history_delete"
android:icon="@drawable/ic_delete_white_24dp"
android:title="@string/delete"
app:showAsAction="ifRoom" />
</menu>

View File

@ -5,7 +5,6 @@
<item
android:id="@+id/menu_history_search"
android:icon="@drawable/action_search"
android:queryHint="Search History"
android:title="@string/search_label"
app:actionViewClass="android.support.v7.widget.SearchView"
app:iconifiedByDefault="true"

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="AppTheme.Base" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">@color/primary</item>
<item name="colorPrimaryDark">@color/primary_dark</item>
@ -9,6 +10,7 @@
<item name="android:textCursorDrawable">@null</item>
<item name="windowNoTitle">true</item>
<item name="windowActionBar">false</item>
<item name="actionModeBackground">@color/actionModeBackground</item>
<item name="progressBackgroundColor">@color/primary_dark</item>
<item name="listBackground">@drawable/list_bg</item>
<item name="android:windowContentOverlay">@null</item>
@ -26,5 +28,5 @@
<item name="android:textColorTertiary">@color/primary</item>
<item name="colorControlNormal">@color/accent</item>
<item name="android:windowTranslucentStatus">true</item>
</style>
</style>
</resources>

View File

@ -11,6 +11,7 @@
<item name="windowNoTitle">true</item>
<item name="windowActionBar">false</item>
<item name="progressBackgroundColor">@color/primary_dark</item>
<item name="actionModeBackground">@color/actionModeBackground</item>
<item name="listBackground">@drawable/list_bg</item>
<item name="android:windowContentOverlay">@null</item>
<item name="windowActionModeOverlay">true</item>

View File

@ -29,4 +29,5 @@
<color name="video_label">#651FFF</color>
<color name="blue800">#1565c0</color>
<color name="blueTransparent">#962e7ac4</color>
<color name="actionModeBackground">#4285F4</color>
</resources>

View File

@ -210,4 +210,5 @@
<string name="history">History</string>
<string name="history_from_current_book">History from current book</string>
<string name="search_history">Search history</string>
<string name="selected_items">%1$d selected</string>
</resources>

View File

@ -12,6 +12,7 @@
<item name="android:textCursorDrawable">@null</item>
<item name="windowNoTitle">true</item>
<item name="windowActionBar">false</item>
<item name="actionModeBackground">@color/actionModeBackground</item>
<item name="progressBackgroundColor">@color/primary_dark</item>
<item name="listBackground">@drawable/list_bg</item>
<item name="android:windowContentOverlay">@null</item>
@ -62,13 +63,12 @@
<style name="SearchViewStyle" parent="Widget.AppCompat.SearchView" />
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar">
<item name="android:textColorPrimary">@color/primary_light</item>
<item name="actionMenuTextColor">@android:color/white</item>
</style>
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat">
<item name="android:textColorPrimary">@color/primary_light</item>
<item name="actionMenuTextColor">@android:color/white</item>
<item name="android:textColorPrimary">@color/primary_text</item>
<item name="actionMenuTextColor">@color/primary_text</item>
</style>
<style name="AppTheme.Dialog" parent="Theme.AppCompat.Light.Dialog.Alert">