diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c6d173802..e61ad86df 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -381,6 +381,11 @@ android:windowSoftInputMode="stateAlwaysHidden" android:configChanges="orientation|screenSize" android:label="@string/app_name" /> + . */ +package app.fedilab.android.activities; + +import android.content.Context; +import android.content.SharedPreferences; +import android.database.sqlite.SQLiteDatabase; +import android.graphics.drawable.ColorDrawable; +import android.os.AsyncTask; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; +import android.widget.TextView; + + +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AlertDialog; +import androidx.core.content.ContextCompat; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.google.android.material.floatingactionbutton.FloatingActionButton; + +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ThreadPoolExecutor; + +import app.fedilab.android.R; +import app.fedilab.android.asynctasks.PostActionAsyncTask; +import app.fedilab.android.asynctasks.RetrieveFeedsAsyncTask; +import app.fedilab.android.asynctasks.SyncBookmarksAsyncTask; +import app.fedilab.android.client.API; +import app.fedilab.android.client.APIResponse; +import app.fedilab.android.client.Entities.Account; +import app.fedilab.android.client.Entities.Status; +import app.fedilab.android.drawers.StatusListAdapter; +import app.fedilab.android.helper.Helper; +import app.fedilab.android.interfaces.OnRetrieveFeedsInterface; +import app.fedilab.android.interfaces.OnSyncBookmarksInterface; +import app.fedilab.android.sqlite.AccountDAO; +import app.fedilab.android.sqlite.Sqlite; + + +/** + * Created by Thomas on 15/11/2019. + * Bookmark activity + */ + +public class BookmarkActivity extends BaseActivity implements OnRetrieveFeedsInterface, OnSyncBookmarksInterface { + + private List statuses; + private StatusListAdapter statusListAdapter; + private RelativeLayout textviewNoAction; + private RelativeLayout mainLoader; + private RecyclerView lv_status; + private ImageView pp_actionBar; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE); + int theme = sharedpreferences.getInt(Helper.SET_THEME, Helper.THEME_DARK); + switch (theme) { + case Helper.THEME_LIGHT: + setTheme(R.style.AppTheme_Fedilab); + break; + case Helper.THEME_DARK: + setTheme(R.style.AppThemeDark); + break; + case Helper.THEME_BLACK: + setTheme(R.style.AppThemeBlack); + break; + default: + setTheme(R.style.AppThemeDark); + } + + if (getSupportActionBar() != null) + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + actionBar.setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(BookmarkActivity.this, R.color.cyanea_primary))); + LayoutInflater inflater = (LayoutInflater) this.getSystemService(LAYOUT_INFLATER_SERVICE); + assert inflater != null; + View view = inflater.inflate(R.layout.simple_action_bar, new LinearLayout(getApplicationContext()), false); + actionBar.setCustomView(view, new ActionBar.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM); + TextView title = actionBar.getCustomView().findViewById(R.id.toolbar_title); + pp_actionBar = actionBar.getCustomView().findViewById(R.id.pp_actionBar); + title.setText(R.string.bookmarks); + ImageView close_conversation = actionBar.getCustomView().findViewById(R.id.close_conversation); + if (close_conversation != null) { + close_conversation.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + finish(); + } + }); + } + } else { + setTitle(R.string.bookmarks); + } + setContentView(R.layout.activity_bookmark); + SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); + String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); + String instance = sharedpreferences.getString(Helper.PREF_INSTANCE, null); + Account account = new AccountDAO(getApplicationContext(), db).getUniqAccount(userId, instance); + + Helper.loadGiF(getApplicationContext(), account.getAvatar(), pp_actionBar); + + lv_status = findViewById(R.id.lv_status); + + mainLoader = findViewById(R.id.loader); + textviewNoAction = findViewById(R.id.no_action); + mainLoader.setVisibility(View.VISIBLE); + new RetrieveFeedsAsyncTask(getApplicationContext(), RetrieveFeedsAsyncTask.Type.CACHE_BOOKMARKS, null, BookmarkActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + + } + + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + finish(); + return true; + case R.id.action_export_bookmarks: + new SyncBookmarksAsyncTask(BookmarkActivity.this, SyncBookmarksAsyncTask.sync.EXPORT, BookmarkActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + return true; + case R.id.action_import_bookmarks: + new SyncBookmarksAsyncTask(BookmarkActivity.this, SyncBookmarksAsyncTask.sync.IMPORT, BookmarkActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + @Override + public boolean onCreateOptionsMenu(@NotNull Menu menu) { + getMenuInflater().inflate(R.menu.bookmarks, menu); + return true; + } + + + @Override + public void onResume() { + super.onResume(); + } + + + @Override + public void onRetrieveFeeds(APIResponse apiResponse) { + mainLoader.setVisibility(View.GONE); + FloatingActionButton delete_all = null; + try { + delete_all = findViewById(R.id.delete_all); + } catch (Exception ignored) { + } + final boolean isOnWifi = Helper.isOnWIFI(BookmarkActivity.this); + statuses = apiResponse.getStatuses(); + if (statuses != null && statuses.size() > 0) { + LinearLayoutManager mLayoutManager = new LinearLayoutManager(BookmarkActivity.this); + statusListAdapter = new StatusListAdapter(RetrieveFeedsAsyncTask.Type.CACHE_BOOKMARKS, null, isOnWifi, this.statuses); + lv_status.setAdapter(statusListAdapter); + lv_status.setLayoutManager(mLayoutManager); + } else { + textviewNoAction.setVisibility(View.VISIBLE); + } + + if (delete_all != null) + delete_all.setOnClickListener(view -> { + final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + int theme = sharedpreferences.getInt(Helper.SET_THEME, Helper.THEME_DARK); + int style; + if (theme == Helper.THEME_DARK) { + style = R.style.DialogDark; + } else if (theme == Helper.THEME_BLACK) { + style = R.style.DialogBlack; + } else { + style = R.style.Dialog; + } + AlertDialog.Builder builder = new AlertDialog.Builder(BookmarkActivity.this, style); + builder.setTitle(R.string.delete_all); + builder.setIcon(android.R.drawable.ic_dialog_alert) + .setPositiveButton(R.string.yes, (dialogConfirm, which) -> { + statuses = new ArrayList<>(); + statuses.clear(); + statusListAdapter = new StatusListAdapter(RetrieveFeedsAsyncTask.Type.CACHE_BOOKMARKS, null, isOnWifi, statuses); + lv_status.setAdapter(statusListAdapter); + statusListAdapter.notifyDataSetChanged(); + textviewNoAction.setVisibility(View.VISIBLE); + new PostActionAsyncTask(BookmarkActivity.this, API.StatusAction.UNBOOKMARK).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + dialogConfirm.dismiss(); + }) + .setNegativeButton(R.string.no, (dialogConfirm, which) -> dialogConfirm.dismiss()) + .show(); + }); + } + + @Override + public void onRetrieveBookmarks(List bookmarks) { + if( bookmarks != null) { + statuses = new ArrayList<>(); + statuses.clear(); + statuses.addAll(bookmarks); + final boolean isOnWifi = Helper.isOnWIFI(BookmarkActivity.this); + statusListAdapter = new StatusListAdapter(RetrieveFeedsAsyncTask.Type.CACHE_BOOKMARKS, null, isOnWifi, statuses); + lv_status.setAdapter(statusListAdapter); + statusListAdapter.notifyDataSetChanged(); + textviewNoAction.setVisibility(View.VISIBLE); + } + } +} diff --git a/app/src/main/java/app/fedilab/android/asynctasks/SyncBookmarksAsyncTask.java b/app/src/main/java/app/fedilab/android/asynctasks/SyncBookmarksAsyncTask.java new file mode 100644 index 000000000..92f2b9bb0 --- /dev/null +++ b/app/src/main/java/app/fedilab/android/asynctasks/SyncBookmarksAsyncTask.java @@ -0,0 +1,99 @@ +/* Copyright 2019 Thomas Schneider + * + * This file is a part of Fedilab + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with Fedilab; if not, + * see . */ +package app.fedilab.android.asynctasks; + +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.os.AsyncTask; +import android.os.SystemClock; + +import java.lang.ref.WeakReference; +import java.util.List; + +import app.fedilab.android.client.API; +import app.fedilab.android.client.APIResponse; +import app.fedilab.android.interfaces.OnSyncBookmarksInterface; +import app.fedilab.android.sqlite.Sqlite; +import app.fedilab.android.sqlite.StatusCacheDAO; + + +/** + * Created by Thomas on 15/11/2019. + * Sync bookmarks + */ + +public class SyncBookmarksAsyncTask extends AsyncTask { + + public enum sync{ + EXPORT, + IMPORT + } + + private List statusList; + private OnSyncBookmarksInterface listener; + private WeakReference contextReference; + private sync type; + + public SyncBookmarksAsyncTask(Context context, sync type, OnSyncBookmarksInterface onSyncBookmarksInterface) { + this.contextReference = new WeakReference<>(context); + this.type = type; + this.listener = onSyncBookmarksInterface; + } + + + @Override + protected Void doInBackground(Void... params) { + SQLiteDatabase db = Sqlite.getInstance(contextReference.get(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); + if( type == sync.EXPORT){ + String max_id = null; + do { + APIResponse apiResponse = new API(contextReference.get()).getBookmarks(max_id); + max_id = apiResponse.getMax_id(); + List statuses = apiResponse.getStatuses(); + for (app.fedilab.android.client.Entities.Status tmpStatus : statuses) { + app.fedilab.android.client.Entities.Status status = new StatusCacheDAO(contextReference.get(), db).getStatus(StatusCacheDAO.BOOKMARK_CACHE, tmpStatus.getId()); + if( status == null) { + new StatusCacheDAO(contextReference.get(), db).insertStatus(StatusCacheDAO.BOOKMARK_CACHE, tmpStatus); + } + } + try { + Thread.sleep(200); + } catch (InterruptedException e) { + SystemClock.sleep(200); + } + } while (max_id != null); + }else{ + List statuses = new StatusCacheDAO(contextReference.get(), db).getAllStatus(StatusCacheDAO.BOOKMARK_CACHE); + if( statuses != null) { + for (app.fedilab.android.client.Entities.Status tmpStatus : statuses) { + new API(contextReference.get()).postAction(API.StatusAction.BOOKMARK, tmpStatus.getId()); + try { + Thread.sleep(200); + } catch (InterruptedException e) { + SystemClock.sleep(200); + } + } + } + } + statusList = new StatusCacheDAO(contextReference.get(), db).getAllStatus(StatusCacheDAO.BOOKMARK_CACHE); + return null; + } + + @Override + protected void onPostExecute(Void result) { + listener.onRetrieveBookmarks(statusList); + } + +} diff --git a/app/src/main/java/app/fedilab/android/client/API.java b/app/src/main/java/app/fedilab/android/client/API.java index 40731af84..370b3dcfe 100644 --- a/app/src/main/java/app/fedilab/android/client/API.java +++ b/app/src/main/java/app/fedilab/android/client/API.java @@ -2603,6 +2603,36 @@ public class API { } + + /** + * Retrieves bookmarked status for the authenticated account *synchronously* + * + * @param max_id String id max + * @return APIResponse + */ + @SuppressWarnings("SameParameterValue") + public APIResponse getBookmarks(String max_id) { + + HashMap params = new HashMap<>(); + if (max_id != null) + params.put("max_id", max_id); + params.put("limit", "40"); + statuses = new ArrayList<>(); + try { + HttpsConnection httpsConnection = new HttpsConnection(context, this.instance); + String response = httpsConnection.get(getAbsoluteUrl("/bookmarks"), 10, params, prefKeyOauthTokenT); + apiResponse.setSince_id(httpsConnection.getSince_id()); + apiResponse.setMax_id(httpsConnection.getMax_id()); + statuses = parseStatuses(context, new JSONArray(response)); + } catch (HttpsConnection.HttpsConnectionException e) { + setError(e.getStatusCode(), e); + } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) { + e.printStackTrace(); + } + apiResponse.setStatuses(statuses); + return apiResponse; + } + /** * Makes the post action for a status * diff --git a/app/src/main/java/app/fedilab/android/fragments/DisplayBookmarksFragment.java b/app/src/main/java/app/fedilab/android/fragments/DisplayBookmarksFragment.java index aaca21ede..98a1eb655 100644 --- a/app/src/main/java/app/fedilab/android/fragments/DisplayBookmarksFragment.java +++ b/app/src/main/java/app/fedilab/android/fragments/DisplayBookmarksFragment.java @@ -15,9 +15,7 @@ package app.fedilab.android.fragments; * see . */ import android.content.Context; -import android.content.DialogInterface; import android.content.SharedPreferences; -import android.database.sqlite.SQLiteDatabase; import android.os.AsyncTask; import android.os.Bundle; @@ -46,8 +44,6 @@ import app.fedilab.android.client.APIResponse; import app.fedilab.android.client.Entities.Status; import app.fedilab.android.drawers.StatusListAdapter; import app.fedilab.android.helper.Helper; -import app.fedilab.android.sqlite.Sqlite; -import app.fedilab.android.sqlite.StatusCacheDAO; import app.fedilab.android.R; import app.fedilab.android.activities.MainActivity; import app.fedilab.android.asynctasks.RetrieveFeedsAsyncTask; @@ -101,7 +97,6 @@ public class DisplayBookmarksFragment extends Fragment implements OnRetrieveFeed @Override public void onRetrieveFeeds(APIResponse apiResponse) { - final SQLiteDatabase db = Sqlite.getInstance(context, Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); mainLoader.setVisibility(View.GONE); FloatingActionButton delete_all = null; try { @@ -109,7 +104,6 @@ public class DisplayBookmarksFragment extends Fragment implements OnRetrieveFeed } catch (Exception ignored) { } final boolean isOnWifi = Helper.isOnWIFI(context); - final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); statuses = apiResponse.getStatuses(); if (statuses != null && statuses.size() > 0) { LinearLayoutManager mLayoutManager = new LinearLayoutManager(context); @@ -121,44 +115,33 @@ public class DisplayBookmarksFragment extends Fragment implements OnRetrieveFeed } if (delete_all != null) - delete_all.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); - int theme = sharedpreferences.getInt(Helper.SET_THEME, Helper.THEME_DARK); - int style; - if (theme == Helper.THEME_DARK) { - style = R.style.DialogDark; - } else if (theme == Helper.THEME_BLACK) { - style = R.style.DialogBlack; - } else { - style = R.style.Dialog; - } - AlertDialog.Builder builder = new AlertDialog.Builder(context, style); - builder.setTitle(R.string.delete_all); - builder.setIcon(android.R.drawable.ic_dialog_alert) - .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogConfirm, int which) { - - statuses = new ArrayList<>(); - statuses.clear(); - statusListAdapter = new StatusListAdapter(RetrieveFeedsAsyncTask.Type.CACHE_BOOKMARKS, null, isOnWifi, statuses); - lv_status.setAdapter(statusListAdapter); - statusListAdapter.notifyDataSetChanged(); - textviewNoAction.setVisibility(View.VISIBLE); - new PostActionAsyncTask(context, API.StatusAction.UNBOOKMARK).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - dialogConfirm.dismiss(); - } - }) - .setNegativeButton(R.string.no, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogConfirm, int which) { - dialogConfirm.dismiss(); - } - }) - .show(); + delete_all.setOnClickListener(view -> { + final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + int theme = sharedpreferences.getInt(Helper.SET_THEME, Helper.THEME_DARK); + int style; + if (theme == Helper.THEME_DARK) { + style = R.style.DialogDark; + } else if (theme == Helper.THEME_BLACK) { + style = R.style.DialogBlack; + } else { + style = R.style.Dialog; } + AlertDialog.Builder builder = new AlertDialog.Builder(context, style); + builder.setTitle(R.string.delete_all); + builder.setIcon(android.R.drawable.ic_dialog_alert) + .setPositiveButton(R.string.yes, (dialogConfirm, which) -> { + + statuses = new ArrayList<>(); + statuses.clear(); + statusListAdapter = new StatusListAdapter(RetrieveFeedsAsyncTask.Type.CACHE_BOOKMARKS, null, isOnWifi, statuses); + lv_status.setAdapter(statusListAdapter); + statusListAdapter.notifyDataSetChanged(); + textviewNoAction.setVisibility(View.VISIBLE); + new PostActionAsyncTask(context, API.StatusAction.UNBOOKMARK).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + dialogConfirm.dismiss(); + }) + .setNegativeButton(R.string.no, (dialogConfirm, which) -> dialogConfirm.dismiss()) + .show(); }); } } diff --git a/app/src/main/java/app/fedilab/android/interfaces/OnSyncBookmarksInterface.java b/app/src/main/java/app/fedilab/android/interfaces/OnSyncBookmarksInterface.java new file mode 100644 index 000000000..a136a070c --- /dev/null +++ b/app/src/main/java/app/fedilab/android/interfaces/OnSyncBookmarksInterface.java @@ -0,0 +1,30 @@ +/* Copyright 2019 Thomas Schneider + * + * This file is a part of Fedilab + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with Fedilab; if not, + * see . */ +package app.fedilab.android.interfaces; + +import java.util.List; + +import app.fedilab.android.client.Entities.Status; + + +/** + * Created by Thomas on 15/11/2019. + * Interface when syncing bookmarks + */ + +public interface OnSyncBookmarksInterface { + + void onRetrieveBookmarks(List statuses); +} diff --git a/app/src/main/res/drawable/ic_export_bookmarks.xml b/app/src/main/res/drawable/ic_export_bookmarks.xml new file mode 100644 index 000000000..70e551331 --- /dev/null +++ b/app/src/main/res/drawable/ic_export_bookmarks.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_import_bookmarks.xml b/app/src/main/res/drawable/ic_import_bookmarks.xml new file mode 100644 index 000000000..b8e836142 --- /dev/null +++ b/app/src/main/res/drawable/ic_import_bookmarks.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/activity_bookmark.xml b/app/src/main/res/layout/activity_bookmark.xml new file mode 100644 index 000000000..3792acbf2 --- /dev/null +++ b/app/src/main/res/layout/activity_bookmark.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/bookmarks.xml b/app/src/main/res/menu/bookmarks.xml new file mode 100644 index 000000000..d553bc074 --- /dev/null +++ b/app/src/main/res/menu/bookmarks.xml @@ -0,0 +1,15 @@ + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 438cfb3d4..a68822e64 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1287,4 +1287,6 @@ Export the theme Tap here to export the current theme An error occurred when selecting the theme file + Export bookmarks to the instance + Import bookmarks from the instance \ No newline at end of file