From 09dd044f3de86e17c51a358774dfcd8b411cb94a Mon Sep 17 00:00:00 2001 From: "Andrei.Rosca" Date: Mon, 11 Jun 2018 14:44:00 +0200 Subject: [PATCH 1/2] undo delete --- .../newpipe/download/DeleteManager.java | 178 ++++++++++++++++++ .../newpipe/download/DownloadActivity.java | 51 ++++- .../giga/ui/adapter/MissionAdapter.java | 57 +++--- .../giga/ui/fragment/MissionsFragment.java | 45 ++++- 4 files changed, 291 insertions(+), 40 deletions(-) create mode 100644 app/src/main/java/org/schabi/newpipe/download/DeleteManager.java diff --git a/app/src/main/java/org/schabi/newpipe/download/DeleteManager.java b/app/src/main/java/org/schabi/newpipe/download/DeleteManager.java new file mode 100644 index 000000000..210cf668d --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/download/DeleteManager.java @@ -0,0 +1,178 @@ +package org.schabi.newpipe.download; + +import android.app.Activity; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.design.widget.BaseTransientBottomBar; +import android.support.design.widget.Snackbar; +import android.view.View; + +import org.schabi.newpipe.R; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import io.reactivex.Completable; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; +import io.reactivex.subjects.PublishSubject; +import us.shandian.giga.get.DownloadManager; +import us.shandian.giga.get.DownloadMission; + +public class DeleteManager { + + private static final String KEY_STATE = "delete_manager_state"; + + private View mView; + private HashSet mPendingMap; + private List mDisposableList; + private DownloadManager mDownloadManager; + private PublishSubject publishSubject = PublishSubject.create(); + + DeleteManager(Activity activity) { + mPendingMap = new HashSet<>(); + mDisposableList = new ArrayList<>(); + mView = activity.findViewById(android.R.id.content); + } + + public Observable getUndoObservable() { + return publishSubject; + } + + public boolean contains(@NonNull DownloadMission mission) { + return mPendingMap.contains(mission.url); + } + + public void add(@NonNull DownloadMission mission) { + mPendingMap.add(mission.url); + + if (mPendingMap.size() == 1) { + showUndoDeleteSnackbar(mission); + } + } + + public void setDownloadManager(@NonNull DownloadManager downloadManager) { + mDownloadManager = downloadManager; + + if (mPendingMap.size() < 1) { + //nothing to do + return; + } + + showUndoDeleteSnackbar(); + } + + public void restoreState(@Nullable Bundle savedInstanceState) { + if (savedInstanceState == null) { + // nothing to do + return; + } + + List list = savedInstanceState.getStringArrayList(KEY_STATE); + if (list != null) { + mPendingMap.addAll(list); + } + } + + public void saveState(@Nullable Bundle outState) { + if (outState == null) { + // nothing to do + return; + } + + for (Disposable disposable : mDisposableList) { + disposable.dispose(); + } + + outState.putStringArrayList(KEY_STATE, new ArrayList<>(mPendingMap)); + } + + private void showUndoDeleteSnackbar() { + if (mPendingMap.size() < 1) { + // nothing to do + return; + } + + String url = mPendingMap.iterator().next(); + + for (int i = 0; i < mDownloadManager.getCount(); i++) { + DownloadMission mission = mDownloadManager.getMission(i); + if (url.equals(mission.url)) { + showUndoDeleteSnackbar(mission); + break; + } + } + } + + private void showUndoDeleteSnackbar(@NonNull DownloadMission mission) { + final Snackbar snackbar = Snackbar.make(mView, mission.name, Snackbar.LENGTH_INDEFINITE); + final Disposable disposable = Observable.timer(3, TimeUnit.SECONDS) + .subscribeOn(AndroidSchedulers.mainThread()) + .subscribe(l -> snackbar.dismiss()); + + mDisposableList.add(disposable); + + snackbar.setAction(R.string.undo, v -> { + mPendingMap.remove(mission.url); + publishSubject.onNext(mission); + disposable.dispose(); + snackbar.dismiss(); + }); + + snackbar.addCallback(new BaseTransientBottomBar.BaseCallback() { + @Override + public void onDismissed(Snackbar transientBottomBar, int event) { + if (!disposable.isDisposed()) { + mPendingMap.remove(mission.url); + Completable.fromAction(() -> deletePending(mission)) + .subscribeOn(Schedulers.io()) + .subscribe(); + } + snackbar.removeCallback(this); + mDisposableList.remove(disposable); + showUndoDeleteSnackbar(); + } + }); + + snackbar.show(); + } + + public void deletePending() { + if (mPendingMap.size() < 1) { + // nothing to do + return; + } + + HashSet idSet = new HashSet<>(); + for (int i = 0; i < mDownloadManager.getCount(); i++) { + if (contains(mDownloadManager.getMission(i))) { + idSet.add(i); + } + } + + for (Integer id : idSet) { + mDownloadManager.deleteMission(id); + } + + mPendingMap.clear(); + } + + private void deletePending(@NonNull DownloadMission mission) { + if (!contains(mission)) { + // nothing to do + return; + } + + for (int i = 0; i < mDownloadManager.getCount(); i++) { + if (mission.url.equals(mDownloadManager.getMission(i).url)) { + mDownloadManager.deleteMission(i); + break; + } + } + } +} diff --git a/app/src/main/java/org/schabi/newpipe/download/DownloadActivity.java b/app/src/main/java/org/schabi/newpipe/download/DownloadActivity.java index 6512f5270..f9c48a533 100644 --- a/app/src/main/java/org/schabi/newpipe/download/DownloadActivity.java +++ b/app/src/main/java/org/schabi/newpipe/download/DownloadActivity.java @@ -15,12 +15,17 @@ import org.schabi.newpipe.R; import org.schabi.newpipe.settings.SettingsActivity; import org.schabi.newpipe.util.ThemeHelper; +import io.reactivex.Completable; +import io.reactivex.schedulers.Schedulers; import us.shandian.giga.service.DownloadManagerService; import us.shandian.giga.ui.fragment.AllMissionsFragment; import us.shandian.giga.ui.fragment.MissionsFragment; public class DownloadActivity extends AppCompatActivity { + private static final String MISSIONS_FRAGMENT_TAG = "fragment_tag"; + private DeleteManager mDeleteManager; + @Override protected void onCreate(Bundle savedInstanceState) { // Service @@ -42,21 +47,35 @@ public class DownloadActivity extends AppCompatActivity { actionBar.setDisplayShowTitleEnabled(true); } - // Fragment - getWindow().getDecorView().getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { - @Override - public void onGlobalLayout() { - updateFragments(); - getWindow().getDecorView().getViewTreeObserver().removeGlobalOnLayoutListener(this); - } - }); + mDeleteManager = new DeleteManager(this); + mDeleteManager.restoreState(savedInstanceState); + + MissionsFragment fragment = (MissionsFragment) getFragmentManager().findFragmentByTag(MISSIONS_FRAGMENT_TAG); + if (fragment != null) { + fragment.setDeleteManager(mDeleteManager); + } else { + getWindow().getDecorView().getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + updateFragments(); + getWindow().getDecorView().getViewTreeObserver().removeGlobalOnLayoutListener(this); + } + }); + } + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + mDeleteManager.saveState(outState); + super.onSaveInstanceState(outState); } private void updateFragments() { - MissionsFragment fragment = new AllMissionsFragment(); + fragment.setDeleteManager(mDeleteManager); + getFragmentManager().beginTransaction() - .replace(R.id.frame, fragment) + .replace(R.id.frame, fragment, MISSIONS_FRAGMENT_TAG) .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE) .commit(); } @@ -80,6 +99,7 @@ public class DownloadActivity extends AppCompatActivity { case R.id.action_settings: { Intent intent = new Intent(this, SettingsActivity.class); startActivity(intent); + deletePending(); return true; } default: @@ -87,4 +107,15 @@ public class DownloadActivity extends AppCompatActivity { } } + @Override + public void onBackPressed() { + super.onBackPressed(); + deletePending(); + } + + private void deletePending() { + Completable.fromAction(mDeleteManager::deletePending) + .subscribeOn(Schedulers.io()) + .subscribe(); + } } diff --git a/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java b/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java index 12c81c127..4054c8a15 100644 --- a/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java +++ b/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java @@ -25,10 +25,13 @@ import android.widget.TextView; import android.widget.Toast; import org.schabi.newpipe.R; +import org.schabi.newpipe.download.DeleteManager; import java.io.File; import java.lang.ref.WeakReference; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Locale; import java.util.Map; @@ -52,18 +55,34 @@ public class MissionAdapter extends RecyclerView.Adapter mItemList; private DownloadManagerService.DMBinder mBinder; private int mLayout; - public MissionAdapter(Activity context, DownloadManagerService.DMBinder binder, DownloadManager manager, boolean isLinear) { + public MissionAdapter(Activity context, DownloadManagerService.DMBinder binder, DownloadManager downloadManager, DeleteManager deleteManager, boolean isLinear) { mContext = context; - mManager = manager; + mDownloadManager = downloadManager; + mDeleteManager = deleteManager; mBinder = binder; mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - mLayout = isLinear ? R.layout.mission_item_linear : R.layout.mission_item; + + mItemList = new ArrayList<>(); + updateItemList(); + } + + public void updateItemList() { + mItemList.clear(); + + for (int i = 0; i < mDownloadManager.getCount(); i++) { + DownloadMission mission = mDownloadManager.getMission(i); + if (!mDeleteManager.contains(mission)) { + mItemList.add(mDownloadManager.getMission(i)); + } + } } @Override @@ -102,7 +121,7 @@ public class MissionAdapter extends RecyclerView.Adapter= Build.VERSION_CODES.LOLLIPOP) { - intent.addFlags(FLAG_GRANT_PREFIX_URI_PERMISSION); - } - //mContext.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION); - Log.v(TAG, "Starting intent: " + intent); - mContext.startActivity(intent); - } - private void viewFileWithFileProvider(File file, String mimetype) { String ourPackage = mContext.getApplicationContext().getPackageName(); Uri uri = FileProvider.getUriForFile(mContext, ourPackage + ".provider", file); diff --git a/app/src/main/java/us/shandian/giga/ui/fragment/MissionsFragment.java b/app/src/main/java/us/shandian/giga/ui/fragment/MissionsFragment.java index 2ff83086f..bf5a82c0e 100644 --- a/app/src/main/java/us/shandian/giga/ui/fragment/MissionsFragment.java +++ b/app/src/main/java/us/shandian/giga/ui/fragment/MissionsFragment.java @@ -10,6 +10,8 @@ import android.content.SharedPreferences; import android.os.Bundle; import android.os.IBinder; import android.preference.PreferenceManager; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; @@ -19,13 +21,17 @@ import android.view.View; import android.view.ViewGroup; import org.schabi.newpipe.R; +import org.schabi.newpipe.download.DeleteManager; +import io.reactivex.disposables.Disposable; +import io.reactivex.functions.Consumer; import us.shandian.giga.get.DownloadManager; +import us.shandian.giga.get.DownloadMission; import us.shandian.giga.service.DownloadManagerService; import us.shandian.giga.ui.adapter.MissionAdapter; public abstract class MissionsFragment extends Fragment { - private DownloadManager mManager; + private DownloadManager mDownloadManager; private DownloadManagerService.DMBinder mBinder; private SharedPreferences mPrefs; @@ -37,14 +43,19 @@ public abstract class MissionsFragment extends Fragment { private GridLayoutManager mGridManager; private LinearLayoutManager mLinearManager; private Context mActivity; + private DeleteManager mDeleteManager; + private Disposable mDeleteDisposable; private ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder binder) { mBinder = (DownloadManagerService.DMBinder) binder; - mManager = setupDownloadManager(mBinder); - updateList(); + mDownloadManager = setupDownloadManager(mBinder); + if (mDeleteManager != null) { + mDeleteManager.setDownloadManager(mDownloadManager); + updateList(); + } } @Override @@ -55,6 +66,14 @@ public abstract class MissionsFragment extends Fragment { }; + public void setDeleteManager(@NonNull DeleteManager deleteManager) { + mDeleteManager = deleteManager; + if (mDownloadManager != null) { + mDeleteManager.setDownloadManager(mDownloadManager); + updateList(); + } + } + @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.missions, container, false); @@ -104,10 +123,26 @@ public abstract class MissionsFragment extends Fragment { mActivity = activity; } + @Override + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + if (mDeleteManager != null) { + mDeleteDisposable = mDeleteManager.getUndoObservable().subscribe(mission -> { + if (mAdapter != null) { + mAdapter.updateItemList(); + mAdapter.notifyDataSetChanged(); + } + }); + } + } + @Override public void onDestroyView() { super.onDestroyView(); getActivity().unbindService(mConnection); + if (mDeleteDisposable != null) { + mDeleteDisposable.dispose(); + } } @Override @@ -129,7 +164,7 @@ public abstract class MissionsFragment extends Fragment { } private void updateList() { - mAdapter = new MissionAdapter((Activity) mActivity, mBinder, mManager, mLinear); + mAdapter = new MissionAdapter((Activity) mActivity, mBinder, mDownloadManager, mDeleteManager, mLinear); if (mLinear) { mList.setLayoutManager(mLinearManager); @@ -143,7 +178,7 @@ public abstract class MissionsFragment extends Fragment { mSwitch.setIcon(mLinear ? R.drawable.grid : R.drawable.list); } - mPrefs.edit().putBoolean("linear", mLinear).commit(); + mPrefs.edit().putBoolean("linear", mLinear).apply(); } protected abstract DownloadManager setupDownloadManager(DownloadManagerService.DMBinder binder); From 8b8652d44c3a92156915a92e73bef3f268610888 Mon Sep 17 00:00:00 2001 From: "Andrei.Rosca" Date: Fri, 15 Jun 2018 16:15:55 +0200 Subject: [PATCH 2/2] undo delete - code format --- ...anager.java => DeleteDownloadManager.java} | 36 +++++-------------- .../newpipe/download/DownloadActivity.java | 14 ++++---- .../giga/ui/adapter/MissionAdapter.java | 12 +++---- .../giga/ui/fragment/MissionsFragment.java | 22 ++++++------ 4 files changed, 31 insertions(+), 53 deletions(-) rename app/src/main/java/org/schabi/newpipe/download/{DeleteManager.java => DeleteDownloadManager.java} (86%) diff --git a/app/src/main/java/org/schabi/newpipe/download/DeleteManager.java b/app/src/main/java/org/schabi/newpipe/download/DeleteDownloadManager.java similarity index 86% rename from app/src/main/java/org/schabi/newpipe/download/DeleteManager.java rename to app/src/main/java/org/schabi/newpipe/download/DeleteDownloadManager.java index 210cf668d..f2912a6fa 100644 --- a/app/src/main/java/org/schabi/newpipe/download/DeleteManager.java +++ b/app/src/main/java/org/schabi/newpipe/download/DeleteDownloadManager.java @@ -24,7 +24,7 @@ import io.reactivex.subjects.PublishSubject; import us.shandian.giga.get.DownloadManager; import us.shandian.giga.get.DownloadMission; -public class DeleteManager { +public class DeleteDownloadManager { private static final String KEY_STATE = "delete_manager_state"; @@ -34,7 +34,7 @@ public class DeleteManager { private DownloadManager mDownloadManager; private PublishSubject publishSubject = PublishSubject.create(); - DeleteManager(Activity activity) { + DeleteDownloadManager(Activity activity) { mPendingMap = new HashSet<>(); mDisposableList = new ArrayList<>(); mView = activity.findViewById(android.R.id.content); @@ -59,19 +59,13 @@ public class DeleteManager { public void setDownloadManager(@NonNull DownloadManager downloadManager) { mDownloadManager = downloadManager; - if (mPendingMap.size() < 1) { - //nothing to do - return; - } + if (mPendingMap.size() < 1) return; showUndoDeleteSnackbar(); } public void restoreState(@Nullable Bundle savedInstanceState) { - if (savedInstanceState == null) { - // nothing to do - return; - } + if (savedInstanceState == null) return; List list = savedInstanceState.getStringArrayList(KEY_STATE); if (list != null) { @@ -80,10 +74,7 @@ public class DeleteManager { } public void saveState(@Nullable Bundle outState) { - if (outState == null) { - // nothing to do - return; - } + if (outState == null) return; for (Disposable disposable : mDisposableList) { disposable.dispose(); @@ -93,10 +84,7 @@ public class DeleteManager { } private void showUndoDeleteSnackbar() { - if (mPendingMap.size() < 1) { - // nothing to do - return; - } + if (mPendingMap.size() < 1) return; String url = mPendingMap.iterator().next(); @@ -128,11 +116,11 @@ public class DeleteManager { @Override public void onDismissed(Snackbar transientBottomBar, int event) { if (!disposable.isDisposed()) { - mPendingMap.remove(mission.url); Completable.fromAction(() -> deletePending(mission)) .subscribeOn(Schedulers.io()) .subscribe(); } + mPendingMap.remove(mission.url); snackbar.removeCallback(this); mDisposableList.remove(disposable); showUndoDeleteSnackbar(); @@ -143,10 +131,7 @@ public class DeleteManager { } public void deletePending() { - if (mPendingMap.size() < 1) { - // nothing to do - return; - } + if (mPendingMap.size() < 1) return; HashSet idSet = new HashSet<>(); for (int i = 0; i < mDownloadManager.getCount(); i++) { @@ -163,11 +148,6 @@ public class DeleteManager { } private void deletePending(@NonNull DownloadMission mission) { - if (!contains(mission)) { - // nothing to do - return; - } - for (int i = 0; i < mDownloadManager.getCount(); i++) { if (mission.url.equals(mDownloadManager.getMission(i).url)) { mDownloadManager.deleteMission(i); diff --git a/app/src/main/java/org/schabi/newpipe/download/DownloadActivity.java b/app/src/main/java/org/schabi/newpipe/download/DownloadActivity.java index f9c48a533..4a2c85149 100644 --- a/app/src/main/java/org/schabi/newpipe/download/DownloadActivity.java +++ b/app/src/main/java/org/schabi/newpipe/download/DownloadActivity.java @@ -24,7 +24,7 @@ import us.shandian.giga.ui.fragment.MissionsFragment; public class DownloadActivity extends AppCompatActivity { private static final String MISSIONS_FRAGMENT_TAG = "fragment_tag"; - private DeleteManager mDeleteManager; + private DeleteDownloadManager mDeleteDownloadManager; @Override protected void onCreate(Bundle savedInstanceState) { @@ -47,12 +47,12 @@ public class DownloadActivity extends AppCompatActivity { actionBar.setDisplayShowTitleEnabled(true); } - mDeleteManager = new DeleteManager(this); - mDeleteManager.restoreState(savedInstanceState); + mDeleteDownloadManager = new DeleteDownloadManager(this); + mDeleteDownloadManager.restoreState(savedInstanceState); MissionsFragment fragment = (MissionsFragment) getFragmentManager().findFragmentByTag(MISSIONS_FRAGMENT_TAG); if (fragment != null) { - fragment.setDeleteManager(mDeleteManager); + fragment.setDeleteManager(mDeleteDownloadManager); } else { getWindow().getDecorView().getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override @@ -66,13 +66,13 @@ public class DownloadActivity extends AppCompatActivity { @Override protected void onSaveInstanceState(Bundle outState) { - mDeleteManager.saveState(outState); + mDeleteDownloadManager.saveState(outState); super.onSaveInstanceState(outState); } private void updateFragments() { MissionsFragment fragment = new AllMissionsFragment(); - fragment.setDeleteManager(mDeleteManager); + fragment.setDeleteManager(mDeleteDownloadManager); getFragmentManager().beginTransaction() .replace(R.id.frame, fragment, MISSIONS_FRAGMENT_TAG) @@ -114,7 +114,7 @@ public class DownloadActivity extends AppCompatActivity { } private void deletePending() { - Completable.fromAction(mDeleteManager::deletePending) + Completable.fromAction(mDeleteDownloadManager::deletePending) .subscribeOn(Schedulers.io()) .subscribe(); } diff --git a/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java b/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java index 4054c8a15..8127c3467 100644 --- a/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java +++ b/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java @@ -25,7 +25,7 @@ import android.widget.TextView; import android.widget.Toast; import org.schabi.newpipe.R; -import org.schabi.newpipe.download.DeleteManager; +import org.schabi.newpipe.download.DeleteDownloadManager; import java.io.File; import java.lang.ref.WeakReference; @@ -56,15 +56,15 @@ public class MissionAdapter extends RecyclerView.Adapter mItemList; private DownloadManagerService.DMBinder mBinder; private int mLayout; - public MissionAdapter(Activity context, DownloadManagerService.DMBinder binder, DownloadManager downloadManager, DeleteManager deleteManager, boolean isLinear) { + public MissionAdapter(Activity context, DownloadManagerService.DMBinder binder, DownloadManager downloadManager, DeleteDownloadManager deleteDownloadManager, boolean isLinear) { mContext = context; mDownloadManager = downloadManager; - mDeleteManager = deleteManager; + mDeleteDownloadManager = deleteDownloadManager; mBinder = binder; mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); @@ -79,7 +79,7 @@ public class MissionAdapter extends RecyclerView.Adapter { + if (mDeleteDownloadManager != null) { + mDeleteDisposable = mDeleteDownloadManager.getUndoObservable().subscribe(mission -> { if (mAdapter != null) { mAdapter.updateItemList(); mAdapter.notifyDataSetChanged(); @@ -164,7 +162,7 @@ public abstract class MissionsFragment extends Fragment { } private void updateList() { - mAdapter = new MissionAdapter((Activity) mActivity, mBinder, mDownloadManager, mDeleteManager, mLinear); + mAdapter = new MissionAdapter((Activity) mActivity, mBinder, mDownloadManager, mDeleteDownloadManager, mLinear); if (mLinear) { mList.setLayoutManager(mLinearManager);