Commit 5fb14ae1 authored by Andreas Redmer's avatar Andreas Redmer 👍

Merge branch '157-recently-commented-apps' into 'master'

added fetch for latest comments and 2 level cache for UIThread and missing internet connection

Closes #157

See merge request !79
parents 08444c61 d1fb8175
Pipeline #57523242 (#469) passed with stages
in 6 minutes and 20 seconds
......@@ -24,8 +24,8 @@ android {
applicationId "org.gdroid.gdroid"
minSdkVersion 14
targetSdkVersion 28
versionCode 10000
versionName "0.10.0"
versionCode 10001
versionName "0.10.1"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
javaCompileOptions {
......
......@@ -613,7 +613,7 @@ public class AppDetailActivity extends AppCompatActivity implements FetchListene
dialog.dismiss();
String shareText = "@gdroid@mastodon.technology ";
shareText+= "#"+Util.convertPackageNameToHashtag(mApp.id);
shareText+= " #fdroid_app_comments";
shareText+= " #" + Const.HASHTAG_APP_COMMENTS;
Intent sharingIntent = new Intent(android.content.Intent.ACTION_SEND);
sharingIntent.setType("text/plain");
sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, shareText);
......
/*
* Copyright (C) 2019 Andreas Redmer <ar-gdroid@abga.be>
*
* 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.gdroid.gdroid;
class Const {
public static final String HASHTAG_APP_COMMENTS = "fdroid_app_comments";
}
......@@ -346,19 +346,16 @@ public class MainActivity extends AppCompatActivity
final Context context = getApplicationContext();
if (screen.equals("home")) {
appCollectionDescriptorList.clear();
// trigger an update here, because the app must be reactive, the swich to the new screen happes right away
// trigger an update here, because the app must be reactive, the switch to the new screen happens right away
runOnUiThread(new Runnable() {
@Override
public void run() {
appCollectionAdapter.notifyDataSetChanged();
}
});
AppCollectionDescriptor a = new AppCollectionDescriptor(context, "newest_apps");
appCollectionDescriptorList.add(a);
AppCollectionDescriptor a2 = new AppCollectionDescriptor(context, "recently_updated");
appCollectionDescriptorList.add(a2);
AppCollectionDescriptor a3 = new AppCollectionDescriptor(context, "highly_rated");
appCollectionDescriptorList.add(a3);
appCollectionDescriptorList.add(new AppCollectionDescriptor(context, "newest_apps"));
appCollectionDescriptorList.add(new AppCollectionDescriptor(context, "recently_updated"));
appCollectionDescriptorList.add(new AppCollectionDescriptor(context, "highly_rated"));
// trigger an update here, because the next collection is a bit slow
runOnUiThread(new Runnable() {
......@@ -367,12 +364,26 @@ public class MainActivity extends AppCompatActivity
appCollectionAdapter.notifyDataSetChanged();
}
});
AppCollectionDescriptor a5 = new AppCollectionDescriptor(context, "similar_to_my_apps");
appCollectionDescriptorList.add(a5);
AppCollectionDescriptor a6 = new AppCollectionDescriptor(context, "you_might_also_like");
appCollectionDescriptorList.add(a6);
AppCollectionDescriptor a4 = new AppCollectionDescriptor(context, "random_apps");
appCollectionDescriptorList.add(a4);
appCollectionDescriptorList.add(new AppCollectionDescriptor(context, "similar_to_my_apps"));
// trigger an update here, because the next collection is a bit slow
runOnUiThread(new Runnable() {
@Override
public void run() {
appCollectionAdapter.notifyDataSetChanged();
}
});
appCollectionDescriptorList.add(new AppCollectionDescriptor(context, "you_might_also_like"));
// trigger an update here, because the next collection is a bit slow
runOnUiThread(new Runnable() {
@Override
public void run() {
appCollectionAdapter.notifyDataSetChanged();
}
});
appCollectionDescriptorList.add(new AppCollectionDescriptor(context, "recently_commented"));
appCollectionDescriptorList.add(new AppCollectionDescriptor(context, "random_apps"));
// AppCollectionDescriptor a7 = new AppCollectionDescriptor(context, "popular apps");
// appCollectionDescriptorList.add(a7);
// AppCollectionDescriptor a8 = new AppCollectionDescriptor(context, "app of the day");
......
......@@ -20,6 +20,7 @@ package org.gdroid.gdroid;
import android.annotation.TargetApi;
import android.app.Activity;
import android.arch.persistence.room.util.StringUtil;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.SharedPreferences;
......@@ -28,30 +29,41 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.os.Build;
import android.os.Looper;
import android.os.StrictMode;
import android.preference.PreferenceManager;
import android.support.v4.os.ConfigurationCompat;
import android.support.v4.os.LocaleListCompat;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import org.gdroid.gdroid.beans.AppBeanNameComparator;
import org.gdroid.gdroid.beans.AppDatabase;
import org.gdroid.gdroid.beans.ApplicationBean;
import org.gdroid.gdroid.beans.CommentBean;
import org.gdroid.gdroid.beans.OrderByCol;
import org.gdroid.gdroid.installer.DefaultInstaller;
import org.gdroid.gdroid.installer.Installer;
import org.gdroid.gdroid.installer.RootInstaller;
import org.gdroid.gdroid.pref.Pref;
import org.gdroid.gdroid.tasks.DownloadCommentsTask;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringJoiner;
import java.util.concurrent.ExecutionException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Util {
......@@ -586,6 +598,11 @@ public class Util {
return pkgName.replace('.', '_');
}
public static String convertHashtagToPackageName(String hashtag)
{
return hashtag.replace('_', '.');
}
/**
* The most preferred ABI is the first element in the list.
......@@ -599,4 +616,82 @@ public class Util {
return new String[]{Build.CPU_ABI, Build.CPU_ABI2};
}
private static List<ApplicationBean> cachedRecentlyCommentedApps = new ArrayList<>();
public static List<ApplicationBean> getRecentlyCommentedApps(Context mContext, int mLimit) {
if (isUIThread() && !cachedRecentlyCommentedApps.isEmpty())
{
return cachedRecentlyCommentedApps;
}
List<ApplicationBean> ret = new ArrayList<>(40);
List<String> appIds = new ArrayList<>(40*2);
List<CommentBean> comments = new ArrayList<>(40);
final DownloadCommentsTask downloadCommentsTask = new DownloadCommentsTask(comments, new Runnable() {
@Override
public void run() {
}
});
try {
// fetch
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
comments = downloadCommentsTask.doInBackground("https://mastodon.technology/api/v1/timelines/tag/" + Const.HASHTAG_APP_COMMENTS + "?limit=40");
// extract app IDs
for (CommentBean cb : comments) {
Pattern pattern = Pattern.compile("#<span>\\w+</span>", Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(cb.content);
while (matcher.find()) {
String possibleHashtag = matcher.group();
possibleHashtag = possibleHashtag.replace("#<span>", "");
possibleHashtag = possibleHashtag.replace("</span>", "");
if (possibleHashtag.equals(Const.HASHTAG_APP_COMMENTS))
continue;
if (!appIds.contains(possibleHashtag))
appIds.add(possibleHashtag);
// arrived here we have a match, most likely an app id
}
}
// store each successful fetch in Pref
if (!appIds.isEmpty()) {
Pref.get().setLastCommentedApps(TextUtils.join(";", appIds));
}
}
catch (Throwable t)
{
// fallback to the cached list from Pref below
}
if (appIds.isEmpty())
{
final String[] idArray = Pref.get().getLastCommentedApps().split(";");
appIds = new ArrayList<>(Arrays.asList(idArray));
}
// we have to get the apps one by one from the DB to retain the order
for (String appId : appIds)
{
AppDatabase db = AppDatabase.get(mContext);
ApplicationBean ab = db.appDao().getApplicationBean(convertHashtagToPackageName(appId));
if (ab != null)
{
ret.add(ab);
}
}
cachedRecentlyCommentedApps = ret;
int limit = Math.min(ret.size()-1,mLimit);
limit = Math.max(0,limit);
return new ArrayList(ret.subList(0, limit));
}
public static boolean isUIThread()
{
return Thread.currentThread().equals( Looper.getMainLooper().getThread() );
}
}
......@@ -116,6 +116,11 @@ public class AppCollectionDescriptor implements Comparable<AppCollectionDescript
}
db.close();
}
else if (collectionName.equals("recently_commented"))
{
applicationBeanList.clear();
applicationBeanList.addAll(Util.getRecentlyCommentedApps(mContext,mLimit));
}
else if (collectionName.equals("starred"))
{
applicationBeanList.clear();
......
......@@ -54,6 +54,7 @@ public class Pref {
private static final String PREF_LAST_UPDATE_CHECK = "lastUpdateCheck";
private static final String PREF_LAST_FDROID_ETAG = "last_fdroid_etag";
private static final String PREF_LAST_GDROID_ETAG = "last_gdroid_etag";
private static final String PREF_CACHED_COMMENTED_APPS = "cached_commented_apps";
private static final int DEFAULT_LAST_UPDATE_CHECK = -1;
......@@ -97,6 +98,14 @@ public class Pref {
preferences.edit().putString(PREF_LAST_GDROID_ETAG, lastGDroidEtag).apply();
}
public String getLastCommentedApps() {
return preferences.getString(PREF_CACHED_COMMENTED_APPS, "");
}
public void setLastCommentedApps(String lastCommentedApps) {
preferences.edit().putString(PREF_CACHED_COMMENTED_APPS, lastCommentedApps).apply();
}
/**
* Needs to be setup before anything else tries to access it.
*/
......
......@@ -38,7 +38,7 @@ public class DownloadCommentsTask extends AsyncTask<String, Void, List<CommentBe
this.runnableOnPostExecute = runnableOnPostExecute;
}
protected List<CommentBean> doInBackground(String... urls) {
public List<CommentBean> doInBackground(String... urls) {
String urldisplay = urls[0];
try {
InputStream in = new java.net.URL(urldisplay).openStream();
......@@ -53,7 +53,7 @@ public class DownloadCommentsTask extends AsyncTask<String, Void, List<CommentBe
return new ArrayList<>();
}
protected void onPostExecute(List<CommentBean> result) {
public void onPostExecute(List<CommentBean> result) {
mCommentBeansInActivity.clear();
mCommentBeansInActivity.addAll(result);
runnableOnPostExecute.run();
......
......@@ -86,6 +86,7 @@
<string name="more">Mehr</string>
<string name="newest_apps">Neueste Apps</string>
<string name="recently_updated">Kürzlich aktualisierte Apps</string>
<string name="recently_commented">Kürzlich kommentierte Apps</string>
<string name="highly_rated">Gut bewertet</string>
<string name="similar_to_my_apps">Ähnlich zu meinen Apps</string>
<string name="you_might_also_like">Könnte dir auch gefallen</string>
......
......@@ -183,6 +183,7 @@
<string name="more">More</string>
<string name="newest_apps">Newest apps</string>
<string name="recently_updated">Recently updated</string>
<string name="recently_commented">Recently commented</string>
<string name="highly_rated">Highly rated</string>
<string name="similar_to_my_apps">Similar to my apps</string>
<string name="you_might_also_like">You might also like</string>
......
* show recently commented apps on home page
* app comments are now clickable and can be openened in a mastodon app or browser
* bugfix: incompletely visible comments can now be seen completely (added a more-button)
* translation updates
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment