mirror of
				https://codeberg.org/tom79/Fedilab.git
				synced 2025-10-20 11:20:16 +03:00 
			
		
		
		
	Follow Hashtags
This commit is contained in:
		
							parent
							
								
									170dbbd0cf
								
							
						
					
					
						commit
						cdbfb17d94
					
				
					 17 changed files with 527 additions and 16 deletions
				
			
		|  | @ -251,6 +251,11 @@ | ||||||
|             android:configChanges="keyboardHidden|orientation|screenSize" |             android:configChanges="keyboardHidden|orientation|screenSize" | ||||||
|             android:label="@string/action_lists" |             android:label="@string/action_lists" | ||||||
|             android:theme="@style/AppThemeBar" /> |             android:theme="@style/AppThemeBar" /> | ||||||
|  |         <activity | ||||||
|  |             android:name=".activities.FollowedTagActivity" | ||||||
|  |             android:configChanges="keyboardHidden|orientation|screenSize" | ||||||
|  |             android:label="@string/followed_tags" | ||||||
|  |             android:theme="@style/AppThemeBar" /> | ||||||
|         <activity |         <activity | ||||||
|             android:name=".activities.SettingsActivity" |             android:name=".activities.SettingsActivity" | ||||||
|             android:configChanges="keyboardHidden|orientation|screenSize" |             android:configChanges="keyboardHidden|orientation|screenSize" | ||||||
|  |  | ||||||
|  | @ -103,6 +103,7 @@ import app.fedilab.android.activities.ContextActivity; | ||||||
| import app.fedilab.android.activities.DraftActivity; | import app.fedilab.android.activities.DraftActivity; | ||||||
| import app.fedilab.android.activities.FilterActivity; | import app.fedilab.android.activities.FilterActivity; | ||||||
| import app.fedilab.android.activities.FollowRequestActivity; | import app.fedilab.android.activities.FollowRequestActivity; | ||||||
|  | import app.fedilab.android.activities.FollowedTagActivity; | ||||||
| import app.fedilab.android.activities.InstanceActivity; | import app.fedilab.android.activities.InstanceActivity; | ||||||
| import app.fedilab.android.activities.InstanceHealthActivity; | import app.fedilab.android.activities.InstanceHealthActivity; | ||||||
| import app.fedilab.android.activities.LoginActivity; | import app.fedilab.android.activities.LoginActivity; | ||||||
|  | @ -359,6 +360,9 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt | ||||||
|             } else if (id == R.id.nav_list) { |             } else if (id == R.id.nav_list) { | ||||||
|                 Intent intent = new Intent(this, MastodonListActivity.class); |                 Intent intent = new Intent(this, MastodonListActivity.class); | ||||||
|                 startActivity(intent); |                 startActivity(intent); | ||||||
|  |             } else if (id == R.id.nav_followed_tags) { | ||||||
|  |                 Intent intent = new Intent(this, FollowedTagActivity.class); | ||||||
|  |                 startActivity(intent); | ||||||
|             } else if (id == R.id.nav_settings) { |             } else if (id == R.id.nav_settings) { | ||||||
|                 Intent intent = new Intent(this, SettingsActivity.class); |                 Intent intent = new Intent(this, SettingsActivity.class); | ||||||
|                 startActivity(intent); |                 startActivity(intent); | ||||||
|  |  | ||||||
|  | @ -0,0 +1,202 @@ | ||||||
|  | package app.fedilab.android.activities; | ||||||
|  | /* Copyright 2022 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 <http://www.gnu.org/licenses>. */ | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | import android.graphics.drawable.ColorDrawable; | ||||||
|  | import android.os.Bundle; | ||||||
|  | import android.text.InputFilter; | ||||||
|  | import android.view.Menu; | ||||||
|  | import android.view.MenuItem; | ||||||
|  | import android.view.View; | ||||||
|  | 
 | ||||||
|  | import androidx.annotation.NonNull; | ||||||
|  | import androidx.appcompat.app.AlertDialog; | ||||||
|  | import androidx.core.content.ContextCompat; | ||||||
|  | import androidx.fragment.app.FragmentManager; | ||||||
|  | import androidx.fragment.app.FragmentTransaction; | ||||||
|  | import androidx.lifecycle.ViewModelProvider; | ||||||
|  | import androidx.recyclerview.widget.LinearLayoutManager; | ||||||
|  | 
 | ||||||
|  | import java.util.ArrayList; | ||||||
|  | 
 | ||||||
|  | import app.fedilab.android.BaseMainActivity; | ||||||
|  | import app.fedilab.android.R; | ||||||
|  | import app.fedilab.android.client.entities.api.Tag; | ||||||
|  | import app.fedilab.android.client.entities.app.Timeline; | ||||||
|  | import app.fedilab.android.databinding.ActivityFollowedTagsBinding; | ||||||
|  | import app.fedilab.android.databinding.PopupAddFollowedTagtBinding; | ||||||
|  | import app.fedilab.android.helper.Helper; | ||||||
|  | import app.fedilab.android.helper.ThemeHelper; | ||||||
|  | import app.fedilab.android.ui.drawer.FollowedTagAdapter; | ||||||
|  | import app.fedilab.android.ui.fragment.timeline.FragmentMastodonTimeline; | ||||||
|  | import app.fedilab.android.viewmodel.mastodon.TagVM; | ||||||
|  | import es.dmoral.toasty.Toasty; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | public class FollowedTagActivity extends BaseActivity implements FollowedTagAdapter.ActionOnTag { | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     private ActivityFollowedTagsBinding binding; | ||||||
|  |     private boolean canGoBack; | ||||||
|  |     private TagVM tagVM; | ||||||
|  |     private Tag tag; | ||||||
|  |     private ArrayList<Tag> tagList; | ||||||
|  |     private FollowedTagAdapter followedTagAdapter; | ||||||
|  |     private FragmentMastodonTimeline fragmentMastodonTimeline; | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     protected void onCreate(Bundle savedInstanceState) { | ||||||
|  |         super.onCreate(savedInstanceState); | ||||||
|  |         ThemeHelper.applyThemeBar(this); | ||||||
|  |         binding = ActivityFollowedTagsBinding.inflate(getLayoutInflater()); | ||||||
|  |         setContentView(binding.getRoot()); | ||||||
|  |         canGoBack = false; | ||||||
|  |         if (getSupportActionBar() != null) { | ||||||
|  |             getSupportActionBar().setDisplayHomeAsUpEnabled(true); | ||||||
|  |             getSupportActionBar().setDisplayShowHomeEnabled(true); | ||||||
|  |             getSupportActionBar().setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary))); | ||||||
|  |         } | ||||||
|  |         tagVM = new ViewModelProvider(FollowedTagActivity.this).get(TagVM.class); | ||||||
|  |         tagVM.followedTags(BaseMainActivity.currentInstance, BaseMainActivity.currentToken) | ||||||
|  |                 .observe(FollowedTagActivity.this, tags -> { | ||||||
|  |                     if (tags != null && tags.tags != null && tags.tags.size() > 0) { | ||||||
|  |                         tagList = new ArrayList<>(tags.tags); | ||||||
|  |                         followedTagAdapter = new FollowedTagAdapter(tagList); | ||||||
|  |                         followedTagAdapter.actionOnTag = this; | ||||||
|  |                         binding.notContent.setVisibility(View.GONE); | ||||||
|  |                         binding.recyclerView.setAdapter(followedTagAdapter); | ||||||
|  |                         binding.recyclerView.setLayoutManager(new LinearLayoutManager(FollowedTagActivity.this)); | ||||||
|  |                     } else { | ||||||
|  |                         binding.notContent.setVisibility(View.VISIBLE); | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public boolean onOptionsItemSelected(MenuItem item) { | ||||||
|  |         if (item.getItemId() == android.R.id.home) { | ||||||
|  |             onBackPressed(); | ||||||
|  |             return true; | ||||||
|  |         } else if (item.getItemId() == R.id.action_unfollow && tag != null) { | ||||||
|  |             AlertDialog.Builder alt_bld = new AlertDialog.Builder(FollowedTagActivity.this, Helper.dialogStyle()); | ||||||
|  |             alt_bld.setTitle(R.string.action_unfollow_tag); | ||||||
|  |             alt_bld.setMessage(R.string.action_unfollow_tag_confirm); | ||||||
|  |             alt_bld.setPositiveButton(R.string.unfollow, (dialog, id) -> { | ||||||
|  |                 tagVM.unfollow(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, tag.name); | ||||||
|  |                 int position = 0; | ||||||
|  |                 for (Tag tagTmp : tagList) { | ||||||
|  |                     if (tagTmp.name.equalsIgnoreCase(tag.name)) { | ||||||
|  |                         break; | ||||||
|  |                     } | ||||||
|  |                     position++; | ||||||
|  |                 } | ||||||
|  |                 tagList.remove(position); | ||||||
|  |                 followedTagAdapter.notifyItemRemoved(position); | ||||||
|  |                 ThemeHelper.slideViewsToRight(binding.fragmentContainer, binding.recyclerView, () -> { | ||||||
|  |                     canGoBack = false; | ||||||
|  |                     if (fragmentMastodonTimeline != null) { | ||||||
|  |                         fragmentMastodonTimeline.onDestroyView(); | ||||||
|  |                     } | ||||||
|  |                     invalidateOptionsMenu(); | ||||||
|  |                     setTitle(R.string.action_lists); | ||||||
|  |                 }); | ||||||
|  |                 if (tagList.size() == 0) { | ||||||
|  |                     binding.notContent.setVisibility(View.VISIBLE); | ||||||
|  |                 } else { | ||||||
|  |                     binding.notContent.setVisibility(View.GONE); | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |             alt_bld.setNegativeButton(R.string.cancel, (dialog, id) -> dialog.dismiss()); | ||||||
|  |             AlertDialog alert = alt_bld.create(); | ||||||
|  |             alert.show(); | ||||||
|  |         } else if (item.getItemId() == R.id.action_follow_tag) { | ||||||
|  |             AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(FollowedTagActivity.this, Helper.dialogStyle()); | ||||||
|  |             PopupAddFollowedTagtBinding popupAddFollowedTagtBinding = PopupAddFollowedTagtBinding.inflate(getLayoutInflater()); | ||||||
|  |             dialogBuilder.setView(popupAddFollowedTagtBinding.getRoot()); | ||||||
|  |             popupAddFollowedTagtBinding.addTag.setFilters(new InputFilter[]{new InputFilter.LengthFilter(255)}); | ||||||
|  |             dialogBuilder.setPositiveButton(R.string.validate, (dialog, id) -> { | ||||||
|  |                 if (popupAddFollowedTagtBinding.addTag.getText() != null && popupAddFollowedTagtBinding.addTag.getText().toString().trim().length() > 0) { | ||||||
|  |                     tagVM.follow(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, popupAddFollowedTagtBinding.addTag.getText().toString().trim()) | ||||||
|  |                             .observe(FollowedTagActivity.this, newTag -> { | ||||||
|  |                                 if (tagList == null) { | ||||||
|  |                                     tagList = new ArrayList<>(); | ||||||
|  |                                 } | ||||||
|  |                                 if (newTag != null && followedTagAdapter != null) { | ||||||
|  |                                     tagList.add(0, newTag); | ||||||
|  |                                     followedTagAdapter.notifyItemInserted(0); | ||||||
|  |                                 } else { | ||||||
|  |                                     Toasty.error(FollowedTagActivity.this, getString(R.string.toast_error), Toasty.LENGTH_LONG).show(); | ||||||
|  |                                 } | ||||||
|  |                             }); | ||||||
|  |                     dialog.dismiss(); | ||||||
|  |                 } else { | ||||||
|  |                     popupAddFollowedTagtBinding.addTag.setError(getString(R.string.not_valid_tag_name)); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |             }); | ||||||
|  |             dialogBuilder.setNegativeButton(R.string.cancel, (dialog, id) -> dialog.dismiss()); | ||||||
|  |             dialogBuilder.create().show(); | ||||||
|  |         } | ||||||
|  |         return super.onOptionsItemSelected(item); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public boolean onCreateOptionsMenu(@NonNull Menu menu) { | ||||||
|  |         if (!canGoBack) { | ||||||
|  |             getMenuInflater().inflate(R.menu.menu_main_followed_tag, menu); | ||||||
|  |         } else { | ||||||
|  |             getMenuInflater().inflate(R.menu.menu_followed_tag, menu); | ||||||
|  |         } | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public void onBackPressed() { | ||||||
|  |         if (canGoBack) { | ||||||
|  |             canGoBack = false; | ||||||
|  |             ThemeHelper.slideViewsToRight(binding.fragmentContainer, binding.recyclerView, () -> { | ||||||
|  |                 if (fragmentMastodonTimeline != null) { | ||||||
|  |                     fragmentMastodonTimeline.onDestroyView(); | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |             setTitle(R.string.action_lists); | ||||||
|  |             invalidateOptionsMenu(); | ||||||
|  |         } else { | ||||||
|  |             super.onBackPressed(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public void click(Tag tag) { | ||||||
|  |         this.tag = tag; | ||||||
|  |         canGoBack = true; | ||||||
|  |         ThemeHelper.slideViewsToLeft(binding.recyclerView, binding.fragmentContainer, () -> { | ||||||
|  |             fragmentMastodonTimeline = new FragmentMastodonTimeline(); | ||||||
|  |             Bundle bundle = new Bundle(); | ||||||
|  |             bundle.putSerializable(Helper.ARG_SEARCH_KEYWORD, tag.name); | ||||||
|  |             bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, Timeline.TimeLineEnum.TAG); | ||||||
|  |             setTitle(tag.name); | ||||||
|  |             fragmentMastodonTimeline.setArguments(bundle); | ||||||
|  |             FragmentManager fragmentManager = getSupportFragmentManager(); | ||||||
|  |             FragmentTransaction fragmentTransaction = | ||||||
|  |                     fragmentManager.beginTransaction(); | ||||||
|  |             fragmentTransaction.replace(R.id.fragment_container, fragmentMastodonTimeline); | ||||||
|  |             fragmentTransaction.commit(); | ||||||
|  |             invalidateOptionsMenu(); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -28,6 +28,7 @@ import android.view.MenuItem; | ||||||
| import androidx.annotation.NonNull; | import androidx.annotation.NonNull; | ||||||
| import androidx.appcompat.app.ActionBar; | import androidx.appcompat.app.ActionBar; | ||||||
| import androidx.core.content.ContextCompat; | import androidx.core.content.ContextCompat; | ||||||
|  | import androidx.lifecycle.ViewModelProvider; | ||||||
| import androidx.localbroadcastmanager.content.LocalBroadcastManager; | import androidx.localbroadcastmanager.content.LocalBroadcastManager; | ||||||
| 
 | 
 | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
|  | @ -45,6 +46,8 @@ import app.fedilab.android.exception.DBException; | ||||||
| import app.fedilab.android.helper.Helper; | import app.fedilab.android.helper.Helper; | ||||||
| import app.fedilab.android.helper.ThemeHelper; | import app.fedilab.android.helper.ThemeHelper; | ||||||
| import app.fedilab.android.ui.fragment.timeline.FragmentMastodonTimeline; | import app.fedilab.android.ui.fragment.timeline.FragmentMastodonTimeline; | ||||||
|  | import app.fedilab.android.viewmodel.mastodon.ReorderVM; | ||||||
|  | import app.fedilab.android.viewmodel.mastodon.TagVM; | ||||||
| import es.dmoral.toasty.Toasty; | import es.dmoral.toasty.Toasty; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -53,6 +56,9 @@ public class HashTagActivity extends BaseActivity { | ||||||
| 
 | 
 | ||||||
|     public static int position; |     public static int position; | ||||||
|     private String tag; |     private String tag; | ||||||
|  |     private boolean pinnedTag; | ||||||
|  |     private boolean followedTag; | ||||||
|  |     private TagVM tagVM; | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|     protected void onCreate(Bundle savedInstanceState) { |     protected void onCreate(Bundle savedInstanceState) { | ||||||
|  | @ -67,7 +73,8 @@ public class HashTagActivity extends BaseActivity { | ||||||
|         } |         } | ||||||
|         if (tag == null) |         if (tag == null) | ||||||
|             finish(); |             finish(); | ||||||
| 
 |         pinnedTag = false; | ||||||
|  |         followedTag = false; | ||||||
|         setSupportActionBar(binding.toolbar); |         setSupportActionBar(binding.toolbar); | ||||||
|         ActionBar actionBar = getSupportActionBar(); |         ActionBar actionBar = getSupportActionBar(); | ||||||
|         //Remove title |         //Remove title | ||||||
|  | @ -81,6 +88,29 @@ public class HashTagActivity extends BaseActivity { | ||||||
|             getSupportActionBar().setDisplayShowHomeEnabled(true); |             getSupportActionBar().setDisplayShowHomeEnabled(true); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         tagVM = new ViewModelProvider(HashTagActivity.this).get(TagVM.class); | ||||||
|  |         tagVM.getTag(MainActivity.currentInstance, MainActivity.currentToken, tag).observe(this, returnedTag -> { | ||||||
|  |             if (returnedTag != null) { | ||||||
|  |                 followedTag = returnedTag.following; | ||||||
|  |                 invalidateOptionsMenu(); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |         ReorderVM reorderVM = new ViewModelProvider(HashTagActivity.this).get(ReorderVM.class); | ||||||
|  |         reorderVM.getAllPinned().observe(HashTagActivity.this, pinned -> { | ||||||
|  |             if (pinned != null) { | ||||||
|  |                 if (pinned.pinnedTimelines != null) { | ||||||
|  |                     for (PinnedTimeline pinnedTimeline : pinned.pinnedTimelines) { | ||||||
|  |                         if (pinnedTimeline.tagTimeline != null) { | ||||||
|  |                             if (pinnedTimeline.tagTimeline.name.equalsIgnoreCase(tag)) { | ||||||
|  |                                 pinnedTag = true; | ||||||
|  |                                 invalidateOptionsMenu(); | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|         Bundle bundle = new Bundle(); |         Bundle bundle = new Bundle(); | ||||||
|         bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, Timeline.TimeLineEnum.TAG); |         bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, Timeline.TimeLineEnum.TAG); | ||||||
|         bundle.putString(Helper.ARG_SEARCH_KEYWORD, tag); |         bundle.putString(Helper.ARG_SEARCH_KEYWORD, tag); | ||||||
|  | @ -151,10 +181,19 @@ public class HashTagActivity extends BaseActivity { | ||||||
|                     Intent intentBD = new Intent(Helper.BROADCAST_DATA); |                     Intent intentBD = new Intent(Helper.BROADCAST_DATA); | ||||||
|                     intentBD.putExtras(b); |                     intentBD.putExtras(b); | ||||||
|                     LocalBroadcastManager.getInstance(HashTagActivity.this).sendBroadcast(intentBD); |                     LocalBroadcastManager.getInstance(HashTagActivity.this).sendBroadcast(intentBD); | ||||||
|  |                     pinnedTag = true; | ||||||
|  |                     invalidateOptionsMenu(); | ||||||
|                 } catch (DBException e) { |                 } catch (DBException e) { | ||||||
|                     e.printStackTrace(); |                     e.printStackTrace(); | ||||||
|                 } |                 } | ||||||
|             }).start(); |             }).start(); | ||||||
|  |         } else if (item.getItemId() == R.id.action_follow_tag) { | ||||||
|  |             tagVM.follow(MainActivity.currentInstance, MainActivity.currentToken, tag).observe(this, returnedTag -> { | ||||||
|  |                 if (returnedTag != null) { | ||||||
|  |                     followedTag = returnedTag.following; | ||||||
|  |                     invalidateOptionsMenu(); | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return super.onOptionsItemSelected(item); |         return super.onOptionsItemSelected(item); | ||||||
|  | @ -163,7 +202,15 @@ public class HashTagActivity extends BaseActivity { | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|     public boolean onCreateOptionsMenu(@NonNull Menu menu) { |     public boolean onCreateOptionsMenu(@NonNull Menu menu) { | ||||||
|         getMenuInflater().inflate(R.menu.menu_reorder, menu); |         getMenuInflater().inflate(R.menu.menu_hashtag, menu); | ||||||
|  |         MenuItem pin = menu.findItem(R.id.action_add_timeline); | ||||||
|  |         MenuItem follow = menu.findItem(R.id.action_follow_tag); | ||||||
|  |         if (pinnedTag && pin != null) { | ||||||
|  |             pin.setVisible(false); | ||||||
|  |         } | ||||||
|  |         if (followedTag && follow != null) { | ||||||
|  |             follow.setVisible(false); | ||||||
|  |         } | ||||||
|         return super.onCreateOptionsMenu(menu); |         return super.onCreateOptionsMenu(menu); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -23,18 +23,21 @@ import retrofit2.http.GET; | ||||||
| import retrofit2.http.Header; | import retrofit2.http.Header; | ||||||
| import retrofit2.http.POST; | import retrofit2.http.POST; | ||||||
| import retrofit2.http.Path; | import retrofit2.http.Path; | ||||||
| import retrofit2.http.Query; |  | ||||||
| 
 | 
 | ||||||
| public interface MastodonTagService { | public interface MastodonTagService { | ||||||
| 
 | 
 | ||||||
|     //Get followed tags |     //Get followed tags | ||||||
|     @GET("followed_tags") |     @GET("followed_tags") | ||||||
|     Call<List<Tag>> getFollowedTags( |     Call<List<Tag>> getFollowedTags( | ||||||
|  |             @Header("Authorization") String token | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     //Get followed tags | ||||||
|  |     @GET("tags/{name}") | ||||||
|  |     Call<Tag> getTag( | ||||||
|             @Header("Authorization") String token, |             @Header("Authorization") String token, | ||||||
|             @Query("max_id") String max_id, |             @Path("name") String name | ||||||
|             @Query("since_id") String since_id, |  | ||||||
|             @Query("min_id") String min_id, |  | ||||||
|             @Query("limit") int limit |  | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|     //Follow tag |     //Follow tag | ||||||
|  |  | ||||||
|  | @ -0,0 +1,79 @@ | ||||||
|  | package app.fedilab.android.ui.drawer; | ||||||
|  | /* Copyright 2022 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 <http://www.gnu.org/licenses>. */ | ||||||
|  | 
 | ||||||
|  | import android.view.LayoutInflater; | ||||||
|  | import android.view.ViewGroup; | ||||||
|  | 
 | ||||||
|  | import androidx.annotation.NonNull; | ||||||
|  | import androidx.recyclerview.widget.RecyclerView; | ||||||
|  | 
 | ||||||
|  | import java.util.List; | ||||||
|  | 
 | ||||||
|  | import app.fedilab.android.client.entities.api.Tag; | ||||||
|  | import app.fedilab.android.databinding.DrawerListBinding; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | public class FollowedTagAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { | ||||||
|  |     private final List<Tag> tagList; | ||||||
|  |     public ActionOnTag actionOnTag; | ||||||
|  | 
 | ||||||
|  |     public FollowedTagAdapter(List<Tag> tagList) { | ||||||
|  |         this.tagList = tagList; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     public int getCount() { | ||||||
|  |         return tagList.size(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public Tag getItem(int position) { | ||||||
|  |         return tagList.get(position); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @NonNull | ||||||
|  |     @Override | ||||||
|  |     public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { | ||||||
|  |         DrawerListBinding itemBinding = DrawerListBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false); | ||||||
|  |         return new ListViewHolder(itemBinding); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) { | ||||||
|  |         Tag tag = tagList.get(position); | ||||||
|  |         ListViewHolder holder = (ListViewHolder) viewHolder; | ||||||
|  |         holder.binding.title.setText(tag.name); | ||||||
|  |         holder.binding.title.setOnClickListener(v -> actionOnTag.click(tag)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public int getItemCount() { | ||||||
|  |         return tagList.size(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     public interface ActionOnTag { | ||||||
|  |         void click(Tag tag); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public static class ListViewHolder extends RecyclerView.ViewHolder { | ||||||
|  |         DrawerListBinding binding; | ||||||
|  | 
 | ||||||
|  |         ListViewHolder(DrawerListBinding itemView) { | ||||||
|  |             super(itemView.getRoot()); | ||||||
|  |             binding = itemView; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -112,6 +112,25 @@ public class ReorderVM extends AndroidViewModel { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |     public LiveData<Pinned> getAllPinned() { | ||||||
|  |         pinnedMutableLiveData = new MutableLiveData<>(); | ||||||
|  |         new Thread(() -> { | ||||||
|  |             Pinned pinned = null; | ||||||
|  |             try { | ||||||
|  |                 pinned = new Pinned(getApplication().getApplicationContext()).getAllPinned(currentAccount); | ||||||
|  | 
 | ||||||
|  |             } catch (DBException e) { | ||||||
|  |                 e.printStackTrace(); | ||||||
|  |             } | ||||||
|  |             Handler mainHandler = new Handler(Looper.getMainLooper()); | ||||||
|  |             Pinned finalPinned = pinned; | ||||||
|  |             Runnable myRunnable = () -> pinnedMutableLiveData.setValue(finalPinned); | ||||||
|  |             mainHandler.post(myRunnable); | ||||||
|  |         }).start(); | ||||||
|  |         return pinnedMutableLiveData; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|     public LiveData<BottomMenu> getBottomMenu() { |     public LiveData<BottomMenu> getBottomMenu() { | ||||||
|         bottomMenuMutableLiveData = new MutableLiveData<>(); |         bottomMenuMutableLiveData = new MutableLiveData<>(); | ||||||
|         new Thread(() -> { |         new Thread(() -> { | ||||||
|  |  | ||||||
|  | @ -28,8 +28,6 @@ import java.util.concurrent.TimeUnit; | ||||||
| 
 | 
 | ||||||
| import app.fedilab.android.client.endpoints.MastodonTagService; | import app.fedilab.android.client.endpoints.MastodonTagService; | ||||||
| import app.fedilab.android.client.entities.api.Pagination; | import app.fedilab.android.client.entities.api.Pagination; | ||||||
| import app.fedilab.android.client.entities.api.Status; |  | ||||||
| import app.fedilab.android.client.entities.api.Statuses; |  | ||||||
| import app.fedilab.android.client.entities.api.Tag; | import app.fedilab.android.client.entities.api.Tag; | ||||||
| import app.fedilab.android.client.entities.api.Tags; | import app.fedilab.android.client.entities.api.Tags; | ||||||
| import app.fedilab.android.helper.Helper; | import app.fedilab.android.helper.Helper; | ||||||
|  | @ -76,19 +74,15 @@ public class TagVM extends AndroidViewModel { | ||||||
|     /** |     /** | ||||||
|      * Return followed tags with pagination |      * Return followed tags with pagination | ||||||
|      * |      * | ||||||
|      * @return {@link LiveData} containing a {@link Statuses}. Note: Not to be confused with {@link Status} |      * @return {@link LiveData} containing a {@link Tags}. Note: Not to be confused with {@link Tag} | ||||||
|      */ |      */ | ||||||
|     public LiveData<Tags> followedTags(@NonNull String instance, String token, |     public LiveData<Tags> followedTags(@NonNull String instance, String token) { | ||||||
|                                        String maxId, |  | ||||||
|                                        String sinceId, |  | ||||||
|                                        String minId, |  | ||||||
|                                        int count) { |  | ||||||
|         tagsMutableLiveData = new MutableLiveData<>(); |         tagsMutableLiveData = new MutableLiveData<>(); | ||||||
|         MastodonTagService mastodonTagService = init(instance); |         MastodonTagService mastodonTagService = init(instance); | ||||||
|         new Thread(() -> { |         new Thread(() -> { | ||||||
|             List<Tag> tagList = null; |             List<Tag> tagList = null; | ||||||
|             Pagination pagination = null; |             Pagination pagination = null; | ||||||
|             Call<List<Tag>> followedTagsListCall = mastodonTagService.getFollowedTags(token, maxId, sinceId, minId, count); |             Call<List<Tag>> followedTagsListCall = mastodonTagService.getFollowedTags(token); | ||||||
|             if (followedTagsListCall != null) { |             if (followedTagsListCall != null) { | ||||||
|                 try { |                 try { | ||||||
|                     Response<List<Tag>> tagsResponse = followedTagsListCall.execute(); |                     Response<List<Tag>> tagsResponse = followedTagsListCall.execute(); | ||||||
|  | @ -111,6 +105,37 @@ public class TagVM extends AndroidViewModel { | ||||||
|         return tagsMutableLiveData; |         return tagsMutableLiveData; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Return tag | ||||||
|  |      * | ||||||
|  |      * @return {@link LiveData} containing a {@link Tag} | ||||||
|  |      */ | ||||||
|  |     public LiveData<Tag> getTag(@NonNull String instance, String token, | ||||||
|  |                                 String tagName) { | ||||||
|  |         tagMutableLiveData = new MutableLiveData<>(); | ||||||
|  |         MastodonTagService mastodonTagService = init(instance); | ||||||
|  |         new Thread(() -> { | ||||||
|  |             Tag tag = null; | ||||||
|  |             Call<Tag> tagCall = mastodonTagService.getTag(token, tagName); | ||||||
|  |             if (tagCall != null) { | ||||||
|  |                 try { | ||||||
|  |                     Response<Tag> tagResponse = tagCall.execute(); | ||||||
|  |                     if (tagResponse.isSuccessful()) { | ||||||
|  |                         tag = tagResponse.body(); | ||||||
|  |                     } | ||||||
|  |                 } catch (Exception e) { | ||||||
|  |                     e.printStackTrace(); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             Handler mainHandler = new Handler(Looper.getMainLooper()); | ||||||
|  |             Tag finalTag = tag; | ||||||
|  |             Runnable myRunnable = () -> tagMutableLiveData.setValue(finalTag); | ||||||
|  |             mainHandler.post(myRunnable); | ||||||
|  |         }).start(); | ||||||
|  |         return tagMutableLiveData; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * Follow a tag |      * Follow a tag | ||||||
|      * |      * | ||||||
|  |  | ||||||
							
								
								
									
										22
									
								
								app/src/main/res/drawable/ic_baseline_post_add_24.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								app/src/main/res/drawable/ic_baseline_post_add_24.xml
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,22 @@ | ||||||
|  | <vector xmlns:android="http://schemas.android.com/apk/res/android" | ||||||
|  |     android:width="24dp" | ||||||
|  |     android:height="24dp" | ||||||
|  |     android:tint="#FFFFFF" | ||||||
|  |     android:viewportWidth="24" | ||||||
|  |     android:viewportHeight="24"> | ||||||
|  |     <path | ||||||
|  |         android:fillColor="@android:color/white" | ||||||
|  |         android:pathData="M17,19.22H5V7h7V5H5C3.9,5 3,5.9 3,7v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2v-7h-2V19.22z" /> | ||||||
|  |     <path | ||||||
|  |         android:fillColor="@android:color/white" | ||||||
|  |         android:pathData="M19,2h-2v3h-3c0.01,0.01 0,2 0,2h3v2.99c0.01,0.01 2,0 2,0V7h3V5h-3V2z" /> | ||||||
|  |     <path | ||||||
|  |         android:fillColor="@android:color/white" | ||||||
|  |         android:pathData="M7,9h8v2h-8z" /> | ||||||
|  |     <path | ||||||
|  |         android:fillColor="@android:color/white" | ||||||
|  |         android:pathData="M7,12l0,2l8,0l0,-2l-3,0z" /> | ||||||
|  |     <path | ||||||
|  |         android:fillColor="@android:color/white" | ||||||
|  |         android:pathData="M7,15h8v2h-8z" /> | ||||||
|  | </vector> | ||||||
							
								
								
									
										10
									
								
								app/src/main/res/drawable/ic_baseline_tag_24.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								app/src/main/res/drawable/ic_baseline_tag_24.xml
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | ||||||
|  | <vector xmlns:android="http://schemas.android.com/apk/res/android" | ||||||
|  |     android:width="24dp" | ||||||
|  |     android:height="24dp" | ||||||
|  |     android:tint="#FFFFFF" | ||||||
|  |     android:viewportWidth="24" | ||||||
|  |     android:viewportHeight="24"> | ||||||
|  |     <path | ||||||
|  |         android:fillColor="@android:color/white" | ||||||
|  |         android:pathData="M20,10L20,8h-4L16,4h-2v4h-4L10,4L8,4v4L4,8v2h4v4L4,14v2h4v4h2v-4h4v4h2v-4h4v-2h-4v-4h4zM14,14h-4v-4h4v4z" /> | ||||||
|  | </vector> | ||||||
							
								
								
									
										33
									
								
								app/src/main/res/layout/activity_followed_tags.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								app/src/main/res/layout/activity_followed_tags.xml
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,33 @@ | ||||||
|  | <?xml version="1.0" encoding="utf-8"?> | ||||||
|  | <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||||
|  |     android:layout_width="match_parent" | ||||||
|  |     android:layout_height="match_parent" | ||||||
|  |     android:orientation="vertical" | ||||||
|  |     android:padding="@dimen/fab_margin"> | ||||||
|  | 
 | ||||||
|  |     <androidx.recyclerview.widget.RecyclerView | ||||||
|  |         android:id="@+id/recycler_view" | ||||||
|  |         android:layout_width="match_parent" | ||||||
|  |         android:layout_height="match_parent" | ||||||
|  |         android:clipToPadding="false" | ||||||
|  |         android:scrollbars="none" /> | ||||||
|  | 
 | ||||||
|  |     <TextView | ||||||
|  |         android:id="@+id/not_content" | ||||||
|  |         android:layout_width="match_parent" | ||||||
|  |         android:layout_height="match_parent" | ||||||
|  |         android:layout_gravity="center" | ||||||
|  |         android:gravity="center" | ||||||
|  |         android:padding="10dp" | ||||||
|  |         android:text="@string/action_followed_tag_empty" | ||||||
|  |         android:textSize="20sp" | ||||||
|  |         android:textStyle="bold" | ||||||
|  |         android:typeface="serif" | ||||||
|  |         android:visibility="gone" /> | ||||||
|  | 
 | ||||||
|  |     <androidx.fragment.app.FragmentContainerView | ||||||
|  |         android:id="@+id/fragment_container" | ||||||
|  |         android:layout_width="match_parent" | ||||||
|  |         android:layout_height="match_parent" | ||||||
|  |         android:visibility="gone" /> | ||||||
|  | </RelativeLayout> | ||||||
							
								
								
									
										17
									
								
								app/src/main/res/layout/popup_add_followed_tagt.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								app/src/main/res/layout/popup_add_followed_tagt.xml
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,17 @@ | ||||||
|  | <?xml version="1.0" encoding="utf-8"?> | ||||||
|  | <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||||
|  |     android:layout_width="match_parent" | ||||||
|  |     android:layout_height="wrap_content" | ||||||
|  |     android:layout_margin="@dimen/fab_margin" | ||||||
|  |     android:orientation="vertical"> | ||||||
|  | 
 | ||||||
|  |     <androidx.appcompat.widget.AppCompatEditText | ||||||
|  |         android:id="@+id/add_tag" | ||||||
|  |         android:layout_width="match_parent" | ||||||
|  |         android:layout_height="wrap_content" | ||||||
|  |         android:layout_marginTop="20dp" | ||||||
|  |         android:layout_marginBottom="20dp" | ||||||
|  |         android:hint="@string/write_the_tag_to_follow" | ||||||
|  |         android:inputType="text" | ||||||
|  |         android:singleLine="true" /> | ||||||
|  | </LinearLayout> | ||||||
|  | @ -25,6 +25,10 @@ | ||||||
|                         android:id="@+id/nav_list" |                         android:id="@+id/nav_list" | ||||||
|                         android:icon="@drawable/ic_baseline_view_list_24" |                         android:icon="@drawable/ic_baseline_view_list_24" | ||||||
|                         android:title="@string/action_lists" /> |                         android:title="@string/action_lists" /> | ||||||
|  |                     <item | ||||||
|  |                         android:id="@+id/nav_followed_tags" | ||||||
|  |                         android:icon="@drawable/ic_baseline_tag_24" | ||||||
|  |                         android:title="@string/followed_tags" /> | ||||||
|                     <item |                     <item | ||||||
|                         android:id="@+id/nav_follow_requests" |                         android:id="@+id/nav_follow_requests" | ||||||
|                         android:icon="@drawable/ic_baseline_group_add_24" |                         android:icon="@drawable/ic_baseline_group_add_24" | ||||||
|  |  | ||||||
							
								
								
									
										9
									
								
								app/src/main/res/menu/menu_followed_tag.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								app/src/main/res/menu/menu_followed_tag.xml
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | ||||||
|  | <?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/action_unfollow" | ||||||
|  |         android:icon="@drawable/ic_baseline_delete_24" | ||||||
|  |         android:title="@string/action_unfollow_tag" | ||||||
|  |         app:showAsAction="ifRoom" /> | ||||||
|  | </menu> | ||||||
							
								
								
									
										14
									
								
								app/src/main/res/menu/menu_hashtag.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								app/src/main/res/menu/menu_hashtag.xml
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,14 @@ | ||||||
|  | <?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/action_follow_tag" | ||||||
|  |         android:icon="@drawable/ic_baseline_post_add_24" | ||||||
|  |         android:title="@string/follow_tag" | ||||||
|  |         app:showAsAction="ifRoom" /> | ||||||
|  |     <item | ||||||
|  |         android:id="@+id/action_add_timeline" | ||||||
|  |         android:icon="@drawable/ic_baseline_add_24" | ||||||
|  |         android:title="@string/add_instances" | ||||||
|  |         app:showAsAction="ifRoom" /> | ||||||
|  | </menu> | ||||||
							
								
								
									
										9
									
								
								app/src/main/res/menu/menu_main_followed_tag.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								app/src/main/res/menu/menu_main_followed_tag.xml
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | ||||||
|  | <?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/action_follow_tag" | ||||||
|  |         android:icon="@drawable/ic_baseline_post_add_24" | ||||||
|  |         android:title="@string/action_tag_follow" | ||||||
|  |         app:showAsAction="ifRoom" /> | ||||||
|  | </menu> | ||||||
|  | @ -1974,4 +1974,13 @@ | ||||||
|     <string name="restart_the_app_theme">You should restart the application to apply changes.</string> |     <string name="restart_the_app_theme">You should restart the application to apply changes.</string> | ||||||
|     <string name="set_language_picker_title">Languages in picker</string> |     <string name="set_language_picker_title">Languages in picker</string> | ||||||
|     <string name="set_language_picker">Allow to reduce the list of languages in the picker when composing a message.</string> |     <string name="set_language_picker">Allow to reduce the list of languages in the picker when composing a message.</string> | ||||||
|  |     <string name="action_followed_tag_empty">You don\'t follow any tags!</string> | ||||||
|  |     <string name="action_unfollow_tag">Unfollow tag</string> | ||||||
|  |     <string name="action_unfollow_tag_confirm">Are you sure you want to unfollow this tag?</string> | ||||||
|  |     <string name="unfollow">Unfollow</string> | ||||||
|  |     <string name="action_tag_follow">Follow a tag</string> | ||||||
|  |     <string name="write_the_tag_to_follow">Write the tag to follow</string> | ||||||
|  |     <string name="not_valid_tag_name">Tag name is not valid!</string> | ||||||
|  |     <string name="followed_tags">Followed tags</string> | ||||||
|  |     <string name="follow_tag">Follow tag</string> | ||||||
| </resources> | </resources> | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue