diff --git a/app/src/main/java/com/keylesspalace/tusky/AccountFragment.java b/app/src/main/java/com/keylesspalace/tusky/AccountFragment.java index f5044783a..758a0c5b2 100644 --- a/app/src/main/java/com/keylesspalace/tusky/AccountFragment.java +++ b/app/src/main/java/com/keylesspalace/tusky/AccountFragment.java @@ -37,7 +37,7 @@ import java.util.List; import retrofit2.Call; import retrofit2.Callback; -public class AccountFragment extends Fragment implements AccountActionListener { +public class AccountFragment extends BaseFragment implements AccountActionListener { private static final String TAG = "Account"; // logging tag private Call> listCall; @@ -176,25 +176,23 @@ public class AccountFragment extends Fragment implements AccountActionListener { default: case FOLLOWS: { listCall = api.accountFollowing(accountId, fromId, uptoId, null); - listCall.enqueue(cb); break; } case FOLLOWERS: { listCall = api.accountFollowers(accountId, fromId, uptoId, null); - listCall.enqueue(cb); break; } case BLOCKS: { listCall = api.blocks(fromId, uptoId, null); - listCall.enqueue(cb); break; } case MUTES: { listCall = api.mutes(fromId, uptoId, null); - listCall.enqueue(cb); break; } } + callList.add(listCall); + listCall.enqueue(cb); } private void fetchAccounts() { @@ -264,11 +262,14 @@ public class AccountFragment extends Fragment implements AccountActionListener { } }; + Call call; if (!block) { - api.unblockAccount(id).enqueue(cb); + call = api.unblockAccount(id); } else { - api.blockAccount(id).enqueue(cb); + call = api.blockAccount(id); } + callList.add(call); + call.enqueue(cb); } private void onBlockSuccess(boolean blocked, int position) { diff --git a/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java b/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java index d79421b56..cb0f0dec6 100644 --- a/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java @@ -34,6 +34,7 @@ import com.google.gson.GsonBuilder; import java.io.IOException; +import okhttp3.Dispatcher; import okhttp3.Interceptor; import okhttp3.OkHttpClient; import okhttp3.Request; @@ -48,6 +49,7 @@ import retrofit2.converter.gson.GsonConverterFactory; public class BaseActivity extends AppCompatActivity { protected MastodonAPI mastodonAPI; protected TuskyAPI tuskyAPI; + protected Dispatcher mastodonApiDispatcher; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { @@ -64,6 +66,12 @@ public class BaseActivity extends AppCompatActivity { */ } + @Override + protected void onDestroy() { + mastodonApiDispatcher.cancelAll(); + super.onDestroy(); + } + @Override public void finish() { super.finish(); @@ -95,6 +103,8 @@ public class BaseActivity extends AppCompatActivity { } protected void createMastodonAPI() { + mastodonApiDispatcher = new Dispatcher(); + OkHttpClient okHttpClient = new OkHttpClient.Builder() .addInterceptor(new Interceptor() { @Override @@ -111,6 +121,7 @@ public class BaseActivity extends AppCompatActivity { return chain.proceed(newRequest); } }) + .dispatcher(mastodonApiDispatcher) .build(); Gson gson = new GsonBuilder() diff --git a/app/src/main/java/com/keylesspalace/tusky/BaseFragment.java b/app/src/main/java/com/keylesspalace/tusky/BaseFragment.java new file mode 100644 index 000000000..c8ccf5a03 --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/BaseFragment.java @@ -0,0 +1,43 @@ +/* Copyright 2017 Andrew Dawson + * + * This file is part of Tusky. + * + * Tusky 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 + * . */ + +package com.keylesspalace.tusky; + +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; + +import java.util.ArrayList; +import java.util.List; + +import retrofit2.Call; + +public class BaseFragment extends Fragment { + protected List callList; + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + callList = new ArrayList<>(); + } + + @Override + public void onDestroy() { + for (Call call : callList) { + call.cancel(); + } + super.onDestroy(); + } +} diff --git a/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java b/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java index 3b045c182..7e57eb7ac 100644 --- a/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java @@ -72,7 +72,6 @@ import android.widget.TextView; import com.keylesspalace.tusky.entity.Media; import com.keylesspalace.tusky.entity.Status; -import com.squareup.picasso.Picasso; import java.io.ByteArrayOutputStream; import java.io.FileNotFoundException; @@ -822,12 +821,8 @@ public class ComposeActivity extends BaseActivity { LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(side, side); layoutParams.setMargins(margin, 0, margin, marginBottom); view.setLayoutParams(layoutParams); - - Picasso.with(this) - .load(uri) - .resize(side, side) - .centerCrop() - .into(view); + view.setScaleType(ImageView.ScaleType.CENTER_CROP); + view.setImageBitmap(preview); view.setOnClickListener(new View.OnClickListener() { @Override @@ -986,20 +981,24 @@ public class ComposeActivity extends BaseActivity { waitForMediaLatch.countDown(); } else { Log.d(TAG, "Upload request failed. " + response.message()); - onUploadFailure(item); + onUploadFailure(item, call.isCanceled()); } } @Override public void onFailure(Call call, Throwable t) { Log.d(TAG, t.getMessage()); - onUploadFailure(item); + onUploadFailure(item, false); } }); } - private void onUploadFailure(QueuedMedia item) { - displayTransientError(R.string.error_media_upload_sending); + private void onUploadFailure(QueuedMedia item, boolean isCanceled) { + if (isCanceled) { + /* if the upload was voluntarily cancelled, such as if the user clicked on it to remove + * it from the queue, then don't display this error message. */ + displayTransientError(R.string.error_media_upload_sending); + } if (finishingUploadDialog != null) { finishingUploadDialog.cancel(); } @@ -1059,7 +1058,7 @@ public class ComposeActivity extends BaseActivity { MediaMetadataRetriever retriever = new MediaMetadataRetriever(); retriever.setDataSource(this, uri); Bitmap source = retriever.getFrameAtTime(); - Bitmap bitmap = ThumbnailUtils.extractThumbnail(source, 96, 96); + Bitmap bitmap = ThumbnailUtils.extractThumbnail(source, 128, 128); source.recycle(); addMediaToQueue(QueuedMedia.Type.VIDEO, bitmap, uri, mediaSize); break; @@ -1073,7 +1072,7 @@ public class ComposeActivity extends BaseActivity { return; } Bitmap source = BitmapFactory.decodeStream(stream); - Bitmap bitmap = ThumbnailUtils.extractThumbnail(source, 96, 96); + Bitmap bitmap = ThumbnailUtils.extractThumbnail(source, 128, 128); source.recycle(); try { if (stream != null) { diff --git a/app/src/main/java/com/keylesspalace/tusky/NotificationsFragment.java b/app/src/main/java/com/keylesspalace/tusky/NotificationsFragment.java index b0cbe28b9..c452ae963 100644 --- a/app/src/main/java/com/keylesspalace/tusky/NotificationsFragment.java +++ b/app/src/main/java/com/keylesspalace/tusky/NotificationsFragment.java @@ -161,6 +161,7 @@ public class NotificationsFragment extends SFragment implements onFetchNotificationsFailure((Exception) t); } }); + callList.add(listCall); } private void sendFetchNotificationsRequest() { diff --git a/app/src/main/java/com/keylesspalace/tusky/SFragment.java b/app/src/main/java/com/keylesspalace/tusky/SFragment.java index 976797c8f..967a0e033 100644 --- a/app/src/main/java/com/keylesspalace/tusky/SFragment.java +++ b/app/src/main/java/com/keylesspalace/tusky/SFragment.java @@ -19,9 +19,9 @@ import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; +import android.provider.ContactsContract; import android.support.annotation.Nullable; import android.support.v4.app.DialogFragment; -import android.support.v4.app.Fragment; import android.support.v4.app.FragmentTransaction; import android.support.v7.widget.PopupMenu; import android.support.v7.widget.RecyclerView; @@ -45,7 +45,7 @@ import retrofit2.Callback; * adapters. I feel like the profile pages and thread viewer, which I haven't made yet, will also * overlap functionality. So, I'm momentarily leaving it and hopefully working on those will clear * up what needs to be where. */ -public class SFragment extends Fragment { +public class SFragment extends BaseFragment { protected String loggedInAccountId; protected String loggedInUsername; @@ -103,11 +103,14 @@ public class SFragment extends Fragment { } }; + Call call; if (reblog) { - getApi().reblogStatus(id).enqueue(cb); + call = getApi().reblogStatus(id); } else { - getApi().unreblogStatus(id).enqueue(cb); + call = getApi().unreblogStatus(id); } + call.enqueue(cb); + callList.add(call); } protected void favourite(final Status status, final boolean favourite, @@ -134,15 +137,19 @@ public class SFragment extends Fragment { } }; + Call call; if (favourite) { - getApi().favouriteStatus(id).enqueue(cb); + call = getApi().favouriteStatus(id); } else { - getApi().unfavouriteStatus(id).enqueue(cb); + call = getApi().unfavouriteStatus(id); } + call.enqueue(cb); + callList.add(call); } private void block(String id) { - getApi().blockAccount(id).enqueue(new Callback() { + Call call = getApi().blockAccount(id); + call.enqueue(new Callback() { @Override public void onResponse(Call call, retrofit2.Response response) { @@ -153,10 +160,12 @@ public class SFragment extends Fragment { } }); + callList.add(call); } private void delete(String id) { - getApi().deleteStatus(id).enqueue(new Callback() { + Call call = getApi().deleteStatus(id); + call.enqueue(new Callback() { @Override public void onResponse(Call call, retrofit2.Response response) { @@ -167,6 +176,7 @@ public class SFragment extends Fragment { } }); + callList.add(call); } protected void more(Status status, View view, final AdapterItemRemover adapter, diff --git a/app/src/main/java/com/keylesspalace/tusky/TimelineFragment.java b/app/src/main/java/com/keylesspalace/tusky/TimelineFragment.java index fff484cb7..cd8e12a38 100644 --- a/app/src/main/java/com/keylesspalace/tusky/TimelineFragment.java +++ b/app/src/main/java/com/keylesspalace/tusky/TimelineFragment.java @@ -193,30 +193,27 @@ public class TimelineFragment extends SFragment implements default: case HOME: { listCall = api.homeTimeline(fromId, uptoId, null); - listCall.enqueue(cb); break; } case PUBLIC: { listCall = api.publicTimeline(null, fromId, uptoId, null); - listCall.enqueue(cb); break; } case TAG: { listCall = api.hashtagTimeline(hashtagOrId, null, fromId, uptoId, null); - listCall.enqueue(cb); break; } case USER: { listCall = api.accountStatuses(hashtagOrId, fromId, uptoId, null); - listCall.enqueue(cb); break; } case FAVOURITES: { listCall = api.favourites(fromId, uptoId, null); - listCall.enqueue(cb); break; } } + callList.add(listCall); + listCall.enqueue(cb); } private void sendFetchTimelineRequest() { diff --git a/app/src/main/java/com/keylesspalace/tusky/ViewThreadFragment.java b/app/src/main/java/com/keylesspalace/tusky/ViewThreadFragment.java index 1dfaad1d9..11120541b 100644 --- a/app/src/main/java/com/keylesspalace/tusky/ViewThreadFragment.java +++ b/app/src/main/java/com/keylesspalace/tusky/ViewThreadFragment.java @@ -79,7 +79,8 @@ public class ViewThreadFragment extends SFragment implements StatusActionListene private void sendStatusRequest(final String id) { MastodonAPI api = ((BaseActivity) getActivity()).mastodonAPI; - api.status(id).enqueue(new Callback() { + Call call = api.status(id); + call.enqueue(new Callback() { @Override public void onResponse(Call call, retrofit2.Response response) { if (response.isSuccessful()) { @@ -95,12 +96,14 @@ public class ViewThreadFragment extends SFragment implements StatusActionListene onThreadRequestFailure(id); } }); + callList.add(call); } private void sendThreadRequest(final String id) { MastodonAPI api = ((BaseActivity) getActivity()).mastodonAPI; - api.statusContext(id).enqueue(new Callback() { + Call call = api.statusContext(id); + call.enqueue(new Callback() { @Override public void onResponse(Call call, retrofit2.Response response) { if (response.isSuccessful()) { @@ -118,6 +121,7 @@ public class ViewThreadFragment extends SFragment implements StatusActionListene onThreadRequestFailure(id); } }); + callList.add(call); } private void onThreadRequestFailure(final String id) {