mirror of
https://codeberg.org/tom79/Fedilab.git
synced 2024-12-22 16:50:04 +02:00
Merge branch 'develop'
This commit is contained in:
commit
d70c285bea
29 changed files with 561 additions and 105 deletions
|
@ -13,8 +13,8 @@ android {
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdk 21
|
minSdk 21
|
||||||
targetSdk 33
|
targetSdk 33
|
||||||
versionCode 463
|
versionCode 464
|
||||||
versionName "3.14.1"
|
versionName "3.14.2"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
flavorDimensions "default"
|
flavorDimensions "default"
|
||||||
|
|
|
@ -249,6 +249,11 @@
|
||||||
android:configChanges="keyboardHidden|orientation|screenSize"
|
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||||
android:label="@string/Suggestions"
|
android:label="@string/Suggestions"
|
||||||
android:theme="@style/AppThemeBar" />
|
android:theme="@style/AppThemeBar" />
|
||||||
|
<activity
|
||||||
|
android:name=".activities.DirectoryActivity"
|
||||||
|
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||||
|
android:label="@string/Directory"
|
||||||
|
android:theme="@style/AppThemeBar" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".activities.PartnerShipActivity"
|
android:name=".activities.PartnerShipActivity"
|
||||||
android:configChanges="keyboardHidden|orientation|screenSize"
|
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
[
|
[
|
||||||
|
{
|
||||||
|
"version": "3.14.2",
|
||||||
|
"code": "464",
|
||||||
|
"note": "Added:\n- Display familiar followers on profiles\n- Display and filter Instance directory\n\nChanged:\n- Bot and reply icon indicators more visible\n- Single media are hidden with link previews\n\nFixed:\n- Fix a crash with Art timelines\n- Friendica: media cannot be downloaded/shared\n- Fix a crash with pinned timelines"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"version": "3.14.1",
|
"version": "3.14.1",
|
||||||
"code": "463",
|
"code": "463",
|
||||||
|
|
|
@ -109,6 +109,7 @@ import app.fedilab.android.activities.BaseActivity;
|
||||||
import app.fedilab.android.activities.CacheActivity;
|
import app.fedilab.android.activities.CacheActivity;
|
||||||
import app.fedilab.android.activities.ComposeActivity;
|
import app.fedilab.android.activities.ComposeActivity;
|
||||||
import app.fedilab.android.activities.ContextActivity;
|
import app.fedilab.android.activities.ContextActivity;
|
||||||
|
import app.fedilab.android.activities.DirectoryActivity;
|
||||||
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;
|
||||||
|
@ -409,6 +410,9 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
|
||||||
} else if (id == R.id.nav_suggestions) {
|
} else if (id == R.id.nav_suggestions) {
|
||||||
Intent intent = new Intent(this, SuggestionActivity.class);
|
Intent intent = new Intent(this, SuggestionActivity.class);
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
|
} else if (id == R.id.nav_directory) {
|
||||||
|
Intent intent = new Intent(this, DirectoryActivity.class);
|
||||||
|
startActivity(intent);
|
||||||
} else if (id == R.id.nav_cache) {
|
} else if (id == R.id.nav_cache) {
|
||||||
Intent intent = new Intent(BaseMainActivity.this, CacheActivity.class);
|
Intent intent = new Intent(BaseMainActivity.this, CacheActivity.class);
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
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 static app.fedilab.android.client.entities.app.Timeline.TimeLineEnum.ACCOUNT_DIRECTORY;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import app.fedilab.android.R;
|
||||||
|
import app.fedilab.android.databinding.ActivityDirectoryBinding;
|
||||||
|
import app.fedilab.android.helper.Helper;
|
||||||
|
import app.fedilab.android.ui.fragment.timeline.FragmentMastodonAccount;
|
||||||
|
|
||||||
|
|
||||||
|
public class DirectoryActivity extends BaseBarActivity {
|
||||||
|
|
||||||
|
private static boolean local = false;
|
||||||
|
private static String order = "active";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
ActivityDirectoryBinding binding = ActivityDirectoryBinding.inflate(getLayoutInflater());
|
||||||
|
setContentView(binding.getRoot());
|
||||||
|
if (getSupportActionBar() != null) {
|
||||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
}
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putBoolean(Helper.ARG_DIRECTORY_LOCAL, local);
|
||||||
|
bundle.putString(Helper.ARG_DIRECTORY_ORDER, order);
|
||||||
|
bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, ACCOUNT_DIRECTORY);
|
||||||
|
Helper.addFragment(getSupportFragmentManager(), R.id.nav_host_fragment_directory, new FragmentMastodonAccount(), bundle, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
getMenuInflater().inflate(R.menu.menu_directory, menu);
|
||||||
|
if (order.equals("active")) {
|
||||||
|
menu.findItem(R.id.order_active).setChecked(true);
|
||||||
|
} else {
|
||||||
|
menu.findItem(R.id.order_new).setChecked(true);
|
||||||
|
}
|
||||||
|
menu.findItem(R.id.action_local).setChecked(local);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(@NotNull MenuItem item) {
|
||||||
|
if (item.getItemId() == android.R.id.home) {
|
||||||
|
finish();
|
||||||
|
return true;
|
||||||
|
} else if (item.getItemId() == R.id.action_local) {
|
||||||
|
item.setChecked(!item.isChecked());
|
||||||
|
local = item.isChecked();
|
||||||
|
} else if (item.getItemId() == R.id.order_active) {
|
||||||
|
order = "active";
|
||||||
|
} else if (item.getItemId() == R.id.order_new) {
|
||||||
|
order = "new";
|
||||||
|
}
|
||||||
|
recreate();
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -34,6 +34,7 @@ import android.text.method.LinkMovementMethod;
|
||||||
import android.text.style.ForegroundColorSpan;
|
import android.text.style.ForegroundColorSpan;
|
||||||
import android.text.style.UnderlineSpan;
|
import android.text.style.UnderlineSpan;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
@ -76,6 +77,7 @@ import app.fedilab.android.BaseMainActivity;
|
||||||
import app.fedilab.android.R;
|
import app.fedilab.android.R;
|
||||||
import app.fedilab.android.client.entities.api.Account;
|
import app.fedilab.android.client.entities.api.Account;
|
||||||
import app.fedilab.android.client.entities.api.Attachment;
|
import app.fedilab.android.client.entities.api.Attachment;
|
||||||
|
import app.fedilab.android.client.entities.api.FamiliarFollowers;
|
||||||
import app.fedilab.android.client.entities.api.Field;
|
import app.fedilab.android.client.entities.api.Field;
|
||||||
import app.fedilab.android.client.entities.api.IdentityProof;
|
import app.fedilab.android.client.entities.api.IdentityProof;
|
||||||
import app.fedilab.android.client.entities.api.MastodonList;
|
import app.fedilab.android.client.entities.api.MastodonList;
|
||||||
|
@ -86,6 +88,7 @@ import app.fedilab.android.client.entities.app.RemoteInstance;
|
||||||
import app.fedilab.android.client.entities.app.Timeline;
|
import app.fedilab.android.client.entities.app.Timeline;
|
||||||
import app.fedilab.android.client.entities.app.WellKnownNodeinfo;
|
import app.fedilab.android.client.entities.app.WellKnownNodeinfo;
|
||||||
import app.fedilab.android.databinding.ActivityProfileBinding;
|
import app.fedilab.android.databinding.ActivityProfileBinding;
|
||||||
|
import app.fedilab.android.databinding.NotificationsRelatedAccountsBinding;
|
||||||
import app.fedilab.android.exception.DBException;
|
import app.fedilab.android.exception.DBException;
|
||||||
import app.fedilab.android.helper.CrossActionHelper;
|
import app.fedilab.android.helper.CrossActionHelper;
|
||||||
import app.fedilab.android.helper.Helper;
|
import app.fedilab.android.helper.Helper;
|
||||||
|
@ -106,6 +109,7 @@ public class ProfileActivity extends BaseActivity {
|
||||||
|
|
||||||
|
|
||||||
private RelationShip relationship;
|
private RelationShip relationship;
|
||||||
|
private FamiliarFollowers familiarFollowers;
|
||||||
private Account account;
|
private Account account;
|
||||||
private ScheduledExecutorService scheduledExecutorService;
|
private ScheduledExecutorService scheduledExecutorService;
|
||||||
private action doAction;
|
private action doAction;
|
||||||
|
@ -255,6 +259,13 @@ public class ProfileActivity extends BaseActivity {
|
||||||
updateAccount();
|
updateAccount();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
accountsVM.getFamiliarFollowers(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, accountListToCheck).observe(ProfileActivity.this, familiarFollowersList -> {
|
||||||
|
if (familiarFollowersList != null && familiarFollowersList.size() > 0) {
|
||||||
|
this.familiarFollowers = familiarFollowersList.get(0);
|
||||||
|
updateAccount();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
//Retrieve identity proofs
|
//Retrieve identity proofs
|
||||||
accountsVM.getIdentityProofs(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id).observe(ProfileActivity.this, identityProofs -> {
|
accountsVM.getIdentityProofs(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id).observe(ProfileActivity.this, identityProofs -> {
|
||||||
this.identityProofList = identityProofs;
|
this.identityProofList = identityProofs;
|
||||||
|
@ -567,6 +578,27 @@ public class ProfileActivity extends BaseActivity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (familiarFollowers != null && familiarFollowers.accounts != null && familiarFollowers.accounts.size() > 0) {
|
||||||
|
binding.relatedAccounts.removeAllViews();
|
||||||
|
for (Account account : familiarFollowers.accounts) {
|
||||||
|
NotificationsRelatedAccountsBinding notificationsRelatedAccountsBinding = NotificationsRelatedAccountsBinding.inflate(LayoutInflater.from(ProfileActivity.this));
|
||||||
|
MastodonHelper.loadProfileMediaMastodonRound(ProfileActivity.this, notificationsRelatedAccountsBinding.profilePicture, account);
|
||||||
|
notificationsRelatedAccountsBinding.acc.setText(account.username);
|
||||||
|
notificationsRelatedAccountsBinding.relatedAccountContainer.setOnClickListener(v -> {
|
||||||
|
Intent intent = new Intent(ProfileActivity.this, ProfileActivity.class);
|
||||||
|
Bundle b = new Bundle();
|
||||||
|
b.putSerializable(Helper.ARG_ACCOUNT, account);
|
||||||
|
intent.putExtras(b);
|
||||||
|
ActivityOptionsCompat options = ActivityOptionsCompat
|
||||||
|
.makeSceneTransitionAnimation(ProfileActivity.this, notificationsRelatedAccountsBinding.profilePicture, getString(R.string.activity_porfile_pp));
|
||||||
|
// start the new activity
|
||||||
|
startActivity(intent, options.toBundle());
|
||||||
|
});
|
||||||
|
binding.relatedAccounts.addView(notificationsRelatedAccountsBinding.getRoot());
|
||||||
|
}
|
||||||
|
binding.familiarFollowers.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
binding.accountFollow.setEnabled(true);
|
binding.accountFollow.setEnabled(true);
|
||||||
//Visibility depending of the relationship
|
//Visibility depending of the relationship
|
||||||
if (relationship != null) {
|
if (relationship != null) {
|
||||||
|
|
|
@ -18,6 +18,7 @@ package app.fedilab.android.client.endpoints;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import app.fedilab.android.client.entities.api.Account;
|
import app.fedilab.android.client.entities.api.Account;
|
||||||
|
import app.fedilab.android.client.entities.api.FamiliarFollowers;
|
||||||
import app.fedilab.android.client.entities.api.FeaturedTag;
|
import app.fedilab.android.client.entities.api.FeaturedTag;
|
||||||
import app.fedilab.android.client.entities.api.IdentityProof;
|
import app.fedilab.android.client.entities.api.IdentityProof;
|
||||||
import app.fedilab.android.client.entities.api.MastodonList;
|
import app.fedilab.android.client.entities.api.MastodonList;
|
||||||
|
@ -253,6 +254,13 @@ public interface MastodonAccountsService {
|
||||||
@Query("id[]") List<String> ids
|
@Query("id[]") List<String> ids
|
||||||
);
|
);
|
||||||
|
|
||||||
|
//Get familiar followers
|
||||||
|
@GET("accounts/familiar_followers ")
|
||||||
|
Call<List<FamiliarFollowers>> getFamiliarFollowers(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Query("id[]") List<String> ids
|
||||||
|
);
|
||||||
|
|
||||||
//Get search
|
//Get search
|
||||||
@GET("accounts/search")
|
@GET("accounts/search")
|
||||||
Call<List<Account>> searchAccounts(
|
Call<List<Account>> searchAccounts(
|
||||||
|
@ -409,4 +417,15 @@ public interface MastodonAccountsService {
|
||||||
@Header("Authorization") String token,
|
@Header("Authorization") String token,
|
||||||
@Path("account_id") String account_id
|
@Path("account_id") String account_id
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
//Get user suggestions
|
||||||
|
@GET("directory")
|
||||||
|
Call<List<Account>> getDirectory(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Query("offset") Integer offset,
|
||||||
|
@Query("limit") Integer limit,
|
||||||
|
@Query("order") String order,
|
||||||
|
@Query("local") Boolean local
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
package app.fedilab.android.client.entities.api;
|
||||||
|
/* 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 com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class FamiliarFollowers implements Serializable {
|
||||||
|
|
||||||
|
@SerializedName("id")
|
||||||
|
public String id;
|
||||||
|
@SerializedName("accounts")
|
||||||
|
public List<Account> accounts;
|
||||||
|
}
|
|
@ -382,6 +382,8 @@ public class Timeline {
|
||||||
TREND_MESSAGE("TREND_MESSAGE"),
|
TREND_MESSAGE("TREND_MESSAGE"),
|
||||||
@SerializedName("ACCOUNT_SUGGESTION")
|
@SerializedName("ACCOUNT_SUGGESTION")
|
||||||
ACCOUNT_SUGGESTION("ACCOUNT_SUGGESTION"),
|
ACCOUNT_SUGGESTION("ACCOUNT_SUGGESTION"),
|
||||||
|
@SerializedName("ACCOUNT_DIRECTORY")
|
||||||
|
ACCOUNT_DIRECTORY("ACCOUNT_DIRECTORY"),
|
||||||
@SerializedName("PUBLIC_TREND_MESSAGE")
|
@SerializedName("PUBLIC_TREND_MESSAGE")
|
||||||
TREND_MESSAGE_PUBLIC("TREND_MESSAGE_PUBLIC"),
|
TREND_MESSAGE_PUBLIC("TREND_MESSAGE_PUBLIC"),
|
||||||
@SerializedName("STATUS_HISTORY")
|
@SerializedName("STATUS_HISTORY")
|
||||||
|
|
|
@ -253,6 +253,8 @@ public class Helper {
|
||||||
public static final String ARG_WORK_ID = "ARG_WORK_ID";
|
public static final String ARG_WORK_ID = "ARG_WORK_ID";
|
||||||
public static final String ARG_LIST_ID = "ARG_LIST_ID";
|
public static final String ARG_LIST_ID = "ARG_LIST_ID";
|
||||||
public static final String ARG_SEARCH_KEYWORD = "ARG_SEARCH_KEYWORD";
|
public static final String ARG_SEARCH_KEYWORD = "ARG_SEARCH_KEYWORD";
|
||||||
|
public static final String ARG_DIRECTORY_ORDER = "ARG_DIRECTORY_ORDER";
|
||||||
|
public static final String ARG_DIRECTORY_LOCAL = "ARG_DIRECTORY_LOCAL";
|
||||||
public static final String ARG_SEARCH_TYPE = "ARG_SEARCH_TYPE";
|
public static final String ARG_SEARCH_TYPE = "ARG_SEARCH_TYPE";
|
||||||
public static final String ARG_SEARCH_KEYWORD_CACHE = "ARG_SEARCH_KEYWORD_CACHE";
|
public static final String ARG_SEARCH_KEYWORD_CACHE = "ARG_SEARCH_KEYWORD_CACHE";
|
||||||
public static final String ARG_VIEW_MODEL_KEY = "ARG_VIEW_MODEL_KEY";
|
public static final String ARG_VIEW_MODEL_KEY = "ARG_VIEW_MODEL_KEY";
|
||||||
|
@ -1809,7 +1811,7 @@ public class Helper {
|
||||||
binding.aboutSupport.setVisibility(View.GONE);
|
binding.aboutSupport.setVisibility(View.GONE);
|
||||||
binding.aboutSupportPaypal.setVisibility(View.GONE);
|
binding.aboutSupportPaypal.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
binding.accountFollow.setImageResource(R.drawable.ic_baseline_person_add_24);
|
binding.accountFollow.setIconResource(R.drawable.ic_baseline_person_add_24);
|
||||||
binding.aboutSupport.setOnClickListener(v -> {
|
binding.aboutSupport.setOnClickListener(v -> {
|
||||||
Intent intentLiberapay = new Intent(Intent.ACTION_VIEW);
|
Intent intentLiberapay = new Intent(Intent.ACTION_VIEW);
|
||||||
intentLiberapay.setData(Uri.parse("https://liberapay.com/tom79"));
|
intentLiberapay.setData(Uri.parse("https://liberapay.com/tom79"));
|
||||||
|
|
|
@ -40,6 +40,9 @@ import androidx.work.OneTimeWorkRequest;
|
||||||
import androidx.work.WorkManager;
|
import androidx.work.WorkManager;
|
||||||
|
|
||||||
import com.bumptech.glide.Glide;
|
import com.bumptech.glide.Glide;
|
||||||
|
import com.bumptech.glide.load.resource.bitmap.CenterCrop;
|
||||||
|
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
|
||||||
|
import com.bumptech.glide.request.RequestOptions;
|
||||||
import com.google.gson.annotations.SerializedName;
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
|
@ -268,6 +271,52 @@ public class MastodonHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void loadProfileMediaMastodonRound(Activity activity, ImageView view, Account account) {
|
||||||
|
Context context = view.getContext();
|
||||||
|
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
|
boolean disableGif = sharedpreferences.getBoolean(context.getString(R.string.SET_DISABLE_GIF), false);
|
||||||
|
@DrawableRes int placeholder = R.drawable.ic_person;
|
||||||
|
if (Helper.isValidContextForGlide(activity != null ? activity : context)) {
|
||||||
|
if (account == null) {
|
||||||
|
Glide.with(activity != null ? activity : context)
|
||||||
|
.asDrawable()
|
||||||
|
.load(placeholder)
|
||||||
|
.apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(16)))
|
||||||
|
.thumbnail(0.1f)
|
||||||
|
.placeholder(placeholder)
|
||||||
|
.into(view);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String targetedUrl = disableGif ? account.avatar_static : account.avatar;
|
||||||
|
if (targetedUrl != null) {
|
||||||
|
if (disableGif || (!targetedUrl.endsWith(".gif"))) {
|
||||||
|
Glide.with(activity != null ? activity : context)
|
||||||
|
.asDrawable()
|
||||||
|
.load(targetedUrl)
|
||||||
|
.apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(10)))
|
||||||
|
.thumbnail(0.1f)
|
||||||
|
.placeholder(placeholder)
|
||||||
|
.into(view);
|
||||||
|
} else {
|
||||||
|
Glide.with(activity != null ? activity : context)
|
||||||
|
.asGif()
|
||||||
|
.load(targetedUrl)
|
||||||
|
.apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(10)))
|
||||||
|
.thumbnail(0.1f)
|
||||||
|
.placeholder(placeholder)
|
||||||
|
.into(view);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Glide.with(activity != null ? activity : context)
|
||||||
|
.asDrawable()
|
||||||
|
.load(placeholder)
|
||||||
|
.apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(10)))
|
||||||
|
.thumbnail(0.1f)
|
||||||
|
.into(view);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a date in String -> format yyyy-MM-dd HH:mm:ss
|
* Convert a date in String -> format yyyy-MM-dd HH:mm:ss
|
||||||
*
|
*
|
||||||
|
|
|
@ -100,7 +100,10 @@ public class MediaHelper {
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
String mime = getMimeType(url);
|
String mime = getMimeType(url);
|
||||||
final String fileName = URLUtil.guessFileName(url, null, null);
|
String fileName = URLUtil.guessFileName(url, null, null);
|
||||||
|
if (fileName.endsWith(".bin")) {
|
||||||
|
fileName = fileName.replace(".bin", ".mp4");
|
||||||
|
}
|
||||||
request.allowScanningByMediaScanner();
|
request.allowScanningByMediaScanner();
|
||||||
if (mime.toLowerCase().startsWith("video")) {
|
if (mime.toLowerCase().startsWith("video")) {
|
||||||
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_MOVIES, context.getString(R.string.app_name) + "/" + fileName);
|
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_MOVIES, context.getString(R.string.app_name) + "/" + fileName);
|
||||||
|
@ -134,9 +137,11 @@ public class MediaHelper {
|
||||||
.into(new CustomTarget<File>() {
|
.into(new CustomTarget<File>() {
|
||||||
@Override
|
@Override
|
||||||
public void onResourceReady(@NotNull File file, Transition<? super File> transition) {
|
public void onResourceReady(@NotNull File file, Transition<? super File> transition) {
|
||||||
final String fileName = URLUtil.guessFileName(url, null, null);
|
String fileName = URLUtil.guessFileName(url, null, null);
|
||||||
|
|
||||||
|
|
||||||
|
if (fileName.endsWith(".bin")) {
|
||||||
|
fileName = fileName.replace(".bin", ".jpg");
|
||||||
|
}
|
||||||
File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
|
File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
|
||||||
File targeted_folder = new File(path, context.getString(R.string.app_name));
|
File targeted_folder = new File(path, context.getString(R.string.app_name));
|
||||||
if (!targeted_folder.exists()) {
|
if (!targeted_folder.exists()) {
|
||||||
|
|
|
@ -464,7 +464,7 @@ public class PinnedTimelineHelper {
|
||||||
break;
|
break;
|
||||||
case NITTER:
|
case NITTER:
|
||||||
item.setIcon(R.drawable.nitter);
|
item.setIcon(R.drawable.nitter);
|
||||||
if (pinnedTimeline.remoteInstance.displayName.trim().length() > 0) {
|
if (pinnedTimeline.remoteInstance.displayName != null && pinnedTimeline.remoteInstance.displayName.trim().length() > 0) {
|
||||||
item.setTitle(pinnedTimeline.remoteInstance.displayName);
|
item.setTitle(pinnedTimeline.remoteInstance.displayName);
|
||||||
} else {
|
} else {
|
||||||
item.setTitle(pinnedTimeline.remoteInstance.host);
|
item.setTitle(pinnedTimeline.remoteInstance.host);
|
||||||
|
|
|
@ -163,6 +163,8 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
||||||
public static final int STATUS_FILTERED = 3;
|
public static final int STATUS_FILTERED = 3;
|
||||||
public static final int STATUS_FILTERED_HIDE = 4;
|
public static final int STATUS_FILTERED_HIDE = 4;
|
||||||
public static final int STATUS_PIXELFED = 5;
|
public static final int STATUS_PIXELFED = 5;
|
||||||
|
private static float measuredWidth = -1;
|
||||||
|
private static float measuredWidthArt = -1;
|
||||||
private final List<Status> statusList;
|
private final List<Status> statusList;
|
||||||
private final boolean minified;
|
private final boolean minified;
|
||||||
private final Timeline.TimeLineEnum timelineType;
|
private final Timeline.TimeLineEnum timelineType;
|
||||||
|
@ -171,10 +173,7 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
||||||
public FetchMoreCallBack fetchMoreCallBack;
|
public FetchMoreCallBack fetchMoreCallBack;
|
||||||
private Context context;
|
private Context context;
|
||||||
private boolean visiblePixelfed;
|
private boolean visiblePixelfed;
|
||||||
|
|
||||||
private RecyclerView mRecyclerView;
|
private RecyclerView mRecyclerView;
|
||||||
private static float measuredWidth = -1;
|
|
||||||
private static float measuredWidthArt = -1;
|
|
||||||
|
|
||||||
public StatusAdapter(List<Status> statuses, Timeline.TimeLineEnum timelineType, boolean minified, boolean canBeFederated, boolean checkRemotely) {
|
public StatusAdapter(List<Status> statuses, Timeline.TimeLineEnum timelineType, boolean minified, boolean canBeFederated, boolean checkRemotely) {
|
||||||
this.statusList = statuses;
|
this.statusList = statuses;
|
||||||
|
@ -1288,7 +1287,9 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
||||||
}
|
}
|
||||||
LayoutInflater inflater = ((Activity) context).getLayoutInflater();
|
LayoutInflater inflater = ((Activity) context).getLayoutInflater();
|
||||||
//--- MEDIA ATTACHMENT ---
|
//--- MEDIA ATTACHMENT ---
|
||||||
if (statusToDeal.media_attachments != null && statusToDeal.media_attachments.size() > 0) {
|
boolean cardDisplayed = (statusToDeal.card != null && (display_card || statusToDeal.isFocused) && statusToDeal.quote_id == null);
|
||||||
|
if (statusToDeal.media_attachments != null && statusToDeal.media_attachments.size() > 0 && (!cardDisplayed || statusToDeal.media_attachments.size() > 1)) {
|
||||||
|
|
||||||
holder.binding.attachmentsList.removeAllViews();
|
holder.binding.attachmentsList.removeAllViews();
|
||||||
holder.binding.mediaContainer.removeAllViews();
|
holder.binding.mediaContainer.removeAllViews();
|
||||||
if ((loadMediaType.equals("ASK") || (loadMediaType.equals("WIFI") && !TimelineHelper.isOnWIFI(context))) && !statusToDeal.canLoadMedia) {
|
if ((loadMediaType.equals("ASK") || (loadMediaType.equals("WIFI") && !TimelineHelper.isOnWIFI(context))) && !statusToDeal.canLoadMedia) {
|
||||||
|
@ -2246,53 +2247,6 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public List<Attachment> getPreloadItems(int position) {
|
|
||||||
List<Attachment> attachments = new ArrayList<>();
|
|
||||||
if (position == 0 && statusList.size() > 0) {
|
|
||||||
for (Status status : statusList.subList(0, 1)) {
|
|
||||||
Status statusToDeal = status.reblog != null ? status.reblog : status;
|
|
||||||
if (statusToDeal.media_attachments != null && statusToDeal.media_attachments.size() > 0) {
|
|
||||||
attachments.addAll(statusToDeal.media_attachments);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (position > 0 && position < (statusList.size() - 1)) {
|
|
||||||
for (Status status : statusList.subList(position - 1, position + 1)) {
|
|
||||||
Status statusToDeal = status.reblog != null ? status.reblog : status;
|
|
||||||
if (statusToDeal.media_attachments != null && statusToDeal.media_attachments.size() > 0) {
|
|
||||||
attachments.addAll(statusToDeal.media_attachments);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (Status status : statusList.subList(position, position)) {
|
|
||||||
Status statusToDeal = status.reblog != null ? status.reblog : status;
|
|
||||||
if (statusToDeal.media_attachments != null && statusToDeal.media_attachments.size() > 0) {
|
|
||||||
attachments.addAll(statusToDeal.media_attachments);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return attachments;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public RequestBuilder<Drawable> getPreloadRequestBuilder(@NonNull Attachment attachment) {
|
|
||||||
float focusX = 0.f;
|
|
||||||
float focusY = 0.f;
|
|
||||||
if (attachment.meta != null && attachment.meta.focus != null) {
|
|
||||||
focusX = attachment.meta.focus.x;
|
|
||||||
focusY = attachment.meta.focus.y;
|
|
||||||
}
|
|
||||||
int mediaH = 0;
|
|
||||||
int mediaW = 0;
|
|
||||||
if (attachment.meta != null && attachment.meta.small != null) {
|
|
||||||
mediaH = attachment.meta.small.height;
|
|
||||||
mediaW = attachment.meta.small.width;
|
|
||||||
}
|
|
||||||
return prepareRequestBuilder(context, attachment, mediaW, mediaH, focusX, focusY, attachment.sensitive, timelineType == Timeline.TimeLineEnum.ART).load(attachment);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a broadcast to other open fragments that content a timeline
|
* Send a broadcast to other open fragments that content a timeline
|
||||||
*
|
*
|
||||||
|
@ -2317,21 +2271,6 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
||||||
LocalBroadcastManager.getInstance(context).sendBroadcast(intentBC);
|
LocalBroadcastManager.getInstance(context).sendBroadcast(intentBC);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* private static boolean mediaObfuscated(Status status) {
|
|
||||||
//Media is not sensitive and doesn't have a spoiler text
|
|
||||||
if (!status.isMediaObfuscated) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!status.sensitive && (status.spoiler_text == null || status.spoiler_text.trim().isEmpty())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (status.isMediaObfuscated && status.spoiler_text != null && !status.spoiler_text.trim().isEmpty()) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return status.sensitive;
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
public static void applyColor(Context context, StatusViewHolder holder) {
|
public static void applyColor(Context context, StatusViewHolder holder) {
|
||||||
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
int currentNightMode = context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
|
int currentNightMode = context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
|
||||||
|
@ -2418,6 +2357,68 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public List<Attachment> getPreloadItems(int position) {
|
||||||
|
List<Attachment> attachments = new ArrayList<>();
|
||||||
|
if (position == 0 && statusList.size() > 0) {
|
||||||
|
for (Status status : statusList.subList(0, 1)) {
|
||||||
|
Status statusToDeal = status.reblog != null ? status.reblog : status;
|
||||||
|
if (statusToDeal.media_attachments != null && statusToDeal.media_attachments.size() > 0) {
|
||||||
|
attachments.addAll(statusToDeal.media_attachments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (position > 0 && position < (statusList.size() - 1)) {
|
||||||
|
for (Status status : statusList.subList(position - 1, position + 1)) {
|
||||||
|
Status statusToDeal = status.reblog != null ? status.reblog : status;
|
||||||
|
if (statusToDeal.media_attachments != null && statusToDeal.media_attachments.size() > 0) {
|
||||||
|
attachments.addAll(statusToDeal.media_attachments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (Status status : statusList.subList(position, position)) {
|
||||||
|
Status statusToDeal = status.reblog != null ? status.reblog : status;
|
||||||
|
if (statusToDeal.media_attachments != null && statusToDeal.media_attachments.size() > 0) {
|
||||||
|
attachments.addAll(statusToDeal.media_attachments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return attachments;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* private static boolean mediaObfuscated(Status status) {
|
||||||
|
//Media is not sensitive and doesn't have a spoiler text
|
||||||
|
if (!status.isMediaObfuscated) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!status.sensitive && (status.spoiler_text == null || status.spoiler_text.trim().isEmpty())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (status.isMediaObfuscated && status.spoiler_text != null && !status.spoiler_text.trim().isEmpty()) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return status.sensitive;
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public RequestBuilder<Drawable> getPreloadRequestBuilder(@NonNull Attachment attachment) {
|
||||||
|
float focusX = 0.f;
|
||||||
|
float focusY = 0.f;
|
||||||
|
if (attachment.meta != null && attachment.meta.focus != null) {
|
||||||
|
focusX = attachment.meta.focus.x;
|
||||||
|
focusY = attachment.meta.focus.y;
|
||||||
|
}
|
||||||
|
int mediaH = 0;
|
||||||
|
int mediaW = 0;
|
||||||
|
if (attachment.meta != null && attachment.meta.small != null) {
|
||||||
|
mediaH = attachment.meta.small.height;
|
||||||
|
mediaW = attachment.meta.small.width;
|
||||||
|
}
|
||||||
|
return prepareRequestBuilder(context, attachment, mediaW, mediaH, focusX, focusY, attachment.sensitive, timelineType == Timeline.TimeLineEnum.ART).load(attachment);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
|
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
|
||||||
super.onAttachedToRecyclerView(recyclerView);
|
super.onAttachedToRecyclerView(recyclerView);
|
||||||
|
@ -2615,7 +2616,7 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
||||||
holder.bindingArt.artMedia.setScaleType(ImageView.ScaleType.FIT_CENTER);
|
holder.bindingArt.artMedia.setScaleType(ImageView.ScaleType.FIT_CENTER);
|
||||||
holder.bindingArt.artMedia.setLayoutParams(lp);
|
holder.bindingArt.artMedia.setLayoutParams(lp);
|
||||||
RequestBuilder<Drawable> requestBuilder = prepareRequestBuilder(context, status.art_attachment, mediaW * ratio, mediaH * ratio, 1.0f, 1.0f, status.sensitive, true);
|
RequestBuilder<Drawable> requestBuilder = prepareRequestBuilder(context, status.art_attachment, mediaW * ratio, mediaH * ratio, 1.0f, 1.0f, status.sensitive, true);
|
||||||
requestBuilder.into(holder.bindingArt.artMedia);
|
requestBuilder.load(status.art_attachment.preview_url).into(holder.bindingArt.artMedia);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,8 @@ package app.fedilab.android.ui.fragment.timeline;
|
||||||
* see <http://www.gnu.org/licenses>. */
|
* see <http://www.gnu.org/licenses>. */
|
||||||
|
|
||||||
|
|
||||||
|
import static app.fedilab.android.helper.MastodonHelper.ACCOUNTS_PER_CALL;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
@ -63,6 +65,8 @@ public class FragmentMastodonAccount extends Fragment {
|
||||||
private FedilabProfileTLPageAdapter.follow_type followType;
|
private FedilabProfileTLPageAdapter.follow_type followType;
|
||||||
private String viewModelKey;
|
private String viewModelKey;
|
||||||
private Timeline.TimeLineEnum timelineType;
|
private Timeline.TimeLineEnum timelineType;
|
||||||
|
private String order;
|
||||||
|
private Boolean local;
|
||||||
|
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater,
|
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||||
ViewGroup container, Bundle savedInstanceState) {
|
ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
@ -72,6 +76,8 @@ public class FragmentMastodonAccount extends Fragment {
|
||||||
followType = (FedilabProfileTLPageAdapter.follow_type) getArguments().getSerializable(Helper.ARG_FOLLOW_TYPE);
|
followType = (FedilabProfileTLPageAdapter.follow_type) getArguments().getSerializable(Helper.ARG_FOLLOW_TYPE);
|
||||||
viewModelKey = getArguments().getString(Helper.ARG_VIEW_MODEL_KEY, "");
|
viewModelKey = getArguments().getString(Helper.ARG_VIEW_MODEL_KEY, "");
|
||||||
timelineType = (Timeline.TimeLineEnum) getArguments().get(Helper.ARG_TIMELINE_TYPE);
|
timelineType = (Timeline.TimeLineEnum) getArguments().get(Helper.ARG_TIMELINE_TYPE);
|
||||||
|
order = getArguments().getString(Helper.ARG_DIRECTORY_ORDER, "active");
|
||||||
|
local = getArguments().getBoolean(Helper.ARG_DIRECTORY_LOCAL, false);
|
||||||
}
|
}
|
||||||
flagLoading = false;
|
flagLoading = false;
|
||||||
binding = FragmentPaginationBinding.inflate(inflater, container, false);
|
binding = FragmentPaginationBinding.inflate(inflater, container, false);
|
||||||
|
@ -159,7 +165,15 @@ public class FragmentMastodonAccount extends Fragment {
|
||||||
.observe(getViewLifecycleOwner(), this::initializeAccountCommonView);
|
.observe(getViewLifecycleOwner(), this::initializeAccountCommonView);
|
||||||
} else {
|
} else {
|
||||||
accountsVM.getBlocks(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, String.valueOf(MastodonHelper.accountsPerCall(requireActivity())), max_id, null)
|
accountsVM.getBlocks(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, String.valueOf(MastodonHelper.accountsPerCall(requireActivity())), max_id, null)
|
||||||
|
.observe(getViewLifecycleOwner(), this::dealWithPagination);
|
||||||
|
}
|
||||||
|
} else if (timelineType == Timeline.TimeLineEnum.ACCOUNT_DIRECTORY) {
|
||||||
|
if (firstLoad) {
|
||||||
|
accountsVM.getDirectory(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, 0, ACCOUNTS_PER_CALL, order, local)
|
||||||
.observe(getViewLifecycleOwner(), this::initializeAccountCommonView);
|
.observe(getViewLifecycleOwner(), this::initializeAccountCommonView);
|
||||||
|
} else {
|
||||||
|
accountsVM.getDirectory(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, offset, ACCOUNTS_PER_CALL, order, local)
|
||||||
|
.observe(getViewLifecycleOwner(), this::dealWithPagination);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -223,8 +237,10 @@ public class FragmentMastodonAccount extends Fragment {
|
||||||
|
|
||||||
this.accounts = accounts.accounts;
|
this.accounts = accounts.accounts;
|
||||||
accountAdapter = new AccountAdapter(this.accounts, timelineType == Timeline.TimeLineEnum.MUTED_TIMELINE_HOME);
|
accountAdapter = new AccountAdapter(this.accounts, timelineType == Timeline.TimeLineEnum.MUTED_TIMELINE_HOME);
|
||||||
if (search == null) {
|
if (search == null && timelineType != Timeline.TimeLineEnum.ACCOUNT_DIRECTORY) {
|
||||||
flagLoading = accounts.pagination.max_id == null;
|
flagLoading = accounts.pagination.max_id == null;
|
||||||
|
} else if (timelineType != Timeline.TimeLineEnum.ACCOUNT_DIRECTORY) {
|
||||||
|
offset += ACCOUNTS_PER_CALL;
|
||||||
} else {
|
} else {
|
||||||
offset += MastodonHelper.SEARCH_PER_CALL;
|
offset += MastodonHelper.SEARCH_PER_CALL;
|
||||||
}
|
}
|
||||||
|
@ -288,6 +304,8 @@ public class FragmentMastodonAccount extends Fragment {
|
||||||
max_id = fetched_accounts.pagination.max_id;
|
max_id = fetched_accounts.pagination.max_id;
|
||||||
if (search != null) {
|
if (search != null) {
|
||||||
offset += MastodonHelper.SEARCH_PER_CALL;
|
offset += MastodonHelper.SEARCH_PER_CALL;
|
||||||
|
} else if (timelineType == Timeline.TimeLineEnum.ACCOUNT_DIRECTORY) {
|
||||||
|
offset += ACCOUNTS_PER_CALL;
|
||||||
}
|
}
|
||||||
accountAdapter.notifyItemRangeInserted(startId, fetched_accounts.accounts.size());
|
accountAdapter.notifyItemRangeInserted(startId, fetched_accounts.accounts.size());
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -76,6 +76,7 @@ import es.dmoral.toasty.Toasty;
|
||||||
public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.FetchMoreCallBack {
|
public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.FetchMoreCallBack {
|
||||||
|
|
||||||
|
|
||||||
|
private static final int PRELOAD_AHEAD_ITEMS = 10;
|
||||||
public UpdateCounters update;
|
public UpdateCounters update;
|
||||||
private FragmentPaginationBinding binding;
|
private FragmentPaginationBinding binding;
|
||||||
private TimelinesVM timelinesVM;
|
private TimelinesVM timelinesVM;
|
||||||
|
@ -88,8 +89,6 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
||||||
private StatusAdapter statusAdapter;
|
private StatusAdapter statusAdapter;
|
||||||
private Timeline.TimeLineEnum timelineType;
|
private Timeline.TimeLineEnum timelineType;
|
||||||
private List<Status> timelineStatuses;
|
private List<Status> timelineStatuses;
|
||||||
private static final int PRELOAD_AHEAD_ITEMS = 10;
|
|
||||||
private ViewPreloadSizeProvider<Attachment> preloadSizeProvider;
|
|
||||||
//Handle actions that can be done in other fragments
|
//Handle actions that can be done in other fragments
|
||||||
private final BroadcastReceiver receive_action = new BroadcastReceiver() {
|
private final BroadcastReceiver receive_action = new BroadcastReceiver() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -179,6 +178,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
private ViewPreloadSizeProvider<Attachment> preloadSizeProvider;
|
||||||
private boolean checkRemotely;
|
private boolean checkRemotely;
|
||||||
private String accountIDInRemoteInstance;
|
private String accountIDInRemoteInstance;
|
||||||
private boolean isViewInitialized;
|
private boolean isViewInitialized;
|
||||||
|
|
|
@ -38,6 +38,7 @@ import app.fedilab.android.client.endpoints.MastodonAccountsService;
|
||||||
import app.fedilab.android.client.entities.api.Account;
|
import app.fedilab.android.client.entities.api.Account;
|
||||||
import app.fedilab.android.client.entities.api.Accounts;
|
import app.fedilab.android.client.entities.api.Accounts;
|
||||||
import app.fedilab.android.client.entities.api.Domains;
|
import app.fedilab.android.client.entities.api.Domains;
|
||||||
|
import app.fedilab.android.client.entities.api.FamiliarFollowers;
|
||||||
import app.fedilab.android.client.entities.api.FeaturedTag;
|
import app.fedilab.android.client.entities.api.FeaturedTag;
|
||||||
import app.fedilab.android.client.entities.api.Field;
|
import app.fedilab.android.client.entities.api.Field;
|
||||||
import app.fedilab.android.client.entities.api.Filter;
|
import app.fedilab.android.client.entities.api.Filter;
|
||||||
|
@ -90,6 +91,7 @@ public class AccountsVM extends AndroidViewModel {
|
||||||
private MutableLiveData<List<IdentityProof>> identityProofListMutableLiveData;
|
private MutableLiveData<List<IdentityProof>> identityProofListMutableLiveData;
|
||||||
private MutableLiveData<RelationShip> relationShipMutableLiveData;
|
private MutableLiveData<RelationShip> relationShipMutableLiveData;
|
||||||
private MutableLiveData<List<RelationShip>> relationShipListMutableLiveData;
|
private MutableLiveData<List<RelationShip>> relationShipListMutableLiveData;
|
||||||
|
private MutableLiveData<List<FamiliarFollowers>> familiarFollowersListMutableLiveData;
|
||||||
private MutableLiveData<Filter> filterMutableLiveData;
|
private MutableLiveData<Filter> filterMutableLiveData;
|
||||||
private MutableLiveData<List<Filter>> filterListMutableLiveData;
|
private MutableLiveData<List<Filter>> filterListMutableLiveData;
|
||||||
private MutableLiveData<List<Tag>> tagListMutableLiveData;
|
private MutableLiveData<List<Tag>> tagListMutableLiveData;
|
||||||
|
@ -1025,6 +1027,38 @@ public class AccountsVM extends AndroidViewModel {
|
||||||
return relationShipListMutableLiveData;
|
return relationShipListMutableLiveData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtain a list of all accounts that follow a given account, filtered for accounts you follow.
|
||||||
|
*
|
||||||
|
* @param ids {@link List} of account IDs to check
|
||||||
|
* @return {@link LiveData} containing a {@link List} of {@link FamiliarFollowers}s to given account(s)
|
||||||
|
*/
|
||||||
|
public LiveData<List<FamiliarFollowers>> getFamiliarFollowers(@NonNull String instance, String token, @NonNull List<String> ids) {
|
||||||
|
familiarFollowersListMutableLiveData = new MutableLiveData<>();
|
||||||
|
MastodonAccountsService mastodonAccountsService = init(instance);
|
||||||
|
new Thread(() -> {
|
||||||
|
List<FamiliarFollowers> familiarFollowers = null;
|
||||||
|
Call<List<FamiliarFollowers>> familiarFollowersCall = mastodonAccountsService.getFamiliarFollowers(token, ids);
|
||||||
|
|
||||||
|
if (familiarFollowersCall != null) {
|
||||||
|
try {
|
||||||
|
Response<List<FamiliarFollowers>> familiarFollowersResponse = familiarFollowersCall.execute();
|
||||||
|
if (familiarFollowersResponse.isSuccessful()) {
|
||||||
|
familiarFollowers = familiarFollowersResponse.body();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||||
|
List<FamiliarFollowers> finalFamiliarFollowers = familiarFollowers;
|
||||||
|
Runnable myRunnable = () -> familiarFollowersListMutableLiveData.setValue(finalFamiliarFollowers);
|
||||||
|
mainHandler.post(myRunnable);
|
||||||
|
}).start();
|
||||||
|
return familiarFollowersListMutableLiveData;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Search for matching accounts by username or display name.
|
* Search for matching accounts by username or display name.
|
||||||
*
|
*
|
||||||
|
@ -1561,7 +1595,7 @@ public class AccountsVM extends AndroidViewModel {
|
||||||
* Accounts the user has had past positive interactions with, but is not yet following.
|
* Accounts the user has had past positive interactions with, but is not yet following.
|
||||||
*
|
*
|
||||||
* @param limit Maximum number of results to return. Defaults to 40.
|
* @param limit Maximum number of results to return. Defaults to 40.
|
||||||
* @return {@link LiveData} containing a {@link List} of {@link Account}s
|
* @return {@link LiveData} containing a {@link List} of {@link Suggestion}s
|
||||||
*/
|
*/
|
||||||
public LiveData<Suggestions> getSuggestions(@NonNull String instance, String token, String limit) {
|
public LiveData<Suggestions> getSuggestions(@NonNull String instance, String token, String limit) {
|
||||||
suggestionsMutableLiveData = new MutableLiveData<>();
|
suggestionsMutableLiveData = new MutableLiveData<>();
|
||||||
|
@ -1587,6 +1621,37 @@ public class AccountsVM extends AndroidViewModel {
|
||||||
return suggestionsMutableLiveData;
|
return suggestionsMutableLiveData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List accounts visible in the directory.
|
||||||
|
*
|
||||||
|
* @param limit Maximum number of results to return. Defaults to 40.
|
||||||
|
* @return {@link LiveData} containing a {@link List} of {@link Account}s
|
||||||
|
*/
|
||||||
|
public LiveData<Accounts> getDirectory(@NonNull String instance, String token, Integer offset, Integer limit, String order, Boolean local) {
|
||||||
|
accountsMutableLiveData = new MutableLiveData<>();
|
||||||
|
MastodonAccountsService mastodonAccountsService = init(instance);
|
||||||
|
new Thread(() -> {
|
||||||
|
Call<List<Account>> accountsCall = mastodonAccountsService.getDirectory(token, offset, limit, order, local);
|
||||||
|
Accounts accounts = new Accounts();
|
||||||
|
|
||||||
|
if (accountsCall != null) {
|
||||||
|
try {
|
||||||
|
Response<List<Account>> directoryResponse = accountsCall.execute();
|
||||||
|
if (directoryResponse.isSuccessful()) {
|
||||||
|
accounts.pagination = MastodonHelper.getOffSetPagination(directoryResponse.headers());
|
||||||
|
accounts.accounts = directoryResponse.body();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||||
|
Runnable myRunnable = () -> accountsMutableLiveData.setValue(accounts);
|
||||||
|
mainHandler.post(myRunnable);
|
||||||
|
}).start();
|
||||||
|
return accountsMutableLiveData;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove an account from follow suggestions.
|
* Remove an account from follow suggestions.
|
||||||
*
|
*
|
||||||
|
|
10
app/src/main/res/drawable/ic_baseline_android_24.xml
Normal file
10
app/src/main/res/drawable/ic_baseline_android_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="?attr/colorControlNormal"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M17.6,9.48l1.84,-3.18c0.16,-0.31 0.04,-0.69 -0.26,-0.85c-0.29,-0.15 -0.65,-0.06 -0.83,0.22l-1.88,3.24c-2.86,-1.21 -6.08,-1.21 -8.94,0L5.65,5.67c-0.19,-0.29 -0.58,-0.38 -0.87,-0.2C4.5,5.65 4.41,6.01 4.56,6.3L6.4,9.48C3.3,11.25 1.28,14.44 1,18h22C22.72,14.44 20.7,11.25 17.6,9.48zM7,15.25c-0.69,0 -1.25,-0.56 -1.25,-1.25c0,-0.69 0.56,-1.25 1.25,-1.25S8.25,13.31 8.25,14C8.25,14.69 7.69,15.25 7,15.25zM17,15.25c-0.69,0 -1.25,-0.56 -1.25,-1.25c0,-0.69 0.56,-1.25 1.25,-1.25s1.25,0.56 1.25,1.25C18.25,14.69 17.69,15.25 17,15.25z" />
|
||||||
|
</vector>
|
|
@ -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="M19,3h-1L18,1h-2v2L8,3L8,1L6,1v2L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM12,6c1.66,0 3,1.34 3,3s-1.34,3 -3,3 -3,-1.34 -3,-3 1.34,-3 3,-3zM18,18L6,18v-1c0,-2 4,-3.1 6,-3.1s6,1.1 6,3.1v1z" />
|
||||||
|
</vector>
|
31
app/src/main/res/layout/activity_directory.xml
Normal file
31
app/src/main/res/layout/activity_directory.xml
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?><!--
|
||||||
|
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>.
|
||||||
|
-->
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:id="@+id/drawer_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<androidx.fragment.app.FragmentContainerView
|
||||||
|
android:id="@+id/nav_host_fragment_directory"
|
||||||
|
android:name="androidx.navigation.fragment.NavHostFragment"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:defaultNavHost="true"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||||
|
</LinearLayout>
|
|
@ -341,6 +341,44 @@
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/info" />
|
app:layout_constraintTop_toBottomOf="@+id/info" />
|
||||||
|
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.LinearLayoutCompat
|
||||||
|
android:id="@+id/familiar_followers"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="6dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="10dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/fields_container"
|
||||||
|
tools:visibility="visible">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/type_of_concat"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:text="@string/also_followed_by" />
|
||||||
|
|
||||||
|
<HorizontalScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="10dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.LinearLayoutCompat
|
||||||
|
android:id="@+id/related_accounts"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal" />
|
||||||
|
</HorizontalScrollView>
|
||||||
|
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||||
|
|
||||||
<!-- End Fields container -->
|
<!-- End Fields container -->
|
||||||
<androidx.appcompat.widget.LinearLayoutCompat
|
<androidx.appcompat.widget.LinearLayoutCompat
|
||||||
android:id="@+id/warning_container"
|
android:id="@+id/warning_container"
|
||||||
|
@ -350,7 +388,7 @@
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/fields_container"
|
app:layout_constraintTop_toBottomOf="@+id/familiar_followers"
|
||||||
tools:visibility="visible">
|
tools:visibility="visible">
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
|
|
@ -119,23 +119,6 @@
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:src="@tools:sample/avatars" />
|
tools:src="@tools:sample/avatars" />
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageView
|
|
||||||
android:id="@+id/reply_icon"
|
|
||||||
android:layout_width="16dp"
|
|
||||||
android:layout_height="16dp"
|
|
||||||
android:src="@drawable/ic_baseline_reply_16"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent" />
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageView
|
|
||||||
android:id="@+id/bot_icon"
|
|
||||||
android:layout_width="16dp"
|
|
||||||
android:layout_height="16dp"
|
|
||||||
android:src="@drawable/ic_baseline_reply_16"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent" />
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
|
||||||
|
@ -147,8 +130,31 @@
|
||||||
<androidx.appcompat.widget.LinearLayoutCompat
|
<androidx.appcompat.widget.LinearLayoutCompat
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
|
android:id="@+id/reply_icon"
|
||||||
|
android:layout_width="20dp"
|
||||||
|
android:layout_height="20dp"
|
||||||
|
android:layout_marginStart="6dp"
|
||||||
|
android:src="@drawable/ic_baseline_reply_16"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
|
android:id="@+id/bot_icon"
|
||||||
|
android:layout_width="20dp"
|
||||||
|
android:layout_height="20dp"
|
||||||
|
android:layout_marginStart="6dp"
|
||||||
|
android:src="@drawable/ic_baseline_android_24"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
android:id="@+id/display_name"
|
android:id="@+id/display_name"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
|
|
|
@ -72,20 +72,19 @@
|
||||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||||
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/account_follow"
|
android:id="@+id/account_follow"
|
||||||
style="@style/Widget.AppCompat.Button.Colored"
|
style="@style/Widget.Material3.Button.Icon"
|
||||||
android:layout_width="48dp"
|
android:layout_width="48dp"
|
||||||
android:layout_height="48dp"
|
android:layout_height="48dp"
|
||||||
android:layout_margin="10dp"
|
android:layout_gravity="center_vertical"
|
||||||
|
android:layout_marginStart="6dp"
|
||||||
|
android:layout_marginTop="6dp"
|
||||||
android:contentDescription="@string/make_an_action"
|
android:contentDescription="@string/make_an_action"
|
||||||
android:scaleType="fitCenter"
|
android:scaleType="fitCenter"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
app:layout_constraintStart_toEndOf="@id/avatar_container"
|
tools:icon="@drawable/ic_baseline_person_add_24"
|
||||||
app:layout_constraintTop_toBottomOf="@id/banner_container"
|
|
||||||
tools:src="@drawable/ic_baseline_person_add_24"
|
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
|
|
|
@ -63,6 +63,11 @@
|
||||||
android:icon="@drawable/ic_baseline_account_circle_24"
|
android:icon="@drawable/ic_baseline_account_circle_24"
|
||||||
android:title="@string/Suggestions"
|
android:title="@string/Suggestions"
|
||||||
android:visible="true" />
|
android:visible="true" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/nav_directory"
|
||||||
|
android:icon="@drawable/ic_baseline_perm_contact_calendar_24"
|
||||||
|
android:title="@string/Directory"
|
||||||
|
android:visible="true" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/nav_about_instance"
|
android:id="@+id/nav_about_instance"
|
||||||
android:icon="@drawable/ic_info_outline_white_24dp"
|
android:icon="@drawable/ic_info_outline_white_24dp"
|
||||||
|
|
23
app/src/main/res/menu/menu_directory.xml
Normal file
23
app/src/main/res/menu/menu_directory.xml
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
<?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">
|
||||||
|
<group
|
||||||
|
android:id="@+id/order"
|
||||||
|
android:checkableBehavior="single">
|
||||||
|
<item
|
||||||
|
android:id="@+id/order_active"
|
||||||
|
android:checked="true"
|
||||||
|
android:title="@string/active"
|
||||||
|
app:showAsAction="never" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/order_new"
|
||||||
|
android:title="@string/_new"
|
||||||
|
app:showAsAction="never" />
|
||||||
|
</group>
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_local"
|
||||||
|
android:checkable="true"
|
||||||
|
android:title="@string/local"
|
||||||
|
app:actionViewClass="android.widget.CheckBox"
|
||||||
|
app:showAsAction="never" />
|
||||||
|
</menu>
|
|
@ -973,4 +973,7 @@
|
||||||
<string name="silenced">Ztišen(a)</string>
|
<string name="silenced">Ztišen(a)</string>
|
||||||
<string name="set_display_compact_buttons">Kompaktní tlačítka akcí</string>
|
<string name="set_display_compact_buttons">Kompaktní tlačítka akcí</string>
|
||||||
<string name="set_display_compact_buttons_description">Tlačítka v dolní části zpráv nezaberou celou šířku</string>
|
<string name="set_display_compact_buttons_description">Tlačítka v dolní části zpráv nezaberou celou šířku</string>
|
||||||
|
<string name="reply_visibility">Viditelnost odpovědí</string>
|
||||||
|
<string name="exclude_visibility">Viditelnost vyloučení</string>
|
||||||
|
<string name="also_followed_by">Sledován(a):</string>
|
||||||
</resources>
|
</resources>
|
|
@ -13,7 +13,7 @@
|
||||||
<string name="download">Baixar</string>
|
<string name="download">Baixar</string>
|
||||||
<string name="download_file">Baixar %1$s</string>
|
<string name="download_file">Baixar %1$s</string>
|
||||||
<string name="save_over">Mídia salva</string>
|
<string name="save_over">Mídia salva</string>
|
||||||
<string name="download_from" formatted="false">Arquivo: %1$s</string>
|
<string name="download_from" formatted="false">Ficheiro: %1$s</string>
|
||||||
<string name="password">Senha</string>
|
<string name="password">Senha</string>
|
||||||
<string name="email">E-mail</string>
|
<string name="email">E-mail</string>
|
||||||
<string name="accounts">Contas</string>
|
<string name="accounts">Contas</string>
|
||||||
|
@ -547,7 +547,7 @@
|
||||||
<string name="import_theme_title">Toque aqui para importar um tema exportado previamente</string>
|
<string name="import_theme_title">Toque aqui para importar um tema exportado previamente</string>
|
||||||
<string name="export_theme">Exportar o tema</string>
|
<string name="export_theme">Exportar o tema</string>
|
||||||
<string name="export_theme_title">Toque aqui para exportar o tema atual</string>
|
<string name="export_theme_title">Toque aqui para exportar o tema atual</string>
|
||||||
<string name="theme_file_error">Ocorreu um erro ao selecionar o arquivo do tema</string>
|
<string name="theme_file_error">Ocorreu um erro ao selecionar o ficheiro do tema</string>
|
||||||
<string name="user_count">Número de usuários</string>
|
<string name="user_count">Número de usuários</string>
|
||||||
<string name="status_count">Número de status</string>
|
<string name="status_count">Número de status</string>
|
||||||
<string name="instance_count">Número de instâncias</string>
|
<string name="instance_count">Número de instâncias</string>
|
||||||
|
|
|
@ -486,6 +486,7 @@
|
||||||
<string name="unresolved">Unresolved</string>
|
<string name="unresolved">Unresolved</string>
|
||||||
<string name="remote">Remote</string>
|
<string name="remote">Remote</string>
|
||||||
<string name="active">Active</string>
|
<string name="active">Active</string>
|
||||||
|
<string name="_new">New</string>
|
||||||
<string name="pending">Pending</string>
|
<string name="pending">Pending</string>
|
||||||
<string name="disabled">Disabled</string>
|
<string name="disabled">Disabled</string>
|
||||||
<string name="suspended">Suspended</string>
|
<string name="suspended">Suspended</string>
|
||||||
|
@ -2205,4 +2206,6 @@
|
||||||
<string name="set_pixelfed_presentation">Pixelfed presentation for media</string>
|
<string name="set_pixelfed_presentation">Pixelfed presentation for media</string>
|
||||||
<string name="set_display_compact_buttons">Compact action buttons</string>
|
<string name="set_display_compact_buttons">Compact action buttons</string>
|
||||||
<string name="set_display_compact_buttons_description">Buttons at the bottom of messages will not take the whole width</string>
|
<string name="set_display_compact_buttons_description">Buttons at the bottom of messages will not take the whole width</string>
|
||||||
|
<string name="also_followed_by">Followed by:</string>
|
||||||
|
<string name="Directory">Directory</string>
|
||||||
</resources>
|
</resources>
|
12
src/fdroid/fastlane/metadata/android/en/changelogs/464.txt
Normal file
12
src/fdroid/fastlane/metadata/android/en/changelogs/464.txt
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
Added:
|
||||||
|
- Display familiar followers on profiles
|
||||||
|
- Display and filter Instance directory
|
||||||
|
|
||||||
|
Changed:
|
||||||
|
- Bot and reply icon indicators more visible
|
||||||
|
- Single media are hidden with link previews
|
||||||
|
|
||||||
|
Fixed:
|
||||||
|
- Fix a crash with Art timelines
|
||||||
|
- Friendica: media cannot be downloaded/shared
|
||||||
|
- Fix a crash with pinned timelines
|
Loading…
Reference in a new issue