Merge branch 'develop' into improve_admin

This commit is contained in:
Thomas 2022-11-11 16:36:11 +01:00
commit 3dd06cb1a1
13 changed files with 177 additions and 160 deletions

View file

@ -13,8 +13,8 @@ android {
defaultConfig {
minSdk 21
targetSdk 31
versionCode 423
versionName "3.6.2"
versionCode 424
versionName "3.6.3"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
flavorDimensions "default"
@ -95,6 +95,8 @@ dependencies {
implementation "com.github.bumptech.glide:glide:4.12.0"
implementation "com.github.bumptech.glide:okhttp3-integration:4.12.0"
implementation "org.jsoup:jsoup:1.15.1"
implementation 'com.github.mergehez:ArgPlayer:v3.1'
implementation project(path: ':mytransl')
implementation project(path: ':ratethisapp')

View file

@ -1,4 +1,9 @@
[
{
"version": "3.6.3",
"code": "424",
"note": "Fixed:\n- Issue with messages/notifications not correctly displayed\n- Friendica: issues with mentions and tags (open browser)\n- Improve sharing behaviour\n"
},
{
"version": "3.6.2",
"code": "423",

View file

@ -79,6 +79,10 @@ import com.google.android.material.snackbar.Snackbar;
import com.google.android.material.tabs.TabLayout;
import com.jaredrummler.cyanea.Cyanea;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
@ -852,111 +856,114 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
boolean fetchSharedMedia = sharedpreferences.getBoolean(getString(R.string.SET_RETRIEVE_METADATA_IF_URL_FROM_EXTERAL), true);
boolean fetchShareContent = sharedpreferences.getBoolean(getString(R.string.SET_SHARE_DETAILS), true);
if (url[0] != null && count == 1 && (fetchShareContent || fetchSharedMedia)) {
if (!url[0].trim().equalsIgnoreCase(sharedText.trim())) {
Bundle b = new Bundle();
b.putString(Helper.ARG_SHARE_TITLE, sharedSubject);
b.putString(Helper.ARG_SHARE_DESCRIPTION, sharedText);
CrossActionHelper.doCrossShare(BaseMainActivity.this, b);
} else {
new Thread(() -> {
if (url[0].startsWith("www."))
url[0] = "http://" + url[0];
Matcher matcherPattern = Patterns.WEB_URL.matcher(url[0]);
String potentialUrl = null;
while (matcherPattern.find()) {
int matchStart = matcherPattern.start(1);
int matchEnd = matcherPattern.end();
if (matchStart < matchEnd && url[0].length() >= matchEnd)
potentialUrl = url[0].substring(matchStart, matchEnd);
}
// If we actually have a URL then make use of it.
if (potentialUrl != null && potentialUrl.length() > 0) {
Pattern titlePattern = Pattern.compile("<meta [^>]*property=[\"']og:title[\"'] [^>]*content=[\"']([^'^\"]+?)[\"'][^>]*>");
Pattern descriptionPattern = Pattern.compile("<meta [^>]*property=[\"']og:description[\"'] [^>]*content=[\"']([^'^\"]+?)[\"'][^>]*>");
Pattern imagePattern = Pattern.compile("<meta [^>]*property=[\"']og:image[\"'] [^>]*content=[\"']([^'^\"]+?)[\"'][^>]*>");
String originalUrl = url[0];
new Thread(() -> {
if (!url[0].matches("^https?://.*")) url[0] = "http://" + url[0];
Matcher matcherPattern = Patterns.WEB_URL.matcher(url[0]);
String potentialUrl = null;
while (matcherPattern.find()) {
int matchStart = matcherPattern.start(1);
int matchEnd = matcherPattern.end();
if (matchStart < matchEnd && url[0].length() >= matchEnd)
potentialUrl = url[0].substring(matchStart, matchEnd);
}
// If we actually have a URL then make use of it.
if (potentialUrl != null && potentialUrl.length() > 0) {
try {
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.proxy(Helper.getProxy(getApplication().getApplicationContext()))
.readTimeout(10, TimeUnit.SECONDS).build();
Request request = new Request.Builder()
.url(potentialUrl)
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(@NonNull Call call, @NonNull IOException e) {
e.printStackTrace();
try {
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.proxy(Helper.getProxy(getApplication().getApplicationContext()))
.readTimeout(10, TimeUnit.SECONDS).build();
Request request = new Request.Builder()
.url(potentialUrl)
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(@NonNull Call call, @NonNull IOException e) {
e.printStackTrace();
runOnUiThread(() -> Toasty.warning(BaseMainActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show());
}
@Override
public void onResponse(@NonNull Call call, @NonNull final Response response) {
if (response.isSuccessful()) {
try {
String data = response.body().string();
Document html = Jsoup.parse(data);
Element titleEl = html.selectFirst("meta[property='og:title']");
Element descriptionEl = html.selectFirst("meta[property='og:description']");
Element imageUrlEl = html.selectFirst("meta[property='og:image']");
String title = "";
String description = "";
if(titleEl != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
title = Html.fromHtml(titleEl.attr("content"), Html.FROM_HTML_MODE_LEGACY).toString();
} else {
title = Html.fromHtml(titleEl.attr("content")).toString();
}
}
if(descriptionEl != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
description = Html.fromHtml(descriptionEl.attr("content"), Html.FROM_HTML_MODE_LEGACY).toString();
} else {
description = Html.fromHtml(descriptionEl.attr("content")).toString();
}
}
String imageUrl = "";
if(imageUrlEl != null) {
imageUrl = imageUrlEl.attr("content");
}
StringBuilder titleBuilder = new StringBuilder();
if(!originalUrl.trim().equalsIgnoreCase(sharedText.trim())) {
// If the shared text is not just the URL, add it to the top
String toAppend = sharedText.replaceAll("\\s*" + Pattern.quote(originalUrl) + "\\s*", "");
titleBuilder.append(toAppend);
}
if (title.length() > 0) {
// OG title fetched from source
if(titleBuilder.length() > 0) titleBuilder.append("\n\n");
titleBuilder.append(title);
}
String finalImage = imageUrl;
String finalTitle = titleBuilder.toString();
String finalDescription = description;
runOnUiThread(() -> {
Bundle b = new Bundle();
b.putString(Helper.ARG_SHARE_URL, url[0]);
b.putString(Helper.ARG_SHARE_URL_MEDIA, finalImage);
b.putString(Helper.ARG_SHARE_TITLE, finalTitle);
b.putString(Helper.ARG_SHARE_DESCRIPTION, finalDescription);
b.putString(Helper.ARG_SHARE_SUBJECT, sharedSubject);
b.putString(Helper.ARG_SHARE_CONTENT, sharedText);
CrossActionHelper.doCrossShare(BaseMainActivity.this, b);
});
} catch (Exception e) {
e.printStackTrace();
}
} else {
runOnUiThread(() -> Toasty.warning(BaseMainActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show());
}
@Override
public void onResponse(@NonNull Call call, @NonNull final Response response) {
if (response.isSuccessful()) {
try {
String data = response.body().string();
Matcher matcherTitle;
matcherTitle = titlePattern.matcher(data);
Matcher matcherDescription = descriptionPattern.matcher(data);
Matcher matcherImage = imagePattern.matcher(data);
String titleEncoded = null;
String descriptionEncoded = null;
if (fetchShareContent) {
while (matcherTitle.find())
titleEncoded = matcherTitle.group(1);
while (matcherDescription.find())
descriptionEncoded = matcherDescription.group(1);
}
String image = null;
if (fetchSharedMedia) {
while (matcherImage.find())
image = matcherImage.group(1);
}
String title = null;
String description = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
if (titleEncoded != null)
title = Html.fromHtml(titleEncoded, Html.FROM_HTML_MODE_LEGACY).toString();
if (descriptionEncoded != null)
description = Html.fromHtml(descriptionEncoded, Html.FROM_HTML_MODE_LEGACY).toString();
} else {
if (titleEncoded != null)
title = Html.fromHtml(titleEncoded).toString();
if (descriptionEncoded != null)
description = Html.fromHtml(descriptionEncoded).toString();
}
String finalImage = image;
String finalTitle = title;
String finalDescription = description;
runOnUiThread(() -> {
Bundle b = new Bundle();
b.putString(Helper.ARG_SHARE_URL, url[0]);
b.putString(Helper.ARG_SHARE_URL_MEDIA, finalImage);
b.putString(Helper.ARG_SHARE_TITLE, finalTitle);
b.putString(Helper.ARG_SHARE_DESCRIPTION, finalDescription);
b.putString(Helper.ARG_SHARE_SUBJECT, sharedSubject);
b.putString(Helper.ARG_SHARE_CONTENT, sharedText);
CrossActionHelper.doCrossShare(BaseMainActivity.this, b);
});
} catch (Exception e) {
e.printStackTrace();
}
} else {
runOnUiThread(() -> Toasty.warning(BaseMainActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show());
}
}
});
} catch (IndexOutOfBoundsException e) {
Toasty.warning(BaseMainActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show();
}
}
});
} catch (IndexOutOfBoundsException e) {
Toasty.warning(BaseMainActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show();
}
}).start();
}
}
}).start();
} else {
Bundle b = new Bundle();
b.putString(Helper.ARG_SHARE_TITLE, sharedSubject);

View file

@ -15,34 +15,21 @@ package app.fedilab.android.activities;
* see <http://www.gnu.org/licenses>. */
import static androidx.core.text.HtmlCompat.FROM_HTML_MODE_LEGACY;
import android.os.Build;
import android.os.Bundle;
import android.text.Html;
import android.text.SpannableString;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import java.util.ArrayList;
import java.util.List;
import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R;
import app.fedilab.android.client.entities.api.Account;
import app.fedilab.android.databinding.ActivityInstanceProfileBinding;
import app.fedilab.android.helper.Helper;
import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.ui.drawer.AccountAdapter;
import app.fedilab.android.viewmodel.mastodon.NodeInfoVM;
import app.fedilab.android.viewmodel.mastodon.SearchVM;
import es.dmoral.toasty.Toasty;
public class InstanceProfileActivity extends BaseActivity {
@ -75,40 +62,13 @@ public class InstanceProfileActivity extends BaseActivity {
finish();
return;
}
binding.name.setText(nodeInfo.metadata != null ? nodeInfo.metadata.nodeName : instance);
binding.name.setText(instance);
SpannableString descriptionSpan;
if (nodeInfo.metadata != null && nodeInfo.metadata.nodeDescription != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
descriptionSpan = new SpannableString(Html.fromHtml(nodeInfo.metadata.nodeDescription, FROM_HTML_MODE_LEGACY));
else
descriptionSpan = new SpannableString(Html.fromHtml(nodeInfo.metadata.nodeDescription));
binding.description.setText(descriptionSpan, TextView.BufferType.SPANNABLE);
}
binding.userCount.setText(Helper.withSuffix((nodeInfo.usage.users.total)));
binding.statusCount.setText(Helper.withSuffix(((nodeInfo.usage.localPosts))));
String softwareStr = nodeInfo.software.name + " - ";
binding.software.setText(softwareStr);
binding.version.setText(nodeInfo.software.version);
if (nodeInfo.metadata != null && nodeInfo.metadata.staffAccounts != null && nodeInfo.metadata.staffAccounts.size() > 0) {
SearchVM searchVM = new ViewModelProvider(InstanceProfileActivity.this).get(SearchVM.class);
List<Account> accounts = new ArrayList<>();
for (String accountURL : nodeInfo.metadata.staffAccounts) {
searchVM.search(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, accountURL, null, "accounts", false, true, false, 0, null, null, 1)
.observe(InstanceProfileActivity.this, results -> {
if (results.accounts != null && results.accounts.size() > 0) {
accounts.add(results.accounts.get(0));
}
if (accounts.size() == nodeInfo.metadata.staffAccounts.size()) {
AccountAdapter accountsListAdapter = new AccountAdapter(accounts);
binding.lvAccounts.setAdapter(accountsListAdapter);
final LinearLayoutManager mLayoutManager;
mLayoutManager = new LinearLayoutManager(InstanceProfileActivity.this);
binding.lvAccounts.setLayoutManager(mLayoutManager);
}
});
}
}
binding.instanceContainer.setVisibility(View.VISIBLE);
binding.loader.setVisibility(View.GONE);
});

View file

@ -27,4 +27,16 @@ public class Tag implements Serializable {
public String url;
@SerializedName("history")
public List<History> history;
public int getWeight() {
int weight = 0;
for (History h : history) {
try {
weight += Integer.parseInt(h.accounts);
} catch (Exception ignored) {
}
}
return weight;
}
}

View file

@ -37,8 +37,6 @@ public class WellKnownNodeinfo {
public Software software;
@SerializedName("usage")
public Usage usage;
@SerializedName("metadata")
public Metadata metadata;
@SerializedName("openRegistrations")
public boolean openRegistrations;

View file

@ -23,7 +23,7 @@ import retrofit2.http.Query;
public interface InstancesSocialService {
@GET("instances/search?name=true")
@GET("instances/search?name=true&count=50")
Call<InstanceSocial> getInstances(@Header("Authorization") String token, @Query("q") String search);
}

View file

@ -238,21 +238,23 @@ public class FragmentLoginMain extends Fragment {
}
private void retrievesClientId(String instance) {
String oldInstance = instance;
if (!instance.startsWith("http://") && !instance.startsWith("https://")) {
instance = "https://" + instance;
}
String host = instance;
String host;
try {
URL url = new URL(instance);
host = url.getHost();
} catch (MalformedURLException e) {
host = oldInstance;
e.printStackTrace();
}
try {
currentInstanceLogin = URLEncoder.encode(host, "utf-8");
} catch (UnsupportedEncodingException e) {
Toasty.error(requireActivity(), getString(R.string.client_error), Toast.LENGTH_LONG).show();
currentInstanceLogin = host;
}
String scopes = ((LoginActivity) requireActivity()).requestedAdmin() ? Helper.OAUTH_SCOPES_ADMIN : Helper.OAUTH_SCOPES;
AppsVM appsVM = new ViewModelProvider(requireActivity()).get(AppsVM.class);

View file

@ -26,6 +26,8 @@ import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import app.fedilab.android.BaseMainActivity;
@ -89,9 +91,7 @@ public class FragmentMastodonTag extends Fragment {
} else if (timelineType == Timeline.TimeLineEnum.TREND_TAG) {
TimelinesVM timelinesVM = new ViewModelProvider(FragmentMastodonTag.this).get(TimelinesVM.class);
timelinesVM.getTagsTrends(BaseMainActivity.currentToken, BaseMainActivity.currentInstance)
.observe(getViewLifecycleOwner(), tags -> {
initializeTagCommonView(tags);
});
.observe(getViewLifecycleOwner(), this::initializeTagCommonView);
}
}
@ -120,10 +120,23 @@ public class FragmentMastodonTag extends Fragment {
binding.noAction.setVisibility(View.VISIBLE);
binding.noActionText.setText(R.string.no_tags);
return;
} else {
binding.recyclerView.setVisibility(View.VISIBLE);
binding.noAction.setVisibility(View.GONE);
}
Collections.sort(tags, (obj1, obj2) -> Integer.compare(obj2.getWeight(), obj1.getWeight()));
boolean isInCollection = false;
for (Tag tag : tags) {
if (tag.name.compareToIgnoreCase(search) == 0) {
isInCollection = true;
break;
}
}
if (!isInCollection) {
Tag tag = new Tag();
tag.name = search;
tag.history = new ArrayList<>();
tags.add(0, tag);
}
binding.recyclerView.setVisibility(View.VISIBLE);
binding.noAction.setVisibility(View.GONE);
tagAdapter = new TagAdapter(tags);
LinearLayoutManager mLayoutManager = new LinearLayoutManager(requireActivity());
binding.recyclerView.setLayoutManager(mLayoutManager);

View file

@ -22,6 +22,7 @@ import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.MutableLiveData;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
import app.fedilab.android.client.entities.app.InstanceSocial;
@ -73,7 +74,17 @@ public class InstanceSocialVM extends AndroidViewModel {
Response<InstanceSocial> response = instanceSocialCall.execute();
if (response.isSuccessful() && response.body() != null) {
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> instanceSocialMutableLiveData.setValue(response.body());
InstanceSocial instanceSocial = response.body();
InstanceSocial filtered = new InstanceSocial();
filtered.instances = new ArrayList<>();
if (instanceSocial != null && instanceSocial.instances != null) {
for (InstanceSocial.Instance instance : instanceSocial.instances) {
if (instance.up) {
filtered.instances.add(instance);
}
}
}
Runnable myRunnable = () -> instanceSocialMutableLiveData.setValue(filtered);
mainHandler.post(myRunnable);
}
} catch (Exception e) {

View file

@ -1,16 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/admin_account_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:layout_marginTop="5dp"
android:layout_marginEnd="6dp"
android:padding="6dp">
android:layout_marginHorizontal="6dp"
android:layout_marginTop="6dp"
android:backgroundTint="@color/cyanea_primary_dark_reference"
app:cardElevation="0dp">
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="6dp"
android:orientation="vertical">
<androidx.appcompat.widget.LinearLayoutCompat

View file

@ -2,7 +2,7 @@
<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:padding="@dimen/fab_margin"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatTextView

View file

@ -0,0 +1,5 @@
Fixed:
- Issue with messages/notifications not correctly displayed
- Friendica: issues with mentions and tags (open browser)
- Improve sharing behaviour