Fragments/Activities auto-cancel their requests when they're destroyed. Also, fixes a ComposeActivity crash that can occur when a media preview doesn't load.

This commit is contained in:
Vavassor 2017-03-14 20:34:27 -04:00
parent f391538984
commit 0662f35b96
8 changed files with 101 additions and 35 deletions

View File

@ -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<List<Account>> 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<Relationship> 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) {

View File

@ -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()

View File

@ -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
* <http://www.gnu.org/licenses/>. */
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<Call> 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();
}
}

View File

@ -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<Media> 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) {

View File

@ -161,6 +161,7 @@ public class NotificationsFragment extends SFragment implements
onFetchNotificationsFailure((Exception) t);
}
});
callList.add(listCall);
}
private void sendFetchNotificationsRequest() {

View File

@ -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<Status> 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<Status> 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<Relationship>() {
Call<Relationship> call = getApi().blockAccount(id);
call.enqueue(new Callback<Relationship>() {
@Override
public void onResponse(Call<Relationship> call, retrofit2.Response<Relationship> response) {
@ -153,10 +160,12 @@ public class SFragment extends Fragment {
}
});
callList.add(call);
}
private void delete(String id) {
getApi().deleteStatus(id).enqueue(new Callback<ResponseBody>() {
Call<ResponseBody> call = getApi().deleteStatus(id);
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, retrofit2.Response<ResponseBody> response) {
@ -167,6 +176,7 @@ public class SFragment extends Fragment {
}
});
callList.add(call);
}
protected void more(Status status, View view, final AdapterItemRemover adapter,

View File

@ -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() {

View File

@ -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<Status>() {
Call<Status> call = api.status(id);
call.enqueue(new Callback<Status>() {
@Override
public void onResponse(Call<Status> call, retrofit2.Response<Status> 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<StatusContext>() {
Call<StatusContext> call = api.statusContext(id);
call.enqueue(new Callback<StatusContext>() {
@Override
public void onResponse(Call<StatusContext> call, retrofit2.Response<StatusContext> 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) {