3 Commits

Author SHA1 Message Date
Balazs Toldi
b9e61518a4 Improve video preview loading time
This commit improves the loading time of posts with videos on the feed.

Closes #242
2024-07-18 21:32:53 +02:00
SomeTr
706f51c4f5 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1316 of 1316 strings)

Translation: Eternity/App
Translate-URL: https://translate.codeberg.org/projects/infinity-for-lemmy/app/uk/
2024-07-18 12:07:58 +00:00
Bazsalanszky
30a2c1a9b9 Instance Blocking
This commit adds the ability to block/unblock instances from the instance view page.

Closes #231
2024-07-18 14:07:34 +02:00
14 changed files with 177 additions and 69 deletions

View File

@@ -9,7 +9,7 @@ import eu.toldi.infinityforlemmy.apis.RedgifsAPI;
import eu.toldi.infinityforlemmy.post.enrich.CompositePostEnricher;
import eu.toldi.infinityforlemmy.post.enrich.PostEnricher;
import eu.toldi.infinityforlemmy.post.enrich.RedGifsPostEnricher;
import eu.toldi.infinityforlemmy.post.enrich.VideoPostEnricher;
@Module
abstract class PostEnricherModule {
@@ -20,12 +20,6 @@ abstract class PostEnricherModule {
return new RedGifsPostEnricher(redgifsAPI);
}
@Provides
@IntoSet
static PostEnricher provideVideoPostEnricher() {
return new VideoPostEnricher();
}
@Provides
static PostEnricher providePostEnricher(Set<PostEnricher> postEnrichers) {
return new CompositePostEnricher(postEnrichers);

View File

@@ -42,6 +42,7 @@ import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.shape.CornerFamily;
import com.google.android.material.shape.MaterialShapeDrawable;
import com.google.android.material.shape.ShapeAppearanceModel;
import com.google.android.material.snackbar.Snackbar;
import com.google.android.material.tabs.TabLayout;
import java.lang.reflect.Field;
@@ -321,6 +322,12 @@ public abstract class BaseActivity extends AppCompatActivity implements CustomFo
applyAppBarLayoutAndCollapsingToolbarLayoutAndToolbarTheme(appBarLayout, collapsingToolbarLayout, toolbar, true);
}
protected Snackbar applySnackBarTheme(Snackbar snackbar) {
snackbar.setBackgroundTint(customThemeWrapper.getBackgroundColor());
snackbar.setTextColor(customThemeWrapper.getSecondaryTextColor());
return snackbar;
}
protected void applyAppBarLayoutAndCollapsingToolbarLayoutAndToolbarTheme(AppBarLayout appBarLayout, @Nullable CollapsingToolbarLayout collapsingToolbarLayout, Toolbar toolbar, boolean setToolbarBackgroundColor) {
appBarLayout.setBackgroundColor(customThemeWrapper.getColorPrimary());
if (collapsingToolbarLayout != null) {

View File

@@ -8,10 +8,12 @@ import android.net.Uri;
import android.os.Bundle;
import android.text.Spanned;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -23,6 +25,7 @@ import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.card.MaterialCardView;
import com.google.android.material.snackbar.Snackbar;
import java.util.List;
@@ -32,12 +35,17 @@ import javax.inject.Named;
import eu.toldi.infinityforlemmy.Infinity;
import eu.toldi.infinityforlemmy.R;
import eu.toldi.infinityforlemmy.RetrofitHolder;
import eu.toldi.infinityforlemmy.account.FetchBlockedThings;
import eu.toldi.infinityforlemmy.adapters.AdminRecyclerViewAdapter;
import eu.toldi.infinityforlemmy.blockedcommunity.BlockedCommunityData;
import eu.toldi.infinityforlemmy.blockedinstances.BlockedInstanceData;
import eu.toldi.infinityforlemmy.blockeduser.BlockedUserData;
import eu.toldi.infinityforlemmy.customtheme.CustomThemeWrapper;
import eu.toldi.infinityforlemmy.customviews.LinearLayoutManagerBugFixed;
import eu.toldi.infinityforlemmy.customviews.slidr.Slidr;
import eu.toldi.infinityforlemmy.databinding.ActivityInstanceInfoBinding;
import eu.toldi.infinityforlemmy.markdown.MarkdownUtils;
import eu.toldi.infinityforlemmy.site.BlockInstance;
import eu.toldi.infinityforlemmy.site.FetchSiteInfo;
import eu.toldi.infinityforlemmy.site.SiteInfo;
import eu.toldi.infinityforlemmy.site.SiteStatistics;
@@ -54,7 +62,8 @@ import retrofit2.Retrofit;
public class InstanceInfoActivity extends BaseActivity {
public static final String INSTANCE_INFO_DOMAIN = "instance_info_domain";
public static final String EXTRA_INSTANCE_DOMAIN = "instance_info_domain";
public static final String EXTRA_INSTANCE_ID = "instance_info_id";
@Inject
@Named("default")
@@ -67,6 +76,10 @@ public class InstanceInfoActivity extends BaseActivity {
@Named("no_oauth")
RetrofitHolder mRetorifitHolder;
@Inject
@Named("current_account")
SharedPreferences mCurrentAccountSharedPreferences;
ActivityInstanceInfoBinding mInstanceInfoActivityViewBinding;
private CoordinatorLayout coordinatorLayout;
private Toolbar toolbar;
@@ -98,6 +111,9 @@ public class InstanceInfoActivity extends BaseActivity {
private AdminRecyclerViewAdapter mAdminAdapter;
private Retrofit mRetrofit;
private String mInstanceDomain;
private int mInstanceId;
private String mAccessToken;
private String mAccountName;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
@@ -111,9 +127,11 @@ public class InstanceInfoActivity extends BaseActivity {
setSupportActionBar(mInstanceInfoActivityViewBinding.toolbarInstanceInfoActivity);
if (savedInstanceState != null) {
mInstanceDomain = savedInstanceState.getString(INSTANCE_INFO_DOMAIN);
mInstanceDomain = savedInstanceState.getString(EXTRA_INSTANCE_DOMAIN);
mInstanceId = savedInstanceState.getInt(EXTRA_INSTANCE_ID);
} else {
mInstanceDomain = getIntent().getStringExtra(INSTANCE_INFO_DOMAIN);
mInstanceDomain = getIntent().getStringExtra(EXTRA_INSTANCE_DOMAIN);
mInstanceId = getIntent().getIntExtra(EXTRA_INSTANCE_ID, -1);
}
if (mInstanceDomain == null) {
@@ -124,6 +142,8 @@ public class InstanceInfoActivity extends BaseActivity {
mRetrofit = mRetorifitHolder.getRetrofit();
mRetorifitHolder.setBaseURL(originalBaseUrl);
}
mAccessToken = mCurrentAccountSharedPreferences.getString(SharedPreferencesUtils.ACCESS_TOKEN, null);
mAccountName = mCurrentAccountSharedPreferences.getString(SharedPreferencesUtils.ACCOUNT_NAME, null);
setUpBindings();
applyCustomTheme();
@@ -276,24 +296,89 @@ public class InstanceInfoActivity extends BaseActivity {
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
finish();
return true;
}
return false;
}
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(INSTANCE_INFO_DOMAIN, mInstanceDomain);
outState.putString(EXTRA_INSTANCE_DOMAIN, mInstanceDomain);
outState.putInt(EXTRA_INSTANCE_ID, mInstanceId);
}
@Override
protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
mInstanceDomain = savedInstanceState.getString(INSTANCE_INFO_DOMAIN);
mInstanceDomain = savedInstanceState.getString(EXTRA_INSTANCE_DOMAIN);
mInstanceId = savedInstanceState.getInt(EXTRA_INSTANCE_ID);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.instance_info_activity, menu);
if (mInstanceDomain == null || mInstanceDomain.equalsIgnoreCase(mRetorifitHolder.getBaseURL().split("/")[2])) {
menu.findItem(R.id.action_block_instance_info).setVisible(false);
menu.findItem(R.id.action_unblock_instance_info).setVisible(false);
return true;
}
if (mAccessToken == null) {
menu.findItem(R.id.action_block_instance_info).setVisible(false);
menu.findItem(R.id.action_unblock_instance_info).setVisible(false);
return true;
}
FetchBlockedThings.fetchBlockedThings(mRetorifitHolder.getRetrofit(), mAccessToken, mAccountName, new FetchBlockedThings.FetchBlockedThingsListener() {
@Override
public void onFetchBlockedThingsSuccess(List<BlockedUserData> blockedUsers, List<BlockedCommunityData> blockedCommunities, List<BlockedInstanceData> blockedInstances) {
for (BlockedInstanceData blockedInstanceData : blockedInstances) {
if (blockedInstanceData.getDomain().equals(mInstanceDomain)) {
menu.findItem(R.id.action_block_instance_info).setVisible(false);
menu.findItem(R.id.action_unblock_instance_info).setVisible(true);
return;
}
}
menu.findItem(R.id.action_block_instance_info).setVisible(true);
menu.findItem(R.id.action_unblock_instance_info).setVisible(false);
}
@Override
public void onFetchBlockedThingsFailure() {
}
});
return true;
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
if (item.getItemId() == android.R.id.home) {
finish();
return true;
} else if (item.getItemId() == R.id.action_block_instance_info) {
blockInstance(true);
return true;
} else if (item.getItemId() == R.id.action_unblock_instance_info) {
blockInstance(false);
return true;
}
return super.onOptionsItemSelected(item);
}
public void blockInstance(boolean blocked) {
BlockInstance.INSTANCE.blockInstance(mRetorifitHolder.getRetrofit(), mInstanceId, blocked, new BlockInstance.BlockInstanceResponse() {
@Override
public void onResponse() {
if (blocked) {
applySnackBarTheme(Snackbar.make(coordinatorLayout, R.string.block_instance_success, Snackbar.LENGTH_SHORT)).show();
} else {
applySnackBarTheme(Snackbar.make(coordinatorLayout, R.string.unblock_instance_success, Snackbar.LENGTH_SHORT)).show();
}
InstanceInfoActivity.this.invalidateOptionsMenu();
}
@Override
public void onFailure() {
Toast.makeText(InstanceInfoActivity.this, R.string.block_instance_failed, Toast.LENGTH_SHORT).show();
}
});
}
}

View File

@@ -1324,7 +1324,8 @@ public class ViewSubredditDetailActivity extends BaseActivity implements SortTyp
} else if (itemId == R.id.action_view_instance_view_subreddit_detail_activity) {
String instance = communityData.getActorId().split("/")[2];
Intent intent = new Intent(this,InstanceInfoActivity.class);
intent.putExtra(InstanceInfoActivity.INSTANCE_INFO_DOMAIN, instance);
intent.putExtra(InstanceInfoActivity.EXTRA_INSTANCE_DOMAIN, instance);
intent.putExtra(InstanceInfoActivity.EXTRA_INSTANCE_ID, communityData.getInstanceId());
startActivity(intent);
}
return false;

View File

@@ -1326,7 +1326,8 @@ public class ViewUserDetailActivity extends BaseActivity implements SortTypeSele
} else if (itemId == R.id.action_view_instance_view_user_detail_activity) {
String instance = mUserData.getActorId().split("/")[2];
Intent intent = new Intent(this, InstanceInfoActivity.class);
intent.putExtra(InstanceInfoActivity.INSTANCE_INFO_DOMAIN, instance);
intent.putExtra(InstanceInfoActivity.EXTRA_INSTANCE_DOMAIN, instance);
intent.putExtra(InstanceInfoActivity.EXTRA_INSTANCE_ID, mUserData.getInstanceId());
startActivity(intent);
}
return false;

View File

@@ -1,6 +1,5 @@
package eu.toldi.infinityforlemmy.adapters;
import android.app.Activity;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
@@ -153,7 +152,8 @@ public class BlockedInstancesRecyclerViewAdapter extends RecyclerView.Adapter<Re
if (mBlockedInstanceData != null) {
BlockedInstanceData instanceData = mBlockedInstanceData.get(viewHolder.getBindingAdapterPosition());
Intent intent = new Intent(mActivity, InstanceInfoActivity.class);
intent.putExtra(InstanceInfoActivity.INSTANCE_INFO_DOMAIN, instanceData.getDomain());
intent.putExtra(InstanceInfoActivity.EXTRA_INSTANCE_DOMAIN, instanceData.getDomain());
intent.putExtra(InstanceInfoActivity.EXTRA_INSTANCE_ID, instanceData.getId());
mActivity.startActivity(intent);
}
});

View File

@@ -7,6 +7,7 @@ import com.google.common.util.concurrent.ListenableFuture;
import eu.toldi.infinityforlemmy.dto.AccountLoginDTO;
import eu.toldi.infinityforlemmy.dto.AuthDTO;
import eu.toldi.infinityforlemmy.dto.BlockCommunityDTO;
import eu.toldi.infinityforlemmy.dto.BlockInstanceDTO;
import eu.toldi.infinityforlemmy.dto.CommentDTO;
import eu.toldi.infinityforlemmy.dto.CommentVoteDTO;
import eu.toldi.infinityforlemmy.dto.DeleteCommentDTO;
@@ -275,6 +276,11 @@ public interface LemmyAPI {
@Query("auth") String auth
);
@POST("/api/v3/site/block")
Call<String> blockInstance(
@Body BlockInstanceDTO params
);
@GET("api/v3/private_message/list")
Call<String> privateMessagesList(
@Query("page") Integer page,

View File

@@ -249,7 +249,8 @@ public class ParsePost {
post = new Post(id, communityInfo, author, postTimeMillis, title, permalink, downvotes, upvotes, postType, voteType,
nComments, upvoteRatio, nsfw, locked, saved, deleted, distinguished, suggestedSort);
Post.Preview preview = new Post.Preview(url, 0, 0, "", "");
post.setPreviews(new ArrayList<>(List.of(preview)));
post.setVideoUrl(url);
post.setVideoDownloadUrl(url);
} else if (!url.equals("")) {

View File

@@ -0,0 +1,4 @@
package eu.toldi.infinityforlemmy.dto
data class BlockInstanceDTO(val instance_id: Int, val block: Boolean) {
}

View File

@@ -1,43 +0,0 @@
package eu.toldi.infinityforlemmy.post.enrich
import android.media.MediaMetadataRetriever
import android.util.Log
import eu.toldi.infinityforlemmy.post.Post
class VideoPostEnricher : PostEnricher {
override fun enrich(posts: Collection<Post>) {
for (post in posts) {
if (post.postType != Post.VIDEO_TYPE || post.previews.isNotEmpty()) {
continue
}
val url = post.videoUrl ?: continue
if (!url.endsWith(".mp4", ignoreCase = true) &&
!url.endsWith(".webm", ignoreCase = true)
) {
continue
}
val retriever = try {
MediaMetadataRetriever().apply {
setDataSource(url);
}
} catch (e: Exception) {
Log.w(TAG, "Error retrieving metadata", e)
continue
}
val width = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH)
?.toIntOrNull() ?: -1
val height = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT)
?.toIntOrNull() ?: -1
// Glide can extract thumbnails from video URLs (it uses MediaMetadataRetriever too)
post.previews = ArrayList(listOf(Post.Preview(url, width, height, null, null)))
}
}
companion object {
private const val TAG = "VideoPostEnricher"
}
}

View File

@@ -0,0 +1,31 @@
package eu.toldi.infinityforlemmy.site
import eu.toldi.infinityforlemmy.apis.LemmyAPI
import eu.toldi.infinityforlemmy.dto.BlockInstanceDTO
import retrofit2.Retrofit
object BlockInstance {
fun blockInstance(retrofit: Retrofit, instance_id: Int, block: Boolean, blockInstanceResponse: BlockInstanceResponse) {
val LemmyAPI = retrofit.create(LemmyAPI::class.java)
val response = LemmyAPI.blockInstance(BlockInstanceDTO(instance_id,block)).enqueue(
object : retrofit2.Callback<String> {
override fun onResponse(call: retrofit2.Call<String>, response: retrofit2.Response<String>) {
if (response.isSuccessful) {
blockInstanceResponse.onResponse()
} else {
blockInstanceResponse.onFailure()
}
}
override fun onFailure(call: retrofit2.Call<String>, t: Throwable) {
blockInstanceResponse.onFailure()
}
}
)
}
interface BlockInstanceResponse {
fun onResponse()
fun onFailure()
}
}

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/action_block_instance_info"
android:orderInCategory="7"
android:title="@string/action_block_instance" />
<item
android:id="@+id/action_unblock_instance_info"
android:visible="false"
android:orderInCategory="7"
android:title="@string/action_unblock_instance"
/>
</menu>

View File

@@ -1350,4 +1350,5 @@
<string name="sort_scaled">Масштабоване</string>
<string name="instances">Сервери</string>
<string name="no_blocked_instances">Немає заблокованих серверів</string>
<string name="view_instance">Перегляд сервера</string>
</resources>

View File

@@ -1326,4 +1326,9 @@
<string name="instances">Instances</string>
<string name="no_blocked_instances">No blocked instances</string>
<string name="view_instance">View Instance</string>
<string name="action_block_instance">Block Instance</string>
<string name="action_unblock_instance">Unblock Instance</string>
<string name="block_instance_failed">Failed to block instance</string>
<string name="block_instance_success">Instance Blocked!</string>
<string name="unblock_instance_success">Instance unblocked!</string>
</resources>