diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser new file mode 100644 index 0000000..345092e Binary files /dev/null and b/.idea/caches/build_file_checksums.ser differ diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..30aa626 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 635999d..ba7052b 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -24,7 +24,7 @@ - + diff --git a/build.gradle b/build.gradle index 3cdefb4..05a0b71 100644 --- a/build.gradle +++ b/build.gradle @@ -8,12 +8,12 @@ buildscript { } ext { - smackVersion="4.2.3-SNAPSHOT" + smackVersion="4.4.0-alpha2-SNAPSHOT" } dependencies { - classpath 'com.android.tools.build:gradle:3.0.1' + classpath 'com.android.tools.build:gradle:3.1.3' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index c05b4e8..2358be7 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Mon Jan 22 17:04:57 CET 2018 +#Wed Aug 01 11:38:19 CEST 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip diff --git a/mobile/build.gradle b/mobile/build.gradle index f4e8665..130012d 100644 --- a/mobile/build.gradle +++ b/mobile/build.gradle @@ -29,18 +29,24 @@ dependencies { implementation 'com.android.support:support-emoji:27.0.2' implementation 'com.android.support.constraint:constraint-layout:1.0.2' + // The holy ButterKnife \o/ + compile 'com.jakewharton:butterknife:8.8.1' + annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1' - testImplementation 'junit:junit:4.12' - androidTestImplementation 'com.android.support.test:runner:1.0.1' - androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' + // External UI + compile 'de.hdodenhof:circleimageview:2.0.0' + // Smack compile "org.igniterealtime.smack:smack-android-extensions:$smackVersion" compile "org.igniterealtime.smack:smack-omemo-signal:$smackVersion" compile "org.igniterealtime.smack:smack-tcp:$smackVersion" compile "org.igniterealtime.smack:smack-experimental:$smackVersion" - compile 'com.jakewharton:butterknife:8.8.1' - annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1' + // SQLCipher + compile 'net.zetetic:android-database-sqlcipher:3.5.9@aar' - compile 'de.hdodenhof:circleimageview:2.0.0' + // Test stuff + testImplementation 'junit:junit:4.12' + androidTestImplementation 'com.android.support.test:runner:1.0.1' + androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' } diff --git a/mobile/src/main/AndroidManifest.xml b/mobile/src/main/AndroidManifest.xml index 26a700f..cf42e47 100644 --- a/mobile/src/main/AndroidManifest.xml +++ b/mobile/src/main/AndroidManifest.xml @@ -34,6 +34,7 @@ android:resource="@xml/automotive_app_desc" /> + diff --git a/mobile/src/main/java/de/vanitasvitae/slam/SlamApplication.java b/mobile/src/main/java/de/vanitasvitae/slam/SlamApplication.java index d2626c8..48792d5 100644 --- a/mobile/src/main/java/de/vanitasvitae/slam/SlamApplication.java +++ b/mobile/src/main/java/de/vanitasvitae/slam/SlamApplication.java @@ -18,14 +18,16 @@ package de.vanitasvitae.slam; import android.app.Application; +import android.content.Intent; import de.vanitasvitae.slam.mvp.DummyPresenterFactory; import de.vanitasvitae.slam.mvp.PresenterFactory; +import de.vanitasvitae.slam.service.SlamXmppService; public class SlamApplication extends Application { public SlamApplication() { super(); - PresenterFactory.setInstance(new DummyPresenterFactory()); + PresenterFactory.setInstance(new PresenterFactory()); } } diff --git a/mobile/src/main/java/de/vanitasvitae/slam/mvp/contracts/BaseContract.java b/mobile/src/main/java/de/vanitasvitae/slam/mvp/contracts/BaseContract.java new file mode 100644 index 0000000..af7ed15 --- /dev/null +++ b/mobile/src/main/java/de/vanitasvitae/slam/mvp/contracts/BaseContract.java @@ -0,0 +1,32 @@ +/* + * Copyright 2018 Paul Schaub + * + * This code 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. + * + * This program 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 this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package de.vanitasvitae.slam.mvp.contracts; + +/** + * Created by Paul Schaub on 24.02.18. + */ +public interface BaseContract { + + interface BaseView { + void setPresenter(T presenter); + } + + interface BasePresenter { + + } +} diff --git a/mobile/src/main/java/de/vanitasvitae/slam/mvp/contracts/ContactDetailContract.java b/mobile/src/main/java/de/vanitasvitae/slam/mvp/contracts/ContactDetailContract.java index 0c0837b..d75a50f 100644 --- a/mobile/src/main/java/de/vanitasvitae/slam/mvp/contracts/ContactDetailContract.java +++ b/mobile/src/main/java/de/vanitasvitae/slam/mvp/contracts/ContactDetailContract.java @@ -26,7 +26,7 @@ import de.vanitasvitae.slam.xmpp.Resource; */ public interface ContactDetailContract { - interface View { + interface View extends BaseContract.BaseView { void setContactAvatar(); void setNickname(String nickname); void setResources(List presences); @@ -34,7 +34,7 @@ public interface ContactDetailContract { void addFingerprints(List fingerprints); } - interface Presenter { + interface Presenter extends BaseContract.BasePresenter { void onAvatarClick(); void onSharedMediaClick(); void onAudioCallClick(); diff --git a/mobile/src/main/java/de/vanitasvitae/slam/mvp/contracts/ContactListContract.java b/mobile/src/main/java/de/vanitasvitae/slam/mvp/contracts/ContactListContract.java index 74be62f..75787e2 100644 --- a/mobile/src/main/java/de/vanitasvitae/slam/mvp/contracts/ContactListContract.java +++ b/mobile/src/main/java/de/vanitasvitae/slam/mvp/contracts/ContactListContract.java @@ -31,7 +31,7 @@ import de.vanitasvitae.slam.xmpp.Contact; */ public interface ContactListContract { - interface View { + interface View extends BaseContract.BaseView { void addContactListItems(List contacts); void clearContactListItems(); void onUpdateContactPresence(); @@ -41,7 +41,7 @@ public interface ContactListContract { void navigateToContactDetail(BareJid contact); } - interface Presenter { + interface Presenter extends BaseContract.BasePresenter { void onContactListItemClick(); void onContactListItemLongClick(); void addNewContact(); diff --git a/mobile/src/main/java/de/vanitasvitae/slam/mvp/contracts/ConversationContract.java b/mobile/src/main/java/de/vanitasvitae/slam/mvp/contracts/ConversationContract.java index cbfdd29..95e0912 100644 --- a/mobile/src/main/java/de/vanitasvitae/slam/mvp/contracts/ConversationContract.java +++ b/mobile/src/main/java/de/vanitasvitae/slam/mvp/contracts/ConversationContract.java @@ -30,14 +30,14 @@ import de.vanitasvitae.slam.xmpp.message.AbstractMessage; */ public interface ConversationContract { - interface View { + interface View extends BaseContract.BaseView { void addMessageItems(List messages, boolean end); void highlightMessageItem(); void correctMessageItem(); void navigateToContactProfile(); } - interface Presenter { + interface Presenter extends BaseContract.BasePresenter { void setPeersJid(EntityBareJid jid); void onConversationScrolledToTop(); void onComposingMessageChanged(String composingMessage); diff --git a/mobile/src/main/java/de/vanitasvitae/slam/mvp/contracts/ConversationListContract.java b/mobile/src/main/java/de/vanitasvitae/slam/mvp/contracts/ConversationListContract.java index 33abf0b..91a39f8 100644 --- a/mobile/src/main/java/de/vanitasvitae/slam/mvp/contracts/ConversationListContract.java +++ b/mobile/src/main/java/de/vanitasvitae/slam/mvp/contracts/ConversationListContract.java @@ -29,13 +29,13 @@ import de.vanitasvitae.slam.xmpp.Conversation; */ public interface ConversationListContract { - interface View { + interface View extends BaseContract.BaseView { void populateConversationList(List conversations); void navigateToConversation(BareJid contact); void navigateToContactDetail(BareJid contact); } - interface Presenter { + interface Presenter extends BaseContract.BasePresenter { void onConversationClick(); void onConversationLongClick(); void load(); diff --git a/mobile/src/main/java/de/vanitasvitae/slam/mvp/contracts/LoginContract.java b/mobile/src/main/java/de/vanitasvitae/slam/mvp/contracts/LoginContract.java index afa439b..c81f7c4 100644 --- a/mobile/src/main/java/de/vanitasvitae/slam/mvp/contracts/LoginContract.java +++ b/mobile/src/main/java/de/vanitasvitae/slam/mvp/contracts/LoginContract.java @@ -17,13 +17,15 @@ */ package de.vanitasvitae.slam.mvp.contracts; +import de.vanitasvitae.slam.service.SlamXmppService; + /** * Model-View-Presenter contract of the login screen. * Created by Paul Schaub on 01.02.18. */ public interface LoginContract { - interface View { + interface View extends BaseContract.BaseView { void showInvalidJidError(); void hideInvalidJidError(); void showInvalidPasswordError(); @@ -33,11 +35,15 @@ public interface LoginContract { void showProgressIndicator(); void hideProgressIndicator(); void navigateToMainActivity(); + void disableLoginButton(); + void enableLoginButton(); } - interface Presenter { + interface Presenter extends BaseContract.BasePresenter { void jidChanged(String jid); void passwordChanged(String password); void loginClicked(); + void bindService(SlamXmppService service); + void unbindService(); } } diff --git a/mobile/src/main/java/de/vanitasvitae/slam/mvp/contracts/SearchContract.java b/mobile/src/main/java/de/vanitasvitae/slam/mvp/contracts/SearchContract.java index 9d05788..c661837 100644 --- a/mobile/src/main/java/de/vanitasvitae/slam/mvp/contracts/SearchContract.java +++ b/mobile/src/main/java/de/vanitasvitae/slam/mvp/contracts/SearchContract.java @@ -25,7 +25,7 @@ import java.util.List; */ public interface SearchContract { - interface View { + interface View extends BaseContract.BaseView { void addSearchResults(List results); void clearSearchResults(); void showLoadingIndicator(); @@ -34,7 +34,7 @@ public interface SearchContract { void hideEmptySearchResults(); } - interface Presenter { + interface Presenter extends BaseContract.BasePresenter { void onSearchQueryChanged(String query); void onSearchResultClick(); void onSearchScrolledToBottom(); diff --git a/mobile/src/main/java/de/vanitasvitae/slam/mvp/contracts/message/AbstractMessageContract.java b/mobile/src/main/java/de/vanitasvitae/slam/mvp/contracts/message/AbstractMessageContract.java index c9c8132..42cd967 100644 --- a/mobile/src/main/java/de/vanitasvitae/slam/mvp/contracts/message/AbstractMessageContract.java +++ b/mobile/src/main/java/de/vanitasvitae/slam/mvp/contracts/message/AbstractMessageContract.java @@ -17,12 +17,14 @@ */ package de.vanitasvitae.slam.mvp.contracts.message; +import de.vanitasvitae.slam.mvp.contracts.BaseContract; + /** * Model-View-Presenter contract for an abstract message. */ public interface AbstractMessageContract { - interface View { + interface View extends BaseContract.BaseView { void setDirection(Direction direction); void setStatusSending(); void setStatusSendingFailed(); @@ -33,7 +35,7 @@ public interface AbstractMessageContract { void displayErrorMessage(); } - interface Presenter { + interface Presenter extends BaseContract.BasePresenter { void onDeleteMessage(); void onReadMessage(); void onMessageClick(); diff --git a/mobile/src/main/java/de/vanitasvitae/slam/mvp/presenter/LoginPresenter.java b/mobile/src/main/java/de/vanitasvitae/slam/mvp/presenter/LoginPresenter.java index 34e4770..5dc1de3 100644 --- a/mobile/src/main/java/de/vanitasvitae/slam/mvp/presenter/LoginPresenter.java +++ b/mobile/src/main/java/de/vanitasvitae/slam/mvp/presenter/LoginPresenter.java @@ -17,18 +17,38 @@ */ package de.vanitasvitae.slam.mvp.presenter; +import android.content.Context; +import android.support.v7.app.AppCompatActivity; +import android.util.Log; +import android.widget.Toast; + +import org.jivesoftware.smack.SmackException; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.chat2.Chat; +import org.jivesoftware.smack.chat2.ChatManager; +import org.jivesoftware.smack.chat2.IncomingChatMessageListener; +import org.jivesoftware.smack.packet.Message; import org.jxmpp.jid.BareJid; +import org.jxmpp.jid.EntityBareJid; import org.jxmpp.jid.impl.JidCreate; import org.jxmpp.stringprep.XmppStringprepException; +import java.io.IOException; +import java.net.UnknownHostException; + import de.vanitasvitae.slam.mvp.contracts.LoginContract; +import de.vanitasvitae.slam.service.SlamXmppConnection; +import de.vanitasvitae.slam.service.SlamXmppService; +import de.vanitasvitae.slam.xmpp.Account; public class LoginPresenter implements LoginContract.Presenter { + private static final String TAG = "LoginPresenter"; private final LoginContract.View view; private BareJid jid; private String password; + private SlamXmppService service; public LoginPresenter(LoginContract.View view) { this.view = view; @@ -39,19 +59,115 @@ public class LoginPresenter implements LoginContract.Presenter { try { this.jid = JidCreate.entityBareFrom(jid); view.hideInvalidJidError(); + + if (password != null && !password.isEmpty()) { + view.enableLoginButton(); + } + } catch (XmppStringprepException e) { this.jid = null; view.showInvalidJidError(); + view.disableLoginButton(); } } @Override public void passwordChanged(String password) { - + this.password = password; + if (password == null || password.isEmpty()) { + view.disableLoginButton(); + } else if (jid != null) { + view.enableLoginButton(); + } } @Override public void loginClicked() { + if (jid == null) { + view.showInvalidJidError(); + return; + } + if (password == null) { + view.showInvalidPasswordError(); + return; + } + + if (service != null) { + view.disableLoginButton(); + final Account account = new Account(jid); + + Thread loginThread = new Thread(new Runnable() { + @Override + public void run() { + SlamXmppConnection connection = null; + try { + connection = new SlamXmppConnection( + jid.asEntityBareJidOrThrow(), password); + } catch (UnknownHostException e) { + e.printStackTrace(); + ((AppCompatActivity)view).runOnUiThread(new Runnable() { + public void run() { + Toast.makeText((Context)view, "Unknown host", Toast.LENGTH_SHORT).show(); + } + }); + + return; + } + + service.addConnection(account, connection); + final SlamXmppConnection finalConnection = connection; + connection.login(new SlamXmppConnection.LoginCallback() { + @Override + public void success() { + ((AppCompatActivity)view).runOnUiThread(new Runnable() { + public void run() { + view.navigateToMainActivity(); + } + }); + ChatManager.getInstanceFor(finalConnection.getConnection()) + .addIncomingListener(new IncomingChatMessageListener() { + @Override + public void newIncomingMessage(final EntityBareJid from, final Message message, Chat chat) { + final AppCompatActivity activity = (AppCompatActivity) view; + activity.runOnUiThread(new Runnable() { + public void run() { + Toast.makeText(activity.getApplicationContext(), from.toString() + ": " + message.getBody(), Toast.LENGTH_LONG).show(); + } + }); + } + }); + } + + @Override + public void failure(final Exception e) { + e.printStackTrace(); + ((AppCompatActivity)view).runOnUiThread(new Runnable() { + public void run() { + Toast.makeText((Context)view, e.getMessage(), Toast.LENGTH_LONG).show(); + view.enableLoginButton(); + } + }); + } + }); + } + }); + loginThread.start(); + + } else { + view.showServerNotFoundError(); + } + } + + @Override + public void bindService(SlamXmppService service) { + Log.d(TAG, "bindService()"); + this.service = service; + } + + @Override + public void unbindService() { + Log.d(TAG, "unbindService()"); + this.service = null; } } diff --git a/mobile/src/main/java/de/vanitasvitae/slam/mvp/presenter/dummy/DummyConversationPresenter.java b/mobile/src/main/java/de/vanitasvitae/slam/mvp/presenter/dummy/DummyConversationPresenter.java index 8fc5da3..d5f4415 100644 --- a/mobile/src/main/java/de/vanitasvitae/slam/mvp/presenter/dummy/DummyConversationPresenter.java +++ b/mobile/src/main/java/de/vanitasvitae/slam/mvp/presenter/dummy/DummyConversationPresenter.java @@ -27,12 +27,13 @@ import java.util.ArrayList; import java.util.List; import de.vanitasvitae.slam.mvp.contracts.ConversationContract; +import de.vanitasvitae.slam.xmpp.message.AbstractMessage; public class DummyConversationPresenter implements ConversationContract.Presenter { private final ConversationContract.View view; - private final List dummyMessages = new ArrayList<>(); + private final List dummyMessages = new ArrayList<>(); public DummyConversationPresenter(ConversationContract.View view) { this.view = view; @@ -41,6 +42,7 @@ public class DummyConversationPresenter implements ConversationContract.Presente } private void populateDummyMessages() { + /* try { BareJid alice = JidCreate.bareFrom("alice@wonderland.lit"); @@ -66,6 +68,7 @@ public class DummyConversationPresenter implements ConversationContract.Presente } catch (XmppStringprepException e) { e.printStackTrace(); } + */ } @Override diff --git a/mobile/src/main/java/de/vanitasvitae/slam/mvp/presenter/dummy/DummyLoginPresenter.java b/mobile/src/main/java/de/vanitasvitae/slam/mvp/presenter/dummy/DummyLoginPresenter.java index 1bde5e1..6414345 100644 --- a/mobile/src/main/java/de/vanitasvitae/slam/mvp/presenter/dummy/DummyLoginPresenter.java +++ b/mobile/src/main/java/de/vanitasvitae/slam/mvp/presenter/dummy/DummyLoginPresenter.java @@ -23,6 +23,7 @@ import org.jxmpp.jid.impl.JidCreate; import org.jxmpp.stringprep.XmppStringprepException; import de.vanitasvitae.slam.mvp.contracts.LoginContract; +import de.vanitasvitae.slam.service.SlamXmppService; /** * Dummy presenter, that has no model. @@ -82,4 +83,14 @@ public class DummyLoginPresenter implements LoginContract.Presenter { } }, 250); } + + @Override + public void bindService(SlamXmppService service) { + + } + + @Override + public void unbindService() { + + } } diff --git a/mobile/src/main/java/de/vanitasvitae/slam/mvp/view/ContactDetailActivity.java b/mobile/src/main/java/de/vanitasvitae/slam/mvp/view/ContactDetailActivity.java index bfa88bc..863bf9b 100644 --- a/mobile/src/main/java/de/vanitasvitae/slam/mvp/view/ContactDetailActivity.java +++ b/mobile/src/main/java/de/vanitasvitae/slam/mvp/view/ContactDetailActivity.java @@ -45,7 +45,7 @@ public class ContactDetailActivity extends ThemedAppCompatActivity public static final String TAG = "Slam!"; - private final ContactDetailContract.Presenter presenter; + private ContactDetailContract.Presenter presenter; private final ContactDetailResourcesFragment resourcesFragment = new ContactDetailResourcesFragment(); private final ContactDetailInfoFragment infoFragment = new ContactDetailInfoFragment(); @@ -71,7 +71,7 @@ public class ContactDetailActivity extends ThemedAppCompatActivity private int animateProfileCirclePercent = 30; public ContactDetailActivity() { - this.presenter = PresenterFactory.getInstance().createContactDetailPresenter(this); + setPresenter(PresenterFactory.getInstance().createContactDetailPresenter(this)); } @Override @@ -151,6 +151,11 @@ public class ContactDetailActivity extends ThemedAppCompatActivity } + @Override + public void setPresenter(ContactDetailContract.Presenter presenter) { + this.presenter = presenter; + } + class DetailFragmentPagerAdapter extends FragmentPagerAdapter { public DetailFragmentPagerAdapter(FragmentManager fm) { diff --git a/mobile/src/main/java/de/vanitasvitae/slam/mvp/view/ContactListFragment.java b/mobile/src/main/java/de/vanitasvitae/slam/mvp/view/ContactListFragment.java index 680e895..a01a35d 100644 --- a/mobile/src/main/java/de/vanitasvitae/slam/mvp/view/ContactListFragment.java +++ b/mobile/src/main/java/de/vanitasvitae/slam/mvp/view/ContactListFragment.java @@ -47,13 +47,13 @@ public class ContactListFragment extends Fragment implements ContactListContract @BindView(R.id.recycler_list) RecyclerView recyclerView; - private final ContactListContract.Presenter presenter; + private ContactListContract.Presenter presenter; private final List contacts = new ArrayList<>(); public ContactListFragment() { super(); - this.presenter = PresenterFactory.getInstance().createContactListPresenter(this); + setPresenter(PresenterFactory.getInstance().createContactListPresenter(this)); } @Override @@ -142,4 +142,8 @@ public class ContactListFragment extends Fragment implements ContactListContract } + @Override + public void setPresenter(ContactListContract.Presenter presenter) { + this.presenter = presenter; + } } diff --git a/mobile/src/main/java/de/vanitasvitae/slam/mvp/view/ConversationFragment.java b/mobile/src/main/java/de/vanitasvitae/slam/mvp/view/ConversationFragment.java index becb837..b253a4e 100644 --- a/mobile/src/main/java/de/vanitasvitae/slam/mvp/view/ConversationFragment.java +++ b/mobile/src/main/java/de/vanitasvitae/slam/mvp/view/ConversationFragment.java @@ -56,7 +56,7 @@ public class ConversationFragment extends Fragment implements ConversationContra @BindView(R.id.recycler_chat) RecyclerView recyclerView; - private final ConversationContract.Presenter presenter; + private ConversationContract.Presenter presenter; Map messageIdIndizes = new HashMap<>(); List messages = new ArrayList<>(); @@ -87,7 +87,7 @@ public class ConversationFragment extends Fragment implements ConversationContra public ConversationFragment() { super(); - this.presenter = PresenterFactory.getInstance().createConversationPresenter(this); + setPresenter(PresenterFactory.getInstance().createConversationPresenter(this)); } @Override @@ -117,6 +117,11 @@ public class ConversationFragment extends Fragment implements ConversationContra recyclerView.getAdapter().notifyDataSetChanged(); } + @Override + public void setPresenter(ConversationContract.Presenter presenter) { + this.presenter = presenter; + } + @Override public void addMessageItems(List messages, boolean end) { if (end) { diff --git a/mobile/src/main/java/de/vanitasvitae/slam/mvp/view/ConversationListFragment.java b/mobile/src/main/java/de/vanitasvitae/slam/mvp/view/ConversationListFragment.java index 4867017..d56c889 100644 --- a/mobile/src/main/java/de/vanitasvitae/slam/mvp/view/ConversationListFragment.java +++ b/mobile/src/main/java/de/vanitasvitae/slam/mvp/view/ConversationListFragment.java @@ -49,12 +49,12 @@ public class ConversationListFragment extends Fragment implements ConversationLi @BindView(R.id.recycler_list) RecyclerView recyclerView; - private final ConversationListContract.Presenter presenter; + private ConversationListContract.Presenter presenter; private final List conversations = new ArrayList<>(); public ConversationListFragment() { super(); - this.presenter = PresenterFactory.getInstance().createConversationListPresenter(this); + setPresenter(PresenterFactory.getInstance().createConversationListPresenter(this)); } @Override @@ -72,6 +72,11 @@ public class ConversationListFragment extends Fragment implements ConversationLi recyclerView.getAdapter().notifyDataSetChanged(); } + @Override + public void setPresenter(ConversationListContract.Presenter presenter) { + this.presenter = presenter; + } + @Override public void populateConversationList(List conversations) { this.conversations.clear(); diff --git a/mobile/src/main/java/de/vanitasvitae/slam/mvp/view/LoginActivity.java b/mobile/src/main/java/de/vanitasvitae/slam/mvp/view/LoginActivity.java index f93a13f..7252132 100644 --- a/mobile/src/main/java/de/vanitasvitae/slam/mvp/view/LoginActivity.java +++ b/mobile/src/main/java/de/vanitasvitae/slam/mvp/view/LoginActivity.java @@ -17,8 +17,12 @@ */ package de.vanitasvitae.slam.mvp.view; +import android.content.ComponentName; +import android.content.Context; import android.content.Intent; +import android.content.ServiceConnection; import android.os.Bundle; +import android.os.IBinder; import android.support.design.widget.TextInputEditText; import android.support.design.widget.TextInputLayout; import android.support.v7.widget.Toolbar; @@ -40,8 +44,9 @@ import de.vanitasvitae.slam.R; import de.vanitasvitae.slam.mvp.PresenterFactory; import de.vanitasvitae.slam.mvp.view.abstr.ThemedAppCompatActivity; import de.vanitasvitae.slam.mvp.contracts.LoginContract; +import de.vanitasvitae.slam.service.SlamXmppService; -public class LoginActivity extends ThemedAppCompatActivity implements LoginContract.View { +public class LoginActivity extends ThemedAppCompatActivity implements LoginContract.View, ServiceConnection { // Presenter of this view private LoginContract.Presenter presenter; @@ -67,9 +72,11 @@ public class LoginActivity extends ThemedAppCompatActivity implements LoginContr @BindView(R.id.button_login) Button buttonLogin; + private SlamXmppService slamService; + public LoginActivity() { super(); - this.presenter = PresenterFactory.getInstance().createLoginPresenter(this); + setPresenter(PresenterFactory.getInstance().createLoginPresenter(this)); } @Override @@ -103,8 +110,29 @@ public class LoginActivity extends ThemedAppCompatActivity implements LoginContr }); } + @Override + protected void onResume() { + super.onResume(); + // Start xmpp service + Intent serviceIntent = new Intent(this, SlamXmppService.class); + bindService(serviceIntent, this, Context.BIND_AUTO_CREATE); + } + + @Override + protected void onPause() { + super.onPause(); + unbindService(this); + } + + @Override + public void setPresenter(LoginContract.Presenter presenter) { + this.presenter = presenter; + } + @OnClick(R.id.button_login) void login() { + Intent serviceIntent = new Intent(getApplicationContext(), SlamXmppService.class); + getApplicationContext().startService(serviceIntent); presenter.loginClicked(); } @@ -162,5 +190,28 @@ public class LoginActivity extends ThemedAppCompatActivity implements LoginContr startActivity(new Intent(this, MainActivity.class)); finish(); } + + @Override + public void disableLoginButton() { + buttonLogin.setEnabled(false); + } + + @Override + public void enableLoginButton() { + buttonLogin.setEnabled(true); + } + + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + SlamXmppService.SlamBinder binder = (SlamXmppService.SlamBinder) service; + slamService = binder.getService(); + presenter.bindService(slamService); + } + + @Override + public void onServiceDisconnected(ComponentName name) { + slamService = null; + presenter.unbindService(); + } } diff --git a/mobile/src/main/java/de/vanitasvitae/slam/mvp/view/SearchFragment.java b/mobile/src/main/java/de/vanitasvitae/slam/mvp/view/SearchFragment.java index 71ff94a..c03bf5a 100644 --- a/mobile/src/main/java/de/vanitasvitae/slam/mvp/view/SearchFragment.java +++ b/mobile/src/main/java/de/vanitasvitae/slam/mvp/view/SearchFragment.java @@ -31,10 +31,10 @@ import de.vanitasvitae.slam.mvp.contracts.SearchContract; */ public class SearchFragment extends Fragment implements SearchContract.View { - private final SearchContract.Presenter presenter; + private SearchContract.Presenter presenter; public SearchFragment() { - this.presenter = PresenterFactory.getInstance().createSearchPresenter(this); + setPresenter(PresenterFactory.getInstance().createSearchPresenter(this)); } @Override @@ -66,4 +66,9 @@ public class SearchFragment extends Fragment implements SearchContract.View { public void hideEmptySearchResults() { } + + @Override + public void setPresenter(SearchContract.Presenter presenter) { + this.presenter = presenter; + } } diff --git a/mobile/src/main/java/de/vanitasvitae/slam/mvp/view/message/MessageView.java b/mobile/src/main/java/de/vanitasvitae/slam/mvp/view/message/MessageView.java index 48ddcc1..b2dfb36 100644 --- a/mobile/src/main/java/de/vanitasvitae/slam/mvp/view/message/MessageView.java +++ b/mobile/src/main/java/de/vanitasvitae/slam/mvp/view/message/MessageView.java @@ -34,6 +34,8 @@ import de.vanitasvitae.slam.xmpp.message.AbstractMessage; public abstract class MessageView extends RecyclerView.ViewHolder implements AbstractMessageContract.View { + protected AbstractMessageContract.Presenter presenter; + public MessageView(View itemView) { super(itemView); } @@ -83,4 +85,9 @@ public abstract class MessageView extends RecyclerVie public void displayErrorMessage() { } + + @Override + public void setPresenter(AbstractMessageContract.Presenter presenter) { + this.presenter = presenter; + } } diff --git a/mobile/src/main/java/de/vanitasvitae/slam/service/SlamXmppConnection.java b/mobile/src/main/java/de/vanitasvitae/slam/service/SlamXmppConnection.java new file mode 100644 index 0000000..e72075b --- /dev/null +++ b/mobile/src/main/java/de/vanitasvitae/slam/service/SlamXmppConnection.java @@ -0,0 +1,86 @@ +/* + * Copyright 2018 Paul Schaub + * + * This code 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. + * + * This program 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 this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package de.vanitasvitae.slam.service; + +import android.util.Log; + +import org.jivesoftware.smack.AbstractXMPPConnection; +import org.jivesoftware.smack.tcp.XMPPTCPConnection; +import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration; +import org.jivesoftware.smack.util.StringUtils; +import org.jxmpp.jid.EntityBareJid; +import org.jxmpp.jid.parts.Resourcepart; +import org.jxmpp.stringprep.XmppStringprepException; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * Created by Paul Schaub on 24.02.18. + */ +public class SlamXmppConnection { + + private static final String TAG = "SlamConn"; + + private final EntityBareJid jid; + private final AbstractXMPPConnection connection; + private final Resourcepart resourcepart; + + public SlamXmppConnection(EntityBareJid jid, String password) throws UnknownHostException { + this.jid = jid; + // Resource + String res = "Slam-" + StringUtils.randomString(12); + try { + this.resourcepart = Resourcepart.from(res); + } catch (XmppStringprepException e) { + throw new AssertionError("Resourcepart \"" + res + "\" appears to be invalid.", e); + } + + // Configuration + XMPPTCPConnectionConfiguration configuration = XMPPTCPConnectionConfiguration.builder() + .setHost(this.jid.getDomain().toString()) + .setHostAddress(InetAddress.getByName(jid.getDomain().toString())) + .setXmppDomain(jid.asDomainBareJid()) + .setUsernameAndPassword(jid.getLocalpart().toString(), password) + .setResource(resourcepart) + .build(); + + this.connection = new XMPPTCPConnection(configuration); + } + + public AbstractXMPPConnection getConnection() { + return connection; + } + + public void login(final LoginCallback callback) { + try { + getConnection().connect().login(); + Log.d(TAG, "Account " + jid.toString() + " logged in."); + callback.success(); + } catch (Exception e) { + Log.d(TAG, "Account " + jid.toString() + " could not log in."); + e.printStackTrace(); + callback.failure(e); + } + } + + public interface LoginCallback { + void success(); + void failure(Exception e); + } +} diff --git a/mobile/src/main/java/de/vanitasvitae/slam/service/SlamXmppService.java b/mobile/src/main/java/de/vanitasvitae/slam/service/SlamXmppService.java new file mode 100644 index 0000000..bf406f4 --- /dev/null +++ b/mobile/src/main/java/de/vanitasvitae/slam/service/SlamXmppService.java @@ -0,0 +1,78 @@ +/* + * Copyright 2018 Paul Schaub + * + * This code 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. + * + * This program 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 this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package de.vanitasvitae.slam.service; + +import android.app.Service; +import android.content.Intent; +import android.os.Binder; +import android.os.IBinder; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.util.Log; + +import java.util.HashMap; + +import de.vanitasvitae.slam.xmpp.Account; + +/** + * Created by Paul Schaub on 24.02.18. + */ +public class SlamXmppService extends Service { + + private static final String TAG = "SlamService"; + + private final IBinder binder = new SlamBinder(); + private Account currentAccount; + private final HashMap connections = new HashMap<>(); + + @Nullable + @Override + public IBinder onBind(Intent intent) { + return binder; + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + Log.d(TAG,"onStartCommand()"); + return Service.START_STICKY; + } + + public void addConnection(@NonNull Account account, SlamXmppConnection connection) { + Log.d(TAG, "Adding a new connection for " + account.getJid().toString()); + this.connections.put(account, connection); + } + + @Override + public void onDestroy() { + Log.d(TAG, "onDestroy()"); + for (SlamXmppConnection c : connections.values()) { + c.getConnection().disconnect(); + } + super.onDestroy(); + } + + public SlamXmppConnection getConnection(@NonNull Account account) { + return connections.get(account); + } + + public class SlamBinder extends Binder { + public SlamXmppService getService() { + return SlamXmppService.this; + } + } +} diff --git a/mobile/src/main/java/de/vanitasvitae/slam/task/LoginTask.java b/mobile/src/main/java/de/vanitasvitae/slam/task/LoginTask.java new file mode 100644 index 0000000..37d73a8 --- /dev/null +++ b/mobile/src/main/java/de/vanitasvitae/slam/task/LoginTask.java @@ -0,0 +1,69 @@ +package de.vanitasvitae.slam.task; + +import android.os.AsyncTask; +import android.support.annotation.NonNull; + +import org.jxmpp.jid.EntityBareJid; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +import de.vanitasvitae.slam.mvp.contracts.LoginContract; +import de.vanitasvitae.slam.service.SlamXmppConnection; + +/** + * Created by Paul Schaub on 26.02.18. + */ +public class LoginTask extends AsyncTask { + + private final LoginContract.View view; + private Exception exception; + + public LoginTask(LoginContract.View view) { + this.view = view; + } + + @Override + protected SlamXmppConnection doInBackground(Credentials... credentials) { + if (credentials.length != 1) { + throw new IllegalArgumentException("Please only present one set of credentials at a time."); + } + + SlamXmppConnection connection; + try { + connection = new SlamXmppConnection(credentials[0].jid, credentials[0].password); + } catch (UnknownHostException e) { + exception = e; + return null; + } + + return null; + } + + @Override + protected void onPreExecute() { + + } + + @Override + protected void onPostExecute(SlamXmppConnection result) { + if (exception == null) { + return; + } + + if (exception instanceof UnknownHostException) { + view.showServerNotFoundError(); + return; + } + } + + public static class Credentials { + private final EntityBareJid jid; + private final String password; + + public Credentials(@NonNull EntityBareJid jid, @NonNull String password) { + this.jid = jid; + this.password = password; + } + } +} diff --git a/mobile/src/main/java/de/vanitasvitae/slam/xmpp/Account.java b/mobile/src/main/java/de/vanitasvitae/slam/xmpp/Account.java index 2a9a04d..6a9612d 100644 --- a/mobile/src/main/java/de/vanitasvitae/slam/xmpp/Account.java +++ b/mobile/src/main/java/de/vanitasvitae/slam/xmpp/Account.java @@ -17,8 +17,20 @@ */ package de.vanitasvitae.slam.xmpp; +import org.jxmpp.jid.BareJid; + /** * Created by Paul Schaub on 24.02.18. */ public class Account { + + private final BareJid jid; + + public Account(BareJid jid) { + this.jid = jid; + } + + public BareJid getJid() { + return jid; + } }