Begin using API v4

This commit is contained in:
Phillip Thelen 2018-07-05 15:11:40 +02:00
parent b5e6eccf7c
commit 193c25bfa2
18 changed files with 231 additions and 252 deletions

View file

@ -105,7 +105,7 @@ dependencies {
//Tests
testImplementation 'junit:junit:4.12'
testImplementation 'org.assertj:assertj-core:2.6.0'
testImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.3'
testImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.4'
testImplementation 'org.robolectric:robolectric:3.8'
testImplementation 'org.robolectric:shadows-multidex:3.8'
testImplementation 'org.robolectric:shadows-support-v4:3.3.2'
@ -123,7 +123,7 @@ dependencies {
implementation 'com.google.android.gms:play-services-auth:11.4.2'
implementation 'org.apmem.tools:layouts:1.10@aar'
implementation 'com.roughike:bottom-bar:2.3.1'
implementation 'io.realm:android-adapters:3.0.0'
implementation 'io.realm:android-adapters:2.1.1'
implementation(project(':seeds-sdk')) {
exclude group: 'com.google.android.gms'
exclude group: 'com.android.support', module: 'multidex'

View file

@ -69,6 +69,10 @@ public interface ApiService {
@PUT("user/")
Observable<HabitResponse<User>> registrationLanguage(@Header("Accept-Language") String registrationLanguage);
@GET("inbox/messages")
Observable<HabitResponse<List<ChatMessage>>> getInboxMessages();
@GET("user/in-app-rewards")
Observable<HabitResponse<List<ShopItem>>> retrieveInAppRewards();
@GET("user/inventory/buy")

View file

@ -12,12 +12,12 @@ public class Server {
private Server(String addr, boolean attachSuffix) {
if (attachSuffix) {
if (addr.endsWith("/api/v3") || addr.endsWith("/api/v3/")) {
if (addr.endsWith("/api/v4") || addr.endsWith("/api/v4/")) {
this.addr = addr;
} else if (addr.endsWith("/")) {
this.addr = addr + "api/v3/";
this.addr = addr + "api/v4/";
} else {
this.addr = addr + "/api/v3/";
this.addr = addr + "/api/v4/";
}
} else {
this.addr = addr;

View file

@ -229,6 +229,7 @@ public interface ApiClient {
boolean hasAuthenticationKeys();
Observable<User> retrieveUser(boolean withTasks);
Observable<List<ChatMessage>> retrieveInboxMessages();
<T> Observable.Transformer<HabitResponse<T>, T> configureApiCallObserver();

View file

@ -24,6 +24,7 @@ interface UserRepository : BaseRepository {
fun retrieveUser(withTasks: Boolean = false, forced: Boolean = false): Observable<User>
fun getInboxMessages(replyToUserID: String?): Observable<RealmResults<ChatMessage>>
fun retrieveInboxMessages(): Observable<List<ChatMessage>>
fun revive(user: User): Observable<User>

View file

@ -377,6 +377,10 @@ public class ApiClientImpl implements Action1<Throwable>, ApiClient {
return userObservable;
}
public Observable<List<ChatMessage>> retrieveInboxMessages() {
return apiService.getInboxMessages().compose(configureApiCallObserver());
}
public boolean hasAuthenticationKeys() {
return this.hostConfig.getUser() != null;
}

View file

@ -91,10 +91,18 @@ class SocialRepositoryImpl(localRepository: SocialLocalRepository, apiClient: Ap
newGroup
}
}
return observable.map { group ->
group.chat?.forEach { it.groupId = group.id }
group
}.doOnNext({ localRepository.save(it) })
return Observable.zip(observable.doOnNext { localRepository.save(it) }, retrieveGroupChat(id)
.map {
it.forEach {
it.groupId = id
}
return@map it
}
.doOnNext { localRepository.save(it) }
) { group, _ ->
return@zip group
}
}
override fun getGroup(id: String?): Observable<Group> {

View file

@ -49,7 +49,7 @@ class UserRepositoryImpl(localRepository: UserLocalRepository, apiClient: ApiCli
if (forced || this.lastSync == null || Date().time - (this.lastSync?.time ?: 0) > 180000) {
lastSync = Date()
return apiClient.retrieveUser(withTasks)
.doOnNext({ localRepository.saveUser(it) })
.doOnNext { localRepository.saveUser(it) }
.doOnNext { user ->
if (withTasks) {
taskRepository.saveTasks(user.id, user.tasksOrder, user.tasks)
@ -73,6 +73,15 @@ class UserRepositoryImpl(localRepository: UserLocalRepository, apiClient: ApiCli
override fun getInboxMessages(replyToUserID: String?): Observable<RealmResults<ChatMessage>> =
localRepository.getInboxMessages(userId, replyToUserID)
override fun retrieveInboxMessages(): Observable<List<ChatMessage>> {
return apiClient.retrieveInboxMessages().doOnNext { messages ->
messages.forEach {
it.isInboxMessage = true
}
localRepository.save(messages)
}
}
override fun revive(user: User): Observable<User> =
apiClient.revive().map { newUser -> mergeUser(user, newUser) }

View file

@ -16,6 +16,8 @@ interface UserLocalRepository : BaseLocalRepository {
fun saveUser(user: User)
fun saveMessages(messages: List<ChatMessage>)
fun getSkills(user: User): Observable<RealmResults<Skill>>
fun getSpecialItems(user: User): Observable<RealmResults<Skill>>

View file

@ -46,6 +46,12 @@ class RealmUserLocalRepository(realm: Realm) : RealmBaseLocalRepository(realm),
}
}
override fun saveMessages(messages: List<ChatMessage>) {
realm.executeTransaction {
it.insertOrUpdate(messages)
}
}
private fun removeOldTags(userId: String, onlineTags: List<Tag>) {
val tags = realm.where(Tag::class.java).equalTo("userId", userId).findAll().createSnapshot()
val tagsToDelete = tags.filterNot { onlineTags.contains(it) }

View file

@ -36,8 +36,6 @@ open class Group : RealmObject() {
var privacy: String? = null
var chat: RealmList<ChatMessage>? = null
var members: RealmList<User>? = null
var challengeCount: Int = 0

View file

@ -17,7 +17,6 @@ public class Inbox extends RealmObject {
User user;
private boolean optOut;
private RealmList<ChatMessage> messages;
@Ignore
private List<Object> blocks = new ArrayList<>();
private int newMessages;
@ -36,20 +35,6 @@ public class Inbox extends RealmObject {
this.optOut = optOut;
}
/**
* @return The messages
*/
public RealmList<ChatMessage> getMessages() {
return messages;
}
/**
* @param messages The messages
*/
public void setMessages(RealmList<ChatMessage> messages) {
this.messages = messages;
}
/**
* @return The blocks
*/

View file

@ -813,6 +813,7 @@ public class MainActivity extends BaseActivity implements TutorialView.OnTutoria
pushNotificationManager.setUser(user1);
pushNotificationManager.addPushDeviceUsingStoredToken();
})
.flatMap(user1 -> userRepository.retrieveInboxMessages())
.flatMap(user1 -> inventoryRepository.retrieveContent(false))
.flatMap(contentResult -> inventoryRepository.retrieveWorldState())
.subscribe(user1 -> {}, RxErrorHandler.handleEmptyError());

View file

@ -1,215 +0,0 @@
package com.habitrpg.android.habitica.ui.fragments.social;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.AlertDialog;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.components.AppComponent;
import com.habitrpg.android.habitica.data.SocialRepository;
import com.habitrpg.android.habitica.helpers.RxErrorHandler;
import com.habitrpg.android.habitica.models.social.ChatMessage;
import com.habitrpg.android.habitica.models.user.User;
import com.habitrpg.android.habitica.modules.AppModule;
import com.habitrpg.android.habitica.prefs.scanner.IntentIntegrator;
import com.habitrpg.android.habitica.prefs.scanner.IntentResult;
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment;
import com.habitrpg.android.habitica.ui.helpers.UiUtils;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Named;
import butterknife.BindView;
import butterknife.ButterKnife;
public class InboxFragment extends BaseMainFragment
implements SwipeRefreshLayout.OnRefreshListener, View.OnClickListener {
@Inject
SocialRepository socialRepository;
@Inject
@Named(AppModule.NAMED_USER_ID)
String userId;
@BindView(R.id.inbox_messages)
LinearLayout inboxMessagesListView;
@BindView(R.id.inbox_refresh_layout)
SwipeRefreshLayout swipeRefreshLayout;
private View chooseRecipientDialogView;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
this.socialRepository.markPrivateMessagesRead(user)
.subscribe(aVoid -> {}, RxErrorHandler.handleEmptyError());
View v = inflater.inflate(R.layout.fragment_inbox, container, false);
setUnbinder(ButterKnife.bind(this, v));
swipeRefreshLayout.setOnRefreshListener(this);
loadMessages();
return v;
}
private void loadMessages() {
userRepository.getInboxOverviewList()
.first()
.subscribe(this::setInboxMessages, RxErrorHandler.handleEmptyError());
}
@Override
public void onDestroy() {
socialRepository.close();
super.onDestroy();
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
if (this.activity != null) {
this.activity.getMenuInflater().inflate(R.menu.inbox, menu);
}
super.onCreateOptionsMenu(menu, inflater);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
switch (id) {
case R.id.send_message:
openNewMessageDialog();
return true;
}
return super.onOptionsItemSelected(item);
}
private void openNewMessageDialog() {
assert this.activity != null;
this.chooseRecipientDialogView = this.activity.getLayoutInflater().inflate(R.layout.dialog_choose_message_recipient, null);
Button scaneQRCodeButton = (Button) chooseRecipientDialogView.findViewById(R.id.scanQRCodeButton);
AlertDialog alert = new AlertDialog.Builder(this.activity)
.setTitle(getString(R.string.choose_recipient_title))
.setPositiveButton(getString(R.string.action_continue), (dialog, which) -> {
EditText uuidEditText = (EditText) chooseRecipientDialogView.findViewById(R.id.uuidEditText);
openInboxMessages(uuidEditText.getText().toString(), "");
})
.setNeutralButton(getString(R.string.action_cancel), (dialog, which) -> {
UiUtils.dismissKeyboard(this.activity);
dialog.cancel();
})
.create();
scaneQRCodeButton.setOnClickListener((View v) -> {
IntentIntegrator scanIntegrator = new IntentIntegrator(getActivity());
scanIntegrator.initiateScan(this);
});
alert.setView(chooseRecipientDialogView);
alert.show();
}
@Override
public void injectFragment(AppComponent component) {
component.inject(this);
}
@Override
public void onRefresh() {
swipeRefreshLayout.setRefreshing(true);
this.userRepository.retrieveUser(true)
.subscribe(this::onUserReceived, RxErrorHandler.handleEmptyError());
}
public void setInboxMessages(List<ChatMessage> messages) {
if (this.inboxMessagesListView == null) {
return;
}
this.inboxMessagesListView.removeAllViewsInLayout();
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (messages.size() > 0) {
for (ChatMessage message : messages) {
TextView entry = (TextView) inflater.inflate(R.layout.plain_list_item, this.inboxMessagesListView, false);
entry.setText(message.getUser());
entry.setTag(message.getUuid());
entry.setOnClickListener(this);
this.inboxMessagesListView.addView(entry);
}
} else {
TextView tv = new TextView(getContext());
tv.setText(R.string.empty_inbox);
}
}
@Override
public void onClick(View v) {
TextView entry = (TextView) v;
String replyToUserName = entry.getText().toString();
openInboxMessages(entry.getTag().toString(), replyToUserName);
}
private void openInboxMessages(String userID, String username) {
InboxMessageListFragment inboxMessageListFragment = new InboxMessageListFragment();
inboxMessageListFragment.setReceivingUser(username, userID);
if (this.activity != null) {
this.activity.displayFragment(inboxMessageListFragment);
}
}
public void onUserReceived(User user) {
this.user = user;
if (swipeRefreshLayout != null) {
swipeRefreshLayout.setRefreshing(false);
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
IntentResult scanningResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, data);
if (scanningResult != null && scanningResult.getContents() != null) {
if (this.chooseRecipientDialogView != null) {
EditText uuidEditText = (EditText) this.chooseRecipientDialogView.findViewById(R.id.uuidEditText);
String qrCodeUrl = scanningResult.getContents();
Uri uri = Uri.parse(qrCodeUrl);
if (uri == null || uri.getPathSegments().size() < 3) {
return;
}
String userID = uri.getPathSegments().get(2);
uuidEditText.setText(userID);
}
}
}
@Override
public String customTitle() {
if (!isAdded()) {
return "";
}
return getString(R.string.sidebar_inbox);
}
}

View file

@ -0,0 +1,181 @@
package com.habitrpg.android.habitica.ui.fragments.social
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.support.v4.widget.SwipeRefreshLayout
import android.support.v7.app.AlertDialog
import android.view.*
import android.widget.Button
import android.widget.EditText
import android.widget.TextView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.AppComponent
import com.habitrpg.android.habitica.data.SocialRepository
import com.habitrpg.android.habitica.extensions.notNull
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.models.social.ChatMessage
import com.habitrpg.android.habitica.modules.AppModule
import com.habitrpg.android.habitica.prefs.scanner.IntentIntegrator
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
import com.habitrpg.android.habitica.ui.helpers.UiUtils
import io.realm.RealmResults
import kotlinx.android.synthetic.main.fragment_inbox.*
import rx.functions.Action1
import javax.inject.Inject
import javax.inject.Named
class InboxFragment : BaseMainFragment(), SwipeRefreshLayout.OnRefreshListener, View.OnClickListener {
@Inject
lateinit var socialRepository: SocialRepository
@field:[Inject Named(AppModule.NAMED_USER_ID)]
lateinit var userId: String
private var chooseRecipientDialogView: View? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
super.onCreateView(inflater, container, savedInstanceState)
this.socialRepository.markPrivateMessagesRead(user).subscribe(Action1 { }, RxErrorHandler.handleEmptyError())
return inflater.inflate(R.layout.fragment_inbox, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
inbox_refresh_layout?.setOnRefreshListener(this)
loadMessages()
}
private fun loadMessages() {
userRepository.getInboxOverviewList().subscribe(Action1<RealmResults<ChatMessage>> {
setInboxMessages(it)
}, RxErrorHandler.handleEmptyError())
}
override fun onDestroy() {
socialRepository.close()
super.onDestroy()
}
override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
this.activity?.menuInflater?.inflate(R.menu.inbox, menu)
super.onCreateOptionsMenu(menu, inflater)
}
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
val id = item?.itemId
when (id) {
R.id.send_message -> {
openNewMessageDialog()
return true
}
}
return super.onOptionsItemSelected(item)
}
private fun openNewMessageDialog() {
assert(this.activity != null)
this.chooseRecipientDialogView = this.activity?.layoutInflater?.inflate(R.layout.dialog_choose_message_recipient, null)
val scaneQRCodeButton = chooseRecipientDialogView?.findViewById<View>(R.id.scanQRCodeButton) as Button
this.activity.notNull { thisActivity ->
val alert = AlertDialog.Builder(thisActivity)
.setTitle(getString(R.string.choose_recipient_title))
.setPositiveButton(getString(R.string.action_continue)) { _, _ ->
val uuidEditText = chooseRecipientDialogView?.findViewById<View>(R.id.uuidEditText) as EditText
openInboxMessages(uuidEditText.text.toString(), "")
}
.setNeutralButton(getString(R.string.action_cancel)) { dialog, _ ->
UiUtils.dismissKeyboard(thisActivity)
dialog.cancel()
}
.create()
scaneQRCodeButton.setOnClickListener {
val scanIntegrator = IntentIntegrator(getActivity())
scanIntegrator.initiateScan(this)
}
alert.setView(chooseRecipientDialogView)
alert.show()
}
}
override fun injectFragment(component: AppComponent) {
component.inject(this)
}
override fun onRefresh() {
inbox_refresh_layout.isRefreshing = true
this.userRepository.retrieveInboxMessages()
.subscribe(Action1<List<ChatMessage>> {
inbox_refresh_layout.isRefreshing = false
}, RxErrorHandler.handleEmptyError())
}
private fun setInboxMessages(messages: RealmResults<ChatMessage>) {
if (inbox_messages == null) {
return
}
inbox_messages.removeAllViewsInLayout()
val inflater = context?.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
if (messages.isNotEmpty()) {
for (message in messages) {
val entry = inflater.inflate(R.layout.plain_list_item, inbox_messages, false) as TextView
entry.text = message.user
entry.tag = message.uuid
entry.setOnClickListener(this)
inbox_messages.addView(entry)
}
} else {
val tv = TextView(context)
tv.setText(R.string.empty_inbox)
}
}
override fun onClick(v: View) {
val entry = v as TextView
val replyToUserName = entry.text.toString()
openInboxMessages(entry.tag.toString(), replyToUserName)
}
private fun openInboxMessages(userID: String, username: String) {
val inboxMessageListFragment = InboxMessageListFragment()
inboxMessageListFragment.setReceivingUser(username, userID)
this.activity?.displayFragment(inboxMessageListFragment)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
val scanningResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, data)
if (scanningResult != null && scanningResult.contents != null) {
if (this.chooseRecipientDialogView != null) {
val uuidEditText = this.chooseRecipientDialogView?.findViewById<View>(R.id.uuidEditText) as EditText
val qrCodeUrl = scanningResult.contents
val uri = Uri.parse(qrCodeUrl)
if (uri == null || uri.pathSegments.size < 3) {
return
}
val userID = uri.pathSegments[2]
uuidEditText.setText(userID)
}
}
}
override fun customTitle(): String {
return if (!isAdded) {
""
} else getString(R.string.sidebar_inbox)
}
}

View file

@ -104,9 +104,9 @@ class TavernDetailFragment : BaseFragment() {
bindButtons()
compositeSubscription.add(socialRepository.getGroup(Group.TAVERN_ID)
.doOnNext({ if (!it.hasActiveQuest) worldBossSection.visibility = View.GONE })
.doOnNext { if (!it.hasActiveQuest) worldBossSection.visibility = View.GONE }
.filter { it.hasActiveQuest }
.doOnNext({ questProgressView.progress = it.quest})
.doOnNext { questProgressView.progress = it.quest}
.flatMap { inventoryRepository.getQuestContent(it.quest?.key).first() }
.subscribe(Action1 {
questProgressView.quest = it
@ -218,9 +218,9 @@ class TavernDetailFragment : BaseFragment() {
background?.setColorFilter(quest.colors?.extraLightColor ?: 0, PorterDuff.Mode.MULTIPLY)
promptView?.backgroundCompat = background
alert.setButton(AlertDialog.BUTTON_POSITIVE, context.getString(R.string.close), { dialog, _ ->
alert.setButton(AlertDialog.BUTTON_POSITIVE, context.getString(R.string.close)) { dialog, _ ->
dialog.dismiss()
})
}
alert.show() }
}
}

View file

@ -47,9 +47,6 @@ class GroupSerialization : JsonDeserializer<Group>, JsonSerializer<Group> {
if (obj.has("type")) {
group.type = obj.get("type").asString
}
if (obj.has("chat")) {
group.chat = context.deserialize(obj.get("chat"), object : TypeToken<RealmList<ChatMessage>>() {}.type)
}
if (obj.has("leader")) {
if (obj.get("leader").isJsonPrimitive) {
group.leaderID = obj.get("leader").asString

View file

@ -36,9 +36,6 @@ class UserDeserializer : JsonDeserializer<User> {
}
if (obj.has("inbox")) {
user.inbox = context.deserialize(obj.get("inbox"), Inbox::class.java)
for (message in user.inbox.messages) {
message.isInboxMessage = true
}
}
if (obj.has("preferences")) {
user.preferences = context.deserialize(obj.get("preferences"), Preferences::class.java)