diff options
9 files changed, 468 insertions, 1 deletions
diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/FolderOptionsDialogFragment.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/FolderOptionsDialogFragment.java new file mode 100644 index 00000000..26836daa --- /dev/null +++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/FolderOptionsDialogFragment.java @@ -0,0 +1,235 @@ +package de.luhmer.owncloudnewsreader; + +import android.animation.AnimatorListenerAdapter; +import android.app.Activity; +import android.content.Context; +import android.os.Bundle; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.Toast; + +import androidx.fragment.app.DialogFragment; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import javax.inject.Inject; + +import de.luhmer.owncloudnewsreader.database.DatabaseConnectionOrm; +import de.luhmer.owncloudnewsreader.database.model.Feed; +import de.luhmer.owncloudnewsreader.databinding.FragmentDialogFolderoptionsBinding; +import de.luhmer.owncloudnewsreader.di.ApiProvider; +import de.luhmer.owncloudnewsreader.reader.nextcloud.NewsAPI; +import io.reactivex.Completable; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.annotations.NonNull; +import io.reactivex.schedulers.Schedulers; + + +public class FolderOptionsDialogFragment extends DialogFragment { + + protected @Inject ApiProvider mApi; + + private long mFolderId; + private String mDialogTitle; + + private LinkedHashMap<String, MenuAction> mMenuItems; + private NewsReaderListActivity parentActivity; + + protected FragmentDialogFolderoptionsBinding binding; + + + static FolderOptionsDialogFragment newInstance(long folderId, String dialogTitle) { + FolderOptionsDialogFragment f = new FolderOptionsDialogFragment(); + + Bundle args = new Bundle(); + args.putLong("folderid", folderId); + args.putString("title", dialogTitle); + + f.setArguments(args); + return f; + } + + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + ((NewsReaderApplication) requireActivity().getApplication()).getAppComponent().injectFragment(this); + + final Bundle args = requireArguments(); + mFolderId = args.getLong("folderid"); + mDialogTitle = args.getString("title"); + mMenuItems = new LinkedHashMap<>(); + + mMenuItems.put(getString(R.string.action_folder_rename), () -> showRenameFolderView(mFolderId, mDialogTitle)); + + mMenuItems.put(getString(R.string.action_folder_remove), () -> showRemoveFolderView(mFolderId)); + + setStyle(DialogFragment.STYLE_NO_TITLE, R.style.FloatingDialog); + } + + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + binding = FragmentDialogFolderoptionsBinding.inflate(inflater, container, false); + + binding.tvMenuTitle.setText(mDialogTitle); + + List<String> menuItemsList = new ArrayList<>(mMenuItems.keySet()); + + final ArrayAdapter<String> arrayAdapter = new ArrayAdapter<>( + getActivity(), + R.layout.fragment_dialog_listviewitem, + menuItemsList); + + binding.lvMenuList.setAdapter(arrayAdapter); + + binding.lvMenuList.setOnItemClickListener((adapterView, view, i, l) -> { + String key = arrayAdapter.getItem(i); + MenuAction mAction = mMenuItems.get(key); + mAction.execute(); + }); + return binding.getRoot(); + } + + + public void setActivity(Activity parentActivity) { + this.parentActivity = (NewsReaderListActivity)parentActivity; + } + + + public void showProgress(final boolean show) { + int shortAnimTime = getResources().getInteger(android.R.integer.config_shortAnimTime); + + binding.renameFolderDialog.setVisibility(show ? View.GONE : View.VISIBLE); + binding.renameFolderDialog.setVisibility(show ? View.GONE : View.VISIBLE); + + binding.progressView.setVisibility(show ? View.VISIBLE : View.GONE); + binding.progressView.animate().setDuration(shortAnimTime).alpha( + show ? 1 : 0).setListener(new AnimatorListenerAdapter() { + }); + } + + + private void showRenameFolderView(final long folderId, final String folderName) { + binding.renamefolderFoldername.setText(folderName); + binding.buttonRenameConfirm.setEnabled(false); + + binding.lvMenuList.setVisibility(View.GONE); + binding.renameFolderDialog.setVisibility(View.VISIBLE); + + binding.renamefolderFoldername.addTextChangedListener(new TextWatcher() { + @Override + public void afterTextChanged(Editable s) {} + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + binding.buttonRenameConfirm.setEnabled( + !s.toString().equals(folderName) && s.length() != 0); + } + }); + + binding.buttonRenameCancel.setOnClickListener(v -> dismiss()); + + binding.buttonRenameConfirm.setOnClickListener(v -> { + showProgress(true); + setCancelable(false); + getDialog().setCanceledOnTouchOutside(false); + + + Map<String, String> paramMap = new LinkedHashMap<>(); + paramMap.put("name", binding.renamefolderFoldername.getText().toString()); + mApi.getNewsAPI().renameFolder(folderId, paramMap) + .subscribeOn(Schedulers.newThread()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(() -> { + DatabaseConnectionOrm dbConn = new DatabaseConnectionOrm(getContext()); + dbConn.renameFolderById(folderId, binding.renamefolderFoldername.getText().toString()); + + parentActivity.getSlidingListFragment().reloadAdapter(); + parentActivity.startSync(); + dismiss(); + }, throwable -> { + Context context = getContext(); + if (context == null) { + return; + } + Toast.makeText(context.getApplicationContext(), getString(R.string.login_dialog_text_something_went_wrong) + " - " + throwable.getMessage(), Toast.LENGTH_LONG).show(); + dismiss(); + }); + }); + } + + + private void showRemoveFolderView(final long folderId) { + binding.lvMenuList.setVisibility(View.GONE); + binding.removeFolderDialog.setVisibility(View.VISIBLE); + + binding.buttonRemoveCancel.setOnClickListener(v -> dismiss()); + + binding.buttonRemoveConfirm.setOnClickListener(v -> { + showProgress(true); + setCancelable(false); + getDialog().setCanceledOnTouchOutside(false); + + NewsAPI newsApi = mApi.getNewsAPI(); + Observable<Feed> deleteFeedsTask = newsApi.feeds() + .subscribeOn(Schedulers.newThread()) + .flatMap(feedList -> Observable.fromIterable(feedList) + .filter(feed -> folderId == feed.getFolderId()) + ) + .flatMap(feed -> newsApi.deleteFeed(feed.getId()) + .andThen(Observable.just(feed)) + ) + .observeOn(AndroidSchedulers.mainThread()) + .doOnNext(feed -> { + DatabaseConnectionOrm dbConn = new DatabaseConnectionOrm(getContext()); + dbConn.removeFeedById(feed.getId()); + + Long currentFeedId = parentActivity.getNewsReaderDetailFragment().getIdFeed(); + if(currentFeedId != null && currentFeedId == feed.getId()) { + parentActivity.switchToAllUnreadItemsFolder(); + } + }); + Completable.fromObservable(deleteFeedsTask) + .observeOn(Schedulers.newThread()) + .andThen(newsApi.deleteFolder(folderId)) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(() -> { + DatabaseConnectionOrm dbConn = new DatabaseConnectionOrm(getContext()); + dbConn.removeFolderById(folderId); + + Long currentFolderId = parentActivity.getNewsReaderDetailFragment().getIdFolder(); + if(currentFolderId != null && currentFolderId == folderId) { + parentActivity.switchToAllUnreadItemsFolder(); + } + parentActivity.getSlidingListFragment().reloadAdapter(); + parentActivity.updateCurrentRssView(); + dismiss(); + }, throwable -> { + Context context = getContext(); + if (context == null) { + return; + } + Toast.makeText(context.getApplicationContext(), getString(R.string.login_dialog_text_something_went_wrong) + " - " + throwable.getMessage(), Toast.LENGTH_LONG).show(); + dismiss(); + }); + }); + } + + interface MenuAction { + void execute(); + } +} diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/NewsReaderListActivity.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/NewsReaderListActivity.java index a3791ca4..96c393e0 100644 --- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/NewsReaderListActivity.java +++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/NewsReaderListActivity.java @@ -619,6 +619,19 @@ public class NewsReaderListActivity extends PodcastFragmentActivity implements NewsReaderListDialogFragment fragment = NewsReaderListDialogFragment.newInstance(idFeed, titel, iconurl, feedurl); fragment.setActivity(this); fragment.show(ft, "news_reader_list_dialog"); + } else { + String label = dbConn.getFolderById(idFeed).getLabel(); + + FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); + Fragment prev = getSupportFragmentManager().findFragmentByTag("folder_options_dialog"); + if (prev != null) { + ft.remove(prev); + } + ft.addToBackStack(null); + + FolderOptionsDialogFragment fragment = FolderOptionsDialogFragment.newInstance(idFeed, label); + fragment.setActivity(this); + fragment.show(ft, "folder_options_dialog"); } } diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/database/DatabaseConnectionOrm.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/database/DatabaseConnectionOrm.java index 0a92cb29..c05c85e8 100644 --- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/database/DatabaseConnectionOrm.java +++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/database/DatabaseConnectionOrm.java @@ -475,6 +475,19 @@ public class DatabaseConnectionOrm { return daoSession.getRssItemDao().queryRawCreate(where_clause).listLazy(); } + /** + * Removes only the folder, without removing feeds inside the folder + */ + public void removeFolderById(final long folderId) { + daoSession.getFolderDao().deleteByKey(folderId); + } + + public void renameFolderById(long folderId, String newLabel) { + Folder folder = daoSession.getFolderDao().queryBuilder().where(FolderDao.Properties.Id.eq(folderId)).unique(); + folder.setLabel(newLabel); + daoSession.getFolderDao().update(folder); + } + /* public void markAllItemsAsReadForCurrentView() { diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/di/AppComponent.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/di/AppComponent.java index 5a071604..d7a2d8db 100644 --- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/di/AppComponent.java +++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/di/AppComponent.java @@ -3,6 +3,7 @@ package de.luhmer.owncloudnewsreader.di; import javax.inject.Singleton; import dagger.Component; +import de.luhmer.owncloudnewsreader.FolderOptionsDialogFragment; import de.luhmer.owncloudnewsreader.LoginDialogActivity; import de.luhmer.owncloudnewsreader.NewFeedActivity; import de.luhmer.owncloudnewsreader.NewsDetailActivity; @@ -39,6 +40,7 @@ public interface AppComponent { void injectFragment(SettingsFragment fragment); void injectFragment(NewsDetailFragment fragment); void injectFragment(NewsReaderDetailFragment fragment); + void injectFragment(FolderOptionsDialogFragment fragment); void injectService(SyncItemStateService service); void injectService(OwnCloudSyncAdapter ownCloudSyncAdapter); diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/reader/nextcloud/NewsAPI.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/reader/nextcloud/NewsAPI.java index 60de7ae2..9c3f1351 100644 --- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/reader/nextcloud/NewsAPI.java +++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/reader/nextcloud/NewsAPI.java @@ -57,6 +57,9 @@ public interface NewsAPI { @PUT("feeds/{feedId}/rename") Completable renameFeed(@Path("feedId") long feedId, @Body Map<String, String> paramMap); + @PUT("folders/{folderId}") + Completable renameFolder(@Path("folderId") long folderId, @Body Map<String, String> paramMap); + @PUT("feeds/{feedId}/move") Completable moveFeed(@Path("feedId") long feedId, @Body Map<String,Long> folderIdMap); @@ -65,6 +68,9 @@ public interface NewsAPI { @DELETE("feeds/{feedId}") Completable deleteFeed(@Path("feedId") long feedId); + @DELETE("folders/{folderId}") + Completable deleteFolder(@Path("folderId") long folderId); + /** ITEMS **/ @GET("items") diff --git a/News-Android-App/src/main/res/drawable/ic_baseline_folder_24.xml b/News-Android-App/src/main/res/drawable/ic_baseline_folder_24.xml new file mode 100644 index 00000000..9ebe1852 --- /dev/null +++ b/News-Android-App/src/main/res/drawable/ic_baseline_folder_24.xml @@ -0,0 +1,10 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24" + android:tint="@color/material_grey_600"> + <path + android:fillColor="@android:color/white" + android:pathData="M10,4H4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V8c0,-1.1 -0.9,-2 -2,-2h-8l-2,-2z"/> +</vector> diff --git a/News-Android-App/src/main/res/layout/fragment_dialog_feedoptions.xml b/News-Android-App/src/main/res/layout/fragment_dialog_feedoptions.xml index a29b90dc..7fff7eb7 100644 --- a/News-Android-App/src/main/res/layout/fragment_dialog_feedoptions.xml +++ b/News-Android-App/src/main/res/layout/fragment_dialog_feedoptions.xml @@ -225,7 +225,7 @@ android:visibility="gone"> <ProgressBar - android:id="@+id/alter_feedSource_progress" + android:id="@+id/alter_folderSource_progress" style="?android:attr/progressBarStyle" android:layout_width="match_parent" android:layout_height="wrap_content" diff --git a/News-Android-App/src/main/res/layout/fragment_dialog_folderoptions.xml b/News-Android-App/src/main/res/layout/fragment_dialog_folderoptions.xml new file mode 100644 index 00000000..6f3bf966 --- /dev/null +++ b/News-Android-App/src/main/res/layout/fragment_dialog_folderoptions.xml @@ -0,0 +1,181 @@ +<?xml version="1.0" encoding="utf-8"?> + +<RelativeLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:paddingBottom="6dp" + android:paddingStart="@dimen/activity_horizontal_margin" + android:paddingEnd="@dimen/activity_horizontal_margin" + android:paddingTop="@dimen/activity_vertical_margin"> + + <RelativeLayout + android:id="@+id/title_wrapper" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingStart="?android:attr/listPreferredItemPaddingStart" + android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" + android:layout_marginBottom="4dp"> + + <ImageView + android:id="@+id/ic_menu_foldericon" + android:layout_width="24dp" + android:layout_height="24dp" + android:layout_marginEnd="6dp" + android:layout_gravity="center_vertical" + android:src="@drawable/ic_baseline_folder_24"/> + + <TextView + android:id="@+id/tv_menu_title" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_centerVertical="true" + android:layout_toEndOf="@+id/ic_menu_foldericon" + android:textSize="16sp" + android:textStyle="bold" + tools:text="NameOfFolder" /> + + </RelativeLayout> + + <View + android:id="@+id/horizontalDivider" + android:layout_width="match_parent" + android:layout_height="1dp" + android:layout_below="@+id/title_wrapper" + android:layout_marginStart="0dp" + android:layout_marginEnd="0dp" + android:layout_marginTop="12dp" + android:background="#c8ababab"/> + + <ListView + android:id="@+id/lv_menu_list" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_below="@id/horizontalDivider" + android:divider="@null"> + + </ListView> + + <RelativeLayout + android:id="@+id/remove_folder_dialog" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_below="@id/horizontalDivider" + android:visibility="gone"> + + <TextView + android:id="@+id/tv_detail_text" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_alignParentEnd="false" + android:layout_alignParentStart="true" + android:layout_marginBottom="13dp" + android:layout_marginTop="13dp" + android:paddingStart="6dp" + android:paddingEnd="6dp" + android:text="@string/confirm_folder_remove" + android:textSize="18sp" /> + + <LinearLayout + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_alignParentStart="true" + android:layout_below="@+id/tv_detail_text" + android:layout_marginStart="0dp" + android:layout_marginEnd="0dp" + android:orientation="horizontal"> + + <com.google.android.material.button.MaterialButton + android:id="@+id/button_remove_cancel" + style="@style/MaterialButtonStyle" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="0dp" + android:layout_weight="1" + android:text="@android:string/cancel" /> + + <com.google.android.material.button.MaterialButton + android:id="@+id/button_remove_confirm" + style="@style/MaterialButtonStyle" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginEnd="0dp" + android:layout_weight="1" + android:text="@string/folder_remove_button" /> + </LinearLayout> + + </RelativeLayout> + + <RelativeLayout + android:id="@+id/rename_folder_dialog" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_below="@id/horizontalDivider" + android:visibility="gone"> + + <EditText + android:id="@+id/renamefolder_foldername" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_alignParentEnd="false" + android:layout_alignParentStart="true" + android:layout_marginBottom="13dp" + android:layout_marginTop="13dp" + android:imeOptions="actionDone" + android:inputType="textVisiblePassword" + android:lines="1" + android:paddingStart="6dp" + android:paddingEnd="6dp" + android:singleLine="true" + android:textSize="18sp" /> + + <LinearLayout + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_alignParentStart="true" + android:layout_below="@+id/renamefolder_foldername" + android:layout_marginStart="0dp" + android:layout_marginEnd="0dp" + android:orientation="horizontal"> + + <com.google.android.material.button.MaterialButton + android:id="@+id/button_rename_cancel" + style="@style/MaterialButtonStyle" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="0dp" + android:layout_weight="1" + android:text="@android:string/cancel" /> + + <com.google.android.material.button.MaterialButton + android:id="@+id/button_rename_confirm" + style="@style/MaterialButtonStyle" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginEnd="0dp" + android:layout_weight="1" + android:text="@string/folder_rename_button" /> + + </LinearLayout> + </RelativeLayout> + + <RelativeLayout + android:id="@+id/progressView" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_below="@id/horizontalDivider" + android:visibility="gone"> + + <ProgressBar + android:id="@+id/alter_folderSource_progress" + style="?android:attr/progressBarStyle" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:layout_marginBottom="12dp" + android:layout_marginTop="12dp"/> + </RelativeLayout> + +</RelativeLayout>
\ No newline at end of file diff --git a/News-Android-App/src/main/res/values/strings.xml b/News-Android-App/src/main/res/values/strings.xml index 99c83eb6..a69b069b 100644 --- a/News-Android-App/src/main/res/values/strings.xml +++ b/News-Android-App/src/main/res/values/strings.xml @@ -94,6 +94,13 @@ <string name="feed_move_list_description">Select folder to move feed in</string> <string name="move_feed_root_folder">Root folder</string> + <!-- Strings related to FolderOptionsDialogFragment (Rename/Remove Folder) --> + <string name="action_folder_remove">Remove Folder</string> + <string name="action_folder_rename">Rename Folder</string> + <string name="folder_remove_button">Remove</string> + <string name="folder_rename_button">Rename</string> + <string name="confirm_folder_remove">Do you really want to remove this Folder? This cannot be undone!</string> + <!-- Strings related to NewsReaderListDialogFragment (Notification Settings) --> <string name="notification_feed_unique">Receive unique notifications for this feed</string> <string name="notification_feed_default">All feeds with the default settings will appear in one notification</string> |