Add ComposeActivity tests. Add ServiceLocator (#542)

This commit is contained in:
Ivan Kupalov 2018-03-10 00:02:32 +03:00 committed by Konrad Pozniak
parent 4e617dccc7
commit 28e46c9cc0
18 changed files with 337 additions and 154 deletions

View File

@ -36,6 +36,11 @@ android {
androidExtensions { androidExtensions {
experimental = true experimental = true
} }
testOptions {
unitTests {
includeAndroidResources = true
}
}
} }
ext.supportLibraryVersion = '27.1.0' ext.supportLibraryVersion = '27.1.0'
@ -68,12 +73,15 @@ dependencies {
//room //room
implementation 'android.arch.persistence.room:runtime:1.0.0' implementation 'android.arch.persistence.room:runtime:1.0.0'
kapt 'android.arch.persistence.room:compiler: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', { androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations' 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' debugImplementation 'im.dino:dbinspector:3.4.1@aar'
} }

View File

@ -46,6 +46,7 @@ import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import com.keylesspalace.tusky.db.AccountEntity; import com.keylesspalace.tusky.db.AccountEntity;
import com.keylesspalace.tusky.db.AccountManager;
import com.keylesspalace.tusky.entity.Account; import com.keylesspalace.tusky.entity.Account;
import com.keylesspalace.tusky.entity.Relationship; import com.keylesspalace.tusky.entity.Relationship;
import com.keylesspalace.tusky.interfaces.ActionButtonActivity; 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. // Obtain information to fill out the profile.
obtainAccount(); obtainAccount();
AccountEntity activeAccount = TuskyApplication.getAccountManager().getActiveAccount(); AccountEntity activeAccount = TuskyApplication.getInstance(this).getServiceLocator()
.get(AccountManager.class).getActiveAccount();
if (accountId.equals(activeAccount.getAccountId())) { if (accountId.equals(activeAccount.getAccountId())) {
isSelf = true; isSelf = true;

View File

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

View File

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

View File

@ -31,6 +31,7 @@ import android.view.MenuItem
import android.view.View import android.view.View
import android.widget.EditText import android.widget.EditText
import android.widget.TextView import android.widget.TextView
import com.keylesspalace.tusky.db.AccountManager
import com.keylesspalace.tusky.entity.AccessToken import com.keylesspalace.tusky.entity.AccessToken
import com.keylesspalace.tusky.entity.AppCredentials import com.keylesspalace.tusky.entity.AppCredentials
import com.keylesspalace.tusky.network.MastodonApi import com.keylesspalace.tusky.network.MastodonApi
@ -289,7 +290,9 @@ class LoginActivity : AppCompatActivity() {
setLoading(true) 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) val intent = Intent(this, MainActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK

View File

@ -84,6 +84,8 @@ public class MainActivity extends BaseActivity implements ActionButtonActivity {
private static int COMPOSE_RESULT = 1; private static int COMPOSE_RESULT = 1;
AccountManager accountManager;
private FloatingActionButton composeButton; private FloatingActionButton composeButton;
private AccountHeader headerResult; private AccountHeader headerResult;
private Drawer drawer; private Drawer drawer;
@ -97,16 +99,19 @@ public class MainActivity extends BaseActivity implements ActionButtonActivity {
int tabPosition = 0; int tabPosition = 0;
accountManager = TuskyApplication.getInstance(this).getServiceLocator()
.get(AccountManager.class);
if (intent != null) { if (intent != null) {
long accountId = intent.getLongExtra(NotificationHelper.ACCOUNT_ID, -1); 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 // user clicked a notification, show notification tab and switch user if necessary
tabPosition = 1; tabPosition = 1;
AccountEntity account = TuskyApplication.getAccountManager().getActiveAccount(); AccountEntity account = accountManager.getActiveAccount();
if (account == null || accountId != account.getId()) { if (account == null || accountId != account.getId()) {
TuskyApplication.getAccountManager().setActiveAccount(accountId); accountManager.setActiveAccount(accountId);
} }
} }
} }
@ -191,7 +196,8 @@ public class MainActivity extends BaseActivity implements ActionButtonActivity {
} }
@Override @Override
public void onTabReselected(TabLayout.Tab tab) { } public void onTabReselected(TabLayout.Tab tab) {
}
}); });
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
@ -399,7 +405,7 @@ public class MainActivity extends BaseActivity implements ActionButtonActivity {
} }
private boolean handleProfileClick(IProfile profile, boolean current) { private boolean handleProfileClick(IProfile profile, boolean current) {
AccountEntity activeAccount = TuskyApplication.getAccountManager().getActiveAccount(); AccountEntity activeAccount = accountManager.getActiveAccount();
//open profile when active image was clicked //open profile when active image was clicked
if (current && activeAccount != null) { if (current && activeAccount != null) {
@ -420,7 +426,7 @@ public class MainActivity extends BaseActivity implements ActionButtonActivity {
private void changeAccount(long newSelectedId) { private void changeAccount(long newSelectedId) {
TuskyApplication.getAccountManager().setActiveAccount(newSelectedId); accountManager.setActiveAccount(newSelectedId);
Intent intent = new Intent(this, MainActivity.class); Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
@ -432,7 +438,7 @@ public class MainActivity extends BaseActivity implements ActionButtonActivity {
private void logout() { private void logout() {
AccountEntity activeAccount = TuskyApplication.getAccountManager().getActiveAccount(); AccountEntity activeAccount = accountManager.getActiveAccount();
if (activeAccount != null) { if (activeAccount != null) {
@ -440,14 +446,14 @@ public class MainActivity extends BaseActivity implements ActionButtonActivity {
.setTitle(R.string.action_logout) .setTitle(R.string.action_logout)
.setMessage(getString(R.string.action_logout_confirm, activeAccount.getFullName())) .setMessage(getString(R.string.action_logout_confirm, activeAccount.getFullName()))
.setPositiveButton(android.R.string.yes, (dialog, which) -> { .setPositiveButton(android.R.string.yes, (dialog, which) -> {
;
AccountManager accountManager = TuskyApplication.getAccountManager();
NotificationHelper.deleteNotificationChannelsForAccount(accountManager.getActiveAccount(), MainActivity.this); NotificationHelper.deleteNotificationChannelsForAccount(accountManager.getActiveAccount(), MainActivity.this);
AccountEntity newAccount = accountManager.logActiveAccountOut(); AccountEntity newAccount = accountManager.logActiveAccountOut();
if (!NotificationHelper.areNotificationsEnabled(MainActivity.this)) disablePushNotifications(); if (!NotificationHelper.areNotificationsEnabled(MainActivity.this))
disablePushNotifications();
Intent intent; Intent intent;
if (newAccount == null) { if (newAccount == null) {
@ -492,11 +498,9 @@ public class MainActivity extends BaseActivity implements ActionButtonActivity {
.placeholder(R.drawable.account_header_default) .placeholder(R.drawable.account_header_default)
.into(background); .into(background);
AccountManager am = TuskyApplication.getAccountManager(); accountManager.updateActiveAccount(me);
am.updateActiveAccount(me); NotificationHelper.createNotificationChannelsForAccount(accountManager.getActiveAccount(), this);
NotificationHelper.createNotificationChannelsForAccount(am.getActiveAccount(), this);
// Show follow requests in the menu, if this is a locked account. // Show follow requests in the menu, if this is a locked account.
if (me.getLocked() && drawer.getDrawerItem(DRAWER_ITEM_FOLLOW_REQUESTS) == null) { if (me.getLocked() && drawer.getDrawerItem(DRAWER_ITEM_FOLLOW_REQUESTS) == null) {
@ -513,9 +517,8 @@ public class MainActivity extends BaseActivity implements ActionButtonActivity {
} }
private void updateProfiles() { 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 //remove profiles before adding them again to avoid duplicates
List<IProfile> profiles = new ArrayList<>(headerResult.getProfiles()); List<IProfile> profiles = new ArrayList<>(headerResult.getProfiles());

View File

@ -27,6 +27,7 @@ import com.evernote.android.job.JobCreator;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
import com.keylesspalace.tusky.db.AccountEntity; import com.keylesspalace.tusky.db.AccountEntity;
import com.keylesspalace.tusky.db.AccountManager;
import com.keylesspalace.tusky.entity.Notification; import com.keylesspalace.tusky.entity.Notification;
import com.keylesspalace.tusky.json.SpannedTypeAdapter; import com.keylesspalace.tusky.json.SpannedTypeAdapter;
import com.keylesspalace.tusky.network.MastodonApi; import com.keylesspalace.tusky.network.MastodonApi;
@ -101,7 +102,9 @@ public final class NotificationPullJobCreator implements JobCreator {
@Override @Override
protected Result onRunJob(@NonNull Params params) { 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) {
@ -150,8 +153,8 @@ public final class NotificationPullJobCreator implements JobCreator {
} }
account.setLastNotificationId(newestId.toString()); account.setLastNotificationId(newestId.toString());
TuskyApplication.getAccountManager().saveAccount(account); TuskyApplication.getInstance(context).getServiceLocator()
.get(AccountManager.class).saveAccount(account);
} }
private boolean isBiggerThan(BigInteger newId, BigInteger lastShownNotificationId) { private boolean isBiggerThan(BigInteger newId, BigInteger lastShownNotificationId) {

View File

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

View File

@ -21,6 +21,7 @@ import android.arch.persistence.room.Room;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatDelegate; import android.support.v7.app.AppCompatDelegate;
import com.evernote.android.job.JobManager; 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; public static final String APP_THEME_DEFAULT = ThemeUtils.THEME_NIGHT;
private static AppDatabase db; private static AppDatabase db;
private static AccountManager accountManager; private AccountManager accountManager;
public static AppDatabase getDB() { public static AppDatabase getDB() {
return db; return db;
@ -43,24 +44,33 @@ public class TuskyApplication extends Application {
private static UiModeManager uiModeManager; 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 @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
// Initialize Picasso configuration initPicasso();
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 { serviceLocator = new ServiceLocator() {
Picasso.setSingletonInstance(builder.build()); @Override
} catch (IllegalStateException e) { public <T> T get(Class<T> clazz) {
throw new RuntimeException(e); if (clazz.equals(AccountManager.class)) {
//noinspection unchecked
return (T) accountManager;
} else {
throw new IllegalArgumentException("Unknown service " + clazz);
} }
}
};
db = Room.databaseBuilder(getApplicationContext(), AppDatabase.class, "tuskyDB") db = Room.databaseBuilder(getApplicationContext(), AppDatabase.class, "tuskyDB")
.allowMainThreadQueries() .allowMainThreadQueries()
@ -77,8 +87,26 @@ public class TuskyApplication extends Application {
accountManager = new AccountManager(); accountManager = new AccountManager();
} }
public static AccountManager getAccountManager() { protected void initPicasso() {
return accountManager; // 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);
}
} }

View File

@ -192,10 +192,12 @@ public class NotificationsFragment extends SFragment implements
TabLayout layout = activity.findViewById(R.id.tab_layout); TabLayout layout = activity.findViewById(R.id.tab_layout);
onTabSelectedListener = new TabLayout.OnTabSelectedListener() { onTabSelectedListener = new TabLayout.OnTabSelectedListener() {
@Override @Override
public void onTabSelected(TabLayout.Tab tab) {} public void onTabSelected(TabLayout.Tab tab) {
}
@Override @Override
public void onTabUnselected(TabLayout.Tab tab) {} public void onTabUnselected(TabLayout.Tab tab) {
}
@Override @Override
public void onTabReselected(TabLayout.Tab tab) { public void onTabReselected(TabLayout.Tab tab) {
@ -587,7 +589,8 @@ public class NotificationsFragment extends SFragment implements
} }
private void saveNewestNotificationId(List<Notification> notifications) { private void saveNewestNotificationId(List<Notification> notifications) {
AccountManager accountManager = TuskyApplication.getAccountManager(); AccountManager accountManager = TuskyApplication.getInstance(getContext())
.getServiceLocator().get(AccountManager.class);
AccountEntity account = accountManager.getActiveAccount(); AccountEntity account = accountManager.getActiveAccount();
BigInteger lastNoti = new BigInteger(account.getLastNotificationId()); BigInteger lastNoti = new BigInteger(account.getLastNotificationId());

View File

@ -30,6 +30,7 @@ import com.keylesspalace.tusky.PreferencesActivity;
import com.keylesspalace.tusky.R; import com.keylesspalace.tusky.R;
import com.keylesspalace.tusky.TuskyApplication; import com.keylesspalace.tusky.TuskyApplication;
import com.keylesspalace.tusky.db.AccountEntity; import com.keylesspalace.tusky.db.AccountEntity;
import com.keylesspalace.tusky.db.AccountManager;
public class PreferencesFragment extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener { public class PreferencesFragment extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener {
SharedPreferences sharedPreferences; SharedPreferences sharedPreferences;
@ -47,10 +48,16 @@ public class PreferencesFragment extends PreferenceFragment implements SharedPre
return fragment; return fragment;
} }
private AccountManager accountManager;
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
accountManager = TuskyApplication.getInstance(getActivity()).getServiceLocator()
.get(AccountManager.class);
int preference = getArguments().getInt("preference"); int preference = getArguments().getInt("preference");
addPreferencesFromResource(preference); addPreferencesFromResource(preference);
@ -60,7 +67,7 @@ public class PreferencesFragment extends PreferenceFragment implements SharedPre
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) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O && activeAccount != null) {
notificationPreferences.setSummary(getString(R.string.pref_summary_notifications, activeAccount.getFullName())); notificationPreferences.setSummary(getString(R.string.pref_summary_notifications, activeAccount.getFullName()));
@ -119,7 +126,7 @@ 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) {
@ -188,7 +195,7 @@ public class PreferencesFragment extends PreferenceFragment implements SharedPre
default: default:
} }
AccountEntity activeAccount = TuskyApplication.getAccountManager().getActiveAccount(); AccountEntity activeAccount = accountManager.getActiveAccount();
if (activeAccount != null) { if (activeAccount != null) {
switch (key) { switch (key) {
@ -217,7 +224,7 @@ public class PreferencesFragment extends PreferenceFragment implements SharedPre
activeAccount.setNotificationLight(sharedPreferences.getBoolean(key, true)); activeAccount.setNotificationLight(sharedPreferences.getBoolean(key, true));
break; break;
} }
TuskyApplication.getAccountManager().saveAccount(activeAccount); accountManager.saveAccount(activeAccount);
} }

View File

@ -41,6 +41,7 @@ import com.keylesspalace.tusky.ViewTagActivity;
import com.keylesspalace.tusky.ViewThreadActivity; import com.keylesspalace.tusky.ViewThreadActivity;
import com.keylesspalace.tusky.ViewVideoActivity; import com.keylesspalace.tusky.ViewVideoActivity;
import com.keylesspalace.tusky.db.AccountEntity; import com.keylesspalace.tusky.db.AccountEntity;
import com.keylesspalace.tusky.db.AccountManager;
import com.keylesspalace.tusky.entity.Attachment; import com.keylesspalace.tusky.entity.Attachment;
import com.keylesspalace.tusky.entity.Relationship; import com.keylesspalace.tusky.entity.Relationship;
import com.keylesspalace.tusky.entity.Status; import com.keylesspalace.tusky.entity.Status;
@ -71,19 +72,14 @@ public abstract class SFragment extends BaseFragment implements AdapterItemRemov
protected MastodonApi mastodonApi; protected MastodonApi mastodonApi;
@Override @Override
public void onCreate(@Nullable Bundle savedInstanceState) { public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onActivityCreated(savedInstanceState);
AccountEntity activeAccount = TuskyApplication.getInstance(getContext()).getServiceLocator()
AccountEntity activeAccount = TuskyApplication.getAccountManager().getActiveAccount(); .get(AccountManager.class).getActiveAccount();
if (activeAccount != null) { if (activeAccount != null) {
loggedInAccountId = activeAccount.getAccountId(); loggedInAccountId = activeAccount.getAccountId();
loggedInUsername = activeAccount.getUsername(); loggedInUsername = activeAccount.getUsername();
} }
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
BaseActivity activity = (BaseActivity) getActivity(); BaseActivity activity = (BaseActivity) getActivity();
mastodonApi = activity.mastodonApi; mastodonApi = activity.mastodonApi;
} }
@ -153,10 +149,12 @@ public abstract class SFragment extends BaseFragment implements AdapterItemRemov
Call<Relationship> call = mastodonApi.muteAccount(id); Call<Relationship> call = mastodonApi.muteAccount(id);
call.enqueue(new Callback<Relationship>() { call.enqueue(new Callback<Relationship>() {
@Override @Override
public void onResponse(@NonNull Call<Relationship> call, @NonNull Response<Relationship> response) {} public void onResponse(@NonNull Call<Relationship> call, @NonNull Response<Relationship> response) {
}
@Override @Override
public void onFailure(@NonNull Call<Relationship> call, @NonNull Throwable t) {} public void onFailure(@NonNull Call<Relationship> call, @NonNull Throwable t) {
}
}); });
callList.add(call); callList.add(call);
Intent intent = new Intent(TimelineReceiver.Types.MUTE_ACCOUNT); 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<Relationship> call = mastodonApi.blockAccount(id);
call.enqueue(new Callback<Relationship>() { call.enqueue(new Callback<Relationship>() {
@Override @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 @Override
public void onFailure(@NonNull Call<Relationship> call, @NonNull Throwable t) {} public void onFailure(@NonNull Call<Relationship> call, @NonNull Throwable t) {
}
}); });
callList.add(call); callList.add(call);
Intent intent = new Intent(TimelineReceiver.Types.BLOCK_ACCOUNT); 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<ResponseBody> call = mastodonApi.deleteStatus(id);
call.enqueue(new Callback<ResponseBody>() { call.enqueue(new Callback<ResponseBody>() {
@Override @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 @Override
public void onFailure(@NonNull Call<ResponseBody> call, @NonNull Throwable t) {} public void onFailure(@NonNull Call<ResponseBody> call, @NonNull Throwable t) {
}
}); });
callList.add(call); callList.add(call);
} }

View File

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

View File

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

View File

@ -50,12 +50,16 @@ import java.util.List;
public class NotificationHelper { public class NotificationHelper {
/** constants used in Intents */ /**
* constants used in Intents
*/
public static final String ACCOUNT_ID = "account_id"; public static final String ACCOUNT_ID = "account_id";
private static final String TAG = "NotificationHelper"; 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_MENTION = "CHANNEL_MENTION";
private static final String CHANNEL_FOLLOW = "CHANNEL_FOLLOW"; private static final String CHANNEL_FOLLOW = "CHANNEL_FOLLOW";
private static final String CHANNEL_BOOST = "CHANNEL_BOOST"; private static final String CHANNEL_BOOST = "CHANNEL_BOOST";
@ -272,13 +276,15 @@ public class NotificationHelper {
} else { } else {
// on Android < O, notifications are enabled, if at least one account has notification enabled // 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) { public static void clearNotificationsForActiveAccount(Context context) {
AccountManager accountManager = TuskyApplication.getAccountManager(); AccountManager accountManager = TuskyApplication.getInstance(context).getServiceLocator()
.get(AccountManager.class);
AccountEntity account = accountManager.getActiveAccount(); AccountEntity account = accountManager.getActiveAccount();
if (account != null) { if (account != null) {
account.setActiveNotifications("[]"); account.setActiveNotifications("[]");

View File

@ -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")
}
}

View File

@ -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);
}
}

View File

@ -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
}
}