diff --git a/app/build.gradle b/app/build.gradle
index 0183b0169..1cbbc992b 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -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')
diff --git a/app/src/main/assets/release_notes/notes.json b/app/src/main/assets/release_notes/notes.json
index 4eccb6258..633185ba7 100644
--- a/app/src/main/assets/release_notes/notes.json
+++ b/app/src/main/assets/release_notes/notes.json
@@ -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",
diff --git a/app/src/main/java/app/fedilab/android/BaseMainActivity.java b/app/src/main/java/app/fedilab/android/BaseMainActivity.java
index 8f4f77bfa..08ff2d3c1 100644
--- a/app/src/main/java/app/fedilab/android/BaseMainActivity.java
+++ b/app/src/main/java/app/fedilab/android/BaseMainActivity.java
@@ -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("]*property=[\"']og:title[\"'] [^>]*content=[\"']([^'^\"]+?)[\"'][^>]*>");
- Pattern descriptionPattern = Pattern.compile("]*property=[\"']og:description[\"'] [^>]*content=[\"']([^'^\"]+?)[\"'][^>]*>");
- Pattern imagePattern = Pattern.compile("]*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);
diff --git a/app/src/main/java/app/fedilab/android/activities/InstanceProfileActivity.java b/app/src/main/java/app/fedilab/android/activities/InstanceProfileActivity.java
index 20c92bbaa..934127de1 100644
--- a/app/src/main/java/app/fedilab/android/activities/InstanceProfileActivity.java
+++ b/app/src/main/java/app/fedilab/android/activities/InstanceProfileActivity.java
@@ -15,34 +15,21 @@ package app.fedilab.android.activities;
* see . */
-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 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);
});
diff --git a/app/src/main/java/app/fedilab/android/client/entities/api/Tag.java b/app/src/main/java/app/fedilab/android/client/entities/api/Tag.java
index eedf564f1..7c29dc451 100644
--- a/app/src/main/java/app/fedilab/android/client/entities/api/Tag.java
+++ b/app/src/main/java/app/fedilab/android/client/entities/api/Tag.java
@@ -27,4 +27,16 @@ public class Tag implements Serializable {
public String url;
@SerializedName("history")
public List history;
+
+
+ public int getWeight() {
+ int weight = 0;
+ for (History h : history) {
+ try {
+ weight += Integer.parseInt(h.accounts);
+ } catch (Exception ignored) {
+ }
+ }
+ return weight;
+ }
}
diff --git a/app/src/main/java/app/fedilab/android/client/entities/app/WellKnownNodeinfo.java b/app/src/main/java/app/fedilab/android/client/entities/app/WellKnownNodeinfo.java
index 7c183132b..7c376cf6a 100644
--- a/app/src/main/java/app/fedilab/android/client/entities/app/WellKnownNodeinfo.java
+++ b/app/src/main/java/app/fedilab/android/client/entities/app/WellKnownNodeinfo.java
@@ -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;
diff --git a/app/src/main/java/app/fedilab/android/interfaces/InstancesSocialService.java b/app/src/main/java/app/fedilab/android/interfaces/InstancesSocialService.java
index eed29bf86..aba3888a1 100644
--- a/app/src/main/java/app/fedilab/android/interfaces/InstancesSocialService.java
+++ b/app/src/main/java/app/fedilab/android/interfaces/InstancesSocialService.java
@@ -23,7 +23,7 @@ import retrofit2.http.Query;
public interface InstancesSocialService {
- @GET("instances/search?name=true")
+ @GET("instances/search?name=true&count=50")
Call getInstances(@Header("Authorization") String token, @Query("q") String search);
}
diff --git a/app/src/main/java/app/fedilab/android/ui/fragment/login/FragmentLoginMain.java b/app/src/main/java/app/fedilab/android/ui/fragment/login/FragmentLoginMain.java
index 5c1fdf061..c193b1aab 100644
--- a/app/src/main/java/app/fedilab/android/ui/fragment/login/FragmentLoginMain.java
+++ b/app/src/main/java/app/fedilab/android/ui/fragment/login/FragmentLoginMain.java
@@ -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);
diff --git a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTag.java b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTag.java
index e287dd40e..fc252f8d3 100644
--- a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTag.java
+++ b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTag.java
@@ -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);
diff --git a/app/src/main/java/app/fedilab/android/viewmodel/mastodon/InstanceSocialVM.java b/app/src/main/java/app/fedilab/android/viewmodel/mastodon/InstanceSocialVM.java
index ecbffc630..1fac3f555 100644
--- a/app/src/main/java/app/fedilab/android/viewmodel/mastodon/InstanceSocialVM.java
+++ b/app/src/main/java/app/fedilab/android/viewmodel/mastodon/InstanceSocialVM.java
@@ -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 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) {
diff --git a/app/src/main/res/layout/drawer_admin_account.xml b/app/src/main/res/layout/drawer_admin_account.xml
index a5da662eb..c06a9ad4a 100644
--- a/app/src/main/res/layout/drawer_admin_account.xml
+++ b/app/src/main/res/layout/drawer_admin_account.xml
@@ -1,16 +1,18 @@
+ android:layout_marginHorizontal="6dp"
+ android:layout_marginTop="6dp"
+ android:backgroundTint="@color/cyanea_primary_dark_reference"
+ app:cardElevation="0dp">