Merge branch 'develop' into main

This commit is contained in:
Thomas 2022-06-30 18:54:29 +02:00
commit b2b01e5c21
44 changed files with 589 additions and 197 deletions

View file

@ -9,8 +9,8 @@ android {
defaultConfig {
minSdk 21
targetSdk 31
versionCode 391
versionName "3.0.1"
versionCode 393
versionName "3.0.2"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
flavorDimensions "default"

View file

@ -109,6 +109,7 @@ import app.fedilab.android.client.entities.api.Status;
import app.fedilab.android.client.entities.app.Account;
import app.fedilab.android.client.entities.app.BaseAccount;
import app.fedilab.android.client.entities.app.BottomMenu;
import app.fedilab.android.client.entities.app.DomainsBlock;
import app.fedilab.android.client.entities.app.Pinned;
import app.fedilab.android.client.entities.app.PinnedTimeline;
import app.fedilab.android.databinding.ActivityMainBinding;
@ -612,7 +613,7 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
binding.profilePicture.setOnClickListener(v -> binding.drawerLayout.openDrawer(GravityCompat.START));
Helper.loadPP(binding.profilePicture, currentAccount);
headerMainBinding.accountAcc.setText(String.format("%s@%s", currentAccount.mastodon_account.username, currentAccount.instance));
if (currentAccount.mastodon_account.display_name.isEmpty()) {
if (currentAccount.mastodon_account.display_name == null || currentAccount.mastodon_account.display_name.isEmpty()) {
currentAccount.mastodon_account.display_name = currentAccount.mastodon_account.acct;
}
headerMainBinding.accountName.setText(currentAccount.mastodon_account.display_name);
@ -694,7 +695,7 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
binding.toolbarSearch.setOnSearchClickListener(v -> binding.tabLayout.setVisibility(View.VISIBLE));
//For receiving data from other activities
LocalBroadcastManager.getInstance(BaseMainActivity.this).registerReceiver(broadcast_data, new IntentFilter(Helper.BROADCAST_DATA));
if (emojis == null || !emojis.containsKey(MainActivity.currentInstance)) {
if (emojis == null || !emojis.containsKey(BaseMainActivity.currentInstance)) {
new Thread(() -> {
try {
emojis.put(currentInstance, new EmojiInstance(BaseMainActivity.this).getEmojiList(BaseMainActivity.currentInstance));
@ -703,6 +704,7 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
}
}).start();
}
DomainsBlock.updateDomains(BaseMainActivity.this);
}

View file

@ -30,6 +30,7 @@ import androidx.lifecycle.ViewModelProvider;
import java.util.ArrayList;
import java.util.List;
import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.BuildConfig;
import app.fedilab.android.R;
import app.fedilab.android.client.entities.api.Account;
@ -112,12 +113,12 @@ public class AboutActivity extends BaseActivity {
AccountsVM accountsVM = new ViewModelProvider(AboutActivity.this).get(AccountsVM.class);
List<String> ids = new ArrayList<>();
ids.add(account.id);
accountsVM.getRelationships(MainActivity.currentInstance, MainActivity.currentToken, ids)
accountsVM.getRelationships(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, ids)
.observe(AboutActivity.this, relationShips -> {
if (relationShips != null && relationShips.size() > 0) {
if (!relationShips.get(0).following) {
binding.accountFollow.setVisibility(View.VISIBLE);
binding.accountFollow.setOnClickListener(v -> accountsVM.follow(MainActivity.currentInstance, MainActivity.currentToken, account.id, true, false)
binding.accountFollow.setOnClickListener(v -> accountsVM.follow(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, true, false)
.observe(AboutActivity.this, relationShip -> binding.accountFollow.setVisibility(View.GONE)));
}
}

View file

@ -171,14 +171,14 @@ public class AdminAccountActivity extends BaseActivity {
binding.disableAction.setOnClickListener(v -> {
if (adminAccount.disabled) {
adminVM.enable(MainActivity.currentInstance, MainActivity.currentToken, account.id)
adminVM.enable(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id)
.observe(AdminAccountActivity.this, adminAccountResult -> {
adminAccount.disabled = false;
binding.disableAction.setText(R.string.disable);
binding.disabled.setText(R.string.no);
});
} else {
adminVM.performAction(MainActivity.currentInstance, MainActivity.currentToken, account.id, "disable ", null, null, null, null);
adminVM.performAction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, "disable ", null, null, null, null);
adminAccount.disabled = true;
binding.disableAction.setText(R.string.undisable);
binding.disabled.setText(R.string.yes);
@ -187,14 +187,14 @@ public class AdminAccountActivity extends BaseActivity {
binding.approveAction.setOnClickListener(v -> {
if (adminAccount.approved) {
adminVM.reject(MainActivity.currentInstance, MainActivity.currentToken, account.id)
adminVM.reject(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id)
.observe(AdminAccountActivity.this, adminAccountResult -> {
adminAccount.approved = false;
binding.approveAction.setText(R.string.approve);
binding.approved.setText(R.string.no);
});
} else {
adminVM.approve(MainActivity.currentInstance, MainActivity.currentToken, account.id);
adminVM.approve(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id);
adminAccount.approved = true;
binding.approveAction.setText(R.string.reject);
binding.approved.setText(R.string.yes);
@ -203,14 +203,14 @@ public class AdminAccountActivity extends BaseActivity {
binding.silenceAction.setOnClickListener(v -> {
if (adminAccount.disabled) {
adminVM.unsilence(MainActivity.currentInstance, MainActivity.currentToken, account.id)
adminVM.unsilence(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id)
.observe(AdminAccountActivity.this, adminAccountResult -> {
adminAccount.silenced = false;
binding.silenceAction.setText(R.string.silence);
binding.disabled.setText(R.string.no);
});
} else {
adminVM.performAction(MainActivity.currentInstance, MainActivity.currentToken, account.id, "silence", null, null, null, null);
adminVM.performAction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, "silence", null, null, null, null);
adminAccount.silenced = true;
binding.disableAction.setText(R.string.unsilence);
binding.disabled.setText(R.string.yes);
@ -219,14 +219,14 @@ public class AdminAccountActivity extends BaseActivity {
binding.suspendAction.setOnClickListener(v -> {
if (adminAccount.disabled) {
adminVM.unsuspend(MainActivity.currentInstance, MainActivity.currentToken, account.id)
adminVM.unsuspend(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id)
.observe(AdminAccountActivity.this, adminAccountResult -> {
adminAccount.suspended = false;
binding.suspendAction.setText(R.string.suspend);
binding.suspended.setText(R.string.no);
});
} else {
adminVM.performAction(MainActivity.currentInstance, MainActivity.currentToken, account.id, "suspend", null, null, null, null);
adminVM.performAction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, "suspend", null, null, null, null);
adminAccount.suspended = true;
binding.disableAction.setText(R.string.unsuspend);
binding.suspended.setText(R.string.yes);

View file

@ -171,7 +171,7 @@ public class AdminReportActivity extends BaseActivity {
binding.disableAction.setOnClickListener(v -> {
if (adminAccount.disabled) {
adminVM.enable(MainActivity.currentInstance, MainActivity.currentToken, account.id)
adminVM.enable(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id)
.observe(AdminReportActivity.this, adminAccountResult -> {
if (adminAccountResult != null) {
adminAccount.disabled = false;
@ -183,7 +183,7 @@ public class AdminReportActivity extends BaseActivity {
});
} else {
adminVM.performAction(MainActivity.currentInstance, MainActivity.currentToken, account.id, "disable ", null, null, null, null);
adminVM.performAction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, "disable ", null, null, null, null);
adminAccount.disabled = true;
binding.disableAction.setText(R.string.undisable);
binding.disabled.setText(R.string.yes);
@ -192,7 +192,7 @@ public class AdminReportActivity extends BaseActivity {
binding.approveAction.setOnClickListener(v -> {
if (adminAccount.approved) {
adminVM.reject(MainActivity.currentInstance, MainActivity.currentToken, account.id)
adminVM.reject(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id)
.observe(AdminReportActivity.this, adminAccountResult -> {
if (adminAccountResult != null) {
adminAccount.approved = false;
@ -204,7 +204,7 @@ public class AdminReportActivity extends BaseActivity {
});
} else {
adminVM.approve(MainActivity.currentInstance, MainActivity.currentToken, account.id);
adminVM.approve(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id);
adminAccount.approved = true;
binding.approveAction.setText(R.string.reject);
binding.approved.setText(R.string.yes);
@ -213,7 +213,7 @@ public class AdminReportActivity extends BaseActivity {
binding.silenceAction.setOnClickListener(v -> {
if (adminAccount.disabled) {
adminVM.unsilence(MainActivity.currentInstance, MainActivity.currentToken, account.id)
adminVM.unsilence(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id)
.observe(AdminReportActivity.this, adminAccountResult -> {
if (adminAccountResult != null) {
adminAccount.silenced = false;
@ -224,7 +224,7 @@ public class AdminReportActivity extends BaseActivity {
}
});
} else {
adminVM.performAction(MainActivity.currentInstance, MainActivity.currentToken, account.id, "silence", null, null, null, null);
adminVM.performAction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, "silence", null, null, null, null);
adminAccount.silenced = true;
binding.disableAction.setText(R.string.unsilence);
binding.disabled.setText(R.string.yes);
@ -233,7 +233,7 @@ public class AdminReportActivity extends BaseActivity {
binding.suspendAction.setOnClickListener(v -> {
if (adminAccount.disabled) {
adminVM.unsuspend(MainActivity.currentInstance, MainActivity.currentToken, account.id)
adminVM.unsuspend(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id)
.observe(AdminReportActivity.this, adminAccountResult -> {
if (adminAccountResult != null) {
adminAccount.suspended = false;
@ -244,7 +244,7 @@ public class AdminReportActivity extends BaseActivity {
}
});
} else {
adminVM.performAction(MainActivity.currentInstance, MainActivity.currentToken, account.id, "suspend", null, null, null, null);
adminVM.performAction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, "suspend", null, null, null, null);
adminAccount.suspended = true;
binding.disableAction.setText(R.string.unsuspend);
binding.suspended.setText(R.string.yes);

View file

@ -34,6 +34,7 @@ import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.preference.PreferenceManager;
import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R;
import app.fedilab.android.client.entities.api.Status;
import app.fedilab.android.client.entities.app.QuickLoad;
@ -97,11 +98,11 @@ public class ContextActivity extends BaseActivity {
mainHandler.post(myRunnable);
}).start();
StatusesVM timelinesVM = new ViewModelProvider(ContextActivity.this).get(StatusesVM.class);
timelinesVM.getStatus(MainActivity.currentInstance, MainActivity.currentToken, focusedStatus.id).observe(ContextActivity.this, status -> {
timelinesVM.getStatus(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, focusedStatus.id).observe(ContextActivity.this, status -> {
if (status != null) {
StatusCache statusCache = new StatusCache();
statusCache.instance = MainActivity.currentInstance;
statusCache.user_id = MainActivity.currentUserID;
statusCache.instance = BaseMainActivity.currentInstance;
statusCache.user_id = BaseMainActivity.currentUserID;
statusCache.status = status;
statusCache.status_id = status.id;
//Update cache

View file

@ -31,6 +31,7 @@ import androidx.lifecycle.ViewModelProvider;
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.client.entities.api.Status;
@ -92,12 +93,12 @@ public class PartnerShipActivity extends BaseActivity {
AccountsVM accountsVM = new ViewModelProvider(PartnerShipActivity.this).get(AccountsVM.class);
List<String> ids = new ArrayList<>();
ids.add(account.id);
accountsVM.getRelationships(MainActivity.currentInstance, MainActivity.currentToken, ids)
accountsVM.getRelationships(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, ids)
.observe(PartnerShipActivity.this, relationShips -> {
if (relationShips != null && relationShips.size() > 0) {
if (!relationShips.get(0).following) {
binding.accountFollow.setVisibility(View.VISIBLE);
binding.accountFollow.setOnClickListener(v -> accountsVM.follow(MainActivity.currentInstance, MainActivity.currentToken, account.id, true, false)
binding.accountFollow.setOnClickListener(v -> accountsVM.follow(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, true, false)
.observe(PartnerShipActivity.this, relationShip -> binding.accountFollow.setVisibility(View.GONE)));
}
}

View file

@ -348,7 +348,7 @@ public class ProfileActivity extends BaseActivity {
binding.accountMoved.setText(spannableString, TextView.BufferType.SPANNABLE);
binding.accountMoved.setMovementMethod(LinkMovementMethod.getInstance());
}
if (account.acct.contains("@"))
if (account.acct != null && account.acct.contains("@"))
binding.warningMessage.setVisibility(View.VISIBLE);
else
binding.warningMessage.setVisibility(View.GONE);

View file

@ -14,17 +14,18 @@ package app.fedilab.android.activities;
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
@ -39,14 +40,13 @@ import androidx.core.content.ContextCompat;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import app.fedilab.android.R;
import app.fedilab.android.databinding.ActivityWebviewBinding;
import app.fedilab.android.helper.CountDrawable;
import app.fedilab.android.helper.Helper;
import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.sqlite.Sqlite;
import app.fedilab.android.webview.CustomWebview;
import app.fedilab.android.webview.FedilabWebChromeClient;
import app.fedilab.android.webview.FedilabWebViewClient;
@ -55,12 +55,13 @@ import es.dmoral.toasty.Toasty;
public class WebviewActivity extends BaseActivity {
public static List<String> trackingDomains;
private String url;
private boolean peertubeLink;
private CustomWebview webView;
private FedilabWebViewClient FedilabWebViewClient;
private ActivityWebviewBinding binding;
private Menu defaultMenu;
@SuppressLint("SetJavaScriptEnabled")
@Override
@ -133,24 +134,12 @@ public class WebviewActivity extends BaseActivity {
});
if (!url.toLowerCase().startsWith("http://") && !url.toLowerCase().startsWith("https://"))
url = "http://" + url;
if (trackingDomains == null) {
AsyncTask.execute(() -> {
SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
// trackingDomains = new DomainBlockDAO(WebviewActivity.this, db).getAll();
if (trackingDomains == null)
trackingDomains = new ArrayList<>();
// Get a handler that can be used to post to the main thread
Handler mainHandler = new Handler(getMainLooper());
Runnable myRunnable = () -> webView.loadUrl(url);
mainHandler.post(myRunnable);
webView.loadUrl(url);
});
} else
webView.loadUrl(url);
}
/* public void setCount(Context context, String count) {
public void setCount(Context context, String count) {
if (defaultMenu != null && !peertubeLink) {
MenuItem menuItem = defaultMenu.findItem(R.id.action_block);
LayerDrawable icon = (LayerDrawable) menuItem.getIcon();
@ -169,17 +158,20 @@ public class WebviewActivity extends BaseActivity {
icon.mutate();
icon.setDrawableByLayerId(R.id.ic_block_count, badge);
}
}*/
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
if (!peertubeLink)
setCount(WebviewActivity.this, "0");
defaultMenu = menu;
return super.onPrepareOptionsMenu(menu);
}
@Override
public boolean onCreateOptionsMenu(@NotNull Menu menu) {
getMenuInflater().inflate(R.menu.main_webview, menu);
defaultMenu = menu;
if (peertubeLink) {
menu.findItem(R.id.action_go).setVisible(false);
menu.findItem(R.id.action_block).setVisible(false);

View file

@ -46,6 +46,7 @@ import androidx.preference.PreferenceManager;
import java.util.regex.Matcher;
import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R;
import app.fedilab.android.client.entities.app.Account;
import app.fedilab.android.databinding.ActivityWebviewConnectBinding;
@ -55,6 +56,7 @@ import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.viewmodel.mastodon.AccountsVM;
import app.fedilab.android.viewmodel.mastodon.AdminVM;
import app.fedilab.android.viewmodel.mastodon.OauthVM;
import es.dmoral.toasty.Toasty;
public class WebviewConnectActivity extends BaseActivity {
@ -89,9 +91,9 @@ public class WebviewConnectActivity extends BaseActivity {
//update the database
new Account(activity).insertOrUpdate(account);
Handler mainHandler = new Handler(Looper.getMainLooper());
MainActivity.currentToken = account.token;
MainActivity.currentUserID = account.user_id;
MainActivity.api = Account.API.MASTODON;
BaseMainActivity.currentToken = account.token;
BaseMainActivity.currentUserID = account.user_id;
BaseMainActivity.api = Account.API.MASTODON;
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(activity);
SharedPreferences.Editor editor = sharedpreferences.edit();
editor.putString(PREF_USER_TOKEN, account.token);
@ -214,18 +216,23 @@ public class WebviewConnectActivity extends BaseActivity {
//API call to retrieve account information for the new token
AccountsVM accountsVM = new ViewModelProvider(WebviewConnectActivity.this).get(AccountsVM.class);
accountsVM.getConnectedAccount(currentInstanceLogin, account.token).observe(WebviewConnectActivity.this, mastodonAccount -> {
account.mastodon_account = mastodonAccount;
account.user_id = mastodonAccount.id;
//We check if user have really moderator rights
if (requestedAdmin) {
AdminVM adminVM = new ViewModelProvider(WebviewConnectActivity.this).get(AdminVM.class);
adminVM.getAccount(account.instance, account.token, account.user_id).observe(WebviewConnectActivity.this, adminAccount -> {
account.admin = adminAccount != null;
if (mastodonAccount != null) {
account.mastodon_account = mastodonAccount;
account.user_id = mastodonAccount.id;
//We check if user have really moderator rights
if (requestedAdmin) {
AdminVM adminVM = new ViewModelProvider(WebviewConnectActivity.this).get(AdminVM.class);
adminVM.getAccount(account.instance, account.token, account.user_id).observe(WebviewConnectActivity.this, adminAccount -> {
account.admin = adminAccount != null;
proceedLogin(WebviewConnectActivity.this, account);
});
} else {
proceedLogin(WebviewConnectActivity.this, account);
});
}
} else {
proceedLogin(WebviewConnectActivity.this, account);
Toasty.error(WebviewConnectActivity.this, getString(R.string.toast_error), Toasty.LENGTH_SHORT).show();
}
});
});
return true;

View file

@ -225,12 +225,14 @@ public interface MastodonTimelinesService {
@Query("count") int count
);
@Headers({"Accept: text/html,application/xhtml+xml,application/xml"})
@GET("{names}/rss")
Call<Nitter> getNitter(
@Path("names") String id,
@Query("max_position") String max_position
);
@Headers({"Accept: text/html,application/xhtml+xml,application/xml"})
@GET("{account}/rss")
Call<Nitter> getNitterAccount(
@Path("account") String account

View file

@ -33,7 +33,6 @@ import java.util.List;
import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R;
import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.exception.DBException;
import app.fedilab.android.sqlite.Sqlite;
@ -218,8 +217,8 @@ public class BottomMenu implements Serializable {
return -1;
}
if (bottomMenu.user_id == null) {
bottomMenu.user_id = MainActivity.currentUserID;
bottomMenu.instance = MainActivity.currentInstance;
bottomMenu.user_id = BaseMainActivity.currentUserID;
bottomMenu.instance = BaseMainActivity.currentInstance;
}
boolean exists = bottomMenuExists(bottomMenu);
long idReturned;
@ -299,8 +298,8 @@ public class BottomMenu implements Serializable {
public BottomMenu defaultBottomMenu() {
BottomMenu bottomMenu = new BottomMenu();
bottomMenu.user_id = MainActivity.currentUserID;
bottomMenu.instance = MainActivity.currentInstance;
bottomMenu.user_id = BaseMainActivity.currentUserID;
bottomMenu.instance = BaseMainActivity.currentInstance;
bottomMenu.bottom_menu = new ArrayList<>();
MenuItem menuItemHome = new MenuItem();
menuItemHome.position = 0;

View file

@ -0,0 +1,127 @@
package app.fedilab.android.client.entities.app;
import android.content.ContentValues;
import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import androidx.preference.PreferenceManager;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.HttpsURLConnection;
import app.fedilab.android.exception.DBException;
import app.fedilab.android.helper.Helper;
import app.fedilab.android.sqlite.Sqlite;
public class DomainsBlock {
public static final String LAST_DATE_OF_UPDATE = "LAST_DATE_OF_UPDATE";
public static List<String> trackingDomains = null;
private static void getDomains(Context context) {
if (trackingDomains == null) {
try {
SQLiteDatabase db = Sqlite.getInstance(context.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
Cursor c = db.query(Sqlite.TABLE_DOMAINS_TRACKING, null, null, null, null, null, null, null);
trackingDomains = cursorToDomain(c);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void updateDomains(Context _mContext) {
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(_mContext);
String last_date = sharedpreferences.getString(LAST_DATE_OF_UPDATE, null);
Date dateUpdate = new Date(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(10));
Date dateLastUpdate = Helper.stringToDate(_mContext, last_date);
SQLiteDatabase db = Sqlite.getInstance(_mContext.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
if (last_date == null || dateUpdate.after(dateLastUpdate)) {
new Thread(() -> {
try {
HttpsURLConnection connection = (HttpsURLConnection) new URL("https://hosts.fedilab.app/hosts").openConnection();
if (connection.getResponseCode() > HttpsURLConnection.HTTP_MOVED_TEMP) {
return;
}
BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String line;
List<String> domains = new ArrayList<>();
while ((line = br.readLine()) != null) {
if (line.startsWith("0.0.0.0 ")) {
domains.add(line.replace("0.0.0.0 ", "").trim());
}
}
br.close();
connection.disconnect();
insertDomains(db, domains);
SharedPreferences.Editor editor = sharedpreferences.edit();
editor.putString(LAST_DATE_OF_UPDATE, Helper.dateToString(new Date()));
editor.apply();
} catch (IOException | DBException e) {
e.printStackTrace();
}
}).start();
} else {
getDomains(_mContext);
}
}
/**
* Insert a domains in db
*
* @param domains {@link List<String>}
* @throws DBException exception with database
*/
private static void insertDomains(SQLiteDatabase db, List<String> domains) throws DBException {
if (db == null) {
throw new DBException("db is null. Wrong initialization.");
}
db.delete(Sqlite.TABLE_DOMAINS_TRACKING, null, null);
DomainsBlock.trackingDomains = new ArrayList<>();
for (String domain : domains) {
ContentValues values = new ContentValues();
values.put(Sqlite.COL_DOMAIN, domain);
//Inserts token
try {
db.insertOrThrow(Sqlite.TABLE_DOMAINS_TRACKING, null, values);
} catch (Exception e) {
e.printStackTrace();
}
DomainsBlock.trackingDomains.add(domain);
}
}
/***
* Method to hydrate domain from database
* @param c Cursor
* @return List<String>
*/
private static List<String> cursorToDomain(Cursor c) {
//No element found
if (c.getCount() == 0) {
c.close();
return null;
}
List<String> domains = new ArrayList<>();
while (c.moveToNext()) {
domains.add(c.getString(c.getColumnIndexOrThrow(Sqlite.COL_DOMAIN)));
}
//Close the cursor
c.close();
//domains list is returned
return domains;
}
}

View file

@ -76,14 +76,20 @@ public class Nitter implements Serializable {
public static Status convert(Context context, String instance, FeedItem feedItem) {
Status status = new Status();
status.id = feedItem.pubDate;
Matcher matcherLink = Helper.nitterIDPattern.matcher(feedItem.link);
if (matcherLink.find()) {
status.id = matcherLink.group(1);
} else {
status.id = feedItem.pubDate;
}
status.content = feedItem.description;
status.text = feedItem.title;
status.content = status.content.replaceAll("<img [^>]*src=\"[^\"]+\"[^>]*>", "");
status.visibility = "public";
status.created_at = Helper.stringToDateWithFormat(context, feedItem.pubDate, "EEE, dd MMM yyyy HH:mm:ss zzz");
status.uri = feedItem.guid;
status.url = feedItem.link;
if (!accounts.containsValue(feedItem.creator)) {
if (!accounts.containsKey(feedItem.creator)) {
MastodonTimelinesService mastodonTimelinesService = initInstanceXMLOnly(context, instance);
Call<Nitter> accountCall = mastodonTimelinesService.getNitterAccount(feedItem.creator.replace("@", ""));
if (accountCall != null) {

View file

@ -0,0 +1,116 @@
package app.fedilab.android.helper;
/* 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 android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import app.fedilab.android.R;
public class CountDrawable extends Drawable {
private final Paint mBadgePaint;
private final Paint mTextPaint;
private final Rect mTxtRect = new Rect();
private String mCount = "";
private boolean mWillDraw;
public CountDrawable(Context context) {
float mTextSize = context.getResources().getDimension(R.dimen.badge_count_textsize);
mBadgePaint = new Paint();
mBadgePaint.setColor(ContextCompat.getColor(context, R.color.red_1));
mBadgePaint.setAntiAlias(true);
mBadgePaint.setStyle(Paint.Style.FILL);
mTextPaint = new Paint();
mTextPaint.setColor(Color.WHITE);
mTextPaint.setTypeface(Typeface.DEFAULT);
mTextPaint.setTextSize(mTextSize);
mTextPaint.setAntiAlias(true);
mTextPaint.setTextAlign(Paint.Align.CENTER);
}
@Override
public void draw(@NonNull Canvas canvas) {
if (!mWillDraw) {
return;
}
Rect bounds = getBounds();
float width = bounds.right - bounds.left;
float height = bounds.bottom - bounds.top;
// Position the badge in the top-right quadrant of the icon.
/*Using Math.max rather than Math.min */
float radius = ((Math.max(width, height) / 2)) / 2;
float centerX = (width - radius - 1) + 5;
float centerY = radius - 5;
if (mCount.length() <= 2) {
// Draw badge circle.
canvas.drawCircle(centerX, centerY, (int) (radius + 5.5), mBadgePaint);
} else {
canvas.drawCircle(centerX, centerY, (int) (radius + 6.5), mBadgePaint);
}
// Draw badge count text inside the circle.
mTextPaint.getTextBounds(mCount, 0, mCount.length(), mTxtRect);
float textHeight = mTxtRect.bottom - mTxtRect.top;
float textY = centerY + (textHeight / 2f);
if (mCount.length() > 2)
canvas.drawText("99+", centerX, textY, mTextPaint);
else
canvas.drawText(mCount, centerX, textY, mTextPaint);
}
/*
Sets the count (i.e notifications) to display.
*/
public void setCount(String count) {
mCount = count;
// Only draw a badge if there are notifications.
mWillDraw = !count.equalsIgnoreCase("0");
invalidateSelf();
}
@Override
public void setAlpha(int alpha) {
// do nothing
}
@Override
public void setColorFilter(ColorFilter cf) {
// do nothing
}
@Override
public int getOpacity() {
return PixelFormat.UNKNOWN;
}
}

View file

@ -35,7 +35,6 @@ import java.util.concurrent.TimeUnit;
import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R;
import app.fedilab.android.activities.ComposeActivity;
import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.client.endpoints.MastodonSearchService;
import app.fedilab.android.client.entities.api.Results;
import app.fedilab.android.client.entities.api.Status;
@ -265,7 +264,7 @@ public class CrossActionHelper {
* Fetch and federate the remote status
*/
public static void fetchRemoteStatus(@NonNull Context context, @NonNull BaseAccount ownerAccount, String url, Callback callback) {
MastodonSearchService mastodonSearchService = init(context, MainActivity.currentInstance);
MastodonSearchService mastodonSearchService = init(context, BaseMainActivity.currentInstance);
new Thread(() -> {
Call<Results> resultsCall = mastodonSearchService.search(ownerAccount.token, url, null, "statuses", false, true, false, 0, null, null, 1);
Results results = null;
@ -310,7 +309,7 @@ public class CrossActionHelper {
public static void fetchRemoteAccount(@NonNull Context context, @NonNull BaseAccount ownerAccount, app.fedilab.android.client.entities.api.Account targetedAccount, Callback callback) {
MastodonSearchService mastodonSearchService = init(context, MainActivity.currentInstance);
MastodonSearchService mastodonSearchService = init(context, BaseMainActivity.currentInstance);
String search;
if (targetedAccount.acct.contains("@")) { //Not from same instance
search = targetedAccount.acct;
@ -361,9 +360,9 @@ public class CrossActionHelper {
public static void fetchRemoteAccount(@NonNull Context context, String acct, Callback callback) {
MastodonSearchService mastodonSearchService = init(context, MainActivity.currentInstance);
MastodonSearchService mastodonSearchService = init(context, BaseMainActivity.currentInstance);
new Thread(() -> {
Call<Results> resultsCall = mastodonSearchService.search(MainActivity.currentToken, acct, null, "accounts", false, true, false, 0, null, null, 1);
Call<Results> resultsCall = mastodonSearchService.search(BaseMainActivity.currentToken, acct, null, "accounts", false, true, false, 0, null, null, 1);
Results results = null;
if (resultsCall != null) {
try {

View file

@ -284,6 +284,8 @@ public class Helper {
public static final Pattern mediumPattern = Pattern.compile("([\\w@-]*)?\\.?medium.com/@?([/\\w-]+)");
public static final Pattern wikipediaPattern = Pattern.compile("([\\w_-]+)\\.wikipedia.org/(((?!([\"'<])).)*)");
public static final Pattern codePattern = Pattern.compile("code=([\\w-]+)");
public static final Pattern nitterIDPattern = Pattern.compile("/status/(\\d+)");
public static final Pattern urlPattern = Pattern.compile(
"(?i)\\b((?:[a-z][\\w-]+:(?:/{1,3}|[a-z0-9%])|www\\d{0,3}[.]|[a-z0-9.\\-]+[.][a-z]{2,10}/)(?:[^\\s()<>]+|\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*\\))+(?:\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*\\)|[^\\s`!()\\[\\]{};:'\".,<>?«»“”‘’]))",
@ -1032,17 +1034,26 @@ public class Helper {
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context);
boolean disableGif = sharedpreferences.getBoolean(context.getString(R.string.SET_DISABLE_GIF), false);
String targetedUrl = disableGif ? account.mastodon_account.avatar_static : account.mastodon_account.avatar;
if (disableGif || (!targetedUrl.endsWith(".gif"))) {
Glide.with(view.getContext())
.asDrawable()
.load(targetedUrl)
.thumbnail(0.1f)
.apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(10)))
.into(view);
if (targetedUrl != null) {
if (disableGif || (!targetedUrl.endsWith(".gif"))) {
Glide.with(view.getContext())
.asDrawable()
.load(targetedUrl)
.thumbnail(0.1f)
.apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(10)))
.into(view);
} else {
Glide.with(view.getContext())
.asGif()
.load(targetedUrl)
.thumbnail(0.1f)
.apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(10)))
.into(view);
}
} else {
Glide.with(view.getContext())
.asGif()
.load(targetedUrl)
.asDrawable()
.load(R.drawable.ic_person)
.thumbnail(0.1f)
.apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(10)))
.into(view);

View file

@ -71,8 +71,8 @@ public class MastodonHelper {
public static final String RESPONSE_TYPE = "response_type";
public static final String SCOPE = "scope";
public static final String REDIRECT_CONTENT_WEB = "fedilab://backtofedilab";
public static final String OAUTH_SCOPES = "read write follow push";
public static final String OAUTH_SCOPES_ADMIN = "read write follow push admin:read admin:write";
public static final String OAUTH_SCOPES = "read%20write%20follow%20push";
public static final String OAUTH_SCOPES_ADMIN = "read%20write%20follow%20push%20admin:read%20admin:write";
public static final int ACCOUNTS_PER_CALL = 40;
public static final int STATUSES_PER_CALL = 40;

View file

@ -47,6 +47,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;
import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R;
import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.client.endpoints.MastodonNotificationsService;
@ -273,7 +274,7 @@ public class NotificationsHelper {
notifType = Helper.NotifType.POLL;
if (notif_poll) {
title = context.getString(R.string.channel_notif_poll);
if (notification.account.id != null && notification.account.id.equals(MainActivity.currentUserID))
if (notification.account.id != null && notification.account.id.equals(BaseMainActivity.currentUserID))
message = context.getString(R.string.notif_poll_self);
else
message = context.getString(R.string.notif_poll);

View file

@ -52,6 +52,7 @@ import app.fedilab.android.client.entities.app.RemoteInstance;
import app.fedilab.android.client.entities.app.TagTimeline;
import app.fedilab.android.client.entities.app.Timeline;
import app.fedilab.android.databinding.ActivityMainBinding;
import app.fedilab.android.databinding.TabCustomViewBinding;
import app.fedilab.android.exception.DBException;
import app.fedilab.android.ui.fragment.timeline.FragmentMastodonConversation;
import app.fedilab.android.ui.fragment.timeline.FragmentMastodonTimeline;
@ -149,9 +150,13 @@ public class PinnedTimelineHelper {
//Small hack to hide first tabs (they represent the item of the bottom menu)
int toRemove = itemToRemoveInBottomMenu(activity);
List<String> tabTitle = new ArrayList<>();
List<RemoteInstance.InstanceType> tabTypeRemote = new ArrayList<>();
List<Timeline.TimeLineEnum> tabType = new ArrayList<>();
for (int i = 0; i < (BOTTOM_TIMELINE_COUNT - toRemove); i++) {
activityMainBinding.tabLayout.addTab(activityMainBinding.tabLayout.newTab());
tabTitle.add("");
tabType.add(Timeline.TimeLineEnum.HOME);
tabTypeRemote.add(RemoteInstance.InstanceType.MASTODON);
((ViewGroup) activityMainBinding.tabLayout.getChildAt(0)).getChildAt(i).setVisibility(View.GONE);
}
List<PinnedTimeline> pinnedTimelineVisibleList = new ArrayList<>();
@ -166,9 +171,6 @@ public class PinnedTimelineHelper {
break;
case TAG:
name = pinnedTimeline.tagTimeline.name;
if (!name.startsWith("#")) {
name = "#" + name;
}
break;
case REMOTE:
name = pinnedTimeline.remoteInstance.host;
@ -177,8 +179,13 @@ public class PinnedTimelineHelper {
TextView tv = (TextView) LayoutInflater.from(activity).inflate(R.layout.custom_tab_instance, new LinearLayout(activity), false);
tv.setText(name);
tabTitle.add(name);
tabType.add(pinnedTimeline.type);
if (pinnedTimeline.type == Timeline.TimeLineEnum.REMOTE) {
tabTypeRemote.add(pinnedTimeline.remoteInstance.type);
} else {
tabTypeRemote.add(null);
}
tab.setCustomView(tv);
activityMainBinding.tabLayout.addTab(tab);
pinnedTimelineVisibleList.add(pinnedTimeline);
}
@ -225,7 +232,42 @@ public class PinnedTimelineHelper {
}
});
new TabLayoutMediator(activityMainBinding.tabLayout, activityMainBinding.viewPager,
(tab, position) -> tab.setText(tabTitle.get(position))
(tab, position) -> {
TabCustomViewBinding tabCustomViewBinding = TabCustomViewBinding.inflate(activity.getLayoutInflater());
tabCustomViewBinding.title.setText(tabTitle.get(position));
switch (tabType.get(position)) {
case LIST:
tabCustomViewBinding.icon.setImageResource(R.drawable.ic_tl_list);
break;
case TAG:
tabCustomViewBinding.icon.setImageResource(R.drawable.ic_tl_tag);
break;
case REMOTE:
switch (tabTypeRemote.get(position)) {
case PIXELFED:
tabCustomViewBinding.icon.setImageResource(R.drawable.pixelfed);
break;
case MASTODON:
tabCustomViewBinding.icon.setImageResource(R.drawable.mastodon_icon_item);
break;
case MISSKEY:
tabCustomViewBinding.icon.setImageResource(R.drawable.misskey);
break;
case NITTER:
tabCustomViewBinding.icon.setImageResource(R.drawable.nitter);
break;
case GNU:
tabCustomViewBinding.icon.setImageResource(R.drawable.ic_gnu_social);
break;
case PEERTUBE:
tabCustomViewBinding.icon.setImageResource(R.drawable.peertube_icon);
break;
}
break;
}
tab.setCustomView(tabCustomViewBinding.getRoot());
}
).attach();
activityMainBinding.tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {

View file

@ -86,10 +86,12 @@ public class PushHelper {
case "REPEAT_NOTIFICATIONS":
new Thread(() -> {
List<BaseAccount> accounts = new Account(context).getPushNotificationAccounts();
for (BaseAccount account : accounts) {
((Activity) context).runOnUiThread(() -> {
UnifiedPush.unregisterApp(context, account.user_id + "@" + account.instance);
});
if (accounts != null) {
for (BaseAccount account : accounts) {
((Activity) context).runOnUiThread(() -> {
UnifiedPush.unregisterApp(context, account.user_id + "@" + account.instance);
});
}
}
}).start();
new PeriodicWorkRequest.Builder(NotificationsWorker.class, 20, TimeUnit.MINUTES)

View file

@ -32,7 +32,6 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.client.endpoints.MastodonAccountsService;
import app.fedilab.android.client.entities.api.Filter;
import app.fedilab.android.client.entities.api.Notification;
@ -53,7 +52,7 @@ public class TimelineHelper {
.proxy(Helper.getProxy(context))
.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://" + MainActivity.currentInstance + "/api/v1/")
.baseUrl("https://" + BaseMainActivity.currentInstance + "/api/v1/")
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build();
@ -74,7 +73,7 @@ public class TimelineHelper {
if (!BaseMainActivity.filterFetched) {
MastodonAccountsService mastodonAccountsService = init(context);
List<Filter> filterList;
Call<List<Filter>> getFiltersCall = mastodonAccountsService.getFilters(MainActivity.currentToken);
Call<List<Filter>> getFiltersCall = mastodonAccountsService.getFilters(BaseMainActivity.currentToken);
if (getFiltersCall != null) {
try {
Response<List<Filter>> getFiltersResponse = getFiltersCall.execute();
@ -90,7 +89,7 @@ public class TimelineHelper {
}
//If there are filters:
if (BaseMainActivity.mainFilters != null && BaseMainActivity.mainFilters.size() > 0) {
if (BaseMainActivity.mainFilters != null && BaseMainActivity.mainFilters.size() > 0 && statuses != null && statuses.size() > 0) {
for (Filter filter : BaseMainActivity.mainFilters) {
if (filter.irreversible) { //Dealt by the server
continue;
@ -126,7 +125,9 @@ public class TimelineHelper {
}
}
}
statuses.removeAll(statusesToRemove);
if (statuses != null) {
statuses.removeAll(statusesToRemove);
}
return statuses;
}

View file

@ -38,7 +38,6 @@ import java.util.concurrent.TimeUnit;
import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R;
import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.client.endpoints.MastodonStatusesService;
import app.fedilab.android.client.entities.api.Attachment;
import app.fedilab.android.client.entities.api.Poll;
@ -132,7 +131,7 @@ public class PostMessageService extends IntentService {
totalBitRead = 0;
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
boolean watermark = sharedPreferences.getBoolean(context.getString(R.string.SET_WATERMARK), false);
String watermarkText = sharedPreferences.getString(context.getString(R.string.SET_WATERMARK_TEXT) + MainActivity.currentUserID + MainActivity.currentInstance, null);
String watermarkText = sharedPreferences.getString(context.getString(R.string.SET_WATERMARK_TEXT) + BaseMainActivity.currentUserID + BaseMainActivity.currentInstance, null);
for (int i = startingPosition; i < statuses.size(); i++) {
if (statuses.get(i).media_attachments != null && statuses.get(i).media_attachments.size() > 0) {
for (Attachment attachment : statuses.get(i).media_attachments) {

View file

@ -23,7 +23,7 @@ import android.database.sqlite.SQLiteOpenHelper;
public class Sqlite extends SQLiteOpenHelper {
public static final int DB_VERSION = 4;
public static final int DB_VERSION = 5;
public static final String DB_NAME = "fedilab_db";
//Table of owned accounts
@ -81,7 +81,9 @@ public class Sqlite extends SQLiteOpenHelper {
//Bottom menu
public static final String TABLE_BOTTOM_MENU = "TABLE_BOTTOM_MENU";
public static final String COL_BOTTOM_MENU = "BOTTOM_MENU";
//Tracking domains
public static final String TABLE_DOMAINS_TRACKING = "TABLE_DOMAINS_TRACKING";
public static final String COL_DOMAIN = "DOMAIN";
private static final String CREATE_TABLE_USER_ACCOUNT = "CREATE TABLE " + TABLE_USER_ACCOUNT + " ("
+ COL_USER_ID + " TEXT NOT NULL, "
@ -170,6 +172,10 @@ public class Sqlite extends SQLiteOpenHelper {
+ COL_USER_ID + " TEXT NOT NULL, "
+ COL_BOTTOM_MENU + " TEXT NOT NULL)";
private static final String CREATE_DOMAINS_TRACKING = "CREATE TABLE IF NOT EXISTS " + TABLE_DOMAINS_TRACKING + " ("
+ COL_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ COL_DOMAIN + " TEXT NOT NULL)";
public static SQLiteDatabase db;
private static Sqlite sInstance;
@ -198,6 +204,7 @@ public class Sqlite extends SQLiteOpenHelper {
db.execSQL(CREATE_TABLE_SCHEDULE_BOOST);
db.execSQL(CREATE_TABLE_QUICK_LOAD);
db.execSQL(CREATE_TABLE_BOTTOM_MENU);
db.execSQL(CREATE_DOMAINS_TRACKING);
}
@Override
@ -214,6 +221,8 @@ public class Sqlite extends SQLiteOpenHelper {
db.execSQL(CREATE_TABLE_BOTTOM_MENU);
case 3:
db.execSQL("ALTER TABLE " + TABLE_USER_ACCOUNT + " ADD COLUMN " + COL_ADMIN + " INTEGER NOT NULL DEFAULT 0");
case 4:
db.execSQL(CREATE_DOMAINS_TRACKING);
default:
break;
}

View file

@ -31,8 +31,8 @@ import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R;
import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.activities.ProfileActivity;
import app.fedilab.android.client.entities.api.Account;
import app.fedilab.android.databinding.DrawerFollowBinding;
@ -76,20 +76,16 @@ public class AccountFollowRequestAdapter extends RecyclerView.Adapter<RecyclerVi
holderFollow.binding.acceptButton.setVisibility(View.VISIBLE);
holderFollow.binding.title.setText(R.string.follow_request);
AccountsVM accountsVM = new ViewModelProvider((ViewModelStoreOwner) context).get(AccountsVM.class);
holderFollow.binding.acceptButton.setOnClickListener(v -> {
accountsVM.acceptFollow(MainActivity.currentInstance, MainActivity.currentToken, account.id)
.observe((LifecycleOwner) context, relationShip -> {
accountList.remove(position);
notifyItemRemoved(position);
});
});
holderFollow.binding.rejectButton.setOnClickListener(v -> {
accountsVM.rejectFollow(MainActivity.currentInstance, MainActivity.currentToken, account.id)
.observe((LifecycleOwner) context, relationShip -> {
accountList.remove(position);
notifyItemRemoved(position);
});
});
holderFollow.binding.acceptButton.setOnClickListener(v -> accountsVM.acceptFollow(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id)
.observe((LifecycleOwner) context, relationShip -> {
accountList.remove(position);
notifyItemRemoved(position);
}));
holderFollow.binding.rejectButton.setOnClickListener(v -> accountsVM.rejectFollow(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id)
.observe((LifecycleOwner) context, relationShip -> {
accountList.remove(position);
notifyItemRemoved(position);
}));
holderFollow.binding.avatar.setOnClickListener(v -> {
Intent intent = new Intent(context, ProfileActivity.class);
Bundle b = new Bundle();

View file

@ -39,8 +39,8 @@ import com.vanniktech.emoji.one.EmojiOneProvider;
import java.util.List;
import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R;
import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.client.entities.api.Announcement;
import app.fedilab.android.client.entities.api.Reaction;
import app.fedilab.android.databinding.DrawerAnnouncementBinding;
@ -133,9 +133,9 @@ public class AnnouncementAdapter extends RecyclerView.Adapter<AnnouncementAdapte
}
announcementsVM = new ViewModelProvider((ViewModelStoreOwner) context).get(AnnouncementsVM.class);
if (alreadyAdded) {
announcementsVM.removeReaction(MainActivity.currentInstance, MainActivity.currentToken, announcement.id, emojiStr);
announcementsVM.removeReaction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, announcement.id, emojiStr);
} else {
announcementsVM.addReaction(MainActivity.currentInstance, MainActivity.currentToken, announcement.id, emojiStr);
announcementsVM.addReaction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, announcement.id, emojiStr);
}
})
.build(holder.binding.fakeEdittext);
@ -150,12 +150,12 @@ public class AnnouncementAdapter extends RecyclerView.Adapter<AnnouncementAdapte
builder.setTitle(R.string.insert_emoji);
if (emojis != null && emojis.size() > 0) {
GridView gridView = new GridView(context);
gridView.setAdapter(new EmojiAdapter(emojis.get(MainActivity.currentInstance)));
gridView.setAdapter(new EmojiAdapter(emojis.get(BaseMainActivity.currentInstance)));
gridView.setNumColumns(5);
gridView.setOnItemClickListener((parent, view, index, id) -> {
String emojiStr = emojis.get(MainActivity.currentInstance).get(index).shortcode;
String url = emojis.get(MainActivity.currentInstance).get(index).url;
String static_url = emojis.get(MainActivity.currentInstance).get(index).static_url;
String emojiStr = emojis.get(BaseMainActivity.currentInstance).get(index).shortcode;
String url = emojis.get(BaseMainActivity.currentInstance).get(index).url;
String static_url = emojis.get(BaseMainActivity.currentInstance).get(index).static_url;
boolean alreadyAdded = false;
for (Reaction reaction : announcement.reactions) {
if (reaction.name.compareTo(emojiStr) == 0) {
@ -180,9 +180,9 @@ public class AnnouncementAdapter extends RecyclerView.Adapter<AnnouncementAdapte
}
announcementsVM = new ViewModelProvider((ViewModelStoreOwner) context).get(AnnouncementsVM.class);
if (alreadyAdded) {
announcementsVM.removeReaction(MainActivity.currentInstance, MainActivity.currentToken, announcement.id, emojiStr);
announcementsVM.removeReaction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, announcement.id, emojiStr);
} else {
announcementsVM.addReaction(MainActivity.currentInstance, MainActivity.currentToken, announcement.id, emojiStr);
announcementsVM.addReaction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, announcement.id, emojiStr);
}
alertDialogEmoji.dismiss();
});

View file

@ -84,7 +84,6 @@ import java.util.regex.Pattern;
import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R;
import app.fedilab.android.activities.ComposeActivity;
import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.client.entities.api.Attachment;
import app.fedilab.android.client.entities.api.Emoji;
import app.fedilab.android.client.entities.api.EmojiInstance;
@ -1233,10 +1232,10 @@ public class ComposeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
builder.setTitle(R.string.insert_emoji);
if (emojis != null && emojis.size() > 0) {
GridView gridView = new GridView(context);
gridView.setAdapter(new EmojiAdapter(emojis.get(MainActivity.currentInstance)));
gridView.setAdapter(new EmojiAdapter(emojis.get(BaseMainActivity.currentInstance)));
gridView.setNumColumns(5);
gridView.setOnItemClickListener((parent, view, position, id) -> {
holder.binding.content.getText().insert(holder.binding.content.getSelectionStart(), " :" + emojis.get(MainActivity.currentInstance).get(position).shortcode + ": ");
holder.binding.content.getText().insert(holder.binding.content.getSelectionStart(), " :" + emojis.get(BaseMainActivity.currentInstance).get(position).shortcode + ": ");
alertDialogEmoji.dismiss();
});
gridView.setPadding(paddingDp, paddingDp, paddingDp, paddingDp);

View file

@ -27,8 +27,8 @@ import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R;
import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.client.entities.api.Reaction;
import app.fedilab.android.databinding.DrawerReactionBinding;
import app.fedilab.android.helper.Helper;
@ -81,10 +81,10 @@ public class ReactionAdapter extends RecyclerView.Adapter<ReactionAdapter.Reacti
AnnouncementsVM announcementsVM = new ViewModelProvider((ViewModelStoreOwner) context).get(AnnouncementsVM.class);
holder.binding.reactionContainer.setOnClickListener(v -> {
if (reaction.me) {
announcementsVM.removeReaction(MainActivity.currentInstance, MainActivity.currentToken, announcementId, reaction.name);
announcementsVM.removeReaction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, announcementId, reaction.name);
reaction.me = false;
} else {
announcementsVM.addReaction(MainActivity.currentInstance, MainActivity.currentToken, announcementId, reaction.name);
announcementsVM.addReaction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, announcementId, reaction.name);
reaction.me = true;
}
notifyItemChanged(position);

View file

@ -96,7 +96,6 @@ import app.fedilab.android.R;
import app.fedilab.android.activities.ComposeActivity;
import app.fedilab.android.activities.ContextActivity;
import app.fedilab.android.activities.CustomSharingActivity;
import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.activities.MediaActivity;
import app.fedilab.android.activities.ProfileActivity;
import app.fedilab.android.activities.ReportActivity;
@ -257,8 +256,8 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
if (!remote) {
new Thread(() -> {
StatusCache statusCache = new StatusCache();
statusCache.instance = MainActivity.currentInstance;
statusCache.user_id = MainActivity.currentUserID;
statusCache.instance = BaseMainActivity.currentInstance;
statusCache.user_id = BaseMainActivity.currentUserID;
statusCache.status = statusToDeal;
statusCache.status_id = statusToDeal.id;
try {

View file

@ -258,28 +258,33 @@ public class FragmentLoginMain extends Fragment {
scopes,
Helper.WEBSITE_VALUE
).observe(requireActivity(), app -> {
client_idLogin = app.client_id;
client_secretLogin = app.client_secret;
String redirectUrl = MastodonHelper.authorizeURL(currentInstanceLogin, client_idLogin, ((LoginActivity) requireActivity()).requestedAdmin());
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity());
boolean embedded_browser = sharedpreferences.getBoolean(getString(R.string.SET_EMBEDDED_BROWSER), true);
if (embedded_browser) {
Intent i = new Intent(requireActivity(), WebviewConnectActivity.class);
i.putExtra("login_url", redirectUrl);
i.putExtra("requestedAdmin", ((LoginActivity) requireActivity()).requestedAdmin());
startActivity(i);
requireActivity().finish();
} else {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setData(Uri.parse(redirectUrl));
try {
startActivity(intent);
} catch (Exception e) {
Toasty.error(requireActivity(), getString(R.string.toast_error), Toast.LENGTH_LONG).show();
}
if (app != null) {
client_idLogin = app.client_id;
client_secretLogin = app.client_secret;
String redirectUrl = MastodonHelper.authorizeURL(currentInstanceLogin, client_idLogin, ((LoginActivity) requireActivity()).requestedAdmin());
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity());
boolean embedded_browser = sharedpreferences.getBoolean(getString(R.string.SET_EMBEDDED_BROWSER), true);
if (embedded_browser) {
Intent i = new Intent(requireActivity(), WebviewConnectActivity.class);
i.putExtra("login_url", redirectUrl);
i.putExtra("requestedAdmin", ((LoginActivity) requireActivity()).requestedAdmin());
startActivity(i);
requireActivity().finish();
} else {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setData(Uri.parse(redirectUrl));
try {
startActivity(intent);
} catch (Exception e) {
Toasty.error(requireActivity(), getString(R.string.toast_error), Toast.LENGTH_LONG).show();
}
}
} else {
Toasty.error(requireActivity(), getString(R.string.client_error), Toasty.LENGTH_SHORT).show();
}
});
}
}

View file

@ -22,8 +22,8 @@ import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceManager;
import androidx.preference.SwitchPreferenceCompat;
import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R;
import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.helper.Helper;
public class FragmentComposeSettings extends PreferenceFragmentCompat implements SharedPreferences.OnSharedPreferenceChangeListener {
@ -42,7 +42,7 @@ public class FragmentComposeSettings extends PreferenceFragmentCompat implements
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity());
EditTextPreference SET_WATERMARK_TEXT = findPreference(getString(R.string.SET_WATERMARK_TEXT));
if (SET_WATERMARK_TEXT != null) {
String val = sharedPreferences.getString(getString(R.string.SET_WATERMARK_TEXT) + MainActivity.currentUserID + MainActivity.currentInstance, sharedPreferences.getString(getString(R.string.SET_WATERMARK_TEXT), null));
String val = sharedPreferences.getString(getString(R.string.SET_WATERMARK_TEXT) + BaseMainActivity.currentUserID + BaseMainActivity.currentInstance, sharedPreferences.getString(getString(R.string.SET_WATERMARK_TEXT), null));
SET_WATERMARK_TEXT.setText(val);
}
}
@ -51,7 +51,7 @@ public class FragmentComposeSettings extends PreferenceFragmentCompat implements
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (key.equalsIgnoreCase(getString(R.string.SET_WATERMARK_TEXT))) {
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString(getString(R.string.SET_WATERMARK_TEXT) + MainActivity.currentUserID + MainActivity.currentInstance, sharedPreferences.getString(getString(R.string.SET_WATERMARK_TEXT), null));
editor.putString(getString(R.string.SET_WATERMARK_TEXT) + BaseMainActivity.currentUserID + BaseMainActivity.currentInstance, sharedPreferences.getString(getString(R.string.SET_WATERMARK_TEXT), null));
editor.apply();
}
}

View file

@ -43,7 +43,6 @@ import java.util.List;
import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R;
import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.client.entities.api.Notification;
import app.fedilab.android.client.entities.api.Notifications;
import app.fedilab.android.client.entities.api.Pagination;
@ -118,8 +117,8 @@ public class FragmentMastodonNotification extends Fragment implements Notificati
ViewGroup container, Bundle savedInstanceState) {
flagLoading = false;
instance = MainActivity.currentInstance;
user_id = MainActivity.currentUserID;
instance = BaseMainActivity.currentInstance;
user_id = BaseMainActivity.currentUserID;
idOfAddedNotifications = new ArrayList<>();
binding = FragmentPaginationBinding.inflate(inflater, container, false);
View root = binding.getRoot();
@ -287,7 +286,7 @@ public class FragmentMastodonNotification extends Fragment implements Notificati
return;
}
new Thread(() -> {
QuickLoad quickLoad = new QuickLoad(requireActivity()).getSavedValue(MainActivity.currentUserID, MainActivity.currentInstance, notificationType);
QuickLoad quickLoad = new QuickLoad(requireActivity()).getSavedValue(BaseMainActivity.currentUserID, BaseMainActivity.currentInstance, notificationType);
if (direction != FragmentMastodonTimeline.DIRECTION.REFRESH && !fetchingMissing && !binding.swipeContainer.isRefreshing() && direction == null && quickLoad != null && quickLoad.notifications != null && quickLoad.notifications.size() > 0) {
Notifications notifications = new Notifications();
notifications.notifications = quickLoad.notifications;

View file

@ -43,7 +43,6 @@ import java.util.List;
import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R;
import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.client.entities.api.Account;
import app.fedilab.android.client.entities.api.Attachment;
import app.fedilab.android.client.entities.api.Marker;
@ -198,8 +197,8 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
ViewGroup container, Bundle savedInstanceState) {
timelineType = Timeline.TimeLineEnum.HOME;
instance = MainActivity.currentInstance;
user_id = MainActivity.currentUserID;
instance = BaseMainActivity.currentInstance;
user_id = BaseMainActivity.currentUserID;
canBeFederated = true;
if (getArguments() != null) {
timelineType = (Timeline.TimeLineEnum) getArguments().get(Helper.ARG_TIMELINE_TYPE);
@ -236,7 +235,11 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
} else if (list_id != null) {
ident = "@l@" + list_id;
} else if (remoteInstance != null) {
ident = "@R@" + remoteInstance;
if (pinnedTimeline.remoteInstance.type == RemoteInstance.InstanceType.NITTER) {
ident = "@R@" + pinnedTimeline.remoteInstance.host;
} else {
ident = "@R@" + remoteInstance;
}
} else if (search != null) {
ident = "@S@" + search;
} else {
@ -589,13 +592,14 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
if (binding == null || getActivity() == null || !isAdded()) {
return;
}
QuickLoad quickLoad = new QuickLoad(requireActivity()).getSavedValue(MainActivity.currentUserID, MainActivity.currentInstance, timelineType, ident);
QuickLoad quickLoad = new QuickLoad(requireActivity()).getSavedValue(BaseMainActivity.currentUserID, BaseMainActivity.currentInstance, timelineType, ident);
if (!fetchingMissing && !binding.swipeContainer.isRefreshing() && direction == null && quickLoad != null && quickLoad.statuses != null && quickLoad.statuses.size() > 0) {
Statuses statuses = new Statuses();
statuses.statuses = quickLoad.statuses;
statuses.pagination = new Pagination();
statuses.pagination.max_id = quickLoad.statuses.get(quickLoad.statuses.size() - 1).id;
statuses.pagination.min_id = quickLoad.statuses.get(0).id;
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> initializeStatusesCommonView(statuses, quickLoad.position);
mainHandler.post(myRunnable);

View file

@ -213,7 +213,26 @@ public class FragmentNotificationContainer extends Fragment {
binding.tabLayout.setTabTextColors(ThemeHelper.getAttColor(requireActivity(), R.attr.mTextColor), ContextCompat.getColor(requireActivity(), R.color.cyanea_accent_dark_reference));
binding.tabLayout.setTabIconTint(ThemeHelper.getColorStateList(requireActivity()));
binding.tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
binding.viewpager.setCurrentItem(tab.getPosition());
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
Fragment fragment = getParentFragmentManager().findFragmentByTag("f" + binding.viewpager.getCurrentItem());
if (fragment instanceof FragmentMastodonNotification) {
FragmentMastodonNotification fragmentMastodonNotification = ((FragmentMastodonNotification) fragment);
fragmentMastodonNotification.scrollToTop();
}
}
});
new TabLayoutMediator(binding.tabLayout, binding.viewpager,
(tab, position) -> {
binding.viewpager.setCurrentItem(tab.getPosition(), true);
@ -254,33 +273,14 @@ public class FragmentNotificationContainer extends Fragment {
}
).attach();
binding.tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
binding.viewpager.setCurrentItem(tab.getPosition());
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
Fragment fragment = getChildFragmentManager().findFragmentByTag("f" + binding.viewpager.getCurrentItem());
if (fragment instanceof FragmentMastodonNotification) {
FragmentMastodonNotification fragmentMastodonNotification = ((FragmentMastodonNotification) fragment);
fragmentMastodonNotification.scrollToTop();
}
}
});
return binding.getRoot();
}
public void scrollToTop() {
if (binding != null) {
Fragment fragment = getChildFragmentManager().findFragmentByTag("f" + binding.viewpager.getCurrentItem());
Fragment fragment = getParentFragmentManager().findFragmentByTag("f" + binding.viewpager.getCurrentItem());
if (fragment instanceof FragmentMastodonNotification) {
((FragmentMastodonNotification) fragment).scrollToTop();
}

View file

@ -171,6 +171,7 @@ public class TimelinesVM extends AndroidViewModel {
statusesMutableLiveData = new MutableLiveData<>();
new Thread(() -> {
Call<Nitter> publicTlCall = mastodonTimelinesService.getNitter(accountsStr, max_position);
Statuses statuses = new Statuses();
if (publicTlCall != null) {
try {
@ -451,8 +452,10 @@ public class TimelinesVM extends AndroidViewModel {
Statuses statuses = null;
try {
statuses = statusCacheDAO.geStatuses(StatusCache.CacheEnum.HOME, instance, user_id, maxId, minId, sinceId);
if (statuses != null) {
statuses.statuses = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statuses.statuses);
List<Status> filteredStatuses = TimelineHelper.filterStatus(getApplication().getApplicationContext(), statuses.statuses, TimelineHelper.FilterTimeLineType.HOME);
statuses.statuses = SpannableHelper.convertStatus(getApplication().getApplicationContext(), filteredStatuses);
if (statuses.statuses != null && statuses.statuses.size() > 0) {
statuses.pagination = new Pagination();
statuses.pagination.min_id = statuses.statuses.get(0).id;

View file

@ -15,6 +15,8 @@ package app.fedilab.android.webview;
* see <http://www.gnu.org/licenses>. */
import static app.fedilab.android.client.entities.app.DomainsBlock.trackingDomains;
import android.app.Activity;
import android.graphics.Bitmap;
import android.net.http.SslError;
@ -59,7 +61,7 @@ public class FedilabWebViewClient extends WebViewClient {
@Override
public WebResourceResponse shouldInterceptRequest(final WebView view, String url) {
if (WebviewActivity.trackingDomains != null) {
if (trackingDomains != null) {
URI uri;
try {
uri = new URI(url);
@ -67,11 +69,11 @@ public class FedilabWebViewClient extends WebViewClient {
if (domain != null) {
domain = domain.startsWith("www.") ? domain.substring(4) : domain;
}
if (domain != null && WebviewActivity.trackingDomains.contains(domain)) {
if (domain != null && trackingDomains.contains(domain)) {
if (activity instanceof WebviewActivity) {
count++;
domains.add(url);
// ((WebviewActivity) activity).setCount(activity, String.valueOf(count));
((WebviewActivity) activity).setCount(activity, String.valueOf(count));
}
ByteArrayInputStream nothing = new ByteArrayInputStream("".getBytes());
return new WebResourceResponse("text/plain", "utf-8", nothing);
@ -86,11 +88,11 @@ public class FedilabWebViewClient extends WebViewClient {
if (domain != null) {
domain = domain.startsWith("www.") ? domain.substring(4) : domain;
}
if (domain != null && WebviewActivity.trackingDomains.contains(domain)) {
if (domain != null && trackingDomains.contains(domain)) {
if (activity instanceof WebviewActivity) {
count++;
domains.add(url);
// ((WebviewActivity) activity).setCount(activity, String.valueOf(count));
((WebviewActivity) activity).setCount(activity, String.valueOf(count));
}
ByteArrayInputStream nothing = new ByteArrayInputStream("".getBytes());
return new WebResourceResponse("text/plain", "utf-8", nothing);

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:drawable="@drawable/ic_block_script"
android:gravity="center" />
<item
android:id="@+id/ic_block_count"
android:drawable="@color/transparent" />
</layer-list>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM4,12c0,-4.42 3.58,-8 8,-8 1.85,0 3.55,0.63 4.9,1.69L5.69,16.9C4.63,15.55 4,13.85 4,12zM12,20c-1.85,0 -3.55,-0.63 -4.9,-1.69L18.31,7.1C19.37,8.45 20,10.15 20,12c0,4.42 -3.58,8 -8,8z" />
</vector>

View file

@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:autoMirrored="true"
android:tint="#FFFFFF"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M3,13h2v-2L3,11v2zM3,17h2v-2L3,15v2zM3,9h2L5,7L3,7v2zM7,13h14v-2L7,11v2zM7,17h14v-2L7,15v2zM7,7v2h14L21,7L7,7z" />
</vector>

View file

@ -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="M20,10L20,8h-4L16,4h-2v4h-4L10,4L8,4v4L4,8v2h4v4L4,14v2h4v4h2v-4h4v4h2v-4h4v-2h-4v-4h4zM14,14h-4v-4h4v4z" />
</vector>

View file

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/icon"
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_gravity="center" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="4dp"
android:ellipsize="end"
android:maxWidth="150dp"
android:singleLine="true"
android:textSize="16sp" />
</androidx.appcompat.widget.LinearLayoutCompat>

View file

@ -3,7 +3,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_block"
android:icon="@drawable/ic_baseline_block_24"
android:icon="@drawable/browser_calls_blocked"
android:title="@string/calls_blocked"
app:showAsAction="always" />
<item

View file

@ -9,7 +9,7 @@
<dimen name="drawer_padding">2dp</dimen>
<dimen name="layout_height_header">180dp</dimen>
<dimen name="badge_count_textsize">12sp</dimen>
<dimen name="editor_size">35dp</dimen>
<dimen name="top_tool_icon_width">50dp</dimen>
</resources>

View file

@ -0,0 +1,2 @@
- Some bug fixes
- Improve pinned timelines