Follow Hashtags

This commit is contained in:
Thomas 2022-11-15 10:51:46 +01:00
parent 170dbbd0cf
commit cdbfb17d94
17 changed files with 527 additions and 16 deletions

View file

@ -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"

View file

@ -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);

View file

@ -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();
});
}
}

View file

@ -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);
} }

View file

@ -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

View file

@ -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;
}
}
}

View file

@ -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(() -> {

View file

@ -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
* *

View 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>

View 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>

View 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>

View 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>

View file

@ -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"

View 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>

View 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>

View 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>

View file

@ -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>