Add ComposeActivity tests. Add ServiceLocator (#542)

main
Ivan Kupalov 6 years ago committed by Konrad Pozniak
parent 4d4f48065c
commit 5d954a8eab
  1. 12
      app/build.gradle
  2. 4
      app/src/main/java/com/keylesspalace/tusky/AccountActivity.java
  3. 17
      app/src/main/java/com/keylesspalace/tusky/BaseActivity.java
  4. 7
      app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java
  5. 11
      app/src/main/java/com/keylesspalace/tusky/LoginActivity.kt
  6. 49
      app/src/main/java/com/keylesspalace/tusky/MainActivity.java
  7. 21
      app/src/main/java/com/keylesspalace/tusky/NotificationPullJobCreator.java
  8. 4
      app/src/main/java/com/keylesspalace/tusky/SplashActivity.java
  9. 66
      app/src/main/java/com/keylesspalace/tusky/TuskyApplication.java
  10. 19
      app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java
  11. 35
      app/src/main/java/com/keylesspalace/tusky/fragment/PreferencesFragment.java
  12. 38
      app/src/main/java/com/keylesspalace/tusky/fragment/SFragment.java
  13. 10
      app/src/main/java/com/keylesspalace/tusky/network/AuthInterceptor.java
  14. 4
      app/src/main/java/com/keylesspalace/tusky/receiver/NotificationClearBroadcastReceiver.kt
  15. 58
      app/src/main/java/com/keylesspalace/tusky/util/NotificationHelper.java
  16. 103
      app/src/test/java/com/keylesspalace/tusky/ComposeActivityTest.kt
  17. 17
      app/src/test/java/com/keylesspalace/tusky/ExampleUnitTest.java
  18. 18
      app/src/test/java/com/keylesspalace/tusky/FakeTuskyApplication.kt

@ -36,6 +36,11 @@ android {
androidExtensions {
experimental = true
}
testOptions {
unitTests {
includeAndroidResources = true
}
}
}
ext.supportLibraryVersion = '27.1.0'
@ -68,12 +73,15 @@ dependencies {
//room
implementation 'android.arch.persistence.room:runtime:1.0.0'
kapt 'android.arch.persistence.room:compiler:1.0.0'
testImplementation 'junit:junit:4.12'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
testImplementation 'junit:junit:4.12'
testImplementation "org.robolectric:robolectric:3.7.1"
testCompile "org.mockito:mockito-inline:2.15.0"
androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
debugImplementation 'im.dino:dbinspector:3.4.1@aar'
}

@ -46,6 +46,7 @@ import android.widget.ImageView;
import android.widget.TextView;
import com.keylesspalace.tusky.db.AccountEntity;
import com.keylesspalace.tusky.db.AccountManager;
import com.keylesspalace.tusky.entity.Account;
import com.keylesspalace.tusky.entity.Relationship;
import com.keylesspalace.tusky.interfaces.ActionButtonActivity;
@ -193,7 +194,8 @@ public final class AccountActivity extends BaseActivity implements ActionButtonA
// Obtain information to fill out the profile.
obtainAccount();
AccountEntity activeAccount = TuskyApplication.getAccountManager().getActiveAccount();
AccountEntity activeAccount = TuskyApplication.getInstance(this).getServiceLocator()
.get(AccountManager.class).getActiveAccount();
if (accountId.equals(activeAccount.getAccountId())) {
isSelf = true;

@ -34,6 +34,7 @@ import com.evernote.android.job.JobRequest;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.keylesspalace.tusky.db.AccountEntity;
import com.keylesspalace.tusky.db.AccountManager;
import com.keylesspalace.tusky.json.SpannedTypeAdapter;
import com.keylesspalace.tusky.network.AuthInterceptor;
import com.keylesspalace.tusky.network.MastodonApi;
@ -50,11 +51,15 @@ public abstract class BaseActivity extends AppCompatActivity {
public MastodonApi mastodonApi;
protected Dispatcher mastodonApiDispatcher;
private AccountManager accountManager;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
accountManager = TuskyApplication.getInstance(this).getServiceLocator()
.get(AccountManager.class);
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
/* There isn't presently a way to globally change the theme of a whole application at
@ -64,7 +69,7 @@ public abstract class BaseActivity extends AppCompatActivity {
ThemeUtils.setAppNightMode(theme);
int style;
switch(preferences.getString("statusTextSize", "medium")) {
switch (preferences.getString("statusTextSize", "medium")) {
case "large":
style = R.style.TextSizeLarge;
break;
@ -79,7 +84,7 @@ public abstract class BaseActivity extends AppCompatActivity {
}
getTheme().applyStyle(style, false);
if(redirectIfNotLoggedIn()) {
if (redirectIfNotLoggedIn()) {
return;
}
createMastodonApi();
@ -119,8 +124,8 @@ public abstract class BaseActivity extends AppCompatActivity {
}
protected String getBaseUrl() {
AccountEntity account = TuskyApplication.getAccountManager().getActiveAccount();
if(account != null) {
AccountEntity account = accountManager.getActiveAccount();
if (account != null) {
return "https://" + account.getDomain();
} else {
return "";
@ -138,7 +143,7 @@ public abstract class BaseActivity extends AppCompatActivity {
OkHttpClient.Builder okBuilder =
OkHttpUtils.getCompatibleClientBuilder(preferences)
.addInterceptor(new AuthInterceptor())
.addInterceptor(new AuthInterceptor(accountManager))
.dispatcher(mastodonApiDispatcher);
if (BuildConfig.DEBUG) {
@ -155,7 +160,7 @@ public abstract class BaseActivity extends AppCompatActivity {
}
protected boolean redirectIfNotLoggedIn() {
if (TuskyApplication.getAccountManager().getActiveAccount() == null) {
if (accountManager.getActiveAccount() == null) {
Intent intent = new Intent(this, LoginActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);

@ -79,6 +79,7 @@ import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.keylesspalace.tusky.adapter.MentionAutoCompleteAdapter;
import com.keylesspalace.tusky.db.AccountEntity;
import com.keylesspalace.tusky.db.AccountManager;
import com.keylesspalace.tusky.db.TootDao;
import com.keylesspalace.tusky.db.TootEntity;
import com.keylesspalace.tusky.entity.Account;
@ -160,7 +161,7 @@ public final class ComposeActivity extends BaseActivity
// this only exists when a status is trying to be sent, but uploads are still occurring
private ProgressDialog finishingUploadDialog;
private String inReplyToId;
private ArrayList<QueuedMedia> mediaQueued;
private List<QueuedMedia> mediaQueued = new ArrayList<>();
private CountUpDownLatch waitForMediaLatch;
private boolean showMarkSensitive;
private Status.Visibility statusVisibility; // The current values of the options that will be applied
@ -205,7 +206,8 @@ public final class ComposeActivity extends BaseActivity
}
// setup the account image
AccountEntity activeAccount = TuskyApplication.getAccountManager().getActiveAccount();
AccountEntity activeAccount = TuskyApplication.getInstance(this).getServiceLocator()
.get(AccountManager.class).getActiveAccount();
if (activeAccount != null) {
@ -409,7 +411,6 @@ public final class ComposeActivity extends BaseActivity
}
// Initialise the empty media queue state.
mediaQueued = new ArrayList<>();
waitForMediaLatch = new CountUpDownLatch();
statusAlreadyInFlight = false;

@ -31,6 +31,7 @@ import android.view.MenuItem
import android.view.View
import android.widget.EditText
import android.widget.TextView
import com.keylesspalace.tusky.db.AccountManager
import com.keylesspalace.tusky.entity.AccessToken
import com.keylesspalace.tusky.entity.AppCredentials
import com.keylesspalace.tusky.network.MastodonApi
@ -88,7 +89,7 @@ class LoginActivity : AppCompatActivity() {
textView.movementMethod = LinkMovementMethod.getInstance()
}
if(isAdditionalLogin()) {
if (isAdditionalLogin()) {
setSupportActionBar(toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setDisplayShowTitleEnabled(false)
@ -99,7 +100,7 @@ class LoginActivity : AppCompatActivity() {
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if(item.itemId == android.R.id.home) {
if (item.itemId == android.R.id.home) {
onBackPressed()
return true
}
@ -281,7 +282,7 @@ class LoginActivity : AppCompatActivity() {
}
}
private fun isAdditionalLogin() : Boolean {
private fun isAdditionalLogin(): Boolean {
return intent.getBooleanExtra(LOGIN_MODE, false)
}
@ -289,7 +290,9 @@ class LoginActivity : AppCompatActivity() {
setLoading(true)
TuskyApplication.getAccountManager().addAccount(accessToken, domain)
TuskyApplication.getInstance(this).serviceLocator
.get(AccountManager::class.java)
.addAccount(accessToken, domain)
val intent = Intent(this, MainActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK

@ -84,6 +84,8 @@ public class MainActivity extends BaseActivity implements ActionButtonActivity {
private static int COMPOSE_RESULT = 1;
AccountManager accountManager;
private FloatingActionButton composeButton;
private AccountHeader headerResult;
private Drawer drawer;
@ -97,16 +99,19 @@ public class MainActivity extends BaseActivity implements ActionButtonActivity {
int tabPosition = 0;
accountManager = TuskyApplication.getInstance(this).getServiceLocator()
.get(AccountManager.class);
if (intent != null) {
long accountId = intent.getLongExtra(NotificationHelper.ACCOUNT_ID, -1);
if(accountId != -1) {
if (accountId != -1) {
// user clicked a notification, show notification tab and switch user if necessary
tabPosition = 1;
AccountEntity account = TuskyApplication.getAccountManager().getActiveAccount();
AccountEntity account = accountManager.getActiveAccount();
if (account == null || accountId != account.getId()) {
TuskyApplication.getAccountManager().setActiveAccount(accountId);
accountManager.setActiveAccount(accountId);
}
}
}
@ -180,7 +185,7 @@ public class MainActivity extends BaseActivity implements ActionButtonActivity {
tintTab(tab, true);
if(tab.getPosition() == 1) {
if (tab.getPosition() == 1) {
NotificationHelper.clearNotificationsForActiveAccount(MainActivity.this);
}
}
@ -191,7 +196,8 @@ public class MainActivity extends BaseActivity implements ActionButtonActivity {
}
@Override
public void onTabReselected(TabLayout.Tab tab) { }
public void onTabReselected(TabLayout.Tab tab) {
}
});
for (int i = 0; i < 4; i++) {
@ -385,7 +391,7 @@ public class MainActivity extends BaseActivity implements ActionButtonActivity {
})
.build();
if(BuildConfig.DEBUG) {
if (BuildConfig.DEBUG) {
IDrawerItem debugItem = new SecondaryDrawerItem()
.withIdentifier(1337)
.withName("debug")
@ -399,7 +405,7 @@ public class MainActivity extends BaseActivity implements ActionButtonActivity {
}
private boolean handleProfileClick(IProfile profile, boolean current) {
AccountEntity activeAccount = TuskyApplication.getAccountManager().getActiveAccount();
AccountEntity activeAccount = accountManager.getActiveAccount();
//open profile when active image was clicked
if (current && activeAccount != null) {
@ -409,7 +415,7 @@ public class MainActivity extends BaseActivity implements ActionButtonActivity {
return true;
}
//open LoginActivity to add new account
if(profile.getIdentifier() == DRAWER_ITEM_ADD_ACCOUNT ) {
if (profile.getIdentifier() == DRAWER_ITEM_ADD_ACCOUNT) {
startActivity(LoginActivity.getIntent(this, true));
return true;
}
@ -420,7 +426,7 @@ public class MainActivity extends BaseActivity implements ActionButtonActivity {
private void changeAccount(long newSelectedId) {
TuskyApplication.getAccountManager().setActiveAccount(newSelectedId);
accountManager.setActiveAccount(newSelectedId);
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
@ -432,22 +438,22 @@ public class MainActivity extends BaseActivity implements ActionButtonActivity {
private void logout() {
AccountEntity activeAccount = TuskyApplication.getAccountManager().getActiveAccount();
AccountEntity activeAccount = accountManager.getActiveAccount();
if(activeAccount != null) {
if (activeAccount != null) {
new AlertDialog.Builder(this)
.setTitle(R.string.action_logout)
.setMessage(getString(R.string.action_logout_confirm, activeAccount.getFullName()))
.setPositiveButton(android.R.string.yes, (dialog, which) -> {
AccountManager accountManager = TuskyApplication.getAccountManager();
;
NotificationHelper.deleteNotificationChannelsForAccount(accountManager.getActiveAccount(), MainActivity.this);
AccountEntity newAccount = accountManager.logActiveAccountOut();
if (!NotificationHelper.areNotificationsEnabled(MainActivity.this)) disablePushNotifications();
if (!NotificationHelper.areNotificationsEnabled(MainActivity.this))
disablePushNotifications();
Intent intent;
if (newAccount == null) {
@ -492,11 +498,9 @@ public class MainActivity extends BaseActivity implements ActionButtonActivity {
.placeholder(R.drawable.account_header_default)
.into(background);
AccountManager am = TuskyApplication.getAccountManager();
am.updateActiveAccount(me);
accountManager.updateActiveAccount(me);
NotificationHelper.createNotificationChannelsForAccount(am.getActiveAccount(), this);
NotificationHelper.createNotificationChannelsForAccount(accountManager.getActiveAccount(), this);
// Show follow requests in the menu, if this is a locked account.
if (me.getLocked() && drawer.getDrawerItem(DRAWER_ITEM_FOLLOW_REQUESTS) == null) {
@ -513,19 +517,18 @@ public class MainActivity extends BaseActivity implements ActionButtonActivity {
}
private void updateProfiles() {
AccountManager am = TuskyApplication.getAccountManager();
List<AccountEntity> allAccounts = am.getAllAccountsOrderedByActive();
List<AccountEntity> allAccounts = accountManager.getAllAccountsOrderedByActive();
//remove profiles before adding them again to avoid duplicates
List<IProfile> profiles = new ArrayList<>(headerResult.getProfiles());
for(IProfile profile: profiles) {
if(profile.getIdentifier() != DRAWER_ITEM_ADD_ACCOUNT) {
for (IProfile profile : profiles) {
if (profile.getIdentifier() != DRAWER_ITEM_ADD_ACCOUNT) {
headerResult.removeProfile(profile);
}
}
for(AccountEntity acc: allAccounts) {
for (AccountEntity acc : allAccounts) {
headerResult.addProfiles(
new ProfileDrawerItem()
.withName(acc.getDisplayName())

@ -27,6 +27,7 @@ import com.evernote.android.job.JobCreator;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.keylesspalace.tusky.db.AccountEntity;
import com.keylesspalace.tusky.db.AccountManager;
import com.keylesspalace.tusky.entity.Notification;
import com.keylesspalace.tusky.json.SpannedTypeAdapter;
import com.keylesspalace.tusky.network.MastodonApi;
@ -101,14 +102,16 @@ public final class NotificationPullJobCreator implements JobCreator {
@Override
protected Result onRunJob(@NonNull Params params) {
List<AccountEntity> accountList = new ArrayList<>(TuskyApplication.getAccountManager().getAllAccountsOrderedByActive());
AccountManager accountManager = TuskyApplication.getInstance(context).getServiceLocator()
.get(AccountManager.class);
List<AccountEntity> accountList = new ArrayList<>(accountManager.getAllAccountsOrderedByActive());
for(AccountEntity account: accountList) {
for (AccountEntity account : accountList) {
if(account.getNotificationsEnabled()) {
if (account.getNotificationsEnabled()) {
MastodonApi api = createMastodonApi(account.getDomain(), context);
try {
Log.d(TAG, "getting Notifications for "+account.getFullName());
Log.d(TAG, "getting Notifications for " + account.getFullName());
Response<List<Notification>> notifications =
api.notificationsWithAuth(String.format("Bearer %s", account.getAccessToken())).execute();
if (notifications.isSuccessful()) {
@ -136,11 +139,11 @@ public final class NotificationPullJobCreator implements JobCreator {
BigInteger newestId = BigInteger.ZERO;
for(Notification notification: notificationList){
for (Notification notification : notificationList) {
BigInteger currentId = new BigInteger(notification.getId());
if(isBiggerThan(currentId, newestId)) {
if (isBiggerThan(currentId, newestId)) {
newestId = currentId;
}
@ -150,13 +153,13 @@ public final class NotificationPullJobCreator implements JobCreator {
}
account.setLastNotificationId(newestId.toString());
TuskyApplication.getAccountManager().saveAccount(account);
TuskyApplication.getInstance(context).getServiceLocator()
.get(AccountManager.class).saveAccount(account);
}
private boolean isBiggerThan(BigInteger newId, BigInteger lastShownNotificationId) {
return lastShownNotificationId.compareTo(newId) == - 1;
return lastShownNotificationId.compareTo(newId) == -1;
}
}
}

@ -20,6 +20,7 @@ import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import com.keylesspalace.tusky.db.AccountEntity;
import com.keylesspalace.tusky.db.AccountManager;
import com.keylesspalace.tusky.util.NotificationHelper;
public class SplashActivity extends AppCompatActivity {
@ -32,7 +33,8 @@ public class SplashActivity extends AppCompatActivity {
NotificationHelper.deleteLegacyNotificationChannels(this);
AccountEntity activeAccount = TuskyApplication.getAccountManager().getActiveAccount();
AccountEntity activeAccount = TuskyApplication.getInstance(this).getServiceLocator()
.get(AccountManager.class).getActiveAccount();
Intent intent;
if (activeAccount != null) {

@ -21,6 +21,7 @@ import android.arch.persistence.room.Room;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatDelegate;
import com.evernote.android.job.JobManager;
@ -35,7 +36,7 @@ public class TuskyApplication extends Application {
public static final String APP_THEME_DEFAULT = ThemeUtils.THEME_NIGHT;
private static AppDatabase db;
private static AccountManager accountManager;
private AccountManager accountManager;
public static AppDatabase getDB() {
return db;
@ -43,24 +44,33 @@ public class TuskyApplication extends Application {
private static UiModeManager uiModeManager;
public static UiModeManager getUiModeManager() { return uiModeManager; }
public static UiModeManager getUiModeManager() {
return uiModeManager;
}
public static TuskyApplication getInstance(@NonNull Context context) {
return (TuskyApplication) context.getApplicationContext();
}
private ServiceLocator serviceLocator;
@Override
public void onCreate() {
super.onCreate();
// Initialize Picasso configuration
Picasso.Builder builder = new Picasso.Builder(this);
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
builder.downloader(new OkHttp3Downloader(OkHttpUtils.getCompatibleClient(preferences)));
if (BuildConfig.DEBUG) {
builder.listener((picasso, uri, exception) -> exception.printStackTrace());
}
try {
Picasso.setSingletonInstance(builder.build());
} catch (IllegalStateException e) {
throw new RuntimeException(e);
}
initPicasso();
serviceLocator = new ServiceLocator() {
@Override
public <T> T get(Class<T> clazz) {
if (clazz.equals(AccountManager.class)) {
//noinspection unchecked
return (T) accountManager;
} else {
throw new IllegalArgumentException("Unknown service " + clazz);
}
}
};
db = Room.databaseBuilder(getApplicationContext(), AppDatabase.class, "tuskyDB")
.allowMainThreadQueries()
@ -69,7 +79,7 @@ public class TuskyApplication extends Application {
JobManager.create(this).addJobCreator(new NotificationPullJobCreator(this));
uiModeManager = (UiModeManager)getSystemService(Context.UI_MODE_SERVICE);
uiModeManager = (UiModeManager) getSystemService(Context.UI_MODE_SERVICE);
//necessary for Android < APi 21
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
@ -77,8 +87,26 @@ public class TuskyApplication extends Application {
accountManager = new AccountManager();
}
public static AccountManager getAccountManager() {
return accountManager;
protected void initPicasso() {
// Initialize Picasso configuration
Picasso.Builder builder = new Picasso.Builder(this);
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
builder.downloader(new OkHttp3Downloader(OkHttpUtils.getCompatibleClient(preferences)));
if (BuildConfig.DEBUG) {
builder.listener((picasso, uri, exception) -> exception.printStackTrace());
}
try {
Picasso.setSingletonInstance(builder.build());
} catch (IllegalStateException e) {
throw new RuntimeException(e);
}
}
public ServiceLocator getServiceLocator() {
return serviceLocator;
}
}
public interface ServiceLocator {
<T> T get(Class<T> clazz);
}
}

@ -192,10 +192,12 @@ public class NotificationsFragment extends SFragment implements
TabLayout layout = activity.findViewById(R.id.tab_layout);
onTabSelectedListener = new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {}
public void onTabSelected(TabLayout.Tab tab) {
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {}
public void onTabUnselected(TabLayout.Tab tab) {
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
@ -281,7 +283,7 @@ public class NotificationsFragment extends SFragment implements
status.getReblog().setReblogged(reblog);
}
NotificationViewData.Concrete viewdata = (NotificationViewData.Concrete)notifications.getPairedItem(position);
NotificationViewData.Concrete viewdata = (NotificationViewData.Concrete) notifications.getPairedItem(position);
StatusViewData.Builder viewDataBuilder = new StatusViewData.Builder(viewdata.getStatusViewData());
viewDataBuilder.setReblogged(reblog);
@ -318,7 +320,7 @@ public class NotificationsFragment extends SFragment implements
status.getReblog().setFavourited(favourite);
}
NotificationViewData.Concrete viewdata = (NotificationViewData.Concrete)notifications.getPairedItem(position);
NotificationViewData.Concrete viewdata = (NotificationViewData.Concrete) notifications.getPairedItem(position);
StatusViewData.Builder viewDataBuilder = new StatusViewData.Builder(viewdata.getStatusViewData());
viewDataBuilder.setFavourited(favourite);
@ -587,13 +589,14 @@ public class NotificationsFragment extends SFragment implements
}
private void saveNewestNotificationId(List<Notification> notifications) {
AccountManager accountManager = TuskyApplication.getAccountManager();
AccountManager accountManager = TuskyApplication.getInstance(getContext())
.getServiceLocator().get(AccountManager.class);
AccountEntity account = accountManager.getActiveAccount();
BigInteger lastNoti = new BigInteger(account.getLastNotificationId());
for (Notification noti: notifications) {
for (Notification noti : notifications) {
BigInteger a = new BigInteger(noti.getId());
if(isBiggerThan(a, lastNoti)) {
if (isBiggerThan(a, lastNoti)) {
lastNoti = a;
}
}
@ -606,7 +609,7 @@ public class NotificationsFragment extends SFragment implements
private boolean isBiggerThan(BigInteger newId, BigInteger lastShownNotificationId) {
return lastShownNotificationId.compareTo(newId) == - 1;
return lastShownNotificationId.compareTo(newId) == -1;
}
private void update(@Nullable List<Notification> newNotifications, @Nullable String fromId,

@ -30,6 +30,7 @@ import com.keylesspalace.tusky.PreferencesActivity;
import com.keylesspalace.tusky.R;
import com.keylesspalace.tusky.TuskyApplication;
import com.keylesspalace.tusky.db.AccountEntity;
import com.keylesspalace.tusky.db.AccountManager;
public class PreferencesFragment extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener {
SharedPreferences sharedPreferences;
@ -47,20 +48,26 @@ public class PreferencesFragment extends PreferenceFragment implements SharedPre
return fragment;
}
private AccountManager accountManager;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
accountManager = TuskyApplication.getInstance(getActivity()).getServiceLocator()
.get(AccountManager.class);
int preference = getArguments().getInt("preference");
addPreferencesFromResource(preference);
Preference notificationPreferences = findPreference("notificationPreferences");
Preference notificationPreferences = findPreference("notificationPreferences");
if(notificationPreferences != null) {
if (notificationPreferences != null) {
AccountEntity activeAccount = TuskyApplication.getAccountManager().getActiveAccount();
AccountEntity activeAccount = accountManager.getActiveAccount();
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O && activeAccount != null) {
notificationPreferences.setSummary(getString(R.string.pref_summary_notifications, activeAccount.getFullName()));
@ -92,8 +99,8 @@ public class PreferencesFragment extends PreferenceFragment implements SharedPre
}
}
Preference timelineFilterPreferences = findPreference("timelineFilterPreferences");
if(timelineFilterPreferences != null) {
Preference timelineFilterPreferences = findPreference("timelineFilterPreferences");
if (timelineFilterPreferences != null) {
timelineFilterPreferences.setOnPreferenceClickListener(pref -> {
PreferencesActivity activity = (PreferencesActivity) getActivity();
if (activity != null) {
@ -104,8 +111,8 @@ public class PreferencesFragment extends PreferenceFragment implements SharedPre
});
}
Preference httpProxyPreferences = findPreference("httpProxyPreferences");
if(httpProxyPreferences != null) {
Preference httpProxyPreferences = findPreference("httpProxyPreferences");
if (httpProxyPreferences != null) {
httpProxyPreferences.setOnPreferenceClickListener(pref -> {
PreferencesActivity activity = (PreferencesActivity) getActivity();
if (activity != null) {
@ -117,11 +124,11 @@ public class PreferencesFragment extends PreferenceFragment implements SharedPre
});
}
if(preference == R.xml.notification_preferences) {
if (preference == R.xml.notification_preferences) {
AccountEntity activeAccount = TuskyApplication.getAccountManager().getActiveAccount();
AccountEntity activeAccount = accountManager.getActiveAccount();
if(activeAccount != null) {
if (activeAccount != null) {
CheckBoxPreference notificationPref = (CheckBoxPreference) findPreference("notificationsEnabled");
notificationPref.setChecked(activeAccount.getNotificationsEnabled());
@ -188,10 +195,10 @@ public class PreferencesFragment extends PreferenceFragment implements SharedPre
default:
}
AccountEntity activeAccount = TuskyApplication.getAccountManager().getActiveAccount();
AccountEntity activeAccount = accountManager.getActiveAccount();
if(activeAccount != null) {
switch(key) {
if (activeAccount != null) {
switch (key) {
case "notificationsEnabled":
activeAccount.setNotificationsEnabled(sharedPreferences.getBoolean(key, true));
break;
@ -217,7 +224,7 @@ public class PreferencesFragment extends PreferenceFragment implements SharedPre
activeAccount.setNotificationLight(sharedPreferences.getBoolean(key, true));
break;
}
TuskyApplication.getAccountManager().saveAccount(activeAccount);
accountManager.saveAccount(activeAccount);
}

@ -41,6 +41,7 @@ import com.keylesspalace.tusky.ViewTagActivity;
import com.keylesspalace.tusky.ViewThreadActivity;
import com.keylesspalace.tusky.ViewVideoActivity;
import com.keylesspalace.tusky.db.AccountEntity;
import com.keylesspalace.tusky.db.AccountManager;
import com.keylesspalace.tusky.entity.Attachment;
import com.keylesspalace.tusky.entity.Relationship;
import com.keylesspalace.tusky.entity.Status;
@ -71,19 +72,14 @@ public abstract class SFragment extends BaseFragment implements AdapterItemRemov
protected MastodonApi mastodonApi;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AccountEntity activeAccount = TuskyApplication.getAccountManager().getActiveAccount();
if(activeAccount != null) {
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
AccountEntity activeAccount = TuskyApplication.getInstance(getContext()).getServiceLocator()
.get(AccountManager.class).getActiveAccount();
if (activeAccount != null) {
loggedInAccountId = activeAccount.getAccountId();
loggedInUsername = activeAccount.getUsername();
}
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
BaseActivity activity = (BaseActivity) getActivity();
mastodonApi = activity.mastodonApi;
}
@ -153,10 +149,12 @@ public abstract class SFragment extends BaseFragment implements AdapterItemRemov
Call<Relationship> call = mastodonApi.muteAccount(id);
call.enqueue(new Callback<Relationship>() {
@Override
public void onResponse(@NonNull Call<Relationship> call, @NonNull Response<Relationship> response) {}
public void onResponse(@NonNull Call<Relationship> call, @NonNull Response<Relationship> response) {
}
@Override
public void onFailure(@NonNull Call<Relationship> call, @NonNull Throwable t) {}
public void onFailure(@NonNull Call<Relationship> call, @NonNull Throwable t) {
}
});
callList.add(call);
Intent intent = new Intent(TimelineReceiver.Types.MUTE_ACCOUNT);
@ -169,10 +167,12 @@ public abstract class SFragment extends BaseFragment implements AdapterItemRemov
Call<Relationship> call = mastodonApi.blockAccount(id);
call.enqueue(new Callback<Relationship>() {
@Override
public void onResponse(@NonNull Call<Relationship> call, @NonNull retrofit2.Response<Relationship> response) {}
public void onResponse(@NonNull Call<Relationship> call, @NonNull retrofit2.Response<Relationship> response) {
}
@Override
public void onFailure(@NonNull Call<Relationship> call, @NonNull Throwable t) {}
public void onFailure(@NonNull Call<Relationship> call, @NonNull Throwable t) {
}
});
callList.add(call);
Intent intent = new Intent(TimelineReceiver.Types.BLOCK_ACCOUNT);
@ -185,10 +185,12 @@ public abstract class SFragment extends BaseFragment implements AdapterItemRemov
Call<ResponseBody> call = mastodonApi.deleteStatus(id);
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(@NonNull Call<ResponseBody> call, @NonNull retrofit2.Response<ResponseBody> response) {}
public void onResponse(@NonNull Call<ResponseBody> call, @NonNull retrofit2.Response<ResponseBody> response) {
}
@Override
public void onFailure(@NonNull Call<ResponseBody> call, @NonNull Throwable t) {}
public void onFailure(@NonNull Call<ResponseBody> call, @NonNull Throwable t) {
}
});
callList.add(call);
}
@ -275,7 +277,7 @@ public abstract class SFragment extends BaseFragment implements AdapterItemRemov
ViewCompat.setTransitionName(view, url);
ActivityOptionsCompat options =
ActivityOptionsCompat.makeSceneTransitionAnimation(getActivity(),
view, url);
view, url);
startActivity(intent, options.toBundle());
} else {
startActivity(intent);
@ -318,7 +320,7 @@ public abstract class SFragment extends BaseFragment implements AdapterItemRemov
}
protected void openReportPage(String accountId, String accountUsername, String statusId,
Spanned statusContent) {
Spanned statusContent) {
Intent intent = new Intent(getContext(), ReportActivity.class);
intent.putExtra("account_id", accountId);
intent.putExtra("account_username", accountUsername);

@ -4,6 +4,7 @@ import android.support.annotation.NonNull;
import com.keylesspalace.tusky.TuskyApplication;
import com.keylesspalace.tusky.db.AccountEntity;
import com.keylesspalace.tusky.db.AccountManager;
import java.io.IOException;
@ -17,14 +18,17 @@ import okhttp3.Response;
public final class AuthInterceptor implements Interceptor {
public AuthInterceptor() { }
AccountManager accountManager;
public AuthInterceptor(AccountManager accountManager) {
this.accountManager = accountManager;
}
@Override
public Response intercept(@NonNull Chain chain) throws IOException {
AccountEntity currentAccount = TuskyApplication.getAccountManager().getActiveAccount();
Request originalRequest = chain.request();
AccountEntity currentAccount = accountManager.getActiveAccount();
Request.Builder builder = originalRequest.newBuilder();
if (currentAccount != null) {

@ -20,6 +20,7 @@ import android.content.Context
import android.content.Intent
import com.keylesspalace.tusky.TuskyApplication
import com.keylesspalace.tusky.db.AccountManager
import com.keylesspalace.tusky.util.NotificationHelper
class NotificationClearBroadcastReceiver : BroadcastReceiver() {
@ -27,7 +28,8 @@ class NotificationClearBroadcastReceiver : BroadcastReceiver() {
val accountId = intent.getLongExtra(NotificationHelper.ACCOUNT_ID, -1)
val accountManager = TuskyApplication.getAccountManager()
val accountManager = TuskyApplication.getInstance(context)
.serviceLocator.get(AccountManager::class.java)
val account = accountManager.getAccountById(accountId)
if (account != null) {
account.activeNotifications = "[]"

@ -50,12 +50,16 @@ import java.util.List;
public class NotificationHelper {
/** constants used in Intents */
/**
* constants used in Intents
*/
public static final String ACCOUNT_ID = "account_id";
private static final String TAG = "NotificationHelper";
/** notification channels used on Android O+ **/
/**
* notification channels used on Android O+
**/
private static final String CHANNEL_MENTION = "CHANNEL_MENTION";
private static final String CHANNEL_FOLLOW = "CHANNEL_FOLLOW";
private static final String CHANNEL_BOOST = "CHANNEL_BOOST";
@ -65,9 +69,9 @@ public class NotificationHelper {
* Takes a given Mastodon notification and either creates a new Android notification or updates
* the state of the existing notification to reflect the new interaction.
*
* @param context to access application preferences and services
* @param body a new Mastodon notification
* @param account the account for which the notification should be shown
* @param context to access application preferences and services
* @param body a new Mastodon notification
* @param account the account for which the notification should be shown
*/
public static void make(final Context context, Notification body, AccountEntity account) {
@ -110,12 +114,12 @@ public class NotificationHelper {
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
stackBuilder.addParentStack(MainActivity.class);
stackBuilder.addNextIntent(resultIntent);
PendingIntent resultPendingIntent = stackBuilder.getPendingIntent((int)account.getId(),
PendingIntent resultPendingIntent = stackBuilder.getPendingIntent((int) account.getId(),
PendingIntent.FLAG_UPDATE_CURRENT);
Intent deleteIntent = new Intent(context, NotificationClearBroadcastReceiver.class);
deleteIntent.putExtra(ACCOUNT_ID, account.getId());
PendingIntent deletePendingIntent = PendingIntent.getBroadcast(context, (int)account.getId(), deleteIntent,
PendingIntent deletePendingIntent = PendingIntent.getBroadcast(context, (int) account.getId(), deleteIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
final NotificationCompat.Builder builder = new NotificationCompat.Builder(context, getChannelId(account, body))
@ -131,7 +135,7 @@ public class NotificationHelper {
builder.setContentTitle(titleForType(context, body))
.setContentText(bodyForType(body));
if(body.getType() == Notification.Type.MENTION) {
if (body.getType() == Notification.Type.MENTION) {
builder.setStyle(new NotificationCompat.BigTextStyle()
.bigText(bodyForType(body)));
}
@ -171,7 +175,7 @@ public class NotificationHelper {
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
//noinspection ConstantConditions
notificationManager.notify((int)account.getId(), builder.build());
notificationManager.notify((int) account.getId(), builder.build());
}
public static void createNotificationChannelsForAccount(AccountEntity account, Context context) {
@ -180,10 +184,10 @@ public class NotificationHelper {
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
String[] channelIds = new String[]{
CHANNEL_MENTION+account.getIdentifier(),
CHANNEL_FOLLOW+account.getIdentifier(),
CHANNEL_BOOST+account.getIdentifier(),
CHANNEL_FAVOURITE+account.getIdentifier()};
CHANNEL_MENTION + account.getIdentifier(),
CHANNEL_FOLLOW + account.getIdentifier(),
CHANNEL_BOOST + account.getIdentifier(),
CHANNEL_FAVOURITE + account.getIdentifier()};
int[] channelNames = {
R.string.notification_channel_mention_name,
R.string.notification_channel_follow_name,
@ -252,15 +256,15 @@ public class NotificationHelper {
}
public static boolean areNotificationsEnabled(Context context) {
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// on Android >= O, notifications are enabled, if at least one channel is enabled
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
//noinspection ConstantConditions
if(notificationManager.areNotificationsEnabled()) {
for(NotificationChannel channel: notificationManager.getNotificationChannels()) {
if(channel.getImportance() > NotificationManager.IMPORTANCE_NONE) {
if (notificationManager.areNotificationsEnabled()) {
for (NotificationChannel channel : notificationManager.getNotificationChannels()) {
if (channel.getImportance() > NotificationManager.IMPORTANCE_NONE) {
Log.d(TAG, "NotificationsEnabled");
return true;
}
@ -272,13 +276,15 @@ public class NotificationHelper {
} else {
// on Android < O, notifications are enabled, if at least one account has notification enabled
return TuskyApplication.getAccountManager().areNotificationsEnabled();
return TuskyApplication.getInstance(context).getServiceLocator()
.get(AccountManager.class).areNotificationsEnabled();
}
}
public static void clearNotificationsForActiveAccount(Context context) {
AccountManager accountManager = TuskyApplication.getAccountManager();
AccountManager accountManager = TuskyApplication.getInstance(context).getServiceLocator()
.get(AccountManager.class);
AccountEntity account = accountManager.getActiveAccount();
if (account != null) {
account.setActiveNotifications("[]");
@ -286,7 +292,7 @@ public class NotificationHelper {
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
//noinspection ConstantConditions
notificationManager.cancel((int)account.getId());
notificationManager.cancel((int) account.getId());
}
}
@ -318,13 +324,13 @@ public class NotificationHelper {
switch (notification.getType()) {
default:
case MENTION:
return CHANNEL_MENTION+account.getIdentifier();
return CHANNEL_MENTION + account.getIdentifier();
case FOLLOW:
return CHANNEL_FOLLOW+account.getIdentifier();
return CHANNEL_FOLLOW + account.getIdentifier();
case REBLOG:
return CHANNEL_BOOST+account.getIdentifier();
return CHANNEL_BOOST + account.getIdentifier();
case FAVOURITE:
return CHANNEL_FAVOURITE+account.getIdentifier();
return CHANNEL_FAVOURITE + account.getIdentifier();
}
}
@ -354,7 +360,7 @@ public class NotificationHelper {
if (array.length() > 3) {
int length = array.length();
return String.format(context.getString(R.string.notification_summary_large),
array.get(length-1), array.get(length-2), array.get(length-3), length - 3);
array.get(length - 1), array.get(length - 2), array.get(length - 3), length - 3);
} else if (array.length() == 3) {
return String.format(context.getString(R.string.notification_summary_medium),
array.get(2), array.get(1), array.get(0));
@ -389,7 +395,7 @@ public class NotificationHelper {
private static String bodyForType(Notification notification) {
switch (notification.getType()) {
case FOLLOW:
return "@"+ notification.getAccount().getUsername();
return "@" + notification.getAccount().getUsername();
case MENTION:
case FAVOURITE:
case REBLOG:

@ -0,0 +1,103 @@
package com.keylesspalace.tusky
import android.widget.EditText
import com.keylesspalace.tusky.db.AccountEntity
import com.keylesspalace.tusky.db.AccountManager
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito
import org.mockito.Mockito.`when`
import org.robolectric.Robolectric
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import org.robolectric.fakes.RoboMenuItem
/**
* Created by charlag on 3/7/18.
*/
@Config(application = FakeTuskyApplication::class)
@RunWith(RobolectricTestRunner::class)
class ComposeActivityTest {
lateinit var activity: ComposeActivity
lateinit var application: FakeTuskyApplication
lateinit var serviceLocator: TuskyApplication.ServiceLocator
lateinit var accountManagerMock: AccountManager
val account = AccountEntity(
id = 1,
domain = "example.token",
accessToken = "token",
isActive = true,
accountId = "1",
username = "username",
displayName = "Display Name",
profilePictureUrl = "",
notificationsEnabled = true,
notificationsMentioned = true,
notificationsFollowed = true,
notificationsReblogged = true,
notificationsFavorited = true,
notificationSound = true,
notificationVibration = true,
notificationLight = true
)
@Before
fun before() {
val controller = Robolectric.buildActivity(ComposeActivity::class.java)
activity = controller.get()
application = activity.application as FakeTuskyApplication
serviceLocator = Mockito.mock(TuskyApplication.ServiceLocator::class.java)
application.locator = serviceLocator
accountManagerMock = Mockito.mock(AccountManager::class.java)
`when`(serviceLocator.get(AccountManager::class.java)).thenReturn(accountManagerMock)
`when`(accountManagerMock.activeAccount).thenReturn(account)
controller.create().start()
}
@Test
fun whenCloseButtonPressedAndEmpty_finish() {
clickUp()
assertTrue(activity.isFinishing)
}
@Test
fun whenCloseButtonPressedNotEmpty_notFinish() {
insertSomeTextInContent()
clickUp()
assertFalse(activity.isFinishing)
// We would like to check for dialog but Robolectric doesn't work with AppCompat v7 yet
}
@Test
fun whenBackButtonPressedAndEmpty_finish() {
clickBack()
assertTrue(activity.isFinishing)
}
@Test
fun whenBackButtonPressedNotEmpty_notFinish() {
insertSomeTextInContent()
clickBack()
assertFalse(activity.isFinishing)
// We would like to check for dialog but Robolectric doesn't work with AppCompat v7 yet
}
private fun clickUp() {
val menuItem = RoboMenuItem(android.R.id.home)
activity.onOptionsItemSelected(menuItem)
}
private fun clickBack() {
activity.onBackPressed()
}
private fun insertSomeTextInContent() {
activity.findViewById<EditText>(R.id.compose_edit_field).setText("Some text")
}
}

@ -1,17 +0,0 @@
package com.keylesspalace.tusky;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}

@ -0,0 +1,18 @@
package com.keylesspalace.tusky
/**
* Created by charlag on 3/7/18.
*/
class FakeTuskyApplication : TuskyApplication() {
lateinit var locator: ServiceLocator
override fun initPicasso() {
// No-op
}
override fun getServiceLocator(): ServiceLocator {
return locator
}
}
Loading…
Cancel
Save