Fix weak reference to account destroyed in background

This commit is contained in:
Thomas 2022-06-19 17:26:39 +02:00
parent eaf9ac87af
commit 5d2f7c3475
22 changed files with 403 additions and 365 deletions

View file

@ -108,6 +108,7 @@ import app.fedilab.android.client.entities.api.Instance;
import app.fedilab.android.client.entities.api.MastodonList; import app.fedilab.android.client.entities.api.MastodonList;
import app.fedilab.android.client.entities.api.Status; import app.fedilab.android.client.entities.api.Status;
import app.fedilab.android.client.entities.app.Account; 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.BottomMenu;
import app.fedilab.android.client.entities.app.Pinned; import app.fedilab.android.client.entities.app.Pinned;
import app.fedilab.android.client.entities.app.PinnedTimeline; import app.fedilab.android.client.entities.app.PinnedTimeline;
@ -142,7 +143,7 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
public static boolean show_boosts, show_replies, show_art_nsfw; public static boolean show_boosts, show_replies, show_art_nsfw;
public static String regex_home, regex_local, regex_public; public static String regex_home, regex_local, regex_public;
Fragment currentFragment; Fragment currentFragment;
private Account account; private BaseAccount account;
private AppBarConfiguration mAppBarConfiguration; private AppBarConfiguration mAppBarConfiguration;
private ActivityMainBinding binding; private ActivityMainBinding binding;
private Pinned pinned; private Pinned pinned;
@ -242,7 +243,7 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
instanceIntent = extras.getString(Helper.PREF_INSTANCE); instanceIntent = extras.getString(Helper.PREF_INSTANCE);
if (extras.getInt(Helper.INTENT_ACTION) == Helper.NOTIFICATION_INTENT) { if (extras.getInt(Helper.INTENT_ACTION) == Helper.NOTIFICATION_INTENT) {
try { try {
Account account = new Account(BaseMainActivity.this).getUniqAccount(userIdIntent, instanceIntent); BaseAccount account = new Account(BaseMainActivity.this).getUniqAccount(userIdIntent, instanceIntent);
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(BaseMainActivity.this); SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(BaseMainActivity.this);
headerMenuOpen = false; headerMenuOpen = false;
Toasty.info(BaseMainActivity.this, getString(R.string.toast_account_changed, "@" + account.mastodon_account.acct + "@" + account.instance), Toasty.LENGTH_LONG).show(); Toasty.info(BaseMainActivity.this, getString(R.string.toast_account_changed, "@" + account.mastodon_account.acct + "@" + account.instance), Toasty.LENGTH_LONG).show();
@ -379,7 +380,7 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
headerMainBinding.ownerAccounts.setImageResource(R.drawable.ic_baseline_arrow_drop_up_24); headerMainBinding.ownerAccounts.setImageResource(R.drawable.ic_baseline_arrow_drop_up_24);
new Thread(() -> { new Thread(() -> {
try { try {
List<Account> accounts = new Account(BaseMainActivity.this).getAll(); List<BaseAccount> accounts = new Account(BaseMainActivity.this).getAll();
Handler mainHandler = new Handler(Looper.getMainLooper()); Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> { Runnable myRunnable = () -> {
binding.navView.getMenu().clear(); binding.navView.getMenu().clear();
@ -390,7 +391,7 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
SubMenu currentSubmenu = null; SubMenu currentSubmenu = null;
String lastInstance = ""; String lastInstance = "";
if (accounts != null) { if (accounts != null) {
for (final Account account : accounts) { for (final BaseAccount account : accounts) {
if (!currentToken.equalsIgnoreCase(account.token)) { if (!currentToken.equalsIgnoreCase(account.token)) {
if (!lastInstance.trim().equalsIgnoreCase(account.instance.trim())) { if (!lastInstance.trim().equalsIgnoreCase(account.instance.trim())) {
lastInstance = account.instance.toUpperCase(); lastInstance = account.instance.toUpperCase();
@ -682,12 +683,11 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
.observe(BaseMainActivity.this, account1 -> { .observe(BaseMainActivity.this, account1 -> {
//Initialize static var //Initialize static var
getCurrentAccount(BaseMainActivity.this); getCurrentAccount(BaseMainActivity.this);
//Set the Mastodon account
Helper.setMastodonAccount(account1);
new Thread(() -> { new Thread(() -> {
try { try {
//Update account in db //Update account in db
new Account(BaseMainActivity.this).insertOrUpdate(getCurrentAccount(BaseMainActivity.this)); new Account(BaseMainActivity.this).insertOrUpdate(getCurrentAccount(BaseMainActivity.this));
getCurrentAccount(BaseMainActivity.this);
} catch (DBException e) { } catch (DBException e) {
e.printStackTrace(); e.printStackTrace();
} }

View file

@ -75,7 +75,7 @@ public class MainApplication extends MultiDexApplication {
super.attachBaseContext(base); super.attachBaseContext(base);
MultiDex.install(MainApplication.this); MultiDex.install(MainApplication.this);
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(MainApplication.this); SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(MainApplication.this);
boolean send_crash_reports = sharedpreferences.getBoolean(getString(R.string.SET_SEND_CRASH_REPORTS), false); boolean send_crash_reports = sharedpreferences.getBoolean(getString(R.string.SET_SEND_CRASH_REPORTS), true);
if (send_crash_reports) { if (send_crash_reports) {
ACRA.init(this, new CoreConfigurationBuilder() ACRA.init(this, new CoreConfigurationBuilder()
//core configuration: //core configuration:

View file

@ -70,6 +70,7 @@ import app.fedilab.android.client.entities.api.Mention;
import app.fedilab.android.client.entities.api.ScheduledStatus; import app.fedilab.android.client.entities.api.ScheduledStatus;
import app.fedilab.android.client.entities.api.Status; import app.fedilab.android.client.entities.api.Status;
import app.fedilab.android.client.entities.app.Account; import app.fedilab.android.client.entities.app.Account;
import app.fedilab.android.client.entities.app.BaseAccount;
import app.fedilab.android.client.entities.app.StatusDraft; import app.fedilab.android.client.entities.app.StatusDraft;
import app.fedilab.android.databinding.ActivityPaginationBinding; import app.fedilab.android.databinding.ActivityPaginationBinding;
import app.fedilab.android.databinding.PopupContactBinding; import app.fedilab.android.databinding.PopupContactBinding;
@ -122,7 +123,7 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
} }
}; };
private ActivityPaginationBinding binding; private ActivityPaginationBinding binding;
private Account account; private BaseAccount account;
private String instance, token; private String instance, token;
private Uri photoFileUri; private Uri photoFileUri;
private ScheduledStatus scheduledStatus; private ScheduledStatus scheduledStatus;

View file

@ -104,7 +104,7 @@ public class Status implements Serializable, Cloneable {
public transient boolean isFocused = false; public transient boolean isFocused = false;
public transient boolean setCursorToEnd = false; public transient boolean setCursorToEnd = false;
public transient int cursorPosition = 0; public transient int cursorPosition = 0;
public transient boolean submitted = false;
@NonNull @NonNull
public Object clone() throws CloneNotSupportedException { public Object clone() throws CloneNotSupportedException {
return super.clone(); return super.clone();

View file

@ -20,7 +20,6 @@ import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.annotations.SerializedName;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
@ -37,36 +36,10 @@ import app.fedilab.android.sqlite.Sqlite;
* Accounts details are serialized and can be for different softwares * Accounts details are serialized and can be for different softwares
* The type of the software is stored in api field * The type of the software is stored in api field
*/ */
public class Account implements Serializable { public class Account extends BaseAccount implements Serializable {
private final SQLiteDatabase db; private final SQLiteDatabase db;
@SerializedName("user_id")
public String user_id;
@SerializedName("instance")
public String instance;
@SerializedName("api")
public API api;
@SerializedName("software")
public String software;
@SerializedName("token")
public String token;
@SerializedName("refresh_token")
public String refresh_token;
@SerializedName("token_validity")
public long token_validity;
@SerializedName("client_id")
public String client_id;
@SerializedName("client_secret")
public String client_secret;
@SerializedName("created_at")
public Date created_at;
@SerializedName("updated_at")
public Date updated_at;
@SerializedName("mastodon_account")
public app.fedilab.android.client.entities.api.Account mastodon_account;
@SerializedName("admin")
public boolean admin;
private transient Context context; private transient Context context;
@ -81,9 +54,9 @@ public class Account implements Serializable {
} }
/** /**
* Serialized a Mastodon Account class * Serialized a Mastodon BaseAccount class
* *
* @param mastodon_account {@link app.fedilab.android.client.entities.api.Account} to serialize * @param mastodon_account {@link BaseAccount} to serialize
* @return String serialized account * @return String serialized account
*/ */
public static String mastodonAccountToStringStorage(app.fedilab.android.client.entities.api.Account mastodon_account) { public static String mastodonAccountToStringStorage(app.fedilab.android.client.entities.api.Account mastodon_account) {
@ -96,7 +69,7 @@ public class Account implements Serializable {
} }
/** /**
* Unserialized a Mastodon Account * Unserialized a Mastodon BaseAccount
* *
* @param serializedAccount String serialized account * @param serializedAccount String serialized account
* @return {@link app.fedilab.android.client.entities.api.Account} * @return {@link app.fedilab.android.client.entities.api.Account}
@ -111,11 +84,11 @@ public class Account implements Serializable {
} }
/** /**
* Returns all Account in db * Returns all BaseAccount in db
* *
* @return Account List<Account> * @return BaseAccount List<BaseAccount>
*/ */
public List<Account> getPushNotificationAccounts() { public List<BaseAccount> getPushNotificationAccounts() {
try { try {
Cursor c = db.query(Sqlite.TABLE_USER_ACCOUNT, null, "(" + Sqlite.COL_API + " = 'MASTODON' OR " + Sqlite.COL_API + " = 'PLEROMA' OR " + Sqlite.COL_API + " = 'FRIENDICA') AND " + Sqlite.COL_TOKEN + " IS NOT NULL", null, null, null, Sqlite.COL_INSTANCE + " ASC", null); Cursor c = db.query(Sqlite.TABLE_USER_ACCOUNT, null, "(" + Sqlite.COL_API + " = 'MASTODON' OR " + Sqlite.COL_API + " = 'PLEROMA' OR " + Sqlite.COL_API + " = 'FRIENDICA') AND " + Sqlite.COL_TOKEN + " IS NOT NULL", null, null, null, Sqlite.COL_INSTANCE + " ASC", null);
@ -128,11 +101,11 @@ public class Account implements Serializable {
/** /**
* Insert or update a user * Insert or update a user
* *
* @param account {@link Account} * @param account {@link BaseAccount}
* @return long - db id * @return long - db id
* @throws DBException exception with database * @throws DBException exception with database
*/ */
public long insertOrUpdate(Account account) throws DBException { public long insertOrUpdate(BaseAccount account) throws DBException {
if (db == null) { if (db == null) {
throw new DBException("db is null. Wrong initialization."); throw new DBException("db is null. Wrong initialization.");
} }
@ -149,11 +122,11 @@ public class Account implements Serializable {
/** /**
* Insert an account in db * Insert an account in db
* *
* @param account {@link Account} * @param account {@link BaseAccount}
* @return long - db id * @return long - db id
* @throws DBException exception with database * @throws DBException exception with database
*/ */
private long insertAccount(Account account) throws DBException { private long insertAccount(BaseAccount account) throws DBException {
if (db == null) { if (db == null) {
throw new DBException("db is null. Wrong initialization."); throw new DBException("db is null. Wrong initialization.");
} }
@ -185,11 +158,11 @@ public class Account implements Serializable {
/** /**
* Update an account in db * Update an account in db
* *
* @param account {@link Account} * @param account {@link BaseAccount}
* @return long - db id * @return long - db id
* @throws DBException exception with database * @throws DBException exception with database
*/ */
private long updateAccount(Account account) throws DBException { private long updateAccount(BaseAccount account) throws DBException {
if (db == null) { if (db == null) {
throw new DBException("db is null. Wrong initialization."); throw new DBException("db is null. Wrong initialization.");
} }
@ -223,11 +196,11 @@ public class Account implements Serializable {
/** /**
* Check if a user exists in db * Check if a user exists in db
* *
* @param account Account {@link Account} * @param account BaseAccount {@link BaseAccount}
* @return boolean - user exists * @return boolean - user exists
* @throws DBException Exception * @throws DBException Exception
*/ */
public boolean accountExist(Account account) throws DBException { public boolean accountExist(BaseAccount account) throws DBException {
if (db == null) { if (db == null) {
throw new DBException("db is null. Wrong initialization."); throw new DBException("db is null. Wrong initialization.");
} }
@ -240,13 +213,13 @@ public class Account implements Serializable {
} }
/** /**
* Returns an Account by userId and instance * Returns an BaseAccount by userId and instance
* *
* @param userId String * @param userId String
* @param instance String * @param instance String
* @return Account {@link Account} * @return BaseAccount {@link BaseAccount}
*/ */
public Account getUniqAccount(String userId, String instance) throws DBException { public BaseAccount getUniqAccount(String userId, String instance) throws DBException {
if (db == null) { if (db == null) {
throw new DBException("db is null. Wrong initialization."); throw new DBException("db is null. Wrong initialization.");
} }
@ -259,12 +232,12 @@ public class Account implements Serializable {
} }
/** /**
* Returns an Account by token * Returns an BaseAccount by token
* *
* @param token String * @param token String
* @return Account {@link Account} * @return BaseAccount {@link BaseAccount}
*/ */
public Account getAccountByToken(String token) throws DBException { public BaseAccount getAccountByToken(String token) throws DBException {
if (db == null) { if (db == null) {
throw new DBException("db is null. Wrong initialization."); throw new DBException("db is null. Wrong initialization.");
} }
@ -277,11 +250,11 @@ public class Account implements Serializable {
} }
/** /**
* Returns authenticated Account * Returns authenticated BaseAccount
* *
* @return Account {@link Account} * @return BaseAccount {@link BaseAccount}
*/ */
public Account getConnectedAccount() throws DBException { public BaseAccount getConnectedAccount() throws DBException {
if (db == null) { if (db == null) {
throw new DBException("db is null. Wrong initialization."); throw new DBException("db is null. Wrong initialization.");
} }
@ -297,9 +270,9 @@ public class Account implements Serializable {
/** /**
* Returns all accounts that allows cross-account actions * Returns all accounts that allows cross-account actions
* *
* @return Account List<{@link Account}> * @return BaseAccount List<{@link BaseAccount}>
*/ */
public List<Account> getCrossAccounts() throws DBException { public List<BaseAccount> getCrossAccounts() throws DBException {
if (db == null) { if (db == null) {
throw new DBException("db is null. Wrong initialization."); throw new DBException("db is null. Wrong initialization.");
} }
@ -314,9 +287,9 @@ public class Account implements Serializable {
/** /**
* Returns all accounts * Returns all accounts
* *
* @return Account List<{@link Account}> * @return BaseAccount List<{@link BaseAccount}>
*/ */
public List<Account> getAll() throws DBException { public List<BaseAccount> getAll() throws DBException {
if (db == null) { if (db == null) {
throw new DBException("db is null. Wrong initialization."); throw new DBException("db is null. Wrong initialization.");
} }
@ -331,9 +304,9 @@ public class Account implements Serializable {
/** /**
* Returns last used account * Returns last used account
* *
* @return Account {@link Account} * @return BaseAccount {@link BaseAccount}
*/ */
public Account getLastUsedAccount() throws DBException { public BaseAccount getLastUsedAccount() throws DBException {
if (db == null) { if (db == null) {
throw new DBException("db is null. Wrong initialization."); throw new DBException("db is null. Wrong initialization.");
} }
@ -348,10 +321,10 @@ public class Account implements Serializable {
/** /**
* Remove an account from db * Remove an account from db
* *
* @param account {@link Account} * @param account {@link BaseAccount}
* @return int * @return int
*/ */
public int removeUser(Account account) throws DBException { public int removeUser(BaseAccount account) throws DBException {
if (db == null) { if (db == null) {
throw new DBException("db is null. Wrong initialization."); throw new DBException("db is null. Wrong initialization.");
} }
@ -360,15 +333,15 @@ public class Account implements Serializable {
} }
private List<Account> cursorToListUser(Cursor c) { private List<BaseAccount> cursorToListUser(Cursor c) {
//No element found //No element found
if (c.getCount() == 0) { if (c.getCount() == 0) {
c.close(); c.close();
return null; return null;
} }
List<Account> accountList = new ArrayList<>(); List<BaseAccount> accountList = new ArrayList<>();
while (c.moveToNext()) { while (c.moveToNext()) {
Account account = convertCursorToAccount(c); BaseAccount account = convertCursorToAccount(c);
//We don't add in the list the current connected account //We don't add in the list the current connected account
if (!account.token.equalsIgnoreCase(BaseMainActivity.currentToken)) { if (!account.token.equalsIgnoreCase(BaseMainActivity.currentToken)) {
accountList.add(account); accountList.add(account);
@ -379,15 +352,15 @@ public class Account implements Serializable {
return accountList; return accountList;
} }
private List<Account> cursorToListUserWithOwner(Cursor c) { private List<BaseAccount> cursorToListUserWithOwner(Cursor c) {
//No element found //No element found
if (c.getCount() == 0) { if (c.getCount() == 0) {
c.close(); c.close();
return null; return null;
} }
List<Account> accountList = new ArrayList<>(); List<BaseAccount> accountList = new ArrayList<>();
while (c.moveToNext()) { while (c.moveToNext()) {
Account account = convertCursorToAccount(c); BaseAccount account = convertCursorToAccount(c);
//We don't add in the list the current connected account //We don't add in the list the current connected account
accountList.add(account); accountList.add(account);
} }
@ -397,11 +370,11 @@ public class Account implements Serializable {
} }
/*** /***
* Method to hydrate an Account from database * Method to hydrate an BaseAccount from database
* @param c Cursor * @param c Cursor
* @return Account {@link Account} * @return BaseAccount {@link BaseAccount}
*/ */
private Account cursorToUser(Cursor c) { private BaseAccount cursorToUser(Cursor c) {
//No element found //No element found
if (c.getCount() == 0) { if (c.getCount() == 0) {
c.close(); c.close();
@ -410,7 +383,7 @@ public class Account implements Serializable {
//Take the first element //Take the first element
c.moveToFirst(); c.moveToFirst();
//New user //New user
Account account = convertCursorToAccount(c); BaseAccount account = convertCursorToAccount(c);
//Close the cursor //Close the cursor
c.close(); c.close();
return account; return account;
@ -420,10 +393,10 @@ public class Account implements Serializable {
* Read cursor and hydrate without closing it * Read cursor and hydrate without closing it
* *
* @param c - Cursor * @param c - Cursor
* @return Account * @return BaseAccount
*/ */
private Account convertCursorToAccount(Cursor c) { private BaseAccount convertCursorToAccount(Cursor c) {
Account account = new Account(); BaseAccount account = new BaseAccount();
account.user_id = c.getString(c.getColumnIndexOrThrow(Sqlite.COL_USER_ID)); account.user_id = c.getString(c.getColumnIndexOrThrow(Sqlite.COL_USER_ID));
account.client_id = c.getString(c.getColumnIndexOrThrow(Sqlite.COL_APP_CLIENT_ID)); account.client_id = c.getString(c.getColumnIndexOrThrow(Sqlite.COL_APP_CLIENT_ID));
account.client_secret = c.getString(c.getColumnIndexOrThrow(Sqlite.COL_APP_CLIENT_SECRET)); account.client_secret = c.getString(c.getColumnIndexOrThrow(Sqlite.COL_APP_CLIENT_SECRET));

View file

@ -0,0 +1,57 @@
package app.fedilab.android.client.entities.app;
/* 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 com.google.gson.annotations.SerializedName;
import java.io.Serializable;
import java.util.Date;
/**
* Class that manages Accounts from database
* Accounts details are serialized and can be for different softwares
* The type of the software is stored in api field
*/
public class BaseAccount implements Serializable {
@SerializedName("user_id")
public String user_id;
@SerializedName("instance")
public String instance;
@SerializedName("api")
public Account.API api;
@SerializedName("software")
public String software;
@SerializedName("token")
public String token;
@SerializedName("refresh_token")
public String refresh_token;
@SerializedName("token_validity")
public long token_validity;
@SerializedName("client_id")
public String client_id;
@SerializedName("client_secret")
public String client_secret;
@SerializedName("created_at")
public Date created_at;
@SerializedName("updated_at")
public Date updated_at;
@SerializedName("mastodon_account")
public app.fedilab.android.client.entities.api.Account mastodon_account;
@SerializedName("admin")
public boolean admin;
}

View file

@ -118,7 +118,7 @@ public class BottomMenu implements Serializable {
return bottomMenu.bottom_menu.get(position).item_menu_type; return bottomMenu.bottom_menu.get(position).item_menu_type;
} }
public BottomMenu hydrate(Account account, BottomNavigationView bottomNavigationView) { public BottomMenu hydrate(BaseAccount account, BottomNavigationView bottomNavigationView) {
bottomNavigationView.getMenu().clear(); bottomNavigationView.getMenu().clear();
BottomMenu bottomMenu = null; BottomMenu bottomMenu = null;
try { try {
@ -237,7 +237,7 @@ public class BottomMenu implements Serializable {
* @param account Account * @param account Account
* @return BottomMenu - {@link BottomMenu} * @return BottomMenu - {@link BottomMenu}
*/ */
public BottomMenu getAllBottomMenu(Account account) throws DBException { public BottomMenu getAllBottomMenu(BaseAccount account) throws DBException {
if (db == null) { if (db == null) {
throw new DBException("db is null. Wrong initialization."); throw new DBException("db is null. Wrong initialization.");
} }
@ -256,7 +256,7 @@ public class BottomMenu implements Serializable {
* @param account Account * @param account Account
* @return BottomMenu - {@link BottomMenu} * @return BottomMenu - {@link BottomMenu}
*/ */
public BottomMenu getBottomMenu(Account account) throws DBException { public BottomMenu getBottomMenu(BaseAccount account) throws DBException {
if (db == null) { if (db == null) {
throw new DBException("db is null. Wrong initialization."); throw new DBException("db is null. Wrong initialization.");
} }

View file

@ -149,7 +149,7 @@ public class Pinned implements Serializable {
* @param account Account * @param account Account
* @return Pinned - {@link Pinned} * @return Pinned - {@link Pinned}
*/ */
public Pinned getPinned(Account account) throws DBException { public Pinned getPinned(BaseAccount account) throws DBException {
if (db == null) { if (db == null) {
throw new DBException("db is null. Wrong initialization."); throw new DBException("db is null. Wrong initialization.");
} }
@ -178,7 +178,7 @@ public class Pinned implements Serializable {
* @param account Account * @param account Account
* @return Pinned - {@link Pinned} * @return Pinned - {@link Pinned}
*/ */
public Pinned getAllPinned(Account account) throws DBException { public Pinned getAllPinned(BaseAccount account) throws DBException {
if (db == null) { if (db == null) {
throw new DBException("db is null. Wrong initialization."); throw new DBException("db is null. Wrong initialization.");
} }

View file

@ -165,6 +165,7 @@ public class QuickLoad {
* @return long - db id * @return long - db id
* @throws DBException exception with database * @throws DBException exception with database
*/ */
@SuppressWarnings("UnusedReturnValue")
public long deleteForAllAccount() throws DBException { public long deleteForAllAccount() throws DBException {
if (db == null) { if (db == null) {
throw new DBException("db is null. Wrong initialization."); throw new DBException("db is null. Wrong initialization.");
@ -184,7 +185,8 @@ public class QuickLoad {
* @return long - db id * @return long - db id
* @throws DBException exception with database * @throws DBException exception with database
*/ */
public long deleteForAccount(Account account) throws DBException { @SuppressWarnings("UnusedReturnValue")
public long deleteForAccount(BaseAccount account) throws DBException {
if (db == null) { if (db == null) {
throw new DBException("db is null. Wrong initialization."); throw new DBException("db is null. Wrong initialization.");
} }
@ -202,11 +204,11 @@ public class QuickLoad {
/** /**
* Update a status in quickload * Update a status in quickload
* *
* @param account {@link Account} * @param account {@link BaseAccount}
* @param newStatus - Status * @param newStatus - Status
* @throws DBException exception with database * @throws DBException exception with database
*/ */
public void updateStatus(Account account, Status newStatus) throws DBException { public void updateStatus(BaseAccount account, Status newStatus) throws DBException {
if (db == null) { if (db == null) {
throw new DBException("db is null. Wrong initialization."); throw new DBException("db is null. Wrong initialization.");
} }
@ -279,7 +281,7 @@ public class QuickLoad {
* @param id - String id of the status * @param id - String id of the status
* @throws DBException exception with database * @throws DBException exception with database
*/ */
public void deleteStatus(Account account, String id) throws DBException { public void deleteStatus(BaseAccount account, String id) throws DBException {
if (db == null) { if (db == null) {
throw new DBException("db is null. Wrong initialization."); throw new DBException("db is null. Wrong initialization.");
} }
@ -375,7 +377,7 @@ public class QuickLoad {
* @param ident - the name for pinned timeline * @param ident - the name for pinned timeline
* @return SavedValues * @return SavedValues
*/ */
public QuickLoad getSavedValue(Account account, Timeline.TimeLineEnum timeLineType, String ident) { public QuickLoad getSavedValue(BaseAccount account, Timeline.TimeLineEnum timeLineType, String ident) {
if (cannotBeStored(timeLineType)) { if (cannotBeStored(timeLineType)) {
return null; return null;
} }
@ -455,7 +457,7 @@ public class QuickLoad {
* @return Statuses * @return Statuses
* @throws DBException - throws a db exception * @throws DBException - throws a db exception
*/ */
private QuickLoad get(String slug, Account account) throws DBException { private QuickLoad get(String slug, BaseAccount account) throws DBException {
if (db == null) { if (db == null) {
throw new DBException("db is null. Wrong initialization."); throw new DBException("db is null. Wrong initialization.");
} }

View file

@ -169,7 +169,7 @@ public class ScheduledBoost implements Serializable {
* @param account Account * @param account Account
* @return List<ScheduledBoost> - List of {@link ScheduledBoost} * @return List<ScheduledBoost> - List of {@link ScheduledBoost}
*/ */
public List<ScheduledBoost> getScheduled(Account account) throws DBException { public List<ScheduledBoost> getScheduled(BaseAccount account) throws DBException {
if (db == null) { if (db == null) {
throw new DBException("db is null. Wrong initialization."); throw new DBException("db is null. Wrong initialization.");
} }

View file

@ -238,7 +238,7 @@ public class StatusCache {
* @return long - db id * @return long - db id
* @throws DBException exception with database * @throws DBException exception with database
*/ */
public long deleteForAccount(Account account) throws DBException { public long deleteForAccount(BaseAccount account) throws DBException {
if (db == null) { if (db == null) {
throw new DBException("db is null. Wrong initialization."); throw new DBException("db is null. Wrong initialization.");
} }

View file

@ -278,7 +278,7 @@ public class StatusDraft implements Serializable {
* @param account Account * @param account Account
* @return List<StatusDraft> - List of {@link StatusDraft} * @return List<StatusDraft> - List of {@link StatusDraft}
*/ */
public List<StatusDraft> geStatusDraftList(Account account) throws DBException { public List<StatusDraft> geStatusDraftList(BaseAccount account) throws DBException {
if (db == null) { if (db == null) {
throw new DBException("db is null. Wrong initialization."); throw new DBException("db is null. Wrong initialization.");
} }
@ -297,7 +297,7 @@ public class StatusDraft implements Serializable {
* @param account Account * @param account Account
* @return List<StatusDraft> - List of {@link StatusDraft} * @return List<StatusDraft> - List of {@link StatusDraft}
*/ */
public List<StatusDraft> geStatusDraftScheduledList(Account account) throws DBException { public List<StatusDraft> geStatusDraftScheduledList(BaseAccount account) throws DBException {
if (db == null) { if (db == null) {
throw new DBException("db is null. Wrong initialization."); throw new DBException("db is null. Wrong initialization.");
} }

View file

@ -40,6 +40,7 @@ import app.fedilab.android.client.endpoints.MastodonSearchService;
import app.fedilab.android.client.entities.api.Results; import app.fedilab.android.client.entities.api.Results;
import app.fedilab.android.client.entities.api.Status; import app.fedilab.android.client.entities.api.Status;
import app.fedilab.android.client.entities.app.Account; import app.fedilab.android.client.entities.app.Account;
import app.fedilab.android.client.entities.app.BaseAccount;
import app.fedilab.android.exception.DBException; import app.fedilab.android.exception.DBException;
import app.fedilab.android.ui.drawer.AccountsSearchAdapter; import app.fedilab.android.ui.drawer.AccountsSearchAdapter;
import app.fedilab.android.viewmodel.mastodon.AccountsVM; import app.fedilab.android.viewmodel.mastodon.AccountsVM;
@ -67,14 +68,14 @@ public class CrossActionHelper {
final SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context); final SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context);
new Thread(() -> { new Thread(() -> {
try { try {
List<Account> accounts = new Account(context).getCrossAccounts(); List<BaseAccount> accounts = new Account(context).getCrossAccounts();
if (accounts.size() == 1) { if (accounts.size() == 1) {
Handler mainHandler = new Handler(Looper.getMainLooper()); Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> fetchRemote(context, actionType, accounts.get(0), targetedAccount, targetedStatus); Runnable myRunnable = () -> fetchRemote(context, actionType, accounts.get(0), targetedAccount, targetedStatus);
mainHandler.post(myRunnable); mainHandler.post(myRunnable);
} else { } else {
List<app.fedilab.android.client.entities.api.Account> accountList = new ArrayList<>(); List<app.fedilab.android.client.entities.api.Account> accountList = new ArrayList<>();
for (Account account : accounts) { for (BaseAccount account : accounts) {
accountList.add(account.mastodon_account); accountList.add(account.mastodon_account);
} }
Handler mainHandler = new Handler(Looper.getMainLooper()); Handler mainHandler = new Handler(Looper.getMainLooper());
@ -82,9 +83,9 @@ public class CrossActionHelper {
AlertDialog.Builder builderSingle = new AlertDialog.Builder(context, Helper.dialogStyle()); AlertDialog.Builder builderSingle = new AlertDialog.Builder(context, Helper.dialogStyle());
builderSingle.setTitle(context.getString(R.string.choose_accounts)); builderSingle.setTitle(context.getString(R.string.choose_accounts));
final AccountsSearchAdapter accountsSearchAdapter = new AccountsSearchAdapter(context, accountList); final AccountsSearchAdapter accountsSearchAdapter = new AccountsSearchAdapter(context, accountList);
final Account[] accountArray = new Account[accounts.size()]; final BaseAccount[] accountArray = new Account[accounts.size()];
int i = 0; int i = 0;
for (Account account : accounts) { for (BaseAccount account : accounts) {
accountArray[i] = account; accountArray[i] = account;
i++; i++;
} }
@ -92,7 +93,7 @@ public class CrossActionHelper {
builderSingle.setAdapter(accountsSearchAdapter, (dialog, which) -> { builderSingle.setAdapter(accountsSearchAdapter, (dialog, which) -> {
boolean confirmFav = sharedpreferences.getBoolean(context.getString(R.string.SET_NOTIF_VALIDATION_FAV), false); boolean confirmFav = sharedpreferences.getBoolean(context.getString(R.string.SET_NOTIF_VALIDATION_FAV), false);
boolean confirmBoost = sharedpreferences.getBoolean(context.getString(R.string.SET_NOTIF_VALIDATION), true); boolean confirmBoost = sharedpreferences.getBoolean(context.getString(R.string.SET_NOTIF_VALIDATION), true);
Account selectedAccount = accountArray[which]; BaseAccount selectedAccount = accountArray[which];
if ((actionType == TypeOfCrossAction.REBLOG_ACTION && confirmBoost) || (actionType == TypeOfCrossAction.FAVOURITE_ACTION && confirmFav)) { if ((actionType == TypeOfCrossAction.REBLOG_ACTION && confirmBoost) || (actionType == TypeOfCrossAction.FAVOURITE_ACTION && confirmFav)) {
AlertDialog.Builder alt_bld = new AlertDialog.Builder(context, Helper.dialogStyle()); AlertDialog.Builder alt_bld = new AlertDialog.Builder(context, Helper.dialogStyle());
if (actionType == TypeOfCrossAction.REBLOG_ACTION) { if (actionType == TypeOfCrossAction.REBLOG_ACTION) {
@ -127,7 +128,7 @@ public class CrossActionHelper {
/** /**
* Fetch and federate the remote account or status * Fetch and federate the remote account or status
*/ */
private static void fetchRemote(@NonNull Context context, @NonNull TypeOfCrossAction actionType, @NonNull Account ownerAccount, app.fedilab.android.client.entities.api.Account targetedAccount, Status targetedStatus) { private static void fetchRemote(@NonNull Context context, @NonNull TypeOfCrossAction actionType, @NonNull BaseAccount ownerAccount, app.fedilab.android.client.entities.api.Account targetedAccount, Status targetedStatus) {
SearchVM searchVM = new ViewModelProvider((ViewModelStoreOwner) context).get("crossactions", SearchVM.class); SearchVM searchVM = new ViewModelProvider((ViewModelStoreOwner) context).get("crossactions", SearchVM.class);
if (targetedAccount != null) { if (targetedAccount != null) {
@ -165,7 +166,7 @@ public class CrossActionHelper {
/** /**
* Do action when status or account has been fetched * Do action when status or account has been fetched
*/ */
private static void applyAction(@NonNull Context context, @NonNull TypeOfCrossAction actionType, @NonNull Account ownerAccount, app.fedilab.android.client.entities.api.Account targetedAccount, Status targetedStatus) { private static void applyAction(@NonNull Context context, @NonNull TypeOfCrossAction actionType, @NonNull BaseAccount ownerAccount, app.fedilab.android.client.entities.api.Account targetedAccount, Status targetedStatus) {
AccountsVM accountsVM = null; AccountsVM accountsVM = null;
StatusesVM statusesVM = null; StatusesVM statusesVM = null;
@ -263,7 +264,7 @@ public class CrossActionHelper {
/** /**
* Fetch and federate the remote status * Fetch and federate the remote status
*/ */
public static void fetchRemoteStatus(@NonNull Context context, @NonNull Account ownerAccount, String url, Callback callback) { public static void fetchRemoteStatus(@NonNull Context context, @NonNull BaseAccount ownerAccount, String url, Callback callback) {
MastodonSearchService mastodonSearchService = init(context, MainActivity.currentInstance); MastodonSearchService mastodonSearchService = init(context, MainActivity.currentInstance);
new Thread(() -> { new Thread(() -> {
Call<Results> resultsCall = mastodonSearchService.search(ownerAccount.token, url, null, "statuses", false, true, false, 0, null, null, 1); Call<Results> resultsCall = mastodonSearchService.search(ownerAccount.token, url, null, "statuses", false, true, false, 0, null, null, 1);
@ -306,7 +307,7 @@ public class CrossActionHelper {
/** /**
* Fetch and federate the remote status * Fetch and federate the remote status
*/ */
public static void fetchRemoteAccount(@NonNull Context context, @NonNull Account ownerAccount, app.fedilab.android.client.entities.api.Account targetedAccount, Callback callback) { 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, MainActivity.currentInstance);

View file

@ -132,6 +132,7 @@ import app.fedilab.android.activities.WebviewActivity;
import app.fedilab.android.broadcastreceiver.ToastMessage; import app.fedilab.android.broadcastreceiver.ToastMessage;
import app.fedilab.android.client.entities.api.Attachment; import app.fedilab.android.client.entities.api.Attachment;
import app.fedilab.android.client.entities.app.Account; import app.fedilab.android.client.entities.app.Account;
import app.fedilab.android.client.entities.app.BaseAccount;
import app.fedilab.android.client.entities.app.QuickLoad; import app.fedilab.android.client.entities.app.QuickLoad;
import app.fedilab.android.client.entities.app.StatusCache; import app.fedilab.android.client.entities.app.StatusCache;
import app.fedilab.android.exception.DBException; import app.fedilab.android.exception.DBException;
@ -593,68 +594,7 @@ public class Helper {
return date; return date;
} }
/** private static BaseAccount currentAccount;
* Log out the authenticated user by removing its token
*
* @param activity Activity
* @param account {@link Account}
* @throws DBException Exception
*/
public static void removeAccount(Activity activity, Account account) throws DBException {
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(activity);
//Current user
String userId = sharedpreferences.getString(PREF_USER_ID, null);
String instance = sharedpreferences.getString(PREF_USER_INSTANCE, null);
Account accountDB = new Account(activity);
boolean accountRemovedIsLogged = false;
//Remove the current account
if (account == null) {
account = accountDB.getUniqAccount(userId, instance);
accountRemovedIsLogged = true;
}
if (account != null) {
Account finalAccount = account;
OauthVM oauthVM = new ViewModelProvider((ViewModelStoreOwner) activity).get(OauthVM.class);
//Revoke the token
oauthVM.revokeToken(account.instance, account.token, account.client_id, account.client_secret);
//Revoke token and remove user
new Thread(() -> {
try {
accountDB.removeUser(finalAccount);
} catch (DBException e) {
e.printStackTrace();
}
}).start();
}
//If the account removed is not the logged one, no need to log out the current user
if (!accountRemovedIsLogged) {
return;
}
//Log out the current user
Account newAccount = accountDB.getLastUsedAccount();
SharedPreferences.Editor editor = sharedpreferences.edit();
if (newAccount == null) {
editor.putString(PREF_USER_TOKEN, null);
editor.putString(PREF_USER_INSTANCE, null);
editor.putString(PREF_USER_ID, null);
editor.apply();
Intent loginActivity = new Intent(activity, LoginActivity.class);
activity.startActivity(loginActivity);
activity.finish();
} else {
editor.putString(PREF_USER_TOKEN, newAccount.token);
editor.putString(PREF_USER_INSTANCE, newAccount.instance);
editor.putString(PREF_USER_ID, newAccount.user_id);
BaseMainActivity.currentUserID = newAccount.user_id;
BaseMainActivity.currentToken = newAccount.token;
BaseMainActivity.currentInstance = newAccount.instance;
editor.apply();
Intent changeAccount = new Intent(activity, MainActivity.class);
changeAccount.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
activity.startActivity(changeAccount);
}
}
/** /**
* Converts dp to pixel * Converts dp to pixel
@ -910,31 +850,66 @@ public class Helper {
} }
/** /**
* Load a profile picture for the account * Log out the authenticated user by removing its token
* *
* @param view ImageView - the view where the image will be loaded * @param activity Activity
* @param account - {@link Account} * @param account {@link Account}
* @throws DBException Exception
*/ */
public static void loadPP(ImageView view, Account account) { public static void removeAccount(Activity activity, BaseAccount account) throws DBException {
Context context = view.getContext(); SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(activity);
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context); //Current user
boolean disableGif = sharedpreferences.getBoolean(context.getString(R.string.SET_DISABLE_GIF), false); String userId = sharedpreferences.getString(PREF_USER_ID, null);
String targetedUrl = disableGif ? account.mastodon_account.avatar_static : account.mastodon_account.avatar; String instance = sharedpreferences.getString(PREF_USER_INSTANCE, null);
if (disableGif || (!targetedUrl.endsWith(".gif"))) { Account accountDB = new Account(activity);
Glide.with(view.getContext()) boolean accountRemovedIsLogged = false;
.asDrawable() //Remove the current account
.load(targetedUrl) if (account == null) {
.thumbnail(0.1f) account = accountDB.getUniqAccount(userId, instance);
.apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(10))) accountRemovedIsLogged = true;
.into(view);
} else {
Glide.with(view.getContext())
.asGif()
.load(targetedUrl)
.thumbnail(0.1f)
.apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(10)))
.into(view);
} }
if (account != null) {
BaseAccount finalAccount = account;
OauthVM oauthVM = new ViewModelProvider((ViewModelStoreOwner) activity).get(OauthVM.class);
//Revoke the token
oauthVM.revokeToken(account.instance, account.token, account.client_id, account.client_secret);
//Revoke token and remove user
new Thread(() -> {
try {
accountDB.removeUser(finalAccount);
} catch (DBException e) {
e.printStackTrace();
}
}).start();
}
//If the account removed is not the logged one, no need to log out the current user
if (!accountRemovedIsLogged) {
return;
}
//Log out the current user
BaseAccount newAccount = accountDB.getLastUsedAccount();
SharedPreferences.Editor editor = sharedpreferences.edit();
if (newAccount == null) {
editor.putString(PREF_USER_TOKEN, null);
editor.putString(PREF_USER_INSTANCE, null);
editor.putString(PREF_USER_ID, null);
editor.apply();
Intent loginActivity = new Intent(activity, LoginActivity.class);
activity.startActivity(loginActivity);
activity.finish();
} else {
editor.putString(PREF_USER_TOKEN, newAccount.token);
editor.putString(PREF_USER_INSTANCE, newAccount.instance);
editor.putString(PREF_USER_ID, newAccount.user_id);
BaseMainActivity.currentUserID = newAccount.user_id;
BaseMainActivity.currentToken = newAccount.token;
BaseMainActivity.currentInstance = newAccount.instance;
editor.apply();
Intent changeAccount = new Intent(activity, MainActivity.class);
changeAccount.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
activity.startActivity(changeAccount);
}
} }
/** /**
@ -1045,154 +1020,31 @@ public class Helper {
} }
/** /**
* Sends notification with intent * Load a profile picture for the account
* *
* @param context Context * @param view ImageView - the view where the image will be loaded
* @param intent Intent associated to the notifcation * @param account - {@link Account}
* @param icon Bitmap profile picture
* @param title String title of the notification
* @param message String message for the notification
*/ */
@SuppressLint("UnspecifiedImmutableFlag") public static void loadPP(ImageView view, BaseAccount account) {
public static void notify_user(Context context, int notificationId, Account account, Intent intent, Bitmap icon, NotifType notifType, String title, String message) { Context context = view.getContext();
final SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context);
// prepare intent which is triggered if the user click on the notification boolean disableGif = sharedpreferences.getBoolean(context.getString(R.string.SET_DISABLE_GIF), false);
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context); String targetedUrl = disableGif ? account.mastodon_account.avatar_static : account.mastodon_account.avatar;
int requestCode = (int) System.currentTimeMillis(); if (disableGif || (!targetedUrl.endsWith(".gif"))) {
PendingIntent pIntent; Glide.with(view.getContext())
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { .asDrawable()
pIntent = PendingIntent.getActivity(context, requestCode, intent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT); .load(targetedUrl)
.thumbnail(0.1f)
.apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(10)))
.into(view);
} else { } else {
pIntent = PendingIntent.getActivity(context, requestCode, intent, PendingIntent.FLAG_ONE_SHOT); Glide.with(view.getContext())
.asGif()
.load(targetedUrl)
.thumbnail(0.1f)
.apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(10)))
.into(view);
} }
intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_CLEAR_TOP);
RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
// build notification
String channelId;
String channelTitle;
switch (notifType) {
case FAV:
channelId = "channel_favourite";
channelTitle = context.getString(R.string.channel_notif_fav);
break;
case FOLLLOW:
channelId = "channel_follow";
channelTitle = context.getString(R.string.channel_notif_follow);
break;
case MENTION:
channelId = "channel_mention";
channelTitle = context.getString(R.string.channel_notif_mention);
break;
case POLL:
channelId = "channel_poll";
channelTitle = context.getString(R.string.channel_notif_poll);
break;
case BACKUP:
channelId = "channel_backup";
channelTitle = context.getString(R.string.channel_notif_backup);
break;
case STORE:
channelId = "channel_store";
channelTitle = context.getString(R.string.channel_notif_media);
break;
case TOOT:
channelId = "channel_status";
channelTitle = context.getString(R.string.channel_notif_status);
break;
default:
channelId = "channel_boost";
channelTitle = context.getString(R.string.channel_notif_boost);
}
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context, channelId)
.setSmallIcon(R.drawable.ic_notification).setTicker(message)
.setWhen(System.currentTimeMillis())
.setAutoCancel(true);
if (notifType == NotifType.MENTION) {
if (message.length() > 500) {
message = message.substring(0, 499) + "";
}
notificationBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(message));
}
notificationBuilder.setGroup(account.mastodon_account.acct + "@" + account.instance)
.setContentIntent(pIntent)
.setContentText(message);
int ledColour = Color.BLUE;
int prefColor;
try {
prefColor = sharedpreferences.getInt(context.getString(R.string.SET_LED_COLOUR_VAL), LED_COLOUR);
} catch (ClassCastException e) {
prefColor = Integer.parseInt(sharedpreferences.getString(context.getString(R.string.SET_LED_COLOUR_VAL), String.valueOf(LED_COLOUR)));
}
switch (prefColor) {
case 0: // BLUE
ledColour = Color.BLUE;
break;
case 1: // CYAN
ledColour = Color.CYAN;
break;
case 2: // MAGENTA
ledColour = Color.MAGENTA;
break;
case 3: // GREEN
ledColour = Color.GREEN;
break;
case 4: // RED
ledColour = Color.RED;
break;
case 5: // YELLOW
ledColour = Color.YELLOW;
break;
case 6: // WHITE
ledColour = Color.WHITE;
break;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel;
NotificationManager mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
if (sharedpreferences.getBoolean(context.getString(R.string.SET_NOTIF_SILENT), false)) {
channel = new NotificationChannel(channelId, channelTitle, NotificationManager.IMPORTANCE_LOW);
channel.setSound(null, null);
channel.setVibrationPattern(new long[]{500, 500, 500});
channel.enableVibration(true);
channel.setLightColor(ledColour);
} else {
channel = new NotificationChannel(channelId, channelTitle, NotificationManager.IMPORTANCE_DEFAULT);
String soundUri = sharedpreferences.getString(context.getString(R.string.SET_NOTIF_SOUND), ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + context.getPackageName() + "/" + R.raw.boop);
AudioAttributes audioAttributes = new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_NOTIFICATION)
.build();
channel.setSound(Uri.parse(soundUri), audioAttributes);
}
assert mNotificationManager != null;
mNotificationManager.createNotificationChannel(channel);
} else {
if (sharedpreferences.getBoolean(context.getString(R.string.SET_NOTIF_SILENT), false)) {
notificationBuilder.setVibrate(new long[]{500, 500, 500});
} else {
String soundUri = sharedpreferences.getString(context.getString(R.string.SET_NOTIF_SOUND), ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + context.getPackageName() + "/" + R.raw.boop);
notificationBuilder.setSound(Uri.parse(soundUri));
}
notificationBuilder.setLights(ledColour, 500, 1000);
}
notificationBuilder.setContentTitle(title);
notificationBuilder.setLargeIcon(icon);
notificationManager.notify(notificationId, notificationBuilder.build());
Notification summaryNotification =
new NotificationCompat.Builder(context, channelId)
.setContentTitle(title)
.setContentText(channelTitle)
.setContentIntent(pIntent)
.setLargeIcon(icon)
.setSmallIcon(R.drawable.ic_notification)
.setGroup(account.mastodon_account.acct + "@" + account.instance)
.setGroupSummary(true)
.build();
notificationManager.notify(notificationId, summaryNotification);
} }
/** /**
@ -1547,7 +1399,156 @@ public class Helper {
void onAttachmentCopied(Attachment attachment); void onAttachmentCopied(Attachment attachment);
} }
private static WeakReference<Account> currentAccount; /**
* Sends notification with intent
*
* @param context Context
* @param intent Intent associated to the notifcation
* @param icon Bitmap profile picture
* @param title String title of the notification
* @param message String message for the notification
*/
@SuppressLint("UnspecifiedImmutableFlag")
public static void notify_user(Context context, int notificationId, BaseAccount account, Intent intent, Bitmap icon, NotifType notifType, String title, String message) {
final SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context);
// prepare intent which is triggered if the user click on the notification
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
int requestCode = (int) System.currentTimeMillis();
PendingIntent pIntent;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
pIntent = PendingIntent.getActivity(context, requestCode, intent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT);
} else {
pIntent = PendingIntent.getActivity(context, requestCode, intent, PendingIntent.FLAG_ONE_SHOT);
}
intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_CLEAR_TOP);
RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
// build notification
String channelId;
String channelTitle;
switch (notifType) {
case FAV:
channelId = "channel_favourite";
channelTitle = context.getString(R.string.channel_notif_fav);
break;
case FOLLLOW:
channelId = "channel_follow";
channelTitle = context.getString(R.string.channel_notif_follow);
break;
case MENTION:
channelId = "channel_mention";
channelTitle = context.getString(R.string.channel_notif_mention);
break;
case POLL:
channelId = "channel_poll";
channelTitle = context.getString(R.string.channel_notif_poll);
break;
case BACKUP:
channelId = "channel_backup";
channelTitle = context.getString(R.string.channel_notif_backup);
break;
case STORE:
channelId = "channel_store";
channelTitle = context.getString(R.string.channel_notif_media);
break;
case TOOT:
channelId = "channel_status";
channelTitle = context.getString(R.string.channel_notif_status);
break;
default:
channelId = "channel_boost";
channelTitle = context.getString(R.string.channel_notif_boost);
}
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context, channelId)
.setSmallIcon(R.drawable.ic_notification).setTicker(message)
.setWhen(System.currentTimeMillis())
.setAutoCancel(true);
if (notifType == NotifType.MENTION) {
if (message.length() > 500) {
message = message.substring(0, 499) + "";
}
notificationBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(message));
}
notificationBuilder.setGroup(account.mastodon_account.acct + "@" + account.instance)
.setContentIntent(pIntent)
.setContentText(message);
int ledColour = Color.BLUE;
int prefColor;
try {
prefColor = sharedpreferences.getInt(context.getString(R.string.SET_LED_COLOUR_VAL), LED_COLOUR);
} catch (ClassCastException e) {
prefColor = Integer.parseInt(sharedpreferences.getString(context.getString(R.string.SET_LED_COLOUR_VAL), String.valueOf(LED_COLOUR)));
}
switch (prefColor) {
case 0: // BLUE
ledColour = Color.BLUE;
break;
case 1: // CYAN
ledColour = Color.CYAN;
break;
case 2: // MAGENTA
ledColour = Color.MAGENTA;
break;
case 3: // GREEN
ledColour = Color.GREEN;
break;
case 4: // RED
ledColour = Color.RED;
break;
case 5: // YELLOW
ledColour = Color.YELLOW;
break;
case 6: // WHITE
ledColour = Color.WHITE;
break;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel;
NotificationManager mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
if (sharedpreferences.getBoolean(context.getString(R.string.SET_NOTIF_SILENT), false)) {
channel = new NotificationChannel(channelId, channelTitle, NotificationManager.IMPORTANCE_LOW);
channel.setSound(null, null);
channel.setVibrationPattern(new long[]{500, 500, 500});
channel.enableVibration(true);
channel.setLightColor(ledColour);
} else {
channel = new NotificationChannel(channelId, channelTitle, NotificationManager.IMPORTANCE_DEFAULT);
String soundUri = sharedpreferences.getString(context.getString(R.string.SET_NOTIF_SOUND), ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + context.getPackageName() + "/" + R.raw.boop);
AudioAttributes audioAttributes = new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_NOTIFICATION)
.build();
channel.setSound(Uri.parse(soundUri), audioAttributes);
}
assert mNotificationManager != null;
mNotificationManager.createNotificationChannel(channel);
} else {
if (sharedpreferences.getBoolean(context.getString(R.string.SET_NOTIF_SILENT), false)) {
notificationBuilder.setVibrate(new long[]{500, 500, 500});
} else {
String soundUri = sharedpreferences.getString(context.getString(R.string.SET_NOTIF_SOUND), ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + context.getPackageName() + "/" + R.raw.boop);
notificationBuilder.setSound(Uri.parse(soundUri));
}
notificationBuilder.setLights(ledColour, 500, 1000);
}
notificationBuilder.setContentTitle(title);
notificationBuilder.setLargeIcon(icon);
notificationManager.notify(notificationId, notificationBuilder.build());
Notification summaryNotification =
new NotificationCompat.Builder(context, channelId)
.setContentTitle(title)
.setContentText(channelTitle)
.setContentIntent(pIntent)
.setLargeIcon(icon)
.setSmallIcon(R.drawable.ic_notification)
.setGroup(account.mastodon_account.acct + "@" + account.instance)
.setGroupSummary(true)
.build();
notificationManager.notify(notificationId, summaryNotification);
}
public static void transfertIfExist(Context context) { public static void transfertIfExist(Context context) {
@ -1614,23 +1615,15 @@ public class Helper {
return "@fedilab_fetch_more_" + uuid; return "@fedilab_fetch_more_" + uuid;
} }
public static void setMastodonAccount(app.fedilab.android.client.entities.api.Account mastodon_account) { public static BaseAccount getCurrentAccount(Context context) {
if (currentAccount != null) { if (currentAccount == null) {
currentAccount.get().mastodon_account = mastodon_account;
}
}
public static Account getCurrentAccount(Context context) {
if (currentAccount == null || currentAccount.get() == null || currentAccount.get().mastodon_account == null) {
try { try {
Account account = new Account(context).getUniqAccount(MainActivity.currentUserID, MainActivity.currentInstance); currentAccount = new Account(context).getUniqAccount(MainActivity.currentUserID, MainActivity.currentInstance);
currentAccount = new WeakReference<>(account);
currentAccount.get().mastodon_account = account.mastodon_account;
} catch (DBException e) { } catch (DBException e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
return currentAccount.get(); return currentAccount;
} }
public static class CacheTask { public static class CacheTask {

View file

@ -53,6 +53,7 @@ import app.fedilab.android.client.endpoints.MastodonNotificationsService;
import app.fedilab.android.client.entities.api.Notification; import app.fedilab.android.client.entities.api.Notification;
import app.fedilab.android.client.entities.api.Notifications; import app.fedilab.android.client.entities.api.Notifications;
import app.fedilab.android.client.entities.app.Account; import app.fedilab.android.client.entities.app.Account;
import app.fedilab.android.client.entities.app.BaseAccount;
import app.fedilab.android.exception.DBException; import app.fedilab.android.exception.DBException;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
import retrofit2.Call; import retrofit2.Call;
@ -70,7 +71,7 @@ public class NotificationsHelper {
SharedPreferences prefs = PreferenceManager SharedPreferences prefs = PreferenceManager
.getDefaultSharedPreferences(context); .getDefaultSharedPreferences(context);
String[] slugArray = slug.split("@"); String[] slugArray = slug.split("@");
Account accountDb = new Account(context).getUniqAccount(slugArray[0], slugArray[1]); BaseAccount accountDb = new Account(context).getUniqAccount(slugArray[0], slugArray[1]);
if (accountDb == null) { if (accountDb == null) {
return; return;
} }
@ -147,7 +148,7 @@ public class NotificationsHelper {
return retrofit.create(MastodonNotificationsService.class); return retrofit.create(MastodonNotificationsService.class);
} }
public static void onRetrieveNotifications(Context context, Notifications newNotifications, final Account account) { public static void onRetrieveNotifications(Context context, Notifications newNotifications, final BaseAccount account) {
List<Notification> notificationsReceived = newNotifications.notifications; List<Notification> notificationsReceived = newNotifications.notifications;
if (notificationsReceived == null || notificationsReceived.size() == 0 || account == null) if (notificationsReceived == null || notificationsReceived.size() == 0 || account == null)
return; return;

View file

@ -38,6 +38,7 @@ import java.util.concurrent.TimeUnit;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.client.entities.app.Account; import app.fedilab.android.client.entities.app.Account;
import app.fedilab.android.client.entities.app.BaseAccount;
import app.fedilab.android.jobs.NotificationsWorker; import app.fedilab.android.jobs.NotificationsWorker;
public class PushHelper { public class PushHelper {
@ -49,7 +50,7 @@ public class PushHelper {
switch (typeOfNotification) { switch (typeOfNotification) {
case "PUSH_NOTIFICATIONS": case "PUSH_NOTIFICATIONS":
new Thread(() -> { new Thread(() -> {
List<Account> accounts = new Account(context).getPushNotificationAccounts(); List<BaseAccount> accounts = new Account(context).getPushNotificationAccounts();
Handler mainHandler = new Handler(Looper.getMainLooper()); Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> { Runnable myRunnable = () -> {
List<String> distributors = UnifiedPush.getDistributors(context, new ArrayList<>()); List<String> distributors = UnifiedPush.getDistributors(context, new ArrayList<>());
@ -84,8 +85,8 @@ public class PushHelper {
break; break;
case "REPEAT_NOTIFICATIONS": case "REPEAT_NOTIFICATIONS":
new Thread(() -> { new Thread(() -> {
List<Account> accounts = new Account(context).getPushNotificationAccounts(); List<BaseAccount> accounts = new Account(context).getPushNotificationAccounts();
for (Account account : accounts) { for (BaseAccount account : accounts) {
((Activity) context).runOnUiThread(() -> { ((Activity) context).runOnUiThread(() -> {
UnifiedPush.unregisterApp(context, account.user_id + "@" + account.instance); UnifiedPush.unregisterApp(context, account.user_id + "@" + account.instance);
}); });
@ -98,8 +99,8 @@ public class PushHelper {
case "NO_NOTIFICATIONS": case "NO_NOTIFICATIONS":
WorkManager.getInstance(context).cancelAllWorkByTag(Helper.WORKER_REFRESH_NOTIFICATION); WorkManager.getInstance(context).cancelAllWorkByTag(Helper.WORKER_REFRESH_NOTIFICATION);
new Thread(() -> { new Thread(() -> {
List<Account> accounts = new Account(context).getPushNotificationAccounts(); List<BaseAccount> accounts = new Account(context).getPushNotificationAccounts();
for (Account account : accounts) { for (BaseAccount account : accounts) {
((Activity) context).runOnUiThread(() -> { ((Activity) context).runOnUiThread(() -> {
UnifiedPush.unregisterApp(context, account.user_id + "@" + account.instance); UnifiedPush.unregisterApp(context, account.user_id + "@" + account.instance);
}); });
@ -110,7 +111,7 @@ public class PushHelper {
} }
private static void registerAppWithDialog(Context context, List<Account> accounts) { private static void registerAppWithDialog(Context context, List<BaseAccount> accounts) {
if (accounts == null) { if (accounts == null) {
return; return;
} }
@ -119,7 +120,7 @@ public class PushHelper {
if (distributors.size() == 1) { if (distributors.size() == 1) {
UnifiedPush.saveDistributor(context, distributors.get(0)); UnifiedPush.saveDistributor(context, distributors.get(0));
} }
for (Account account : accounts) { for (BaseAccount account : accounts) {
UnifiedPush.registerApp(context, account.user_id + "@" + account.instance, new ArrayList<>(), ""); UnifiedPush.registerApp(context, account.user_id + "@" + account.instance, new ArrayList<>(), "");
} }
return; return;
@ -131,7 +132,7 @@ public class PushHelper {
alert.setSingleChoiceItems(distributorsStr, -1, (dialog, item) -> { alert.setSingleChoiceItems(distributorsStr, -1, (dialog, item) -> {
String distributor = distributorsStr[item]; String distributor = distributorsStr[item];
UnifiedPush.saveDistributor(context, distributor); UnifiedPush.saveDistributor(context, distributor);
for (Account account : accounts) { for (BaseAccount account : accounts) {
UnifiedPush.registerApp(context, account.user_id + "@" + account.instance, new ArrayList<>(), ""); UnifiedPush.registerApp(context, account.user_id + "@" + account.instance, new ArrayList<>(), "");
} }
dialog.dismiss(); dialog.dismiss();

View file

@ -35,6 +35,7 @@ import app.fedilab.android.R;
import app.fedilab.android.client.endpoints.MastodonNotificationsService; import app.fedilab.android.client.endpoints.MastodonNotificationsService;
import app.fedilab.android.client.entities.api.PushSubscription; import app.fedilab.android.client.entities.api.PushSubscription;
import app.fedilab.android.client.entities.app.Account; import app.fedilab.android.client.entities.app.Account;
import app.fedilab.android.client.entities.app.BaseAccount;
import app.fedilab.android.exception.DBException; import app.fedilab.android.exception.DBException;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
import retrofit2.Call; import retrofit2.Call;
@ -79,7 +80,7 @@ public class PushNotifications {
ECDH finalEcdh = ecdh; ECDH finalEcdh = ecdh;
new Thread(() -> { new Thread(() -> {
String[] slugArray = slug.split("@"); String[] slugArray = slug.split("@");
Account accountDb = null; BaseAccount accountDb = null;
try { try {
accountDb = new Account(context).getUniqAccount(slugArray[0], slugArray[1]); accountDb = new Account(context).getUniqAccount(slugArray[0], slugArray[1]);
} catch (DBException e) { } catch (DBException e) {

View file

@ -45,6 +45,7 @@ import app.fedilab.android.client.entities.api.Poll;
import app.fedilab.android.client.entities.api.ScheduledStatus; import app.fedilab.android.client.entities.api.ScheduledStatus;
import app.fedilab.android.client.entities.api.Status; import app.fedilab.android.client.entities.api.Status;
import app.fedilab.android.client.entities.app.Account; import app.fedilab.android.client.entities.app.Account;
import app.fedilab.android.client.entities.app.BaseAccount;
import app.fedilab.android.client.entities.app.PostState; import app.fedilab.android.client.entities.app.PostState;
import app.fedilab.android.client.entities.app.StatusDraft; import app.fedilab.android.client.entities.app.StatusDraft;
import app.fedilab.android.exception.DBException; import app.fedilab.android.exception.DBException;
@ -141,7 +142,7 @@ public class PostMessageService extends IntentService {
} }
if (watermarkText == null) { if (watermarkText == null) {
try { try {
Account account = new Account(context).getAccountByToken(dataPost.token); BaseAccount account = new Account(context).getAccountByToken(dataPost.token);
watermarkText = account.mastodon_account.username + "@" + account.instance; watermarkText = account.mastodon_account.username + "@" + account.instance;
} catch (DBException e) { } catch (DBException e) {
e.printStackTrace(); e.printStackTrace();

View file

@ -91,7 +91,7 @@ import app.fedilab.android.client.entities.api.Mention;
import app.fedilab.android.client.entities.api.Poll; import app.fedilab.android.client.entities.api.Poll;
import app.fedilab.android.client.entities.api.Status; import app.fedilab.android.client.entities.api.Status;
import app.fedilab.android.client.entities.api.Tag; import app.fedilab.android.client.entities.api.Tag;
import app.fedilab.android.client.entities.app.Account; import app.fedilab.android.client.entities.app.BaseAccount;
import app.fedilab.android.client.entities.app.StatusDraft; import app.fedilab.android.client.entities.app.StatusDraft;
import app.fedilab.android.databinding.ComposeAttachmentItemBinding; import app.fedilab.android.databinding.ComposeAttachmentItemBinding;
import app.fedilab.android.databinding.ComposePollBinding; import app.fedilab.android.databinding.ComposePollBinding;
@ -122,7 +122,7 @@ public class ComposeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
"..--..", ".-.-.-", ".----.",}; "..--..", ".-.-.-", ".----.",};
private final List<Status> statusList; private final List<Status> statusList;
private final int TYPE_NORMAL = 0; private final int TYPE_NORMAL = 0;
private final Account account; private final BaseAccount account;
private final String visibility; private final String visibility;
private final app.fedilab.android.client.entities.api.Account mentionedAccount; private final app.fedilab.android.client.entities.api.Account mentionedAccount;
public ManageDrafts manageDrafts; public ManageDrafts manageDrafts;
@ -130,7 +130,7 @@ public class ComposeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
private Context context; private Context context;
private AlertDialog alertDialogEmoji; private AlertDialog alertDialogEmoji;
public ComposeAdapter(List<Status> statusList, int statusCount, Account account, app.fedilab.android.client.entities.api.Account mentionedAccount, String visibility) { public ComposeAdapter(List<Status> statusList, int statusCount, BaseAccount account, app.fedilab.android.client.entities.api.Account mentionedAccount, String visibility) {
this.statusList = statusList; this.statusList = statusList;
this.statusCount = statusCount; this.statusCount = statusCount;
this.account = account; this.account = account;
@ -1210,8 +1210,13 @@ public class ComposeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
} else { } else {
ImageViewCompat.setImageTintList(holder.binding.buttonPoll, null); ImageViewCompat.setImageTintList(holder.binding.buttonPoll, null);
} }
holder.binding.buttonPost.setEnabled(!statusDraft.submitted);
holder.binding.buttonPost.setOnClickListener(v -> manageDrafts.onSubmit(prepareDraft(statusList, this, account.instance, account.user_id))); holder.binding.buttonPost.setOnClickListener(v -> {
statusDraft.submitted = true;
notifyItemChanged(position);
manageDrafts.onSubmit(prepareDraft(statusList, this, account.instance, account.user_id));
});
} }
} }

View file

@ -44,6 +44,7 @@ import app.fedilab.android.client.entities.api.Poll;
import app.fedilab.android.client.entities.api.ScheduledStatus; import app.fedilab.android.client.entities.api.ScheduledStatus;
import app.fedilab.android.client.entities.api.ScheduledStatuses; import app.fedilab.android.client.entities.api.ScheduledStatuses;
import app.fedilab.android.client.entities.api.Status; import app.fedilab.android.client.entities.api.Status;
import app.fedilab.android.client.entities.app.BaseAccount;
import app.fedilab.android.client.entities.app.QuickLoad; import app.fedilab.android.client.entities.app.QuickLoad;
import app.fedilab.android.client.entities.app.StatusCache; import app.fedilab.android.client.entities.app.StatusCache;
import app.fedilab.android.exception.DBException; import app.fedilab.android.exception.DBException;
@ -322,7 +323,7 @@ public class StatusesVM extends AndroidViewModel {
} }
//The status must also be deleted in cache //The status must also be deleted in cache
try { try {
app.fedilab.android.client.entities.app.Account account = new app.fedilab.android.client.entities.app.Account(getApplication().getApplicationContext()).getAccountByToken(token); BaseAccount account = new app.fedilab.android.client.entities.app.Account(getApplication().getApplicationContext()).getAccountByToken(token);
new StatusCache(getApplication().getApplicationContext()).deleteStatus(id, account.instance); new StatusCache(getApplication().getApplicationContext()).deleteStatus(id, account.instance);
new QuickLoad(getApplication().getApplicationContext()).deleteStatus(account, id); new QuickLoad(getApplication().getApplicationContext()).deleteStatus(account, id);
} catch (DBException e) { } catch (DBException e) {

View file

@ -40,6 +40,7 @@ import app.fedilab.android.client.entities.api.MastodonList;
import app.fedilab.android.client.entities.api.Pagination; import app.fedilab.android.client.entities.api.Pagination;
import app.fedilab.android.client.entities.api.Status; import app.fedilab.android.client.entities.api.Status;
import app.fedilab.android.client.entities.api.Statuses; import app.fedilab.android.client.entities.api.Statuses;
import app.fedilab.android.client.entities.app.BaseAccount;
import app.fedilab.android.client.entities.app.StatusCache; import app.fedilab.android.client.entities.app.StatusCache;
import app.fedilab.android.client.entities.app.StatusDraft; import app.fedilab.android.client.entities.app.StatusDraft;
import app.fedilab.android.exception.DBException; import app.fedilab.android.exception.DBException;
@ -286,7 +287,7 @@ public class TimelinesVM extends AndroidViewModel {
* @param account app.fedilab.android.client.entities.app.Account * @param account app.fedilab.android.client.entities.app.Account
* @return LiveData<ist < StatusDraft>> * @return LiveData<ist < StatusDraft>>
*/ */
public LiveData<List<StatusDraft>> getDrafts(app.fedilab.android.client.entities.app.Account account) { public LiveData<List<StatusDraft>> getDrafts(BaseAccount account) {
statusDraftListMutableLiveData = new MutableLiveData<>(); statusDraftListMutableLiveData = new MutableLiveData<>();
new Thread(() -> { new Thread(() -> {
List<StatusDraft> statusCacheDAO = null; List<StatusDraft> statusCacheDAO = null;

View file

@ -27,7 +27,7 @@
app:title="@string/embedded_browser" /> app:title="@string/embedded_browser" />
<SwitchPreferenceCompat <SwitchPreferenceCompat
app:defaultValue="false" app:defaultValue="true"
app:iconSpaceReserved="false" app:iconSpaceReserved="false"
app:key="@string/SET_SEND_CRASH_REPORTS" app:key="@string/SET_SEND_CRASH_REPORTS"
app:singleLineTitle="false" app:singleLineTitle="false"