Add Dagger (#554)
* Add Dagger DI * Preemptively fix tests * Add missing licenses * DI fixes * ci fixes
This commit is contained in:
parent
720f7c6a0c
commit
a5cffe0fea
41 changed files with 1040 additions and 415 deletions
|
@ -44,6 +44,7 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
ext.supportLibraryVersion = '27.1.0'
|
ext.supportLibraryVersion = '27.1.0'
|
||||||
|
ext.daggerVersion = '2.15'
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation('com.mikepenz:materialdrawer:6.0.6@aar') {
|
implementation('com.mikepenz:materialdrawer:6.0.6@aar') {
|
||||||
|
@ -77,6 +78,13 @@ dependencies {
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
|
||||||
|
|
||||||
testImplementation 'junit:junit:4.12'
|
testImplementation 'junit:junit:4.12'
|
||||||
|
|
||||||
|
implementation "com.google.dagger:dagger:$daggerVersion"
|
||||||
|
kapt "com.google.dagger:dagger-compiler:$daggerVersion"
|
||||||
|
implementation "com.google.dagger:dagger-android:$daggerVersion"
|
||||||
|
implementation "com.google.dagger:dagger-android-support:$daggerVersion"
|
||||||
|
kapt "com.google.dagger:dagger-android-processor:$daggerVersion"
|
||||||
|
|
||||||
testImplementation "org.robolectric:robolectric:3.7.1"
|
testImplementation "org.robolectric:robolectric:3.7.1"
|
||||||
testCompile "org.mockito:mockito-inline:2.15.0"
|
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', {
|
||||||
|
|
2
app/proguard-rules.pro
vendored
2
app/proguard-rules.pro
vendored
|
@ -64,3 +64,5 @@
|
||||||
-assumenosideeffects class kotlin.jvm.internal.Intrinsics {
|
-assumenosideeffects class kotlin.jvm.internal.Intrinsics {
|
||||||
static void checkParameterIsNotNull(java.lang.Object, java.lang.String);
|
static void checkParameterIsNotNull(java.lang.Object, java.lang.String);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-dontwarn com.google.errorprone.annotations.*
|
|
@ -2,6 +2,7 @@ package com.keylesspalace.tusky;
|
||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
import android.support.design.widget.Snackbar;
|
import android.support.design.widget.Snackbar;
|
||||||
import android.support.v7.app.ActionBar;
|
import android.support.v7.app.ActionBar;
|
||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
|
@ -10,17 +11,24 @@ import android.view.View;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.keylesspalace.tusky.di.Injectable;
|
||||||
import com.keylesspalace.tusky.entity.Account;
|
import com.keylesspalace.tusky.entity.Account;
|
||||||
|
import com.keylesspalace.tusky.network.MastodonApi;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import retrofit2.Call;
|
import retrofit2.Call;
|
||||||
import retrofit2.Callback;
|
import retrofit2.Callback;
|
||||||
import retrofit2.Response;
|
import retrofit2.Response;
|
||||||
|
|
||||||
public class AboutActivity extends BaseActivity {
|
public class AboutActivity extends BaseActivity implements Injectable {
|
||||||
private Button appAccountButton;
|
private Button appAccountButton;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public MastodonApi mastodonApi;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
@ -42,12 +50,7 @@ public class AboutActivity extends BaseActivity {
|
||||||
versionTextView.setText(String.format(versionFormat, versionName));
|
versionTextView.setText(String.format(versionFormat, versionName));
|
||||||
|
|
||||||
appAccountButton = findViewById(R.id.tusky_profile_button);
|
appAccountButton = findViewById(R.id.tusky_profile_button);
|
||||||
appAccountButton.setOnClickListener(new View.OnClickListener() {
|
appAccountButton.setOnClickListener(v -> onAccountButtonClick());
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
onAccountButtonClick();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onAccountButtonClick() {
|
private void onAccountButtonClick() {
|
||||||
|
@ -68,10 +71,10 @@ public class AboutActivity extends BaseActivity {
|
||||||
private void searchForAccountThenViewIt() {
|
private void searchForAccountThenViewIt() {
|
||||||
Callback<List<Account>> callback = new Callback<List<Account>>() {
|
Callback<List<Account>> callback = new Callback<List<Account>>() {
|
||||||
@Override
|
@Override
|
||||||
public void onResponse(Call<List<Account>> call, Response<List<Account>> response) {
|
public void onResponse(@NonNull Call<List<Account>> call, @NonNull Response<List<Account>> response) {
|
||||||
if (response.isSuccessful()) {
|
if (response.isSuccessful()) {
|
||||||
List<Account> accountList = response.body();
|
List<Account> accountList = response.body();
|
||||||
if (!accountList.isEmpty()) {
|
if (accountList != null && !accountList.isEmpty()) {
|
||||||
String id = accountList.get(0).getId();
|
String id = accountList.get(0).getId();
|
||||||
getPrivatePreferences().edit()
|
getPrivatePreferences().edit()
|
||||||
.putString("appAccountId", id)
|
.putString("appAccountId", id)
|
||||||
|
@ -86,7 +89,7 @@ public class AboutActivity extends BaseActivity {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(Call<List<Account>> call, Throwable t) {
|
public void onFailure(@NonNull Call<List<Account>> call, @NonNull Throwable t) {
|
||||||
onSearchFailed();
|
onSearchFailed();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -31,6 +31,7 @@ import android.support.design.widget.CollapsingToolbarLayout;
|
||||||
import android.support.design.widget.FloatingActionButton;
|
import android.support.design.widget.FloatingActionButton;
|
||||||
import android.support.design.widget.Snackbar;
|
import android.support.design.widget.Snackbar;
|
||||||
import android.support.design.widget.TabLayout;
|
import android.support.design.widget.TabLayout;
|
||||||
|
import android.support.v4.app.Fragment;
|
||||||
import android.support.v4.content.LocalBroadcastManager;
|
import android.support.v4.content.LocalBroadcastManager;
|
||||||
import android.support.v4.view.ViewCompat;
|
import android.support.v4.view.ViewCompat;
|
||||||
import android.support.v4.view.ViewPager;
|
import android.support.v4.view.ViewPager;
|
||||||
|
@ -51,6 +52,7 @@ 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;
|
||||||
import com.keylesspalace.tusky.interfaces.LinkListener;
|
import com.keylesspalace.tusky.interfaces.LinkListener;
|
||||||
|
import com.keylesspalace.tusky.network.MastodonApi;
|
||||||
import com.keylesspalace.tusky.pager.AccountPagerAdapter;
|
import com.keylesspalace.tusky.pager.AccountPagerAdapter;
|
||||||
import com.keylesspalace.tusky.receiver.TimelineReceiver;
|
import com.keylesspalace.tusky.receiver.TimelineReceiver;
|
||||||
import com.keylesspalace.tusky.util.Assert;
|
import com.keylesspalace.tusky.util.Assert;
|
||||||
|
@ -64,11 +66,17 @@ import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import dagger.android.AndroidInjector;
|
||||||
|
import dagger.android.DispatchingAndroidInjector;
|
||||||
|
import dagger.android.support.HasSupportFragmentInjector;
|
||||||
import retrofit2.Call;
|
import retrofit2.Call;
|
||||||
import retrofit2.Callback;
|
import retrofit2.Callback;
|
||||||
import retrofit2.Response;
|
import retrofit2.Response;
|
||||||
|
|
||||||
public final class AccountActivity extends BaseActivity implements ActionButtonActivity {
|
public final class AccountActivity extends BaseActivity implements ActionButtonActivity,
|
||||||
|
HasSupportFragmentInjector {
|
||||||
private static final String TAG = "AccountActivity"; // logging tag
|
private static final String TAG = "AccountActivity"; // logging tag
|
||||||
|
|
||||||
private enum FollowState {
|
private enum FollowState {
|
||||||
|
@ -77,6 +85,11 @@ public final class AccountActivity extends BaseActivity implements ActionButtonA
|
||||||
REQUESTED,
|
REQUESTED,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public MastodonApi mastodonApi;
|
||||||
|
@Inject
|
||||||
|
public DispatchingAndroidInjector<Fragment> dispatchingAndroidInjector;
|
||||||
|
|
||||||
private String accountId;
|
private String accountId;
|
||||||
private FollowState followState;
|
private FollowState followState;
|
||||||
private boolean blocking;
|
private boolean blocking;
|
||||||
|
@ -690,4 +703,8 @@ public final class AccountActivity extends BaseActivity implements ActionButtonA
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AndroidInjector<Fragment> supportFragmentInjector() {
|
||||||
|
return dispatchingAndroidInjector;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v4.app.Fragment;
|
||||||
import android.support.v4.app.FragmentTransaction;
|
import android.support.v4.app.FragmentTransaction;
|
||||||
import android.support.v7.app.ActionBar;
|
import android.support.v7.app.ActionBar;
|
||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
|
@ -27,7 +28,16 @@ import android.view.MenuItem;
|
||||||
|
|
||||||
import com.keylesspalace.tusky.fragment.AccountListFragment;
|
import com.keylesspalace.tusky.fragment.AccountListFragment;
|
||||||
|
|
||||||
public final class AccountListActivity extends BaseActivity {
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import dagger.android.AndroidInjector;
|
||||||
|
import dagger.android.DispatchingAndroidInjector;
|
||||||
|
import dagger.android.support.HasSupportFragmentInjector;
|
||||||
|
|
||||||
|
public final class AccountListActivity extends BaseActivity implements HasSupportFragmentInjector {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public DispatchingAndroidInjector<Fragment> dispatchingAndroidInjector;
|
||||||
|
|
||||||
private static final String TYPE_EXTRA = "type";
|
private static final String TYPE_EXTRA = "type";
|
||||||
private static final String ARG_EXTRA = "arg";
|
private static final String ARG_EXTRA = "arg";
|
||||||
|
@ -131,4 +141,9 @@ public final class AccountListActivity extends BaseActivity {
|
||||||
}
|
}
|
||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AndroidInjector<Fragment> supportFragmentInjector() {
|
||||||
|
return dispatchingAndroidInjector;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,41 +25,21 @@ import android.os.Bundle;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.text.Spanned;
|
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
|
|
||||||
import com.evernote.android.job.JobManager;
|
import com.evernote.android.job.JobManager;
|
||||||
import com.evernote.android.job.JobRequest;
|
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.AccountEntity;
|
||||||
import com.keylesspalace.tusky.db.AccountManager;
|
import com.keylesspalace.tusky.db.AccountManager;
|
||||||
import com.keylesspalace.tusky.json.SpannedTypeAdapter;
|
|
||||||
import com.keylesspalace.tusky.network.AuthInterceptor;
|
|
||||||
import com.keylesspalace.tusky.network.MastodonApi;
|
|
||||||
import com.keylesspalace.tusky.util.OkHttpUtils;
|
|
||||||
import com.keylesspalace.tusky.util.ThemeUtils;
|
import com.keylesspalace.tusky.util.ThemeUtils;
|
||||||
|
|
||||||
import okhttp3.Dispatcher;
|
|
||||||
import okhttp3.OkHttpClient;
|
|
||||||
import okhttp3.logging.HttpLoggingInterceptor;
|
|
||||||
import retrofit2.Retrofit;
|
|
||||||
import retrofit2.converter.gson.GsonConverterFactory;
|
|
||||||
|
|
||||||
public abstract class BaseActivity extends AppCompatActivity {
|
public abstract class BaseActivity extends AppCompatActivity {
|
||||||
|
|
||||||
public MastodonApi mastodonApi;
|
|
||||||
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
|
||||||
|
@ -84,19 +64,7 @@ public abstract class BaseActivity extends AppCompatActivity {
|
||||||
}
|
}
|
||||||
getTheme().applyStyle(style, false);
|
getTheme().applyStyle(style, false);
|
||||||
|
|
||||||
if (redirectIfNotLoggedIn()) {
|
redirectIfNotLoggedIn();
|
||||||
return;
|
|
||||||
}
|
|
||||||
createMastodonApi();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onDestroy() {
|
|
||||||
if (mastodonApiDispatcher != null) {
|
|
||||||
mastodonApiDispatcher.cancelAll();
|
|
||||||
}
|
|
||||||
super.onDestroy();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -123,44 +91,13 @@ public abstract class BaseActivity extends AppCompatActivity {
|
||||||
return getSharedPreferences(getString(R.string.preferences_file_key), Context.MODE_PRIVATE);
|
return getSharedPreferences(getString(R.string.preferences_file_key), Context.MODE_PRIVATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String getBaseUrl() {
|
|
||||||
AccountEntity account = accountManager.getActiveAccount();
|
|
||||||
if (account != null) {
|
|
||||||
return "https://" + account.getDomain();
|
|
||||||
} else {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void createMastodonApi() {
|
|
||||||
mastodonApiDispatcher = new Dispatcher();
|
|
||||||
|
|
||||||
Gson gson = new GsonBuilder()
|
|
||||||
.registerTypeAdapter(Spanned.class, new SpannedTypeAdapter())
|
|
||||||
.create();
|
|
||||||
|
|
||||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
|
|
||||||
|
|
||||||
OkHttpClient.Builder okBuilder =
|
|
||||||
OkHttpUtils.getCompatibleClientBuilder(preferences)
|
|
||||||
.addInterceptor(new AuthInterceptor(accountManager))
|
|
||||||
.dispatcher(mastodonApiDispatcher);
|
|
||||||
|
|
||||||
if (BuildConfig.DEBUG) {
|
|
||||||
okBuilder.addInterceptor(
|
|
||||||
new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BASIC));
|
|
||||||
}
|
|
||||||
|
|
||||||
Retrofit retrofit = new Retrofit.Builder().baseUrl(getBaseUrl())
|
|
||||||
.client(okBuilder.build())
|
|
||||||
.addConverterFactory(GsonConverterFactory.create(gson))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
mastodonApi = retrofit.create(MastodonApi.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean redirectIfNotLoggedIn() {
|
protected boolean redirectIfNotLoggedIn() {
|
||||||
if (accountManager.getActiveAccount() == null) {
|
// This is very ugly but we cannot inject into parent class and injecting into every
|
||||||
|
// subclass seems inconvenient as well.
|
||||||
|
AccountEntity account = ((TuskyApplication) getApplicationContext())
|
||||||
|
.getServiceLocator().get(AccountManager.class)
|
||||||
|
.getActiveAccount();
|
||||||
|
if (account == 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);
|
||||||
|
|
|
@ -82,10 +82,12 @@ import com.keylesspalace.tusky.db.AccountEntity;
|
||||||
import com.keylesspalace.tusky.db.AccountManager;
|
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.di.Injectable;
|
||||||
import com.keylesspalace.tusky.entity.Account;
|
import com.keylesspalace.tusky.entity.Account;
|
||||||
import com.keylesspalace.tusky.entity.Attachment;
|
import com.keylesspalace.tusky.entity.Attachment;
|
||||||
import com.keylesspalace.tusky.entity.Status;
|
import com.keylesspalace.tusky.entity.Status;
|
||||||
import com.keylesspalace.tusky.fragment.ComposeOptionsFragment;
|
import com.keylesspalace.tusky.fragment.ComposeOptionsFragment;
|
||||||
|
import com.keylesspalace.tusky.network.MastodonApi;
|
||||||
import com.keylesspalace.tusky.network.ProgressRequestBody;
|
import com.keylesspalace.tusky.network.ProgressRequestBody;
|
||||||
import com.keylesspalace.tusky.util.CountUpDownLatch;
|
import com.keylesspalace.tusky.util.CountUpDownLatch;
|
||||||
import com.keylesspalace.tusky.util.DownsizeImageTask;
|
import com.keylesspalace.tusky.util.DownsizeImageTask;
|
||||||
|
@ -115,6 +117,8 @@ import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import okhttp3.MediaType;
|
import okhttp3.MediaType;
|
||||||
import okhttp3.MultipartBody;
|
import okhttp3.MultipartBody;
|
||||||
import retrofit2.Call;
|
import retrofit2.Call;
|
||||||
|
@ -122,7 +126,9 @@ import retrofit2.Callback;
|
||||||
import retrofit2.Response;
|
import retrofit2.Response;
|
||||||
|
|
||||||
public final class ComposeActivity extends BaseActivity
|
public final class ComposeActivity extends BaseActivity
|
||||||
implements ComposeOptionsFragment.Listener, MentionAutoCompleteAdapter.AccountSearchProvider {
|
implements ComposeOptionsFragment.Listener,
|
||||||
|
MentionAutoCompleteAdapter.AccountSearchProvider,
|
||||||
|
Injectable {
|
||||||
private static final String TAG = "ComposeActivity"; // logging tag
|
private static final String TAG = "ComposeActivity"; // logging tag
|
||||||
private static final int STATUS_CHARACTER_LIMIT = 500;
|
private static final int STATUS_CHARACTER_LIMIT = 500;
|
||||||
private static final int STATUS_MEDIA_SIZE_LIMIT = 8388608; // 8MiB
|
private static final int STATUS_MEDIA_SIZE_LIMIT = 8388608; // 8MiB
|
||||||
|
@ -145,6 +151,11 @@ public final class ComposeActivity extends BaseActivity
|
||||||
|
|
||||||
private static TootDao tootDao = TuskyApplication.getDB().tootDao();
|
private static TootDao tootDao = TuskyApplication.getDB().tootDao();
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public MastodonApi mastodonApi;
|
||||||
|
@Inject
|
||||||
|
public AccountManager accountManager;
|
||||||
|
|
||||||
private TextView replyTextView;
|
private TextView replyTextView;
|
||||||
private TextView replyContentTextView;
|
private TextView replyContentTextView;
|
||||||
private EditTextTyped textEditor;
|
private EditTextTyped textEditor;
|
||||||
|
@ -206,11 +217,9 @@ public final class ComposeActivity extends BaseActivity
|
||||||
}
|
}
|
||||||
|
|
||||||
// setup the account image
|
// setup the account image
|
||||||
AccountEntity activeAccount = TuskyApplication.getInstance(this).getServiceLocator()
|
final AccountEntity activeAccount = accountManager.getActiveAccount();
|
||||||
.get(AccountManager.class).getActiveAccount();
|
|
||||||
|
|
||||||
if (activeAccount != null) {
|
if (activeAccount != null) {
|
||||||
|
|
||||||
ImageView composeAvatar = findViewById(R.id.composeAvatar);
|
ImageView composeAvatar = findViewById(R.id.composeAvatar);
|
||||||
|
|
||||||
if (TextUtils.isEmpty(activeAccount.getProfilePictureUrl())) {
|
if (TextUtils.isEmpty(activeAccount.getProfilePictureUrl())) {
|
||||||
|
|
|
@ -32,7 +32,9 @@ import android.util.Log
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import com.keylesspalace.tusky.di.Injectable
|
||||||
import com.keylesspalace.tusky.entity.Account
|
import com.keylesspalace.tusky.entity.Account
|
||||||
|
import com.keylesspalace.tusky.network.MastodonApi
|
||||||
import com.keylesspalace.tusky.util.IOUtils
|
import com.keylesspalace.tusky.util.IOUtils
|
||||||
import com.squareup.picasso.Picasso
|
import com.squareup.picasso.Picasso
|
||||||
import com.theartofdev.edmodo.cropper.CropImage
|
import com.theartofdev.edmodo.cropper.CropImage
|
||||||
|
@ -46,6 +48,7 @@ import retrofit2.Callback
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
import java.io.*
|
import java.io.*
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
private const val TAG = "EditProfileActivity"
|
private const val TAG = "EditProfileActivity"
|
||||||
|
|
||||||
|
@ -66,7 +69,7 @@ private const val AVATAR_SIZE = 120
|
||||||
private const val HEADER_WIDTH = 700
|
private const val HEADER_WIDTH = 700
|
||||||
private const val HEADER_HEIGHT = 335
|
private const val HEADER_HEIGHT = 335
|
||||||
|
|
||||||
class EditProfileActivity : BaseActivity() {
|
class EditProfileActivity : BaseActivity(), Injectable {
|
||||||
|
|
||||||
private var oldDisplayName: String? = null
|
private var oldDisplayName: String? = null
|
||||||
private var oldNote: String? = null
|
private var oldNote: String? = null
|
||||||
|
@ -75,6 +78,9 @@ class EditProfileActivity : BaseActivity() {
|
||||||
private var avatarChanged: Boolean = false
|
private var avatarChanged: Boolean = false
|
||||||
private var headerChanged: Boolean = false
|
private var headerChanged: Boolean = false
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var mastodonApi: MastodonApi
|
||||||
|
|
||||||
private enum class PickType {
|
private enum class PickType {
|
||||||
NOTHING,
|
NOTHING,
|
||||||
AVATAR,
|
AVATAR,
|
||||||
|
@ -100,11 +106,11 @@ class EditProfileActivity : BaseActivity() {
|
||||||
avatarChanged = it.getBoolean(KEY_AVATAR_CHANGED)
|
avatarChanged = it.getBoolean(KEY_AVATAR_CHANGED)
|
||||||
headerChanged = it.getBoolean(KEY_HEADER_CHANGED)
|
headerChanged = it.getBoolean(KEY_HEADER_CHANGED)
|
||||||
|
|
||||||
if(avatarChanged) {
|
if (avatarChanged) {
|
||||||
val avatar = BitmapFactory.decodeFile(getCacheFileForName(AVATAR_FILE_NAME).absolutePath)
|
val avatar = BitmapFactory.decodeFile(getCacheFileForName(AVATAR_FILE_NAME).absolutePath)
|
||||||
avatarPreview.setImageBitmap(avatar)
|
avatarPreview.setImageBitmap(avatar)
|
||||||
}
|
}
|
||||||
if(headerChanged) {
|
if (headerChanged) {
|
||||||
val header = BitmapFactory.decodeFile(getCacheFileForName(HEADER_FILE_NAME).absolutePath)
|
val header = BitmapFactory.decodeFile(getCacheFileForName(HEADER_FILE_NAME).absolutePath)
|
||||||
headerPreview.setImageBitmap(header)
|
headerPreview.setImageBitmap(header)
|
||||||
}
|
}
|
||||||
|
@ -135,13 +141,13 @@ class EditProfileActivity : BaseActivity() {
|
||||||
|
|
||||||
displayNameEditText.setText(oldDisplayName)
|
displayNameEditText.setText(oldDisplayName)
|
||||||
noteEditText.setText(oldNote)
|
noteEditText.setText(oldNote)
|
||||||
if(!avatarChanged) {
|
if (!avatarChanged) {
|
||||||
Picasso.with(avatarPreview.context)
|
Picasso.with(avatarPreview.context)
|
||||||
.load(me.avatar)
|
.load(me.avatar)
|
||||||
.placeholder(R.drawable.avatar_default)
|
.placeholder(R.drawable.avatar_default)
|
||||||
.into(avatarPreview)
|
.into(avatarPreview)
|
||||||
}
|
}
|
||||||
if(!headerChanged) {
|
if (!headerChanged) {
|
||||||
Picasso.with(headerPreview.context)
|
Picasso.with(headerPreview.context)
|
||||||
.load(me.header)
|
.load(me.header)
|
||||||
.placeholder(R.drawable.account_header_default)
|
.placeholder(R.drawable.account_header_default)
|
||||||
|
@ -253,21 +259,21 @@ class EditProfileActivity : BaseActivity() {
|
||||||
RequestBody.create(MultipartBody.FORM, newNote)
|
RequestBody.create(MultipartBody.FORM, newNote)
|
||||||
}
|
}
|
||||||
|
|
||||||
val avatar = if(avatarChanged) {
|
val avatar = if (avatarChanged) {
|
||||||
val avatarBody = RequestBody.create(MediaType.parse("image/png"), getCacheFileForName(AVATAR_FILE_NAME))
|
val avatarBody = RequestBody.create(MediaType.parse("image/png"), getCacheFileForName(AVATAR_FILE_NAME))
|
||||||
MultipartBody.Part.createFormData("avatar", getFileName(), avatarBody)
|
MultipartBody.Part.createFormData("avatar", getFileName(), avatarBody)
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
val header = if(headerChanged) {
|
val header = if (headerChanged) {
|
||||||
val headerBody = RequestBody.create(MediaType.parse("image/png"), getCacheFileForName(HEADER_FILE_NAME))
|
val headerBody = RequestBody.create(MediaType.parse("image/png"), getCacheFileForName(HEADER_FILE_NAME))
|
||||||
MultipartBody.Part.createFormData("header", getFileName(), headerBody)
|
MultipartBody.Part.createFormData("header", getFileName(), headerBody)
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
if(displayName == null && note == null && avatar == null && header == null) {
|
if (displayName == null && note == null && avatar == null && header == null) {
|
||||||
/** if nothing has changed, there is no need to make a network request */
|
/** if nothing has changed, there is no need to make a network request */
|
||||||
finish()
|
finish()
|
||||||
return
|
return
|
||||||
|
@ -413,11 +419,11 @@ class EditProfileActivity : BaseActivity() {
|
||||||
return java.lang.Long.toHexString(Random().nextLong())
|
return java.lang.Long.toHexString(Random().nextLong())
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ResizeImageTask (private val contentResolver: ContentResolver,
|
private class ResizeImageTask(private val contentResolver: ContentResolver,
|
||||||
private val resizeWidth: Int,
|
private val resizeWidth: Int,
|
||||||
private val resizeHeight: Int,
|
private val resizeHeight: Int,
|
||||||
private val cacheFile: File,
|
private val cacheFile: File,
|
||||||
private val listener: Listener) : AsyncTask<Uri, Void, Boolean>() {
|
private val listener: Listener) : AsyncTask<Uri, Void, Boolean>() {
|
||||||
private var resultBitmap: Bitmap? = null
|
private var resultBitmap: Bitmap? = null
|
||||||
|
|
||||||
override fun doInBackground(vararg uris: Uri): Boolean? {
|
override fun doInBackground(vararg uris: Uri): Boolean? {
|
||||||
|
@ -445,7 +451,7 @@ class EditProfileActivity : BaseActivity() {
|
||||||
|
|
||||||
//dont upscale image if its smaller than the desired size
|
//dont upscale image if its smaller than the desired size
|
||||||
val bitmap =
|
val bitmap =
|
||||||
if(sourceBitmap.width <= resizeWidth && sourceBitmap.height <= resizeHeight) {
|
if (sourceBitmap.width <= resizeWidth && sourceBitmap.height <= resizeHeight) {
|
||||||
sourceBitmap
|
sourceBitmap
|
||||||
} else {
|
} else {
|
||||||
Bitmap.createScaledBitmap(sourceBitmap, resizeWidth, resizeHeight, true)
|
Bitmap.createScaledBitmap(sourceBitmap, resizeWidth, resizeHeight, true)
|
||||||
|
|
|
@ -25,7 +25,17 @@ import android.view.MenuItem;
|
||||||
|
|
||||||
import com.keylesspalace.tusky.fragment.TimelineFragment;
|
import com.keylesspalace.tusky.fragment.TimelineFragment;
|
||||||
|
|
||||||
public class FavouritesActivity extends BaseActivity {
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import dagger.android.AndroidInjector;
|
||||||
|
import dagger.android.DispatchingAndroidInjector;
|
||||||
|
import dagger.android.support.HasSupportFragmentInjector;
|
||||||
|
|
||||||
|
public class FavouritesActivity extends BaseActivity implements HasSupportFragmentInjector {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public DispatchingAndroidInjector<Fragment> dispatchingAndroidInjector;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
@ -56,4 +66,9 @@ public class FavouritesActivity extends BaseActivity {
|
||||||
}
|
}
|
||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AndroidInjector<Fragment> supportFragmentInjector() {
|
||||||
|
return dispatchingAndroidInjector;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,19 +12,19 @@ import android.view.LayoutInflater
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ImageView
|
|
||||||
import android.widget.ProgressBar
|
import android.widget.ProgressBar
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import com.keylesspalace.tusky.di.Injectable
|
||||||
import com.keylesspalace.tusky.entity.MastoList
|
import com.keylesspalace.tusky.entity.MastoList
|
||||||
import com.keylesspalace.tusky.fragment.TimelineFragment
|
import com.keylesspalace.tusky.fragment.TimelineFragment
|
||||||
import com.keylesspalace.tusky.network.MastodonApi
|
import com.keylesspalace.tusky.network.MastodonApi
|
||||||
import com.keylesspalace.tusky.util.ThemeUtils
|
import com.keylesspalace.tusky.util.ThemeUtils
|
||||||
import com.mikepenz.google_material_typeface_library.GoogleMaterial
|
import com.mikepenz.google_material_typeface_library.GoogleMaterial
|
||||||
import com.mikepenz.iconics.IconicsDrawable
|
import com.mikepenz.iconics.IconicsDrawable
|
||||||
import com.varunest.sparkbutton.helpers.Utils
|
|
||||||
import retrofit2.Call
|
import retrofit2.Call
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by charlag on 1/4/18.
|
* Created by charlag on 1/4/18.
|
||||||
|
@ -83,7 +83,7 @@ class ListsViewModel(private val api: MastodonApi) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ListsActivity : BaseActivity(), ListsView {
|
class ListsActivity : BaseActivity(), ListsView, Injectable {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
|
@ -92,6 +92,9 @@ class ListsActivity : BaseActivity(), ListsView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var mastodonApi: MastodonApi
|
||||||
|
|
||||||
private lateinit var recyclerView: RecyclerView
|
private lateinit var recyclerView: RecyclerView
|
||||||
private lateinit var progressBar: ProgressBar
|
private lateinit var progressBar: ProgressBar
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ import android.support.annotation.Nullable;
|
||||||
import android.support.design.widget.FloatingActionButton;
|
import android.support.design.widget.FloatingActionButton;
|
||||||
import android.support.design.widget.TabLayout;
|
import android.support.design.widget.TabLayout;
|
||||||
import android.support.graphics.drawable.VectorDrawableCompat;
|
import android.support.graphics.drawable.VectorDrawableCompat;
|
||||||
|
import android.support.v4.app.Fragment;
|
||||||
import android.support.v4.content.ContextCompat;
|
import android.support.v4.content.ContextCompat;
|
||||||
import android.support.v4.content.LocalBroadcastManager;
|
import android.support.v4.content.LocalBroadcastManager;
|
||||||
import android.support.v4.view.ViewPager;
|
import android.support.v4.view.ViewPager;
|
||||||
|
@ -40,6 +41,7 @@ import com.keylesspalace.tusky.db.AccountEntity;
|
||||||
import com.keylesspalace.tusky.db.AccountManager;
|
import com.keylesspalace.tusky.db.AccountManager;
|
||||||
import com.keylesspalace.tusky.entity.Account;
|
import com.keylesspalace.tusky.entity.Account;
|
||||||
import com.keylesspalace.tusky.interfaces.ActionButtonActivity;
|
import com.keylesspalace.tusky.interfaces.ActionButtonActivity;
|
||||||
|
import com.keylesspalace.tusky.network.MastodonApi;
|
||||||
import com.keylesspalace.tusky.pager.TimelinePagerAdapter;
|
import com.keylesspalace.tusky.pager.TimelinePagerAdapter;
|
||||||
import com.keylesspalace.tusky.receiver.TimelineReceiver;
|
import com.keylesspalace.tusky.receiver.TimelineReceiver;
|
||||||
import com.keylesspalace.tusky.util.NotificationHelper;
|
import com.keylesspalace.tusky.util.NotificationHelper;
|
||||||
|
@ -63,11 +65,18 @@ import com.squareup.picasso.Picasso;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import dagger.android.AndroidInjector;
|
||||||
|
import dagger.android.DispatchingAndroidInjector;
|
||||||
|
import dagger.android.support.AndroidSupportInjection;
|
||||||
|
import dagger.android.support.HasSupportFragmentInjector;
|
||||||
import retrofit2.Call;
|
import retrofit2.Call;
|
||||||
import retrofit2.Callback;
|
import retrofit2.Callback;
|
||||||
import retrofit2.Response;
|
import retrofit2.Response;
|
||||||
|
|
||||||
public class MainActivity extends BaseActivity implements ActionButtonActivity {
|
public class MainActivity extends BaseActivity implements ActionButtonActivity,
|
||||||
|
HasSupportFragmentInjector {
|
||||||
private static final String TAG = "MainActivity"; // logging tag
|
private static final String TAG = "MainActivity"; // logging tag
|
||||||
private static final long DRAWER_ITEM_ADD_ACCOUNT = -13;
|
private static final long DRAWER_ITEM_ADD_ACCOUNT = -13;
|
||||||
private static final long DRAWER_ITEM_EDIT_PROFILE = 0;
|
private static final long DRAWER_ITEM_EDIT_PROFILE = 0;
|
||||||
|
@ -82,6 +91,11 @@ public class MainActivity extends BaseActivity implements ActionButtonActivity {
|
||||||
private static final long DRAWER_ITEM_SAVED_TOOT = 9;
|
private static final long DRAWER_ITEM_SAVED_TOOT = 9;
|
||||||
private static final long DRAWER_ITEM_LISTS = 10;
|
private static final long DRAWER_ITEM_LISTS = 10;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public MastodonApi mastodonApi;
|
||||||
|
@Inject
|
||||||
|
public DispatchingAndroidInjector<Fragment> fragmentInjector;
|
||||||
|
|
||||||
private static int COMPOSE_RESULT = 1;
|
private static int COMPOSE_RESULT = 1;
|
||||||
|
|
||||||
AccountManager accountManager;
|
AccountManager accountManager;
|
||||||
|
@ -93,10 +107,7 @@ public class MainActivity extends BaseActivity implements ActionButtonActivity {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
|
||||||
// account switching has to be done before MastodonApi is created in super.onCreate
|
|
||||||
Intent intent = getIntent();
|
Intent intent = getIntent();
|
||||||
|
|
||||||
int tabPosition = 0;
|
int tabPosition = 0;
|
||||||
|
|
||||||
accountManager = TuskyApplication.getInstance(this).getServiceLocator()
|
accountManager = TuskyApplication.getInstance(this).getServiceLocator()
|
||||||
|
@ -550,4 +561,9 @@ public class MainActivity extends BaseActivity implements ActionButtonActivity {
|
||||||
public FloatingActionButton getActionButton() {
|
public FloatingActionButton getActionButton() {
|
||||||
return composeButton;
|
return composeButton;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AndroidInjector<Fragment> supportFragmentInjector() {
|
||||||
|
return fragmentInjector;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -4,19 +4,29 @@ import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.support.design.widget.FloatingActionButton
|
import android.support.design.widget.FloatingActionButton
|
||||||
|
import android.support.v4.app.Fragment
|
||||||
import android.support.v7.widget.Toolbar
|
import android.support.v7.widget.Toolbar
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
import com.keylesspalace.tusky.fragment.TimelineFragment
|
import com.keylesspalace.tusky.fragment.TimelineFragment
|
||||||
import com.keylesspalace.tusky.interfaces.ActionButtonActivity
|
import com.keylesspalace.tusky.interfaces.ActionButtonActivity
|
||||||
|
import dagger.android.AndroidInjector
|
||||||
|
import dagger.android.DispatchingAndroidInjector
|
||||||
|
import dagger.android.support.HasSupportFragmentInjector
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class ModalTimelineActivity : BaseActivity(), ActionButtonActivity, HasSupportFragmentInjector {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Fragment>
|
||||||
|
|
||||||
class ModalTimelineActivity : BaseActivity(), ActionButtonActivity {
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
private const val ARG_KIND = "kind"
|
private const val ARG_KIND = "kind"
|
||||||
private const val ARG_ARG = "arg"
|
private const val ARG_ARG = "arg"
|
||||||
@JvmStatic fun newIntent(context: Context, kind: TimelineFragment.Kind,
|
|
||||||
argument: String?): Intent {
|
@JvmStatic
|
||||||
|
fun newIntent(context: Context, kind: TimelineFragment.Kind,
|
||||||
|
argument: String?): Intent {
|
||||||
val intent = Intent(context, ModalTimelineActivity::class.java)
|
val intent = Intent(context, ModalTimelineActivity::class.java)
|
||||||
intent.putExtra(ARG_KIND, kind)
|
intent.putExtra(ARG_KIND, kind)
|
||||||
intent.putExtra(ARG_ARG, argument)
|
intent.putExtra(ARG_ARG, argument)
|
||||||
|
@ -24,6 +34,7 @@ class ModalTimelineActivity : BaseActivity(), ActionButtonActivity {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lateinit var contentFrame: FrameLayout
|
lateinit var contentFrame: FrameLayout
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
@ -41,8 +52,8 @@ class ModalTimelineActivity : BaseActivity(), ActionButtonActivity {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (supportFragmentManager.findFragmentById(R.id.content_frame) == null) {
|
if (supportFragmentManager.findFragmentById(R.id.content_frame) == null) {
|
||||||
val kind = intent?.getSerializableExtra(ARG_KIND) as? TimelineFragment.Kind ?:
|
val kind = intent?.getSerializableExtra(ARG_KIND) as? TimelineFragment.Kind
|
||||||
TimelineFragment.Kind.HOME
|
?: TimelineFragment.Kind.HOME
|
||||||
val argument = intent?.getStringExtra(ARG_ARG)
|
val argument = intent?.getStringExtra(ARG_ARG)
|
||||||
supportFragmentManager.beginTransaction()
|
supportFragmentManager.beginTransaction()
|
||||||
.replace(R.id.content_frame, TimelineFragment.newInstance(kind, argument))
|
.replace(R.id.content_frame, TimelineFragment.newInstance(kind, argument))
|
||||||
|
@ -60,4 +71,8 @@ class ModalTimelineActivity : BaseActivity(), ActionButtonActivity {
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun supportFragmentInjector(): AndroidInjector<Fragment> {
|
||||||
|
return dispatchingAndroidInjector
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,23 +16,17 @@
|
||||||
package com.keylesspalace.tusky;
|
package com.keylesspalace.tusky;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.text.Spanned;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.evernote.android.job.Job;
|
import com.evernote.android.job.Job;
|
||||||
import com.evernote.android.job.JobCreator;
|
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.AccountEntity;
|
||||||
import com.keylesspalace.tusky.db.AccountManager;
|
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.network.MastodonApi;
|
import com.keylesspalace.tusky.network.MastodonApi;
|
||||||
import com.keylesspalace.tusky.util.NotificationHelper;
|
import com.keylesspalace.tusky.util.NotificationHelper;
|
||||||
import com.keylesspalace.tusky.util.OkHttpUtils;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
@ -40,10 +34,9 @@ import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import okhttp3.OkHttpClient;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import retrofit2.Response;
|
import retrofit2.Response;
|
||||||
import retrofit2.Retrofit;
|
|
||||||
import retrofit2.converter.gson.GsonConverterFactory;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by charlag on 31/10/17.
|
* Created by charlag on 31/10/17.
|
||||||
|
@ -55,65 +48,53 @@ public final class NotificationPullJobCreator implements JobCreator {
|
||||||
|
|
||||||
static final String NOTIFICATIONS_JOB_TAG = "notifications_job_tag";
|
static final String NOTIFICATIONS_JOB_TAG = "notifications_job_tag";
|
||||||
|
|
||||||
private Context context;
|
private final MastodonApi api;
|
||||||
|
private final Context context;
|
||||||
|
private final AccountManager accountManager;
|
||||||
|
|
||||||
NotificationPullJobCreator(Context context) {
|
@Inject NotificationPullJobCreator(MastodonApi api, Context context,
|
||||||
|
AccountManager accountManager) {
|
||||||
|
this.api = api;
|
||||||
this.context = context;
|
this.context = context;
|
||||||
|
this.accountManager = accountManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public Job create(@NonNull String tag) {
|
public Job create(@NonNull String tag) {
|
||||||
if (tag.equals(NOTIFICATIONS_JOB_TAG)) {
|
if (tag.equals(NOTIFICATIONS_JOB_TAG)) {
|
||||||
return new NotificationPullJob(context);
|
return new NotificationPullJob(context, accountManager, api);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MastodonApi createMastodonApi(String domain, Context context) {
|
|
||||||
SharedPreferences preferences = context.getSharedPreferences(
|
|
||||||
context.getString(R.string.preferences_file_key), Context.MODE_PRIVATE);
|
|
||||||
|
|
||||||
OkHttpClient okHttpClient = OkHttpUtils.getCompatibleClientBuilder(preferences)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
Gson gson = new GsonBuilder()
|
|
||||||
.registerTypeAdapter(Spanned.class, new SpannedTypeAdapter())
|
|
||||||
.create();
|
|
||||||
|
|
||||||
Retrofit retrofit = new Retrofit.Builder()
|
|
||||||
.baseUrl("https://" + domain)
|
|
||||||
.client(okHttpClient)
|
|
||||||
.addConverterFactory(GsonConverterFactory.create(gson))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
return retrofit.create(MastodonApi.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final static class NotificationPullJob extends Job {
|
private final static class NotificationPullJob extends Job {
|
||||||
|
|
||||||
private Context context;
|
private final Context context;
|
||||||
|
private final AccountManager accountManager;
|
||||||
|
private final MastodonApi mastodonApi;
|
||||||
|
|
||||||
NotificationPullJob(Context context) {
|
NotificationPullJob(Context context, AccountManager accountManager,
|
||||||
|
MastodonApi mastodonApi) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
|
this.accountManager = accountManager;
|
||||||
|
this.mastodonApi = mastodonApi;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
protected Result onRunJob(@NonNull Params params) {
|
protected Result onRunJob(@NonNull Params params) {
|
||||||
|
|
||||||
AccountManager accountManager = TuskyApplication.getInstance(context).getServiceLocator()
|
|
||||||
.get(AccountManager.class);
|
|
||||||
List<AccountEntity> accountList = new ArrayList<>(accountManager.getAllAccountsOrderedByActive());
|
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 {
|
try {
|
||||||
Log.d(TAG, "getting Notifications for " + account.getFullName());
|
Log.d(TAG, "getting Notifications for " + account.getFullName());
|
||||||
Response<List<Notification>> notifications =
|
Response<List<Notification>> notifications =
|
||||||
api.notificationsWithAuth(String.format("Bearer %s", account.getAccessToken())).execute();
|
mastodonApi.notificationsWithAuth(
|
||||||
|
String.format("Bearer %s", account.getAccessToken()),
|
||||||
|
account.getDomain()
|
||||||
|
)
|
||||||
|
.execute();
|
||||||
if (notifications.isSuccessful()) {
|
if (notifications.isSuccessful()) {
|
||||||
onNotificationsReceived(account, notifications.body());
|
onNotificationsReceived(account, notifications.body());
|
||||||
} else {
|
} else {
|
||||||
|
@ -127,22 +108,15 @@ public final class NotificationPullJobCreator implements JobCreator {
|
||||||
}
|
}
|
||||||
|
|
||||||
return Result.SUCCESS;
|
return Result.SUCCESS;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onNotificationsReceived(AccountEntity account, List<Notification> notificationList) {
|
private void onNotificationsReceived(AccountEntity account, List<Notification> notificationList) {
|
||||||
|
|
||||||
Collections.reverse(notificationList);
|
Collections.reverse(notificationList);
|
||||||
|
|
||||||
BigInteger newId = new BigInteger(account.getLastNotificationId());
|
BigInteger newId = new BigInteger(account.getLastNotificationId());
|
||||||
|
|
||||||
BigInteger newestId = BigInteger.ZERO;
|
BigInteger newestId = BigInteger.ZERO;
|
||||||
|
|
||||||
for (Notification notification : notificationList) {
|
for (Notification notification : notificationList) {
|
||||||
|
|
||||||
BigInteger currentId = new BigInteger(notification.getId());
|
BigInteger currentId = new BigInteger(notification.getId());
|
||||||
|
|
||||||
if (isBiggerThan(currentId, newestId)) {
|
if (isBiggerThan(currentId, newestId)) {
|
||||||
newestId = currentId;
|
newestId = currentId;
|
||||||
}
|
}
|
||||||
|
@ -153,12 +127,10 @@ public final class NotificationPullJobCreator implements JobCreator {
|
||||||
}
|
}
|
||||||
|
|
||||||
account.setLastNotificationId(newestId.toString());
|
account.setLastNotificationId(newestId.toString());
|
||||||
TuskyApplication.getInstance(context).getServiceLocator()
|
accountManager.saveAccount(account);
|
||||||
.get(AccountManager.class).saveAccount(account);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isBiggerThan(BigInteger newId, BigInteger lastShownNotificationId) {
|
private boolean isBiggerThan(BigInteger newId, BigInteger lastShownNotificationId) {
|
||||||
|
|
||||||
return lastShownNotificationId.compareTo(newId) == -1;
|
return lastShownNotificationId.compareTo(newId) == -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,9 @@ import android.view.View;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
|
|
||||||
import com.keylesspalace.tusky.adapter.ReportAdapter;
|
import com.keylesspalace.tusky.adapter.ReportAdapter;
|
||||||
|
import com.keylesspalace.tusky.di.Injectable;
|
||||||
import com.keylesspalace.tusky.entity.Status;
|
import com.keylesspalace.tusky.entity.Status;
|
||||||
|
import com.keylesspalace.tusky.network.MastodonApi;
|
||||||
import com.keylesspalace.tusky.util.HtmlUtils;
|
import com.keylesspalace.tusky.util.HtmlUtils;
|
||||||
import com.keylesspalace.tusky.util.ThemeUtils;
|
import com.keylesspalace.tusky.util.ThemeUtils;
|
||||||
|
|
||||||
|
@ -40,14 +42,19 @@ import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import okhttp3.ResponseBody;
|
import okhttp3.ResponseBody;
|
||||||
import retrofit2.Call;
|
import retrofit2.Call;
|
||||||
import retrofit2.Callback;
|
import retrofit2.Callback;
|
||||||
import retrofit2.Response;
|
import retrofit2.Response;
|
||||||
|
|
||||||
public class ReportActivity extends BaseActivity {
|
public class ReportActivity extends BaseActivity implements Injectable {
|
||||||
private static final String TAG = "ReportActivity"; // logging tag
|
private static final String TAG = "ReportActivity"; // logging tag
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public MastodonApi mastodonApi;
|
||||||
|
|
||||||
private View anyView; // what Snackbar will use to find the root view
|
private View anyView; // what Snackbar will use to find the root view
|
||||||
private ReportAdapter adapter;
|
private ReportAdapter adapter;
|
||||||
private boolean reportAlreadyInFlight;
|
private boolean reportAlreadyInFlight;
|
||||||
|
@ -118,7 +125,7 @@ public class ReportActivity extends BaseActivity {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendReport(final String accountId, final String[] statusIds,
|
private void sendReport(final String accountId, final String[] statusIds,
|
||||||
final String comment) {
|
final String comment) {
|
||||||
Callback<ResponseBody> callback = new Callback<ResponseBody>() {
|
Callback<ResponseBody> callback = new Callback<ResponseBody>() {
|
||||||
@Override
|
@Override
|
||||||
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
|
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
|
||||||
|
@ -145,7 +152,7 @@ public class ReportActivity extends BaseActivity {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onSendFailure(final String accountId, final String[] statusIds,
|
private void onSendFailure(final String accountId, final String[] statusIds,
|
||||||
final String comment) {
|
final String comment) {
|
||||||
Snackbar.make(anyView, R.string.error_generic, Snackbar.LENGTH_LONG)
|
Snackbar.make(anyView, R.string.error_generic, Snackbar.LENGTH_LONG)
|
||||||
.setAction(R.string.action_retry, new View.OnClickListener() {
|
.setAction(R.string.action_retry, new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -35,17 +35,24 @@ import android.widget.ProgressBar;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import com.keylesspalace.tusky.adapter.SearchResultsAdapter;
|
import com.keylesspalace.tusky.adapter.SearchResultsAdapter;
|
||||||
|
import com.keylesspalace.tusky.di.Injectable;
|
||||||
import com.keylesspalace.tusky.entity.SearchResults;
|
import com.keylesspalace.tusky.entity.SearchResults;
|
||||||
import com.keylesspalace.tusky.interfaces.LinkListener;
|
import com.keylesspalace.tusky.interfaces.LinkListener;
|
||||||
|
import com.keylesspalace.tusky.network.MastodonApi;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import retrofit2.Call;
|
import retrofit2.Call;
|
||||||
import retrofit2.Callback;
|
import retrofit2.Callback;
|
||||||
import retrofit2.Response;
|
import retrofit2.Response;
|
||||||
|
|
||||||
public class SearchActivity extends BaseActivity implements SearchView.OnQueryTextListener,
|
public class SearchActivity extends BaseActivity implements SearchView.OnQueryTextListener,
|
||||||
LinkListener {
|
LinkListener, Injectable {
|
||||||
private static final String TAG = "SearchActivity"; // logging tag
|
private static final String TAG = "SearchActivity"; // logging tag
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public MastodonApi mastodonApi;
|
||||||
|
|
||||||
private ProgressBar progressBar;
|
private ProgressBar progressBar;
|
||||||
private TextView messageNoResults;
|
private TextView messageNoResults;
|
||||||
private SearchResultsAdapter adapter;
|
private SearchResultsAdapter adapter;
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
|
|
||||||
package com.keylesspalace.tusky;
|
package com.keylesspalace.tusky;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
import android.app.UiModeManager;
|
import android.app.UiModeManager;
|
||||||
import android.arch.persistence.room.Room;
|
import android.arch.persistence.room.Room;
|
||||||
|
@ -28,15 +29,26 @@ import com.evernote.android.job.JobManager;
|
||||||
import com.jakewharton.picasso.OkHttp3Downloader;
|
import com.jakewharton.picasso.OkHttp3Downloader;
|
||||||
import com.keylesspalace.tusky.db.AccountManager;
|
import com.keylesspalace.tusky.db.AccountManager;
|
||||||
import com.keylesspalace.tusky.db.AppDatabase;
|
import com.keylesspalace.tusky.db.AppDatabase;
|
||||||
|
import com.keylesspalace.tusky.di.AppInjector;
|
||||||
import com.keylesspalace.tusky.util.OkHttpUtils;
|
import com.keylesspalace.tusky.util.OkHttpUtils;
|
||||||
import com.keylesspalace.tusky.util.ThemeUtils;
|
import com.keylesspalace.tusky.util.ThemeUtils;
|
||||||
import com.squareup.picasso.Picasso;
|
import com.squareup.picasso.Picasso;
|
||||||
|
|
||||||
public class TuskyApplication extends Application {
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import dagger.android.AndroidInjector;
|
||||||
|
import dagger.android.DispatchingAndroidInjector;
|
||||||
|
import dagger.android.HasActivityInjector;
|
||||||
|
|
||||||
|
public class TuskyApplication extends Application implements HasActivityInjector {
|
||||||
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 AccountManager accountManager;
|
private AccountManager accountManager;
|
||||||
|
@Inject
|
||||||
|
DispatchingAndroidInjector<Activity> dispatchingAndroidInjector;
|
||||||
|
@Inject
|
||||||
|
NotificationPullJobCreator notificationPullJobCreator;
|
||||||
|
|
||||||
public static AppDatabase getDB() {
|
public static AppDatabase getDB() {
|
||||||
return db;
|
return db;
|
||||||
|
@ -58,8 +70,12 @@ public class TuskyApplication extends Application {
|
||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
initPicasso();
|
|
||||||
|
|
||||||
|
db = Room.databaseBuilder(getApplicationContext(), AppDatabase.class, "tuskyDB")
|
||||||
|
.allowMainThreadQueries()
|
||||||
|
.addMigrations(AppDatabase.MIGRATION_2_3, AppDatabase.MIGRATION_3_4, AppDatabase.MIGRATION_4_5)
|
||||||
|
.build();
|
||||||
|
accountManager = new AccountManager(db);
|
||||||
serviceLocator = new ServiceLocator() {
|
serviceLocator = new ServiceLocator() {
|
||||||
@Override
|
@Override
|
||||||
public <T> T get(Class<T> clazz) {
|
public <T> T get(Class<T> clazz) {
|
||||||
|
@ -72,19 +88,14 @@ public class TuskyApplication extends Application {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
db = Room.databaseBuilder(getApplicationContext(), AppDatabase.class, "tuskyDB")
|
AppInjector.INSTANCE.init(this);
|
||||||
.allowMainThreadQueries()
|
initPicasso();
|
||||||
.addMigrations(AppDatabase.MIGRATION_2_3, AppDatabase.MIGRATION_3_4, AppDatabase.MIGRATION_4_5)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
JobManager.create(this).addJobCreator(new NotificationPullJobCreator(this));
|
|
||||||
|
|
||||||
|
JobManager.create(this).addJobCreator(notificationPullJobCreator);
|
||||||
uiModeManager = (UiModeManager) getSystemService(Context.UI_MODE_SERVICE);
|
uiModeManager = (UiModeManager) getSystemService(Context.UI_MODE_SERVICE);
|
||||||
|
|
||||||
//necessary for Android < APi 21
|
//necessary for Android < APi 21
|
||||||
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
|
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
|
||||||
|
|
||||||
accountManager = new AccountManager();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void initPicasso() {
|
protected void initPicasso() {
|
||||||
|
@ -106,6 +117,11 @@ public class TuskyApplication extends Application {
|
||||||
return serviceLocator;
|
return serviceLocator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AndroidInjector<Activity> activityInjector() {
|
||||||
|
return dispatchingAndroidInjector;
|
||||||
|
}
|
||||||
|
|
||||||
public interface ServiceLocator {
|
public interface ServiceLocator {
|
||||||
<T> T get(Class<T> clazz);
|
<T> T get(Class<T> clazz);
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,17 @@ import android.view.MenuItem;
|
||||||
|
|
||||||
import com.keylesspalace.tusky.fragment.TimelineFragment;
|
import com.keylesspalace.tusky.fragment.TimelineFragment;
|
||||||
|
|
||||||
public class ViewTagActivity extends BaseActivity {
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import dagger.android.AndroidInjector;
|
||||||
|
import dagger.android.DispatchingAndroidInjector;
|
||||||
|
import dagger.android.support.HasSupportFragmentInjector;
|
||||||
|
|
||||||
|
public class ViewTagActivity extends BaseActivity implements HasSupportFragmentInjector {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public DispatchingAndroidInjector<Fragment> dispatchingAndroidInjector;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
@ -59,4 +69,9 @@ public class ViewTagActivity extends BaseActivity {
|
||||||
}
|
}
|
||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AndroidInjector<Fragment> supportFragmentInjector() {
|
||||||
|
return dispatchingAndroidInjector;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,17 @@ import android.view.MenuItem;
|
||||||
import com.keylesspalace.tusky.fragment.ViewThreadFragment;
|
import com.keylesspalace.tusky.fragment.ViewThreadFragment;
|
||||||
import com.keylesspalace.tusky.util.LinkHelper;
|
import com.keylesspalace.tusky.util.LinkHelper;
|
||||||
|
|
||||||
public class ViewThreadActivity extends BaseActivity {
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import dagger.android.AndroidInjector;
|
||||||
|
import dagger.android.DispatchingAndroidInjector;
|
||||||
|
import dagger.android.support.HasSupportFragmentInjector;
|
||||||
|
|
||||||
|
public class ViewThreadActivity extends BaseActivity implements HasSupportFragmentInjector {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public DispatchingAndroidInjector<Fragment> dispatchingAndroidInjector;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
@ -69,4 +79,9 @@ public class ViewThreadActivity extends BaseActivity {
|
||||||
}
|
}
|
||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AndroidInjector<Fragment> supportFragmentInjector() {
|
||||||
|
return dispatchingAndroidInjector;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
|
|
||||||
package com.keylesspalace.tusky.db
|
package com.keylesspalace.tusky.db
|
||||||
|
|
||||||
|
import android.arch.persistence.room.Database
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.keylesspalace.tusky.TuskyApplication
|
import com.keylesspalace.tusky.TuskyApplication
|
||||||
import com.keylesspalace.tusky.entity.Account
|
import com.keylesspalace.tusky.entity.Account
|
||||||
|
@ -26,12 +27,13 @@ import com.keylesspalace.tusky.entity.Account
|
||||||
|
|
||||||
private const val TAG = "AccountManager"
|
private const val TAG = "AccountManager"
|
||||||
|
|
||||||
class AccountManager {
|
class AccountManager(db: AppDatabase) {
|
||||||
|
|
||||||
@Volatile var activeAccount: AccountEntity? = null
|
@Volatile
|
||||||
|
var activeAccount: AccountEntity? = null
|
||||||
|
|
||||||
private var accounts: MutableList<AccountEntity> = mutableListOf()
|
private var accounts: MutableList<AccountEntity> = mutableListOf()
|
||||||
private val accountDao: AccountDao = TuskyApplication.getDB().accountDao()
|
private val accountDao: AccountDao = db.accountDao()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
accounts = accountDao.loadAll().toMutableList()
|
accounts = accountDao.loadAll().toMutableList()
|
||||||
|
@ -50,9 +52,9 @@ class AccountManager {
|
||||||
*/
|
*/
|
||||||
fun addAccount(accessToken: String, domain: String) {
|
fun addAccount(accessToken: String, domain: String) {
|
||||||
|
|
||||||
activeAccount?.let{
|
activeAccount?.let {
|
||||||
it.isActive = false
|
it.isActive = false
|
||||||
Log.d(TAG, "addAccount: saving account with id "+it.id)
|
Log.d(TAG, "addAccount: saving account with id " + it.id)
|
||||||
|
|
||||||
accountDao.insertOrReplace(it)
|
accountDao.insertOrReplace(it)
|
||||||
}
|
}
|
||||||
|
@ -67,8 +69,8 @@ class AccountManager {
|
||||||
* @param account the account to save
|
* @param account the account to save
|
||||||
*/
|
*/
|
||||||
fun saveAccount(account: AccountEntity) {
|
fun saveAccount(account: AccountEntity) {
|
||||||
if(account.id != 0L) {
|
if (account.id != 0L) {
|
||||||
Log.d(TAG, "saveAccount: saving account with id "+account.id)
|
Log.d(TAG, "saveAccount: saving account with id " + account.id)
|
||||||
accountDao.insertOrReplace(account)
|
accountDao.insertOrReplace(account)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,18 +80,18 @@ class AccountManager {
|
||||||
* Logs the current account out by deleting all data of the account.
|
* Logs the current account out by deleting all data of the account.
|
||||||
* @return the new active account, or null if no other account was found
|
* @return the new active account, or null if no other account was found
|
||||||
*/
|
*/
|
||||||
fun logActiveAccountOut() : AccountEntity? {
|
fun logActiveAccountOut(): AccountEntity? {
|
||||||
|
|
||||||
if(activeAccount == null) {
|
if (activeAccount == null) {
|
||||||
return null
|
return null
|
||||||
} else {
|
} else {
|
||||||
accounts.remove(activeAccount!!)
|
accounts.remove(activeAccount!!)
|
||||||
accountDao.delete(activeAccount!!)
|
accountDao.delete(activeAccount!!)
|
||||||
|
|
||||||
if(accounts.size > 0) {
|
if (accounts.size > 0) {
|
||||||
accounts[0].isActive = true
|
accounts[0].isActive = true
|
||||||
activeAccount = accounts[0]
|
activeAccount = accounts[0]
|
||||||
Log.d(TAG, "logActiveAccountOut: saving account with id "+accounts[0].id)
|
Log.d(TAG, "logActiveAccountOut: saving account with id " + accounts[0].id)
|
||||||
accountDao.insertOrReplace(accounts[0])
|
accountDao.insertOrReplace(accounts[0])
|
||||||
} else {
|
} else {
|
||||||
activeAccount = null
|
activeAccount = null
|
||||||
|
@ -106,18 +108,18 @@ class AccountManager {
|
||||||
* @param account the [Account] object returned from the api
|
* @param account the [Account] object returned from the api
|
||||||
*/
|
*/
|
||||||
fun updateActiveAccount(account: Account) {
|
fun updateActiveAccount(account: Account) {
|
||||||
activeAccount?.let{
|
activeAccount?.let {
|
||||||
it.accountId = account.id
|
it.accountId = account.id
|
||||||
it.username = account.username
|
it.username = account.username
|
||||||
it.displayName = account.name
|
it.displayName = account.name
|
||||||
it.profilePictureUrl = account.avatar
|
it.profilePictureUrl = account.avatar
|
||||||
|
|
||||||
Log.d(TAG, "updateActiveAccount: saving account with id "+it.id)
|
Log.d(TAG, "updateActiveAccount: saving account with id " + it.id)
|
||||||
it.id = accountDao.insertOrReplace(it)
|
it.id = accountDao.insertOrReplace(it)
|
||||||
|
|
||||||
val accountIndex = accounts.indexOf(it)
|
val accountIndex = accounts.indexOf(it)
|
||||||
|
|
||||||
if(accountIndex != -1) {
|
if (accountIndex != -1) {
|
||||||
//in case the user was already logged in with this account, remove the old information
|
//in case the user was already logged in with this account, remove the old information
|
||||||
accounts.removeAt(accountIndex)
|
accounts.removeAt(accountIndex)
|
||||||
accounts.add(accountIndex, it)
|
accounts.add(accountIndex, it)
|
||||||
|
@ -134,8 +136,8 @@ class AccountManager {
|
||||||
*/
|
*/
|
||||||
fun setActiveAccount(accountId: Long) {
|
fun setActiveAccount(accountId: Long) {
|
||||||
|
|
||||||
activeAccount?.let{
|
activeAccount?.let {
|
||||||
Log.d(TAG, "setActiveAccount: saving account with id "+it.id)
|
Log.d(TAG, "setActiveAccount: saving account with id " + it.id)
|
||||||
it.isActive = false
|
it.isActive = false
|
||||||
saveAccount(it)
|
saveAccount(it)
|
||||||
}
|
}
|
||||||
|
@ -144,7 +146,7 @@ class AccountManager {
|
||||||
acc.id == accountId
|
acc.id == accountId
|
||||||
}
|
}
|
||||||
|
|
||||||
activeAccount?.let{
|
activeAccount?.let {
|
||||||
it.isActive = true
|
it.isActive = true
|
||||||
accountDao.insertOrReplace(it)
|
accountDao.insertOrReplace(it)
|
||||||
}
|
}
|
||||||
|
@ -154,7 +156,7 @@ class AccountManager {
|
||||||
* @return an immutable list of all accounts in the database with the active account first
|
* @return an immutable list of all accounts in the database with the active account first
|
||||||
*/
|
*/
|
||||||
fun getAllAccountsOrderedByActive(): List<AccountEntity> {
|
fun getAllAccountsOrderedByActive(): List<AccountEntity> {
|
||||||
accounts.sortWith (Comparator { l, r ->
|
accounts.sortWith(Comparator { l, r ->
|
||||||
when {
|
when {
|
||||||
l.isActive && !r.isActive -> -1
|
l.isActive && !r.isActive -> -1
|
||||||
r.isActive && !l.isActive -> 1
|
r.isActive && !l.isActive -> 1
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
/* Copyright 2018 charlag
|
||||||
|
*
|
||||||
|
* This file is a part of Tusky.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Tusky 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 Tusky; if not,
|
||||||
|
* see <http://www.gnu.org/licenses>. */
|
||||||
|
|
||||||
|
package com.keylesspalace.tusky.di
|
||||||
|
|
||||||
|
import com.keylesspalace.tusky.*
|
||||||
|
import dagger.Module
|
||||||
|
import dagger.android.ContributesAndroidInjector
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by charlag on 3/24/18.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Module
|
||||||
|
abstract class ActivitiesModule {
|
||||||
|
@ContributesAndroidInjector(modules = [FragmentBuildersModule::class])
|
||||||
|
abstract fun contributesMainActivity(): MainActivity
|
||||||
|
|
||||||
|
@ContributesAndroidInjector(modules = [FragmentBuildersModule::class])
|
||||||
|
abstract fun contributesAccountActivity(): AccountActivity
|
||||||
|
|
||||||
|
@ContributesAndroidInjector(modules = [FragmentBuildersModule::class])
|
||||||
|
abstract fun contributesListsActivity(): ListsActivity
|
||||||
|
|
||||||
|
@ContributesAndroidInjector
|
||||||
|
abstract fun contributesComposeActivity(): ComposeActivity
|
||||||
|
|
||||||
|
@ContributesAndroidInjector
|
||||||
|
abstract fun contributesEditProfileActivity(): EditProfileActivity
|
||||||
|
|
||||||
|
@ContributesAndroidInjector(modules = [FragmentBuildersModule::class])
|
||||||
|
abstract fun contributesAccountListActivity(): AccountListActivity
|
||||||
|
|
||||||
|
@ContributesAndroidInjector(modules = [FragmentBuildersModule::class])
|
||||||
|
abstract fun contributesModalTimelineActivity(): ModalTimelineActivity
|
||||||
|
|
||||||
|
@ContributesAndroidInjector(modules = [FragmentBuildersModule::class])
|
||||||
|
abstract fun contributesViewTagActivity(): ViewTagActivity
|
||||||
|
|
||||||
|
@ContributesAndroidInjector(modules = [FragmentBuildersModule::class])
|
||||||
|
abstract fun contributesViewThreadActivity(): ViewThreadActivity
|
||||||
|
|
||||||
|
@ContributesAndroidInjector(modules = [FragmentBuildersModule::class])
|
||||||
|
abstract fun contributesFavouritesActivity(): FavouritesActivity
|
||||||
|
|
||||||
|
@ContributesAndroidInjector(modules = [FragmentBuildersModule::class])
|
||||||
|
abstract fun contribtutesSearchAvtivity(): SearchActivity
|
||||||
|
|
||||||
|
@ContributesAndroidInjector()
|
||||||
|
abstract fun contributesAboutActivity(): AboutActivity
|
||||||
|
}
|
46
app/src/main/java/com/keylesspalace/tusky/di/AppComponent.kt
Normal file
46
app/src/main/java/com/keylesspalace/tusky/di/AppComponent.kt
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
/* Copyright 2018 charlag
|
||||||
|
*
|
||||||
|
* This file is a part of Tusky.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Tusky 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 Tusky; if not,
|
||||||
|
* see <http://www.gnu.org/licenses>. */
|
||||||
|
|
||||||
|
package com.keylesspalace.tusky.di
|
||||||
|
|
||||||
|
import com.keylesspalace.tusky.TuskyApplication
|
||||||
|
import dagger.BindsInstance
|
||||||
|
import dagger.Component
|
||||||
|
import dagger.android.AndroidInjectionModule
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by charlag on 3/21/18.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
@Component(modules = [
|
||||||
|
AppModule::class,
|
||||||
|
NetworkModule::class,
|
||||||
|
AndroidInjectionModule::class,
|
||||||
|
ActivitiesModule::class
|
||||||
|
])
|
||||||
|
interface AppComponent {
|
||||||
|
@Component.Builder
|
||||||
|
interface Builder {
|
||||||
|
@BindsInstance
|
||||||
|
fun application(tuskyApp: TuskyApplication): Builder
|
||||||
|
|
||||||
|
fun build(): AppComponent
|
||||||
|
}
|
||||||
|
|
||||||
|
fun inject(app: TuskyApplication)
|
||||||
|
}
|
80
app/src/main/java/com/keylesspalace/tusky/di/AppInjector.kt
Normal file
80
app/src/main/java/com/keylesspalace/tusky/di/AppInjector.kt
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
/* Copyright 2018 charlag
|
||||||
|
*
|
||||||
|
* This file is a part of Tusky.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Tusky 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 Tusky; if not,
|
||||||
|
* see <http://www.gnu.org/licenses>. */
|
||||||
|
|
||||||
|
package com.keylesspalace.tusky.di
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.app.Application
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.support.v4.app.Fragment
|
||||||
|
import android.support.v4.app.FragmentActivity
|
||||||
|
import android.support.v4.app.FragmentManager
|
||||||
|
import com.keylesspalace.tusky.TuskyApplication
|
||||||
|
import dagger.android.AndroidInjection
|
||||||
|
import dagger.android.support.AndroidSupportInjection
|
||||||
|
import dagger.android.support.HasSupportFragmentInjector
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by charlag on 3/24/18.
|
||||||
|
*/
|
||||||
|
|
||||||
|
object AppInjector {
|
||||||
|
fun init(app: TuskyApplication) {
|
||||||
|
DaggerAppComponent.builder().application(app)
|
||||||
|
.build().inject(app)
|
||||||
|
|
||||||
|
app.registerActivityLifecycleCallbacks(object : Application.ActivityLifecycleCallbacks {
|
||||||
|
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
|
||||||
|
handleActivity(activity)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityPaused(activity: Activity?) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityResumed(activity: Activity?) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityStarted(activity: Activity?) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityDestroyed(activity: Activity?) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivitySaveInstanceState(activity: Activity?, outState: Bundle?) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityStopped(activity: Activity?) {
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleActivity(activity: Activity) {
|
||||||
|
if (activity is HasSupportFragmentInjector || activity is Injectable) {
|
||||||
|
AndroidInjection.inject(activity)
|
||||||
|
}
|
||||||
|
if (activity is FragmentActivity) {
|
||||||
|
activity.supportFragmentManager.registerFragmentLifecycleCallbacks(
|
||||||
|
object : FragmentManager.FragmentLifecycleCallbacks() {
|
||||||
|
override fun onFragmentCreated(fm: FragmentManager?, f: Fragment?,
|
||||||
|
savedInstanceState: Bundle?) {
|
||||||
|
if (f is Injectable) {
|
||||||
|
AndroidSupportInjection.inject(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
68
app/src/main/java/com/keylesspalace/tusky/di/AppModule.kt
Normal file
68
app/src/main/java/com/keylesspalace/tusky/di/AppModule.kt
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
/* Copyright 2018 charlag
|
||||||
|
*
|
||||||
|
* This file is a part of Tusky.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Tusky 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 Tusky; if not,
|
||||||
|
* see <http://www.gnu.org/licenses>. */
|
||||||
|
|
||||||
|
|
||||||
|
package com.keylesspalace.tusky.di
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import android.preference.PreferenceManager
|
||||||
|
import android.support.v4.content.LocalBroadcastManager
|
||||||
|
import com.keylesspalace.tusky.R
|
||||||
|
import com.keylesspalace.tusky.TuskyApplication
|
||||||
|
import com.keylesspalace.tusky.db.AccountManager
|
||||||
|
import com.keylesspalace.tusky.network.MastodonApi
|
||||||
|
import com.keylesspalace.tusky.network.TimelineCases
|
||||||
|
import com.keylesspalace.tusky.network.TimelineCasesImpl
|
||||||
|
import dagger.Module
|
||||||
|
import dagger.Provides
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by charlag on 3/21/18.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Module
|
||||||
|
class AppModule {
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
fun providesApplication(app: TuskyApplication): Application = app
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
fun providesContext(app: Application): Context = app
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
fun providesSharedPreferences(app: Application): SharedPreferences {
|
||||||
|
return PreferenceManager.getDefaultSharedPreferences(app)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
fun providesBroadcastManager(app: Application): LocalBroadcastManager {
|
||||||
|
return LocalBroadcastManager.getInstance(app)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
fun providesTimelineUseCases(api: MastodonApi,
|
||||||
|
broadcastManager: LocalBroadcastManager): TimelineCases {
|
||||||
|
return TimelineCasesImpl(api, broadcastManager)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun providesAccountManager(app: TuskyApplication): AccountManager {
|
||||||
|
return app.serviceLocator.get(AccountManager::class.java)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
/* Copyright 2018 charlag
|
||||||
|
*
|
||||||
|
* This file is a part of Tusky.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Tusky 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 Tusky; if not,
|
||||||
|
* see <http://www.gnu.org/licenses>. */
|
||||||
|
|
||||||
|
|
||||||
|
package com.keylesspalace.tusky.di
|
||||||
|
|
||||||
|
import com.keylesspalace.tusky.fragment.*
|
||||||
|
import dagger.Module
|
||||||
|
import dagger.android.ContributesAndroidInjector
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by charlag on 3/24/18.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Module
|
||||||
|
abstract class FragmentBuildersModule {
|
||||||
|
@ContributesAndroidInjector
|
||||||
|
abstract fun accountListFragment(): AccountListFragment
|
||||||
|
|
||||||
|
@ContributesAndroidInjector
|
||||||
|
abstract fun accountMediaFragment(): AccountMediaFragment
|
||||||
|
|
||||||
|
@ContributesAndroidInjector
|
||||||
|
abstract fun viewThreadFragment(): ViewThreadFragment
|
||||||
|
|
||||||
|
@ContributesAndroidInjector
|
||||||
|
abstract fun timelineFragment(): TimelineFragment
|
||||||
|
|
||||||
|
@ContributesAndroidInjector
|
||||||
|
abstract fun notificationsFragment(): NotificationsFragment
|
||||||
|
}
|
23
app/src/main/java/com/keylesspalace/tusky/di/Injectable.kt
Normal file
23
app/src/main/java/com/keylesspalace/tusky/di/Injectable.kt
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
/* Copyright 2018 charlag
|
||||||
|
*
|
||||||
|
* This file is a part of Tusky.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Tusky 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 Tusky; if not,
|
||||||
|
* see <http://www.gnu.org/licenses>. */
|
||||||
|
|
||||||
|
|
||||||
|
package com.keylesspalace.tusky.di
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by charlag on 3/24/18.
|
||||||
|
*/
|
||||||
|
|
||||||
|
interface Injectable
|
111
app/src/main/java/com/keylesspalace/tusky/di/NetworkModule.kt
Normal file
111
app/src/main/java/com/keylesspalace/tusky/di/NetworkModule.kt
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
/* Copyright 2018 charlag
|
||||||
|
*
|
||||||
|
* This file is a part of Tusky.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Tusky 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 Tusky; if not,
|
||||||
|
* see <http://www.gnu.org/licenses>. */
|
||||||
|
|
||||||
|
|
||||||
|
package com.keylesspalace.tusky.di
|
||||||
|
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import android.text.Spanned
|
||||||
|
import com.google.gson.Gson
|
||||||
|
import com.google.gson.GsonBuilder
|
||||||
|
import com.google.gson.JsonDeserializer
|
||||||
|
import com.keylesspalace.tusky.db.AccountManager
|
||||||
|
import com.keylesspalace.tusky.json.SpannedTypeAdapter
|
||||||
|
import com.keylesspalace.tusky.network.InstanceSwitchAuthInterceptor
|
||||||
|
import com.keylesspalace.tusky.network.MastodonApi
|
||||||
|
import com.keylesspalace.tusky.util.OkHttpUtils
|
||||||
|
import dagger.Module
|
||||||
|
import dagger.Provides
|
||||||
|
import dagger.multibindings.ClassKey
|
||||||
|
import dagger.multibindings.IntoMap
|
||||||
|
import dagger.multibindings.IntoSet
|
||||||
|
import okhttp3.Interceptor
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import retrofit2.Converter
|
||||||
|
import retrofit2.Retrofit
|
||||||
|
import retrofit2.converter.gson.GsonConverterFactory
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by charlag on 3/24/18.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Module
|
||||||
|
class NetworkModule {
|
||||||
|
@Provides
|
||||||
|
@IntoMap()
|
||||||
|
@ClassKey(Spanned::class)
|
||||||
|
fun providesSpannedTypeAdapter(): JsonDeserializer<*> = SpannedTypeAdapter()
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun providesGson(adapters: @JvmSuppressWildcards Map<Class<*>, JsonDeserializer<*>>): Gson {
|
||||||
|
return GsonBuilder()
|
||||||
|
.apply {
|
||||||
|
for ((k, v) in adapters) {
|
||||||
|
registerTypeAdapter(k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.create()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@IntoSet
|
||||||
|
@Singleton
|
||||||
|
fun providesConverterFactory(gson: Gson): Converter.Factory = GsonConverterFactory.create(gson)
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@IntoSet
|
||||||
|
@Singleton
|
||||||
|
fun providesAuthInterceptor(accountManager: AccountManager): Interceptor {
|
||||||
|
// should accept AccountManager here probably but I don't want to break things yet
|
||||||
|
return InstanceSwitchAuthInterceptor(accountManager)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun providesHttpClient(interceptors: @JvmSuppressWildcards Set<Interceptor>,
|
||||||
|
preferences: SharedPreferences): OkHttpClient {
|
||||||
|
return OkHttpUtils.getCompatibleClientBuilder(preferences)
|
||||||
|
.apply {
|
||||||
|
interceptors.fold(this) { b, i ->
|
||||||
|
b.addInterceptor(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun providesRetrofit(httpClient: OkHttpClient,
|
||||||
|
converters: @JvmSuppressWildcards Set<Converter.Factory>): Retrofit {
|
||||||
|
return Retrofit.Builder().baseUrl("https://dummy.placeholder/")
|
||||||
|
.client(httpClient)
|
||||||
|
.let { builder ->
|
||||||
|
// Doing it this way in case builder will be immutable so we return the final
|
||||||
|
// instance
|
||||||
|
converters.fold(builder) { b, c ->
|
||||||
|
b.addConverterFactory(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.build()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun providesApi(retrofit: Retrofit): MastodonApi = retrofit.create(MastodonApi::class.java)
|
||||||
|
}
|
|
@ -39,6 +39,7 @@ import com.keylesspalace.tusky.adapter.FollowAdapter;
|
||||||
import com.keylesspalace.tusky.adapter.FollowRequestsAdapter;
|
import com.keylesspalace.tusky.adapter.FollowRequestsAdapter;
|
||||||
import com.keylesspalace.tusky.adapter.FooterViewHolder;
|
import com.keylesspalace.tusky.adapter.FooterViewHolder;
|
||||||
import com.keylesspalace.tusky.adapter.MutesAdapter;
|
import com.keylesspalace.tusky.adapter.MutesAdapter;
|
||||||
|
import com.keylesspalace.tusky.di.Injectable;
|
||||||
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.AccountActionListener;
|
import com.keylesspalace.tusky.interfaces.AccountActionListener;
|
||||||
|
@ -49,11 +50,14 @@ import com.keylesspalace.tusky.view.EndlessOnScrollListener;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import retrofit2.Call;
|
import retrofit2.Call;
|
||||||
import retrofit2.Callback;
|
import retrofit2.Callback;
|
||||||
import retrofit2.Response;
|
import retrofit2.Response;
|
||||||
|
|
||||||
public class AccountListFragment extends BaseFragment implements AccountActionListener {
|
public class AccountListFragment extends BaseFragment implements AccountActionListener,
|
||||||
|
Injectable {
|
||||||
private static final String TAG = "AccountList"; // logging tag
|
private static final String TAG = "AccountList"; // logging tag
|
||||||
|
|
||||||
public AccountListFragment() {
|
public AccountListFragment() {
|
||||||
|
@ -67,13 +71,15 @@ public class AccountListFragment extends BaseFragment implements AccountActionLi
|
||||||
FOLLOW_REQUESTS,
|
FOLLOW_REQUESTS,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public MastodonApi api;
|
||||||
|
|
||||||
private Type type;
|
private Type type;
|
||||||
private String accountId;
|
private String accountId;
|
||||||
private LinearLayoutManager layoutManager;
|
private LinearLayoutManager layoutManager;
|
||||||
private RecyclerView recyclerView;
|
private RecyclerView recyclerView;
|
||||||
private EndlessOnScrollListener scrollListener;
|
private EndlessOnScrollListener scrollListener;
|
||||||
private AccountAdapter adapter;
|
private AccountAdapter adapter;
|
||||||
private MastodonApi api;
|
|
||||||
private boolean bottomLoading;
|
private boolean bottomLoading;
|
||||||
private int bottomFetches;
|
private int bottomFetches;
|
||||||
private boolean topLoading;
|
private boolean topLoading;
|
||||||
|
@ -146,12 +152,6 @@ public class AccountListFragment extends BaseFragment implements AccountActionLi
|
||||||
@Override
|
@Override
|
||||||
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
|
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
|
||||||
super.onActivityCreated(savedInstanceState);
|
super.onActivityCreated(savedInstanceState);
|
||||||
BaseActivity activity = (BaseActivity) getActivity();
|
|
||||||
|
|
||||||
/* MastodonApi on the base activity is only guaranteed to be initialised after the parent
|
|
||||||
* activity is created, so everything needing to access the api object has to be delayed
|
|
||||||
* until here. */
|
|
||||||
api = activity.mastodonApi;
|
|
||||||
// Just use the basic scroll listener to load more accounts.
|
// Just use the basic scroll listener to load more accounts.
|
||||||
scrollListener = new EndlessOnScrollListener(layoutManager) {
|
scrollListener = new EndlessOnScrollListener(layoutManager) {
|
||||||
@Override
|
@Override
|
||||||
|
@ -464,7 +464,7 @@ public class AccountListFragment extends BaseFragment implements AccountActionLi
|
||||||
private void onLoadMore(RecyclerView recyclerView) {
|
private void onLoadMore(RecyclerView recyclerView) {
|
||||||
AccountAdapter adapter = (AccountAdapter) recyclerView.getAdapter();
|
AccountAdapter adapter = (AccountAdapter) recyclerView.getAdapter();
|
||||||
//if we do not have a bottom id, we know we do not need to load more
|
//if we do not have a bottom id, we know we do not need to load more
|
||||||
if(adapter.getBottomId() == null) return;
|
if (adapter.getBottomId() == null) return;
|
||||||
fetchAccounts(adapter.getBottomId(), null, FetchEnd.BOTTOM);
|
fetchAccounts(adapter.getBottomId(), null, FetchEnd.BOTTOM);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,10 +29,10 @@ import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import com.keylesspalace.tusky.BaseActivity
|
|
||||||
import com.keylesspalace.tusky.R
|
import com.keylesspalace.tusky.R
|
||||||
import com.keylesspalace.tusky.ViewMediaActivity
|
import com.keylesspalace.tusky.ViewMediaActivity
|
||||||
import com.keylesspalace.tusky.ViewVideoActivity
|
import com.keylesspalace.tusky.ViewVideoActivity
|
||||||
|
import com.keylesspalace.tusky.di.Injectable
|
||||||
import com.keylesspalace.tusky.entity.Attachment
|
import com.keylesspalace.tusky.entity.Attachment
|
||||||
import com.keylesspalace.tusky.entity.Status
|
import com.keylesspalace.tusky.entity.Status
|
||||||
import com.keylesspalace.tusky.network.MastodonApi
|
import com.keylesspalace.tusky.network.MastodonApi
|
||||||
|
@ -43,6 +43,7 @@ import retrofit2.Call
|
||||||
import retrofit2.Callback
|
import retrofit2.Callback
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by charlag on 26/10/2017.
|
* Created by charlag on 26/10/2017.
|
||||||
|
@ -50,7 +51,7 @@ import java.util.*
|
||||||
* Fragment with multiple columns of media previews for the specified account.
|
* Fragment with multiple columns of media previews for the specified account.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class AccountMediaFragment : BaseFragment() {
|
class AccountMediaFragment : BaseFragment(), Injectable {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
|
@ -66,9 +67,11 @@ class AccountMediaFragment : BaseFragment() {
|
||||||
private const val TAG = "AccountMediaFragment"
|
private const val TAG = "AccountMediaFragment"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var api: MastodonApi
|
||||||
|
|
||||||
private val adapter = MediaGridAdapter()
|
private val adapter = MediaGridAdapter()
|
||||||
private var currentCall: Call<List<Status>>? = null
|
private var currentCall: Call<List<Status>>? = null
|
||||||
private lateinit var api: MastodonApi
|
|
||||||
private val statuses = mutableListOf<Status>()
|
private val statuses = mutableListOf<Status>()
|
||||||
private var fetchingStatus = FetchingStatus.NOT_FETCHING
|
private var fetchingStatus = FetchingStatus.NOT_FETCHING
|
||||||
lateinit private var swipeLayout: SwipeRefreshLayout
|
lateinit private var swipeLayout: SwipeRefreshLayout
|
||||||
|
@ -123,7 +126,6 @@ class AccountMediaFragment : BaseFragment() {
|
||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onActivityCreated(savedInstanceState)
|
||||||
api = (activity as BaseActivity).mastodonApi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
||||||
|
|
|
@ -15,14 +15,10 @@
|
||||||
|
|
||||||
package com.keylesspalace.tusky.fragment;
|
package com.keylesspalace.tusky.fragment;
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
|
|
||||||
import com.keylesspalace.tusky.R;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -44,9 +40,4 @@ public class BaseFragment extends Fragment {
|
||||||
}
|
}
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected SharedPreferences getPrivatePreferences() {
|
|
||||||
return getContext().getSharedPreferences(
|
|
||||||
getString(R.string.preferences_file_key), Context.MODE_PRIVATE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,11 +43,14 @@ import com.keylesspalace.tusky.adapter.FooterViewHolder;
|
||||||
import com.keylesspalace.tusky.adapter.NotificationsAdapter;
|
import com.keylesspalace.tusky.adapter.NotificationsAdapter;
|
||||||
import com.keylesspalace.tusky.db.AccountEntity;
|
import com.keylesspalace.tusky.db.AccountEntity;
|
||||||
import com.keylesspalace.tusky.db.AccountManager;
|
import com.keylesspalace.tusky.db.AccountManager;
|
||||||
|
import com.keylesspalace.tusky.di.Injectable;
|
||||||
import com.keylesspalace.tusky.entity.Attachment;
|
import com.keylesspalace.tusky.entity.Attachment;
|
||||||
import com.keylesspalace.tusky.entity.Notification;
|
import com.keylesspalace.tusky.entity.Notification;
|
||||||
import com.keylesspalace.tusky.entity.Status;
|
import com.keylesspalace.tusky.entity.Status;
|
||||||
import com.keylesspalace.tusky.interfaces.ActionButtonActivity;
|
import com.keylesspalace.tusky.interfaces.ActionButtonActivity;
|
||||||
import com.keylesspalace.tusky.interfaces.StatusActionListener;
|
import com.keylesspalace.tusky.interfaces.StatusActionListener;
|
||||||
|
import com.keylesspalace.tusky.network.MastodonApi;
|
||||||
|
import com.keylesspalace.tusky.network.TimelineCases;
|
||||||
import com.keylesspalace.tusky.receiver.TimelineReceiver;
|
import com.keylesspalace.tusky.receiver.TimelineReceiver;
|
||||||
import com.keylesspalace.tusky.util.CollectionUtil;
|
import com.keylesspalace.tusky.util.CollectionUtil;
|
||||||
import com.keylesspalace.tusky.util.Either;
|
import com.keylesspalace.tusky.util.Either;
|
||||||
|
@ -64,14 +67,18 @@ import java.math.BigInteger;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import retrofit2.Call;
|
import retrofit2.Call;
|
||||||
import retrofit2.Callback;
|
import retrofit2.Callback;
|
||||||
import retrofit2.Response;
|
import retrofit2.Response;
|
||||||
|
|
||||||
public class NotificationsFragment extends SFragment implements
|
public class NotificationsFragment extends SFragment implements
|
||||||
SwipeRefreshLayout.OnRefreshListener, StatusActionListener,
|
SwipeRefreshLayout.OnRefreshListener,
|
||||||
|
StatusActionListener,
|
||||||
NotificationsAdapter.NotificationActionListener,
|
NotificationsAdapter.NotificationActionListener,
|
||||||
SharedPreferences.OnSharedPreferenceChangeListener {
|
SharedPreferences.OnSharedPreferenceChangeListener,
|
||||||
|
Injectable {
|
||||||
private static final String TAG = "NotificationF"; // logging tag
|
private static final String TAG = "NotificationF"; // logging tag
|
||||||
|
|
||||||
private static final int LOAD_AT_ONCE = 30;
|
private static final int LOAD_AT_ONCE = 30;
|
||||||
|
@ -97,6 +104,11 @@ public class NotificationsFragment extends SFragment implements
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public TimelineCases timelineCases;
|
||||||
|
@Inject
|
||||||
|
public MastodonApi mastodonApi;
|
||||||
|
|
||||||
private SwipeRefreshLayout swipeRefreshLayout;
|
private SwipeRefreshLayout swipeRefreshLayout;
|
||||||
private LinearLayoutManager layoutManager;
|
private LinearLayoutManager layoutManager;
|
||||||
private RecyclerView recyclerView;
|
private RecyclerView recyclerView;
|
||||||
|
@ -113,6 +125,11 @@ public class NotificationsFragment extends SFragment implements
|
||||||
private String topId;
|
private String topId;
|
||||||
private boolean alwaysShowSensitiveMedia;
|
private boolean alwaysShowSensitiveMedia;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected TimelineCases timelineCases() {
|
||||||
|
return timelineCases;
|
||||||
|
}
|
||||||
|
|
||||||
// Each element is either a Notification for loading data or a Placeholder
|
// Each element is either a Notification for loading data or a Placeholder
|
||||||
private final PairedList<Either<Placeholder, Notification>, NotificationViewData> notifications
|
private final PairedList<Either<Placeholder, Notification>, NotificationViewData> notifications
|
||||||
= new PairedList<>(new Function<Either<Placeholder, Notification>, NotificationViewData>() {
|
= new PairedList<>(new Function<Either<Placeholder, Notification>, NotificationViewData>() {
|
||||||
|
@ -273,7 +290,7 @@ public class NotificationsFragment extends SFragment implements
|
||||||
public void onReblog(final boolean reblog, final int position) {
|
public void onReblog(final boolean reblog, final int position) {
|
||||||
final Notification notification = notifications.get(position).getAsRight();
|
final Notification notification = notifications.get(position).getAsRight();
|
||||||
final Status status = notification.getStatus();
|
final Status status = notification.getStatus();
|
||||||
reblogWithCallback(status, reblog, new Callback<Status>() {
|
timelineCases.reblogWithCallback(status, reblog, new Callback<Status>() {
|
||||||
@Override
|
@Override
|
||||||
public void onResponse(@NonNull Call<Status> call, @NonNull retrofit2.Response<Status> response) {
|
public void onResponse(@NonNull Call<Status> call, @NonNull retrofit2.Response<Status> response) {
|
||||||
if (response.isSuccessful()) {
|
if (response.isSuccessful()) {
|
||||||
|
@ -310,7 +327,7 @@ public class NotificationsFragment extends SFragment implements
|
||||||
public void onFavourite(final boolean favourite, final int position) {
|
public void onFavourite(final boolean favourite, final int position) {
|
||||||
final Notification notification = notifications.get(position).getAsRight();
|
final Notification notification = notifications.get(position).getAsRight();
|
||||||
final Status status = notification.getStatus();
|
final Status status = notification.getStatus();
|
||||||
favouriteWithCallback(status, favourite, new Callback<Status>() {
|
timelineCases.favouriteWithCallback(status, favourite, new Callback<Status>() {
|
||||||
@Override
|
@Override
|
||||||
public void onResponse(@NonNull Call<Status> call, @NonNull retrofit2.Response<Status> response) {
|
public void onResponse(@NonNull Call<Status> call, @NonNull retrofit2.Response<Status> response) {
|
||||||
if (response.isSuccessful()) {
|
if (response.isSuccessful()) {
|
||||||
|
|
|
@ -41,18 +41,22 @@ 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.di.Injectable;
|
||||||
import com.keylesspalace.tusky.db.AccountManager;
|
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;
|
||||||
import com.keylesspalace.tusky.interfaces.AdapterItemRemover;
|
import com.keylesspalace.tusky.interfaces.AdapterItemRemover;
|
||||||
import com.keylesspalace.tusky.network.MastodonApi;
|
import com.keylesspalace.tusky.network.MastodonApi;
|
||||||
|
import com.keylesspalace.tusky.network.TimelineCases;
|
||||||
import com.keylesspalace.tusky.receiver.TimelineReceiver;
|
import com.keylesspalace.tusky.receiver.TimelineReceiver;
|
||||||
import com.keylesspalace.tusky.util.HtmlUtils;
|
import com.keylesspalace.tusky.util.HtmlUtils;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import okhttp3.ResponseBody;
|
import okhttp3.ResponseBody;
|
||||||
import retrofit2.Call;
|
import retrofit2.Call;
|
||||||
import retrofit2.Callback;
|
import retrofit2.Callback;
|
||||||
|
@ -69,7 +73,8 @@ public abstract class SFragment extends BaseFragment implements AdapterItemRemov
|
||||||
|
|
||||||
protected String loggedInAccountId;
|
protected String loggedInAccountId;
|
||||||
protected String loggedInUsername;
|
protected String loggedInUsername;
|
||||||
protected MastodonApi mastodonApi;
|
|
||||||
|
protected abstract TimelineCases timelineCases();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
|
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
|
||||||
|
@ -80,8 +85,6 @@ public abstract class SFragment extends BaseFragment implements AdapterItemRemov
|
||||||
loggedInAccountId = activeAccount.getAccountId();
|
loggedInAccountId = activeAccount.getAccountId();
|
||||||
loggedInUsername = activeAccount.getUsername();
|
loggedInUsername = activeAccount.getUsername();
|
||||||
}
|
}
|
||||||
BaseActivity activity = (BaseActivity) getActivity();
|
|
||||||
mastodonApi = activity.mastodonApi;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -90,6 +93,11 @@ public abstract class SFragment extends BaseFragment implements AdapterItemRemov
|
||||||
getActivity().overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left);
|
getActivity().overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void openReblog(@Nullable final Status status) {
|
||||||
|
if (status == null) return;
|
||||||
|
viewAccount(status.getAccount().getId());
|
||||||
|
}
|
||||||
|
|
||||||
protected void reply(Status status) {
|
protected void reply(Status status) {
|
||||||
String inReplyToId = status.getActionableId();
|
String inReplyToId = status.getActionableId();
|
||||||
Status actionableStatus = status.getActionableStatus();
|
Status actionableStatus = status.getActionableStatus();
|
||||||
|
@ -113,88 +121,6 @@ public abstract class SFragment extends BaseFragment implements AdapterItemRemov
|
||||||
startActivityForResult(intent, COMPOSE_RESULT);
|
startActivityForResult(intent, COMPOSE_RESULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void reblogWithCallback(final Status status, final boolean reblog,
|
|
||||||
Callback<Status> callback) {
|
|
||||||
String id = status.getActionableId();
|
|
||||||
|
|
||||||
Call<Status> call;
|
|
||||||
if (reblog) {
|
|
||||||
call = mastodonApi.reblogStatus(id);
|
|
||||||
} else {
|
|
||||||
call = mastodonApi.unreblogStatus(id);
|
|
||||||
}
|
|
||||||
call.enqueue(callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void favouriteWithCallback(final Status status, final boolean favourite,
|
|
||||||
final Callback<Status> callback) {
|
|
||||||
String id = status.getActionableId();
|
|
||||||
|
|
||||||
Call<Status> call;
|
|
||||||
if (favourite) {
|
|
||||||
call = mastodonApi.favouriteStatus(id);
|
|
||||||
} else {
|
|
||||||
call = mastodonApi.unfavouriteStatus(id);
|
|
||||||
}
|
|
||||||
call.enqueue(callback);
|
|
||||||
callList.add(call);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void openReblog(@Nullable final Status status) {
|
|
||||||
if (status == null) return;
|
|
||||||
viewAccount(status.getAccount().getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void mute(String id) {
|
|
||||||
Call<Relationship> call = mastodonApi.muteAccount(id);
|
|
||||||
call.enqueue(new Callback<Relationship>() {
|
|
||||||
@Override
|
|
||||||
public void onResponse(@NonNull Call<Relationship> call, @NonNull Response<Relationship> response) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(@NonNull Call<Relationship> call, @NonNull Throwable t) {
|
|
||||||
}
|
|
||||||
});
|
|
||||||
callList.add(call);
|
|
||||||
Intent intent = new Intent(TimelineReceiver.Types.MUTE_ACCOUNT);
|
|
||||||
intent.putExtra("id", id);
|
|
||||||
LocalBroadcastManager.getInstance(getContext())
|
|
||||||
.sendBroadcast(intent);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void block(String id) {
|
|
||||||
Call<Relationship> call = mastodonApi.blockAccount(id);
|
|
||||||
call.enqueue(new Callback<Relationship>() {
|
|
||||||
@Override
|
|
||||||
public void onResponse(@NonNull Call<Relationship> call, @NonNull retrofit2.Response<Relationship> response) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(@NonNull Call<Relationship> call, @NonNull Throwable t) {
|
|
||||||
}
|
|
||||||
});
|
|
||||||
callList.add(call);
|
|
||||||
Intent intent = new Intent(TimelineReceiver.Types.BLOCK_ACCOUNT);
|
|
||||||
intent.putExtra("id", id);
|
|
||||||
LocalBroadcastManager.getInstance(getContext())
|
|
||||||
.sendBroadcast(intent);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void delete(String id) {
|
|
||||||
Call<ResponseBody> call = mastodonApi.deleteStatus(id);
|
|
||||||
call.enqueue(new Callback<ResponseBody>() {
|
|
||||||
@Override
|
|
||||||
public void onResponse(@NonNull Call<ResponseBody> call, @NonNull retrofit2.Response<ResponseBody> response) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(@NonNull Call<ResponseBody> call, @NonNull Throwable t) {
|
|
||||||
}
|
|
||||||
});
|
|
||||||
callList.add(call);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void more(final Status status, View view, final int position) {
|
protected void more(final Status status, View view, final int position) {
|
||||||
final String id = status.getActionableId();
|
final String id = status.getActionableId();
|
||||||
final String accountId = status.getActionableStatus().getAccount().getId();
|
final String accountId = status.getActionableStatus().getAccount().getId();
|
||||||
|
@ -208,60 +134,56 @@ public abstract class SFragment extends BaseFragment implements AdapterItemRemov
|
||||||
} else {
|
} else {
|
||||||
popup.inflate(R.menu.status_more_for_user);
|
popup.inflate(R.menu.status_more_for_user);
|
||||||
}
|
}
|
||||||
popup.setOnMenuItemClickListener(
|
popup.setOnMenuItemClickListener(item -> {
|
||||||
new PopupMenu.OnMenuItemClickListener() {
|
switch (item.getItemId()) {
|
||||||
@Override
|
case R.id.status_share_content: {
|
||||||
public boolean onMenuItemClick(MenuItem item) {
|
StringBuilder sb = new StringBuilder();
|
||||||
switch (item.getItemId()) {
|
sb.append(status.getAccount().getUsername());
|
||||||
case R.id.status_share_content: {
|
sb.append(" - ");
|
||||||
StringBuilder sb = new StringBuilder();
|
sb.append(status.getContent().toString());
|
||||||
sb.append(status.getAccount().getUsername());
|
|
||||||
sb.append(" - ");
|
|
||||||
sb.append(status.getContent().toString());
|
|
||||||
|
|
||||||
Intent sendIntent = new Intent();
|
Intent sendIntent = new Intent();
|
||||||
sendIntent.setAction(Intent.ACTION_SEND);
|
sendIntent.setAction(Intent.ACTION_SEND);
|
||||||
sendIntent.putExtra(Intent.EXTRA_TEXT, sb.toString());
|
sendIntent.putExtra(Intent.EXTRA_TEXT, sb.toString());
|
||||||
sendIntent.setType("text/plain");
|
sendIntent.setType("text/plain");
|
||||||
startActivity(Intent.createChooser(sendIntent, getResources().getText(R.string.send_status_content_to)));
|
startActivity(Intent.createChooser(sendIntent, getResources().getText(R.string.send_status_content_to)));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case R.id.status_share_link: {
|
case R.id.status_share_link: {
|
||||||
Intent sendIntent = new Intent();
|
Intent sendIntent = new Intent();
|
||||||
sendIntent.setAction(Intent.ACTION_SEND);
|
sendIntent.setAction(Intent.ACTION_SEND);
|
||||||
sendIntent.putExtra(Intent.EXTRA_TEXT, statusUrl);
|
sendIntent.putExtra(Intent.EXTRA_TEXT, statusUrl);
|
||||||
sendIntent.setType("text/plain");
|
sendIntent.setType("text/plain");
|
||||||
startActivity(Intent.createChooser(sendIntent, getResources().getText(R.string.send_status_link_to)));
|
startActivity(Intent.createChooser(sendIntent, getResources().getText(R.string.send_status_link_to)));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case R.id.status_copy_link: {
|
case R.id.status_copy_link: {
|
||||||
ClipboardManager clipboard = (ClipboardManager)
|
ClipboardManager clipboard = (ClipboardManager)
|
||||||
getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
|
getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
|
||||||
ClipData clip = ClipData.newPlainText(null, statusUrl);
|
ClipData clip = ClipData.newPlainText(null, statusUrl);
|
||||||
clipboard.setPrimaryClip(clip);
|
clipboard.setPrimaryClip(clip);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case R.id.status_mute: {
|
case R.id.status_mute: {
|
||||||
mute(accountId);
|
timelineCases().mute(accountId);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case R.id.status_block: {
|
case R.id.status_block: {
|
||||||
block(accountId);
|
timelineCases().block(accountId);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case R.id.status_report: {
|
case R.id.status_report: {
|
||||||
openReportPage(accountId, accountUsename, id, content);
|
openReportPage(accountId, accountUsename, id, content);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case R.id.status_delete: {
|
case R.id.status_delete: {
|
||||||
delete(id);
|
timelineCases().delete(id);
|
||||||
removeItem(position);
|
removeItem(position);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
});
|
||||||
});
|
|
||||||
popup.show();
|
popup.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,11 +40,13 @@ import com.keylesspalace.tusky.BuildConfig;
|
||||||
import com.keylesspalace.tusky.R;
|
import com.keylesspalace.tusky.R;
|
||||||
import com.keylesspalace.tusky.adapter.FooterViewHolder;
|
import com.keylesspalace.tusky.adapter.FooterViewHolder;
|
||||||
import com.keylesspalace.tusky.adapter.TimelineAdapter;
|
import com.keylesspalace.tusky.adapter.TimelineAdapter;
|
||||||
|
import com.keylesspalace.tusky.di.Injectable;
|
||||||
import com.keylesspalace.tusky.entity.Attachment;
|
import com.keylesspalace.tusky.entity.Attachment;
|
||||||
import com.keylesspalace.tusky.entity.Status;
|
import com.keylesspalace.tusky.entity.Status;
|
||||||
import com.keylesspalace.tusky.interfaces.ActionButtonActivity;
|
import com.keylesspalace.tusky.interfaces.ActionButtonActivity;
|
||||||
import com.keylesspalace.tusky.interfaces.StatusActionListener;
|
import com.keylesspalace.tusky.interfaces.StatusActionListener;
|
||||||
import com.keylesspalace.tusky.network.MastodonApi;
|
import com.keylesspalace.tusky.network.MastodonApi;
|
||||||
|
import com.keylesspalace.tusky.network.TimelineCases;
|
||||||
import com.keylesspalace.tusky.receiver.TimelineReceiver;
|
import com.keylesspalace.tusky.receiver.TimelineReceiver;
|
||||||
import com.keylesspalace.tusky.util.CollectionUtil;
|
import com.keylesspalace.tusky.util.CollectionUtil;
|
||||||
import com.keylesspalace.tusky.util.Either;
|
import com.keylesspalace.tusky.util.Either;
|
||||||
|
@ -60,6 +62,8 @@ import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import retrofit2.Call;
|
import retrofit2.Call;
|
||||||
import retrofit2.Callback;
|
import retrofit2.Callback;
|
||||||
import retrofit2.Response;
|
import retrofit2.Response;
|
||||||
|
@ -67,7 +71,8 @@ import retrofit2.Response;
|
||||||
public class TimelineFragment extends SFragment implements
|
public class TimelineFragment extends SFragment implements
|
||||||
SwipeRefreshLayout.OnRefreshListener,
|
SwipeRefreshLayout.OnRefreshListener,
|
||||||
StatusActionListener,
|
StatusActionListener,
|
||||||
SharedPreferences.OnSharedPreferenceChangeListener {
|
SharedPreferences.OnSharedPreferenceChangeListener,
|
||||||
|
Injectable {
|
||||||
private static final String TAG = "TimelineF"; // logging tag
|
private static final String TAG = "TimelineF"; // logging tag
|
||||||
private static final String KIND_ARG = "kind";
|
private static final String KIND_ARG = "kind";
|
||||||
private static final String HASHTAG_OR_ID_ARG = "hashtag_or_id";
|
private static final String HASHTAG_OR_ID_ARG = "hashtag_or_id";
|
||||||
|
@ -90,6 +95,11 @@ public class TimelineFragment extends SFragment implements
|
||||||
MIDDLE
|
MIDDLE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
TimelineCases timelineCases;
|
||||||
|
@Inject
|
||||||
|
MastodonApi mastodonApi;
|
||||||
|
|
||||||
private SwipeRefreshLayout swipeRefreshLayout;
|
private SwipeRefreshLayout swipeRefreshLayout;
|
||||||
private TimelineAdapter adapter;
|
private TimelineAdapter adapter;
|
||||||
private Kind kind;
|
private Kind kind;
|
||||||
|
@ -113,6 +123,11 @@ public class TimelineFragment extends SFragment implements
|
||||||
|
|
||||||
private boolean alwaysShowSensitiveMedia;
|
private boolean alwaysShowSensitiveMedia;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected TimelineCases timelineCases() {
|
||||||
|
return timelineCases;
|
||||||
|
}
|
||||||
|
|
||||||
private PairedList<Either<Placeholder, Status>, StatusViewData> statuses =
|
private PairedList<Either<Placeholder, Status>, StatusViewData> statuses =
|
||||||
new PairedList<>(new Function<Either<Placeholder, Status>, StatusViewData>() {
|
new PairedList<>(new Function<Either<Placeholder, Status>, StatusViewData>() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -307,7 +322,7 @@ public class TimelineFragment extends SFragment implements
|
||||||
@Override
|
@Override
|
||||||
public void onReblog(final boolean reblog, final int position) {
|
public void onReblog(final boolean reblog, final int position) {
|
||||||
final Status status = statuses.get(position).getAsRight();
|
final Status status = statuses.get(position).getAsRight();
|
||||||
super.reblogWithCallback(status, reblog, new Callback<Status>() {
|
timelineCases.reblogWithCallback(status, reblog, new Callback<Status>() {
|
||||||
@Override
|
@Override
|
||||||
public void onResponse(@NonNull Call<Status> call, @NonNull Response<Status> response) {
|
public void onResponse(@NonNull Call<Status> call, @NonNull Response<Status> response) {
|
||||||
|
|
||||||
|
@ -342,7 +357,7 @@ public class TimelineFragment extends SFragment implements
|
||||||
public void onFavourite(final boolean favourite, final int position) {
|
public void onFavourite(final boolean favourite, final int position) {
|
||||||
final Status status = statuses.get(position).getAsRight();
|
final Status status = statuses.get(position).getAsRight();
|
||||||
|
|
||||||
super.favouriteWithCallback(status, favourite, new Callback<Status>() {
|
timelineCases.favouriteWithCallback(status, favourite, new Callback<Status>() {
|
||||||
@Override
|
@Override
|
||||||
public void onResponse(@NonNull Call<Status> call, @NonNull Response<Status> response) {
|
public void onResponse(@NonNull Call<Status> call, @NonNull Response<Status> response) {
|
||||||
|
|
||||||
|
|
|
@ -37,11 +37,14 @@ import android.view.ViewGroup;
|
||||||
import com.keylesspalace.tusky.BuildConfig;
|
import com.keylesspalace.tusky.BuildConfig;
|
||||||
import com.keylesspalace.tusky.R;
|
import com.keylesspalace.tusky.R;
|
||||||
import com.keylesspalace.tusky.adapter.ThreadAdapter;
|
import com.keylesspalace.tusky.adapter.ThreadAdapter;
|
||||||
|
import com.keylesspalace.tusky.di.Injectable;
|
||||||
import com.keylesspalace.tusky.entity.Attachment;
|
import com.keylesspalace.tusky.entity.Attachment;
|
||||||
import com.keylesspalace.tusky.entity.Card;
|
import com.keylesspalace.tusky.entity.Card;
|
||||||
import com.keylesspalace.tusky.entity.Status;
|
import com.keylesspalace.tusky.entity.Status;
|
||||||
import com.keylesspalace.tusky.entity.StatusContext;
|
import com.keylesspalace.tusky.entity.StatusContext;
|
||||||
import com.keylesspalace.tusky.interfaces.StatusActionListener;
|
import com.keylesspalace.tusky.interfaces.StatusActionListener;
|
||||||
|
import com.keylesspalace.tusky.network.MastodonApi;
|
||||||
|
import com.keylesspalace.tusky.network.TimelineCases;
|
||||||
import com.keylesspalace.tusky.receiver.TimelineReceiver;
|
import com.keylesspalace.tusky.receiver.TimelineReceiver;
|
||||||
import com.keylesspalace.tusky.util.PairedList;
|
import com.keylesspalace.tusky.util.PairedList;
|
||||||
import com.keylesspalace.tusky.util.ThemeUtils;
|
import com.keylesspalace.tusky.util.ThemeUtils;
|
||||||
|
@ -53,14 +56,21 @@ import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import retrofit2.Call;
|
import retrofit2.Call;
|
||||||
import retrofit2.Callback;
|
import retrofit2.Callback;
|
||||||
import retrofit2.Response;
|
import retrofit2.Response;
|
||||||
|
|
||||||
public class ViewThreadFragment extends SFragment implements
|
public class ViewThreadFragment extends SFragment implements
|
||||||
SwipeRefreshLayout.OnRefreshListener, StatusActionListener {
|
SwipeRefreshLayout.OnRefreshListener, StatusActionListener, Injectable {
|
||||||
private static final String TAG = "ViewThreadFragment";
|
private static final String TAG = "ViewThreadFragment";
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public TimelineCases timelineCases;
|
||||||
|
@Inject
|
||||||
|
public MastodonApi mastodonApi;
|
||||||
|
|
||||||
private SwipeRefreshLayout swipeRefreshLayout;
|
private SwipeRefreshLayout swipeRefreshLayout;
|
||||||
private RecyclerView recyclerView;
|
private RecyclerView recyclerView;
|
||||||
private ThreadAdapter adapter;
|
private ThreadAdapter adapter;
|
||||||
|
@ -87,6 +97,11 @@ public class ViewThreadFragment extends SFragment implements
|
||||||
return fragment;
|
return fragment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected TimelineCases timelineCases() {
|
||||||
|
return timelineCases;
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
|
||||||
|
@ -161,7 +176,7 @@ public class ViewThreadFragment extends SFragment implements
|
||||||
@Override
|
@Override
|
||||||
public void onReblog(final boolean reblog, final int position) {
|
public void onReblog(final boolean reblog, final int position) {
|
||||||
final Status status = statuses.get(position);
|
final Status status = statuses.get(position);
|
||||||
super.reblogWithCallback(statuses.get(position), reblog, new Callback<Status>() {
|
timelineCases.reblogWithCallback(statuses.get(position), reblog, new Callback<Status>() {
|
||||||
@Override
|
@Override
|
||||||
public void onResponse(@NonNull Call<Status> call, @NonNull Response<Status> response) {
|
public void onResponse(@NonNull Call<Status> call, @NonNull Response<Status> response) {
|
||||||
if (response.isSuccessful()) {
|
if (response.isSuccessful()) {
|
||||||
|
@ -194,7 +209,7 @@ public class ViewThreadFragment extends SFragment implements
|
||||||
@Override
|
@Override
|
||||||
public void onFavourite(final boolean favourite, final int position) {
|
public void onFavourite(final boolean favourite, final int position) {
|
||||||
final Status status = statuses.get(position);
|
final Status status = statuses.get(position);
|
||||||
super.favouriteWithCallback(statuses.get(position), favourite, new Callback<Status>() {
|
timelineCases.favouriteWithCallback(statuses.get(position), favourite, new Callback<Status>() {
|
||||||
@Override
|
@Override
|
||||||
public void onResponse(@NonNull Call<Status> call, @NonNull Response<Status> response) {
|
public void onResponse(@NonNull Call<Status> call, @NonNull Response<Status> response) {
|
||||||
if (response.isSuccessful()) {
|
if (response.isSuccessful()) {
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
package com.keylesspalace.tusky.network;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
import okhttp3.Interceptor;
|
|
||||||
import okhttp3.Request;
|
|
||||||
import okhttp3.Response;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by charlag on 31/10/17.
|
|
||||||
*/
|
|
||||||
|
|
||||||
public final class AuthInterceptor implements Interceptor {
|
|
||||||
|
|
||||||
AccountManager accountManager;
|
|
||||||
|
|
||||||
public AuthInterceptor(AccountManager accountManager) {
|
|
||||||
this.accountManager = accountManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Response intercept(@NonNull Chain chain) throws IOException {
|
|
||||||
|
|
||||||
Request originalRequest = chain.request();
|
|
||||||
AccountEntity currentAccount = accountManager.getActiveAccount();
|
|
||||||
|
|
||||||
Request.Builder builder = originalRequest.newBuilder();
|
|
||||||
if (currentAccount != null) {
|
|
||||||
builder.header("Authorization", String.format("Bearer %s", currentAccount.getAccessToken()));
|
|
||||||
}
|
|
||||||
Request newRequest = builder.build();
|
|
||||||
|
|
||||||
return chain.proceed(newRequest);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
/* Copyright 2018 charlag
|
||||||
|
*
|
||||||
|
* This file is a part of Tusky.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Tusky 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 Tusky; if not,
|
||||||
|
* see <http://www.gnu.org/licenses>. */
|
||||||
|
|
||||||
|
package com.keylesspalace.tusky.network;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.keylesspalace.tusky.db.AccountEntity;
|
||||||
|
import com.keylesspalace.tusky.db.AccountManager;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import okhttp3.HttpUrl;
|
||||||
|
import okhttp3.Interceptor;
|
||||||
|
import okhttp3.Request;
|
||||||
|
import okhttp3.Response;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by charlag on 31/10/17.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public final class InstanceSwitchAuthInterceptor implements Interceptor {
|
||||||
|
private AccountManager accountManager;
|
||||||
|
|
||||||
|
public InstanceSwitchAuthInterceptor(AccountManager accountManager) {
|
||||||
|
this.accountManager = accountManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Response intercept(@NonNull Chain chain) throws IOException {
|
||||||
|
|
||||||
|
Request originalRequest = chain.request();
|
||||||
|
AccountEntity currentAccount = accountManager.getActiveAccount();
|
||||||
|
|
||||||
|
Request.Builder builder = originalRequest.newBuilder();
|
||||||
|
|
||||||
|
String instanceHeader = originalRequest.header(MastodonApi.DOMAIN_HEADER);
|
||||||
|
if (instanceHeader != null) {
|
||||||
|
// use domain explicitly specified in custom header
|
||||||
|
builder.url(swapHost(originalRequest.url(), instanceHeader));
|
||||||
|
builder.removeHeader(MastodonApi.DOMAIN_HEADER);
|
||||||
|
} else if (currentAccount != null) {
|
||||||
|
//use domain of current account
|
||||||
|
builder.url(swapHost(originalRequest.url(), currentAccount.getDomain()))
|
||||||
|
.header("Authorization",
|
||||||
|
String.format("Bearer %s", currentAccount.getAccessToken()));
|
||||||
|
}
|
||||||
|
Request newRequest = builder.build();
|
||||||
|
|
||||||
|
return chain.proceed(newRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private HttpUrl swapHost(@NonNull HttpUrl url, @NonNull String host) {
|
||||||
|
return url.newBuilder().host(host).build();
|
||||||
|
}
|
||||||
|
}
|
|
@ -50,6 +50,7 @@ import retrofit2.http.Query;
|
||||||
|
|
||||||
public interface MastodonApi {
|
public interface MastodonApi {
|
||||||
String ENDPOINT_AUTHORIZE = "/oauth/authorize";
|
String ENDPOINT_AUTHORIZE = "/oauth/authorize";
|
||||||
|
String DOMAIN_HEADER = "domain";
|
||||||
|
|
||||||
@GET("api/v1/timelines/home")
|
@GET("api/v1/timelines/home")
|
||||||
Call<List<Status>> homeTimeline(
|
Call<List<Status>> homeTimeline(
|
||||||
|
@ -83,7 +84,7 @@ public interface MastodonApi {
|
||||||
@Query("limit") Integer limit);
|
@Query("limit") Integer limit);
|
||||||
@GET("api/v1/notifications")
|
@GET("api/v1/notifications")
|
||||||
Call<List<Notification>> notificationsWithAuth(
|
Call<List<Notification>> notificationsWithAuth(
|
||||||
@Header("Authorization") String auth);
|
@Header("Authorization") String auth, @Header(DOMAIN_HEADER) String domain);
|
||||||
@POST("api/v1/notifications/clear")
|
@POST("api/v1/notifications/clear")
|
||||||
Call<ResponseBody> clearNotifications();
|
Call<ResponseBody> clearNotifications();
|
||||||
@GET("api/v1/notifications/{id}")
|
@GET("api/v1/notifications/{id}")
|
||||||
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
/* Copyright 2018 charlag
|
||||||
|
*
|
||||||
|
* This file is a part of Tusky.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Tusky 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 Tusky; if not,
|
||||||
|
* see <http://www.gnu.org/licenses>. */
|
||||||
|
|
||||||
|
package com.keylesspalace.tusky.network
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.support.v4.content.LocalBroadcastManager
|
||||||
|
import com.keylesspalace.tusky.entity.Relationship
|
||||||
|
import com.keylesspalace.tusky.entity.Status
|
||||||
|
import com.keylesspalace.tusky.receiver.TimelineReceiver
|
||||||
|
import okhttp3.ResponseBody
|
||||||
|
import retrofit2.Call
|
||||||
|
import retrofit2.Callback
|
||||||
|
import retrofit2.Response
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by charlag on 3/24/18.
|
||||||
|
*/
|
||||||
|
|
||||||
|
interface TimelineCases {
|
||||||
|
fun reblogWithCallback(status: Status, reblog: Boolean, callback: Callback<Status>)
|
||||||
|
fun favouriteWithCallback(status: Status, favourite: Boolean, callback: Callback<Status>)
|
||||||
|
fun mute(id: String)
|
||||||
|
fun block(id: String)
|
||||||
|
fun delete(id: String)
|
||||||
|
}
|
||||||
|
|
||||||
|
class TimelineCasesImpl(
|
||||||
|
private val mastodonApi: MastodonApi,
|
||||||
|
private val broadcastManager: LocalBroadcastManager
|
||||||
|
) : TimelineCases {
|
||||||
|
override fun reblogWithCallback(status: Status, reblog: Boolean, callback: Callback<Status>) {
|
||||||
|
val id = status.actionableId
|
||||||
|
|
||||||
|
val call = if (reblog) {
|
||||||
|
mastodonApi.reblogStatus(id)
|
||||||
|
} else {
|
||||||
|
mastodonApi.unreblogStatus(id)
|
||||||
|
}
|
||||||
|
call.enqueue(callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun favouriteWithCallback(status: Status, favourite: Boolean, callback: Callback<Status>) {
|
||||||
|
val id = status.actionableId
|
||||||
|
|
||||||
|
val call = if (favourite) {
|
||||||
|
mastodonApi.favouriteStatus(id)
|
||||||
|
} else {
|
||||||
|
mastodonApi.unfavouriteStatus(id)
|
||||||
|
}
|
||||||
|
call.enqueue(callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun mute(id: String) {
|
||||||
|
val call = mastodonApi.muteAccount(id)
|
||||||
|
call.enqueue(object : Callback<Relationship> {
|
||||||
|
override fun onResponse(call: Call<Relationship>, response: Response<Relationship>) {}
|
||||||
|
|
||||||
|
override fun onFailure(call: Call<Relationship>, t: Throwable) {}
|
||||||
|
})
|
||||||
|
val intent = Intent(TimelineReceiver.Types.MUTE_ACCOUNT)
|
||||||
|
intent.putExtra("id", id)
|
||||||
|
broadcastManager.sendBroadcast(intent)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun block(id: String) {
|
||||||
|
val call = mastodonApi.blockAccount(id)
|
||||||
|
call.enqueue(object : Callback<Relationship> {
|
||||||
|
override fun onResponse(call: Call<Relationship>, response: retrofit2.Response<Relationship>) {}
|
||||||
|
|
||||||
|
override fun onFailure(call: Call<Relationship>, t: Throwable) {}
|
||||||
|
})
|
||||||
|
val intent = Intent(TimelineReceiver.Types.BLOCK_ACCOUNT)
|
||||||
|
intent.putExtra("id", id)
|
||||||
|
broadcastManager.sendBroadcast(intent)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun delete(id: String) {
|
||||||
|
val call = mastodonApi.deleteStatus(id)
|
||||||
|
call.enqueue(object : Callback<ResponseBody> {
|
||||||
|
override fun onResponse(call: Call<ResponseBody>, response: retrofit2.Response<ResponseBody>) {}
|
||||||
|
|
||||||
|
override fun onFailure(call: Call<ResponseBody>, t: Throwable) {}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -47,6 +47,7 @@ import okhttp3.ConnectionSpec;
|
||||||
import okhttp3.Interceptor;
|
import okhttp3.Interceptor;
|
||||||
import okhttp3.OkHttpClient;
|
import okhttp3.OkHttpClient;
|
||||||
import okhttp3.Request;
|
import okhttp3.Request;
|
||||||
|
import okhttp3.logging.HttpLoggingInterceptor;
|
||||||
|
|
||||||
public class OkHttpUtils {
|
public class OkHttpUtils {
|
||||||
private static final String TAG = "OkHttpUtils"; // logging tag
|
private static final String TAG = "OkHttpUtils"; // logging tag
|
||||||
|
@ -91,6 +92,10 @@ public class OkHttpUtils {
|
||||||
builder.proxy(new Proxy(Proxy.Type.HTTP, address));
|
builder.proxy(new Proxy(Proxy.Type.HTTP, address));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(BuildConfig.DEBUG) {
|
||||||
|
builder.addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BASIC));
|
||||||
|
}
|
||||||
|
|
||||||
return enableHigherTlsOnPreLollipop(builder);
|
return enableHigherTlsOnPreLollipop(builder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,19 @@
|
||||||
|
/* Copyright 2018 charlag
|
||||||
|
*
|
||||||
|
* This file is a part of Tusky.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Tusky 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 Tusky; if not,
|
||||||
|
* see <http://www.gnu.org/licenses>. */
|
||||||
|
|
||||||
|
|
||||||
package com.keylesspalace.tusky
|
package com.keylesspalace.tusky
|
||||||
|
|
||||||
import android.widget.EditText
|
import android.widget.EditText
|
||||||
|
@ -51,12 +67,13 @@ class ComposeActivityTest {
|
||||||
fun before() {
|
fun before() {
|
||||||
val controller = Robolectric.buildActivity(ComposeActivity::class.java)
|
val controller = Robolectric.buildActivity(ComposeActivity::class.java)
|
||||||
activity = controller.get()
|
activity = controller.get()
|
||||||
application = activity.application as FakeTuskyApplication
|
|
||||||
serviceLocator = Mockito.mock(TuskyApplication.ServiceLocator::class.java)
|
|
||||||
application.locator = serviceLocator
|
|
||||||
accountManagerMock = Mockito.mock(AccountManager::class.java)
|
accountManagerMock = Mockito.mock(AccountManager::class.java)
|
||||||
|
serviceLocator = Mockito.mock(TuskyApplication.ServiceLocator::class.java)
|
||||||
`when`(serviceLocator.get(AccountManager::class.java)).thenReturn(accountManagerMock)
|
`when`(serviceLocator.get(AccountManager::class.java)).thenReturn(accountManagerMock)
|
||||||
`when`(accountManagerMock.activeAccount).thenReturn(account)
|
`when`(accountManagerMock.activeAccount).thenReturn(account)
|
||||||
|
activity.accountManager = accountManagerMock
|
||||||
|
application = activity.application as FakeTuskyApplication
|
||||||
|
application.locator = serviceLocator
|
||||||
controller.create().start()
|
controller.create().start()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
0
gradlew
vendored
Executable file → Normal file
0
gradlew
vendored
Executable file → Normal file
0
gradlew.bat
vendored
Normal file → Executable file
0
gradlew.bat
vendored
Normal file → Executable file
Loading…
Reference in a new issue