fix various crashes

This commit is contained in:
Phillip Thelen 2017-09-06 00:04:11 +02:00
parent 64827c3f89
commit 6a529b31d0
26 changed files with 115 additions and 29 deletions

View file

@ -2,8 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
package="com.habitrpg.android.habitica" package="com.habitrpg.android.habitica"
android:versionCode="1926" android:versionCode="1928"
android:versionName="1.1.6" android:versionName="1.2"
android:screenOrientation="portrait" android:screenOrientation="portrait"
android:installLocation="auto" > android:installLocation="auto" >

View file

@ -6,7 +6,7 @@
<string name="SP_APIToken" translatable="false">APIToken</string> <string name="SP_APIToken" translatable="false">APIToken</string>
<string name="SP_username" translatable="false">Username</string> <string name="SP_username" translatable="false">Username</string>
<string name="SP_email" translatable="false">E-mail</string> <string name="SP_email" translatable="false">E-mail</string>
<string name="base_url" translatable="false">https://habitica.com</string> <string name="base_url" translatable="false">https://habitrpg-staging.herokuapp.com</string>
<!-- Local notification actions --> <!-- Local notification actions -->
<string name="accept_party_invite" translatable="false">ACCEPT_PARTY_INVITE</string> <string name="accept_party_invite" translatable="false">ACCEPT_PARTY_INVITE</string>

View file

@ -10,4 +10,6 @@ public interface BaseRepository {
<T extends RealmObject> T getUnmanagedCopy(T object); <T extends RealmObject> T getUnmanagedCopy(T object);
<T extends RealmObject> List<T> getUnmanagedCopy(List<T> list); <T extends RealmObject> List<T> getUnmanagedCopy(List<T> list);
boolean isClosed();
} }

View file

@ -1,5 +1,6 @@
package com.habitrpg.android.habitica.data; package com.habitrpg.android.habitica.data;
import com.habitrpg.android.habitica.models.AchievementResult;
import com.habitrpg.android.habitica.models.inventory.Quest; import com.habitrpg.android.habitica.models.inventory.Quest;
import com.habitrpg.android.habitica.models.members.Member; import com.habitrpg.android.habitica.models.members.Member;
import com.habitrpg.android.habitica.models.responses.PostChatMessageResult; import com.habitrpg.android.habitica.models.responses.PostChatMessageResult;
@ -72,4 +73,6 @@ public interface SocialRepository extends BaseRepository {
Observable<Void> rejectGroupInvite(String groupId); Observable<Void> rejectGroupInvite(String groupId);
Observable<Quest> forceStartQuest(Group party); Observable<Quest> forceStartQuest(Group party);
Observable<AchievementResult> getMemberAchievements(String userId);
} }

View file

@ -7,6 +7,7 @@ import android.util.Log;
import com.amplitude.api.Amplitude; import com.amplitude.api.Amplitude;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;
import com.habitrpg.android.habitica.BuildConfig; import com.habitrpg.android.habitica.BuildConfig;
import com.habitrpg.android.habitica.HabiticaBaseApplication; import com.habitrpg.android.habitica.HabiticaBaseApplication;
@ -117,7 +118,6 @@ import okhttp3.ResponseBody;
import okhttp3.logging.HttpLoggingInterceptor; import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Converter; import retrofit2.Converter;
import retrofit2.HttpException; import retrofit2.HttpException;
import retrofit2.Response;
import retrofit2.Retrofit; import retrofit2.Retrofit;
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory; import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory; import retrofit2.converter.gson.GsonConverterFactory;
@ -157,6 +157,7 @@ public class ApiClientImpl implements Action1<Throwable>, ApiClient {
.doOnError(this); .doOnError(this);
private AlertDialog displayedAlert; private AlertDialog displayedAlert;
private String languageCode; private String languageCode;
private String lastAPICallURL;
//private OnHabitsAPIResult mResultListener; //private OnHabitsAPIResult mResultListener;
//private HostConfig mConfig; //private HostConfig mConfig;
@ -194,8 +195,10 @@ public class ApiClientImpl implements Action1<Throwable>, ApiClient {
if (userAgent != null) { if (userAgent != null) {
builder = builder.header("user-agent", userAgent); builder = builder.header("user-agent", userAgent);
} }
builder = builder.addHeader("Authorization", "Basic " + BuildConfig.STAGING_KEY);
Request request = builder.method(original.method(), original.body()) Request request = builder.method(original.method(), original.body())
.build(); .build();
lastAPICallURL = original.url().toString();
return chain.proceed(request); return chain.proceed(request);
}) })
.build(); .build();
@ -326,18 +329,23 @@ public class ApiClientImpl implements Action1<Throwable>, ApiClient {
} else { } else {
showConnectionProblemDialog(R.string.internal_error_api); showConnectionProblemDialog(R.string.internal_error_api);
} }
} else if (JsonSyntaxException.class.isAssignableFrom(throwableClass)) {
crashlyticsProxy.log("Json Error: " + lastAPICallURL + ", " + throwable.getMessage());
} else { } else {
crashlyticsProxy.logException(throwable); crashlyticsProxy.logException(throwable);
} }
} }
public ErrorResponse getErrorResponse(HttpException error) { public ErrorResponse getErrorResponse(HttpException error) {
Response<?> response = error.response(); ResponseBody errorResponse = error.response().errorBody();
if (errorResponse == null) {
return new ErrorResponse();
}
Converter<ResponseBody, ?> errorConverter = Converter<ResponseBody, ?> errorConverter =
gsonConverter gsonConverter
.responseBodyConverter(ErrorResponse.class, new Annotation[0], retrofitAdapter); .responseBodyConverter(ErrorResponse.class, new Annotation[0], retrofitAdapter);
try { try {
return (ErrorResponse) errorConverter.convert(response.errorBody()); return (ErrorResponse) errorConverter.convert(errorResponse);
} catch (IOException e) { } catch (IOException e) {
return new ErrorResponse(); return new ErrorResponse();
} }

View file

@ -27,6 +27,11 @@ public abstract class BaseRepositoryImpl<T extends BaseLocalRepository> implemen
return localRepository.getUnmanagedCopy(list); return localRepository.getUnmanagedCopy(list);
} }
@Override
public boolean isClosed() {
return localRepository.isClosed();
}
@Override @Override
public <T extends RealmObject> T getUnmanagedCopy(T object) { public <T extends RealmObject> T getUnmanagedCopy(T object) {
return localRepository.getUnmanagedCopy(object); return localRepository.getUnmanagedCopy(object);

View file

@ -4,6 +4,7 @@ import com.habitrpg.android.habitica.data.ApiClient;
import com.habitrpg.android.habitica.data.SocialRepository; import com.habitrpg.android.habitica.data.SocialRepository;
import com.habitrpg.android.habitica.data.local.SocialLocalRepository; import com.habitrpg.android.habitica.data.local.SocialLocalRepository;
import com.habitrpg.android.habitica.helpers.RxErrorHandler; import com.habitrpg.android.habitica.helpers.RxErrorHandler;
import com.habitrpg.android.habitica.models.AchievementResult;
import com.habitrpg.android.habitica.models.inventory.Quest; import com.habitrpg.android.habitica.models.inventory.Quest;
import com.habitrpg.android.habitica.models.members.Member; import com.habitrpg.android.habitica.models.members.Member;
import com.habitrpg.android.habitica.models.responses.PostChatMessageResult; import com.habitrpg.android.habitica.models.responses.PostChatMessageResult;
@ -18,8 +19,6 @@ import java.util.Map;
import io.realm.RealmResults; import io.realm.RealmResults;
import rx.Observable; import rx.Observable;
import rx.functions.Action1;
import rx.functions.Func2;
public class SocialRepositoryImpl extends BaseRepositoryImpl<SocialLocalRepository> implements SocialRepository { public class SocialRepositoryImpl extends BaseRepositoryImpl<SocialLocalRepository> implements SocialRepository {
@ -55,11 +54,17 @@ public class SocialRepositoryImpl extends BaseRepositoryImpl<SocialLocalReposito
@Override @Override
public Observable<Void> flagMessage(ChatMessage chatMessage) { public Observable<Void> flagMessage(ChatMessage chatMessage) {
if (chatMessage.id == null) {
return Observable.just(null);
}
return apiClient.flagMessage(chatMessage.groupId, chatMessage.id); return apiClient.flagMessage(chatMessage.groupId, chatMessage.id);
} }
@Override @Override
public Observable<ChatMessage> likeMessage(ChatMessage chatMessage) { public Observable<ChatMessage> likeMessage(ChatMessage chatMessage) {
if (chatMessage.id == null) {
return Observable.just(null);
}
boolean liked = chatMessage.userLikesMessage(userId); boolean liked = chatMessage.userLikesMessage(userId);
localRepository.likeMessage(chatMessage, userId, !liked); localRepository.likeMessage(chatMessage, userId, !liked);
return apiClient.likeMessage(chatMessage.groupId, chatMessage.id) return apiClient.likeMessage(chatMessage.groupId, chatMessage.id)
@ -115,7 +120,7 @@ public class SocialRepositoryImpl extends BaseRepositoryImpl<SocialLocalReposito
@Override @Override
public Observable<Group> leaveGroup(String id) { public Observable<Group> leaveGroup(String id) {
return apiClient.leaveGroup(id) return apiClient.leaveGroup(id)
.flatMap(aVoid -> localRepository.getGroup(id)) .flatMap(aVoid -> localRepository.getGroup(id).first())
.doOnNext(group -> localRepository.executeTransaction(realm -> group.isMember = false)); .doOnNext(group -> localRepository.executeTransaction(realm -> group.isMember = false));
} }
@ -198,6 +203,9 @@ public class SocialRepositoryImpl extends BaseRepositoryImpl<SocialLocalReposito
@Override @Override
public Observable<Member> getMember(String userId) { public Observable<Member> getMember(String userId) {
if (userId == null) {
return Observable.just(null);
}
return apiClient.getMember(userId); return apiClient.getMember(userId);
} }
@ -251,4 +259,12 @@ public class SocialRepositoryImpl extends BaseRepositoryImpl<SocialLocalReposito
return apiClient.forceStartQuest(party.id, localRepository.getUnmanagedCopy(party)) return apiClient.forceStartQuest(party.id, localRepository.getUnmanagedCopy(party))
.doOnNext(aVoid -> localRepository.setQuestActivity(party, true)); .doOnNext(aVoid -> localRepository.setQuestActivity(party, true));
} }
@Override
public Observable<AchievementResult> getMemberAchievements(String userId) {
if (userId == null) {
return Observable.just(null);
}
return apiClient.getMemberAchievements(userId);
}
} }

View file

@ -22,4 +22,6 @@ public interface BaseLocalRepository {
<T extends RealmObject>void save(List<T> objects); <T extends RealmObject>void save(List<T> objects);
<T extends RealmObject>void save(T object); <T extends RealmObject>void save(T object);
boolean isClosed();
} }

View file

@ -45,6 +45,11 @@ abstract class RealmBaseLocalRepository implements BaseLocalRepository {
realm.executeTransactionAsync(realm1 -> realm1.insertOrUpdate(object)); realm.executeTransactionAsync(realm1 -> realm1.insertOrUpdate(object));
} }
@Override
public boolean isClosed() {
return realm.isClosed();
}
@Override @Override
public <T extends RealmObject> void save(List<T> objects) { public <T extends RealmObject> void save(List<T> objects) {
realm.executeTransactionAsync(realm1 -> realm1.insertOrUpdate(objects)); realm.executeTransactionAsync(realm1 -> realm1.insertOrUpdate(objects));

View file

@ -265,14 +265,14 @@ public class RealmInventoryLocalRepository extends RealmContentLocalRepository i
@Override @Override
public void decrementMysteryItemCount(User user) { public void decrementMysteryItemCount(User user) {
SpecialItem item = realm.where(SpecialItem.class).equalTo("isMysteryItem", true).findFirst(); SpecialItem item = realm.where(SpecialItem.class).equalTo("isMysteryItem", true).findFirst();
if (item.isValid()) { realm.executeTransactionAsync(realm1 -> {
realm.executeTransactionAsync(realm1 -> { if (item != null && item.isValid()) {
item.setOwned(item.getOwned()-1); item.setOwned(item.getOwned() - 1);
if (user.getPurchased() != null && user.getPurchased().getPlan() != null) { }
user.getPurchased().getPlan().mysteryItemCount -= 1; if (user.isValid() && user.getPurchased() != null && user.getPurchased().getPlan() != null) {
} user.getPurchased().getPlan().mysteryItemCount -= 1;
}); }
} });
} }
@Override @Override

View file

@ -46,7 +46,7 @@ public class RealmSocialLocalRepository extends RealmBaseLocalRepository impleme
public Observable<Group> getGroup(String id) { public Observable<Group> getGroup(String id) {
return realm.where(Group.class) return realm.where(Group.class)
.equalTo("id", id) .equalTo("id", id)
.findAllAsync() .findAll()
.asObservable() .asObservable()
.filter(group -> group.isLoaded() && group.isValid() && !group.isEmpty()) .filter(group -> group.isLoaded() && group.isValid() && !group.isEmpty())
.map(groups -> groups.first()); .map(groups -> groups.first());

View file

@ -9,6 +9,7 @@ import java.io.IOException;
import cz.msebera.android.httpclient.HttpException; import cz.msebera.android.httpclient.HttpException;
import rx.functions.Action1; import rx.functions.Action1;
import rx.plugins.RxJavaHooks;
public class RxErrorHandler { public class RxErrorHandler {
@ -18,6 +19,8 @@ public class RxErrorHandler {
public static void init(CrashlyticsProxy crashlyticsProxy) { public static void init(CrashlyticsProxy crashlyticsProxy) {
instance = new RxErrorHandler(); instance = new RxErrorHandler();
instance.crashlyticsProxy = crashlyticsProxy; instance.crashlyticsProxy = crashlyticsProxy;
RxJavaHooks.setOnError(handleEmptyError());
} }
public static Action1<Throwable> handleEmptyError() { public static Action1<Throwable> handleEmptyError() {

View file

@ -2,6 +2,7 @@ package com.habitrpg.android.habitica.models.members;
import android.text.TextUtils; import android.text.TextUtils;
import com.google.gson.annotations.SerializedName;
import com.habitrpg.android.habitica.models.Avatar; import com.habitrpg.android.habitica.models.Avatar;
import com.habitrpg.android.habitica.models.social.UserParty; import com.habitrpg.android.habitica.models.social.UserParty;
import com.habitrpg.android.habitica.models.user.Buffs; import com.habitrpg.android.habitica.models.user.Buffs;
@ -24,6 +25,7 @@ public class Member extends RealmObject implements Avatar {
@PrimaryKey @PrimaryKey
@SerializedName("_id")
private String id; private String id;
private Stats stats; private Stats stats;
private Inbox inbox; private Inbox inbox;

View file

@ -15,4 +15,6 @@ public interface CrashlyticsProxy {
void setUserName(String name); void setUserName(String name);
void fabricLogE(String s1, String s2, Exception e); void fabricLogE(String s1, String s2, Exception e);
void log(String msg);
} }

View file

@ -35,4 +35,9 @@ public class EmptyCrashlyticsProxy implements CrashlyticsProxy {
public void fabricLogE(String s1, String s2, Exception e) { public void fabricLogE(String s1, String s2, Exception e) {
//pass //pass
} }
@Override
public void log(String msg) {
//pass
}
} }

View file

@ -441,7 +441,11 @@ public class CreateChallengeActivity extends BaseActivity {
checkPrizeAndMinimumForTavern(); checkPrizeAndMinimumForTavern();
challengeRepository.getChallengeTasks(challengeId).subscribe(tasks -> tasks.tasks.forEach((s, task) -> addOrUpdateTaskInList(task)), RxErrorHandler.handleEmptyError(), () -> { challengeRepository.getChallengeTasks(challengeId).subscribe(tasks -> {
for (Task task : tasks.tasks.values()) {
addOrUpdateTaskInList(task);
}
}, RxErrorHandler.handleEmptyError(), () -> {
// activate editMode to track taskChanges // activate editMode to track taskChanges
editMode = true; editMode = true;
}); });

View file

@ -253,7 +253,7 @@ public class FullProfileActivity extends BaseActivity {
//mountsTamedCount.setText(String.valueOf(user.getMountsTamedCount())); //mountsTamedCount.setText(String.valueOf(user.getMountsTamedCount()));
// Load the members achievements now // Load the members achievements now
apiClient.getMemberAchievements(this.userId).subscribe(this::fillAchievements, RxErrorHandler.handleEmptyError()); socialRepository.getMemberAchievements(this.userId).subscribe(this::fillAchievements, RxErrorHandler.handleEmptyError());
} }
// endregion // endregion
@ -261,6 +261,9 @@ public class FullProfileActivity extends BaseActivity {
// region Attributes // region Attributes
private void fillAchievements(AchievementResult achievements) { private void fillAchievements(AchievementResult achievements) {
if (achievements == null) {
return;
}
List<Object> items = new ArrayList<>(); List<Object> items = new ArrayList<>();
fillAchievements(achievements.basic, items); fillAchievements(achievements.basic, items);

View file

@ -294,6 +294,10 @@ public class SubscriptionFragment extends BaseFragment implements GemPurchaseAct
} }
} }
if (this.subscriptionDetailsView == null) {
return;
}
if (isSubscribed) { if (isSubscribed) {
this.subscriptionDetailsView.setVisibility(View.VISIBLE); this.subscriptionDetailsView.setVisibility(View.VISIBLE);
this.subscriptionDetailsView.setPlan(plan); this.subscriptionDetailsView.setPlan(plan);

View file

@ -186,7 +186,9 @@ public class InboxFragment extends BaseMainFragment
public void onUserReceived(User user) { public void onUserReceived(User user) {
this.user = user; this.user = user;
swipeRefreshLayout.setRefreshing(false); if (swipeRefreshLayout != null) {
swipeRefreshLayout.setRefreshing(false);
}
} }
@Override @Override

View file

@ -114,7 +114,11 @@ public class QuestDetailFragment extends BaseMainFragment {
party = group; party = group;
quest = group.quest; quest = group.quest;
setQuestParticipants(group.quest.participants); setQuestParticipants(group.quest.participants);
socialRepository.getMember(quest.leader).first().subscribe(member -> questLeaderView.setText(getContext().getString(R.string.quest_leader_header, member.getDisplayName())), RxErrorHandler.handleEmptyError()); socialRepository.getMember(quest.leader).first().subscribe(member -> {
if (getContext() != null && questLeaderView != null) {
questLeaderView.setText(getContext().getString(R.string.quest_leader_header, member.getDisplayName()));
}
}, RxErrorHandler.handleEmptyError());
if (questLeaderResponseWrapper != null) { if (questLeaderResponseWrapper != null) {
if (showParticipatantButtons()) { if (showParticipatantButtons()) {

View file

@ -134,7 +134,11 @@ public class PartyDetailFragment extends BaseFragment {
if (refreshLayout != null) { if (refreshLayout != null) {
refreshLayout.setRefreshing(false); refreshLayout.setRefreshing(false);
} }
}, throwable -> refreshLayout.setRefreshing(false)); }, throwable -> {
if (refreshLayout != null) {
refreshLayout.setRefreshing(false);
}
});
} }
private void updateParty(Group party) { private void updateParty(Group party) {
@ -191,7 +195,7 @@ public class PartyDetailFragment extends BaseFragment {
} }
private void updateQuestContent(QuestContent questContent) { private void updateQuestContent(QuestContent questContent) {
if (questTitleView == null) { if (questTitleView == null && questContent.isManaged()) {
return; return;
} }
questTitleView.setText(questContent.getText()); questTitleView.setText(questContent.getText());

View file

@ -112,6 +112,7 @@ public class YesterdailyDialog extends AlertDialog {
if (userRepository != null && userId != null) { if (userRepository != null && userId != null) {
Observable.just(null) Observable.just(null)
.delay(500, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread()) .delay(500, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())
.filter(aVoid -> !userRepository.isClosed())
.flatMap(aVoid -> userRepository.getUser(userId)) .flatMap(aVoid -> userRepository.getUser(userId))
.first() .first()
.filter(user -> user != null && user.getNeedsCron() != null && user.getNeedsCron()) .filter(user -> user != null && user.getNeedsCron() != null && user.getNeedsCron())

View file

@ -30,6 +30,7 @@ public class MemberSerialization implements JsonDeserializer<Member> {
Member member = realm.where(Member.class).equalTo("id", id).findFirst(); Member member = realm.where(Member.class).equalTo("id", id).findFirst();
if (member == null) { if (member == null) {
member = new Member(); member = new Member();
member.setId(id);
} else { } else {
member = realm.copyFromRealm(member); member = realm.copyFromRealm(member);
} }

View file

@ -7,6 +7,7 @@ import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer; import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.google.gson.internal.LinkedTreeMap;
import com.habitrpg.android.habitica.models.tasks.Task; import com.habitrpg.android.habitica.models.tasks.Task;
import com.habitrpg.android.habitica.models.tasks.TaskList; import com.habitrpg.android.habitica.models.tasks.TaskList;
@ -24,10 +25,14 @@ public class TaskListDeserializer implements JsonDeserializer<TaskList> {
Map<String, Task> taskMap = new HashMap<>(); Map<String, Task> taskMap = new HashMap<>();
for (JsonElement e : json.getAsJsonArray()) { for (JsonElement e : json.getAsJsonArray()) {
Task task = ctx.deserialize(e, Task.class); try {
//Workaround, since gson doesn't call setter methods Task task = ctx.deserialize(e, Task.class);
task.setId(task.getId()); //Workaround, since gson doesn't call setter methods
taskMap.put(task.getId(), task); task.setId(task.getId());
taskMap.put(task.getId(), task);
} catch (ClassCastException ignored) {
}
} }
tasks.tasks = taskMap; tasks.tasks = taskMap;

View file

@ -47,4 +47,9 @@ public class CrashlyticsProxyImpl implements CrashlyticsProxy {
public void fabricLogE(String s1, String s2, Exception e) { public void fabricLogE(String s1, String s2, Exception e) {
Fabric.getLogger().e(s1,s2,e); Fabric.getLogger().e(s1,s2,e);
} }
@Override
public void log(String msg) {
Crashlytics.getInstance().log(msg);
}
} }

View file

@ -1,3 +1,3 @@
org.gradle.configureondemand=true org.gradle.configureondemand=true
org.gradle.daemon=true org.gradle.daemon=true
org.gradle.jvmargs=-Xmx2048m org.gradle.jvmargs=-Xmx6656M