mirror of
https://codeberg.org/tom79/Fedilab.git
synced 2024-12-23 01:00:04 +02: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