From 87e33a779f8c8d76230b9a82d731e0da308871c4 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sun, 19 Feb 2023 15:40:56 +0100 Subject: [PATCH] add stub MainActivity --- app/build.gradle | 4 + .../1.json | 8 +- .../conversations/android/Conversations.java | 3 +- .../android/database/dao/AccountDao.java | 5 + .../android/database/dao/ChatDao.java | 5 + .../android/database/dao/RosterDao.java | 5 + .../android/database/model/Account.java | 27 +---- .../database/model/AccountIdentifier.java | 31 ++++++ .../android/repository/AccountRepository.java | 7 ++ .../android/repository/ChatRepository.java | 16 +++ .../android/ui/activity/MainActivity.java | 7 ++ .../ui/fragment/main/OverviewFragment.java | 102 ++++++++++++++++++ .../android/ui/model/OverviewViewModel.java | 31 ++++++ .../util/ConsistentColorGeneration.java | 66 ++++++++++++ .../android/xmpp/XmppConnection.java | 3 +- .../android/xmpp/model/roster/Group.java | 2 + .../android/xmpp/processor/IqProcessor.java | 1 + app/src/main/res/drawable/ic_chat_24dp.xml | 11 ++ .../res/drawable/ic_manage_accounts_24dp.xml | 16 +++ app/src/main/res/drawable/ic_menu_24dp.xml | 10 ++ app/src/main/res/drawable/ic_person_24dp.xml | 10 ++ .../main/res/drawable/ic_person_add_24dp.xml | 10 ++ .../main/res/drawable/ic_workspaces_24dp.xml | 10 ++ app/src/main/res/layout/activity_main.xml | 32 ++++++ app/src/main/res/layout/fragment_overview.xml | 64 +++++++++++ .../res/layout/item_navigation_header.xml | 16 +++ app/src/main/res/menu/fragment_overview.xml | 35 ++++++ .../main/res/navigation/main_navigation.xml | 28 +++++ app/src/main/res/values/strings.xml | 5 + 29 files changed, 540 insertions(+), 30 deletions(-) create mode 100644 app/src/main/java/im/conversations/android/database/model/AccountIdentifier.java create mode 100644 app/src/main/java/im/conversations/android/repository/ChatRepository.java create mode 100644 app/src/main/java/im/conversations/android/ui/fragment/main/OverviewFragment.java create mode 100644 app/src/main/java/im/conversations/android/ui/model/OverviewViewModel.java create mode 100644 app/src/main/java/im/conversations/android/util/ConsistentColorGeneration.java create mode 100644 app/src/main/res/drawable/ic_chat_24dp.xml create mode 100644 app/src/main/res/drawable/ic_manage_accounts_24dp.xml create mode 100644 app/src/main/res/drawable/ic_menu_24dp.xml create mode 100644 app/src/main/res/drawable/ic_person_24dp.xml create mode 100644 app/src/main/res/drawable/ic_person_add_24dp.xml create mode 100644 app/src/main/res/drawable/ic_workspaces_24dp.xml create mode 100644 app/src/main/res/layout/activity_main.xml create mode 100644 app/src/main/res/layout/fragment_overview.xml create mode 100644 app/src/main/res/layout/item_navigation_header.xml create mode 100644 app/src/main/res/menu/fragment_overview.xml create mode 100644 app/src/main/res/navigation/main_navigation.xml diff --git a/app/build.gradle b/app/build.gradle index 30f16f468..86bece7a0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -106,6 +106,10 @@ dependencies { // XMPP Address library implementation 'org.jxmpp:jxmpp-jid:1.0.3' + // Consistent Color Generation + implementation 'org.hsluv:hsluv:0.2' + + // DNS library (XMPP needs to resolve SRV records) implementation 'de.measite.minidns:minidns-hla:0.2.4' diff --git a/app/schemas/im.conversations.android.database.ConversationsDatabase/1.json b/app/schemas/im.conversations.android.database.ConversationsDatabase/1.json index 1aebf3510..0c726c566 100644 --- a/app/schemas/im.conversations.android.database.ConversationsDatabase/1.json +++ b/app/schemas/im.conversations.android.database.ConversationsDatabase/1.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 1, - "identityHash": "070e419bfe6857a47cda745017f04a57", + "identityHash": "6186e2691813f4fbd804b90fd770e18b", "entities": [ { "tableName": "account", @@ -2290,7 +2290,7 @@ }, { "tableName": "roster_group", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`rosterItemId` INTEGER NOT NULL, `groupId` INTEGER NOT NULL, PRIMARY KEY(`rosterItemId`, `groupId`), FOREIGN KEY(`rosterItemId`) REFERENCES `roster`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`groupId`) REFERENCES `group`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`rosterItemId` INTEGER NOT NULL, `groupId` INTEGER NOT NULL, PRIMARY KEY(`rosterItemId`, `groupId`), FOREIGN KEY(`rosterItemId`) REFERENCES `roster`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`groupId`) REFERENCES `group`(`id`) ON UPDATE NO ACTION ON DELETE RESTRICT )", "fields": [ { "fieldPath": "rosterItemId", @@ -2337,7 +2337,7 @@ }, { "table": "group", - "onDelete": "CASCADE", + "onDelete": "RESTRICT", "onUpdate": "NO ACTION", "columns": [ "groupId" @@ -2352,7 +2352,7 @@ "views": [], "setupQueries": [ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '070e419bfe6857a47cda745017f04a57')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '6186e2691813f4fbd804b90fd770e18b')" ] } } \ No newline at end of file diff --git a/app/src/main/java/im/conversations/android/Conversations.java b/app/src/main/java/im/conversations/android/Conversations.java index c7982790b..9108693fc 100644 --- a/app/src/main/java/im/conversations/android/Conversations.java +++ b/app/src/main/java/im/conversations/android/Conversations.java @@ -2,7 +2,6 @@ package im.conversations.android; import android.app.Application; import androidx.appcompat.app.AppCompatDelegate; -import com.google.android.material.color.DynamicColors; import im.conversations.android.dns.Resolver; import im.conversations.android.notification.Channels; import im.conversations.android.xmpp.ConnectionPool; @@ -32,6 +31,6 @@ public class Conversations extends Application { ConnectionPool.getInstance(this).reconfigure(); AppCompatDelegate.setDefaultNightMode( AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM); // For night mode theme - DynamicColors.applyToActivitiesIfAvailable(this); + // DynamicColors.applyToActivitiesIfAvailable(this); } } diff --git a/app/src/main/java/im/conversations/android/database/dao/AccountDao.java b/app/src/main/java/im/conversations/android/database/dao/AccountDao.java index 09a6ae25d..01c4bfade 100644 --- a/app/src/main/java/im/conversations/android/database/dao/AccountDao.java +++ b/app/src/main/java/im/conversations/android/database/dao/AccountDao.java @@ -1,11 +1,13 @@ package im.conversations.android.database.dao; +import androidx.lifecycle.LiveData; import androidx.room.Dao; import androidx.room.Insert; import androidx.room.Query; import com.google.common.util.concurrent.ListenableFuture; import im.conversations.android.database.entity.AccountEntity; import im.conversations.android.database.model.Account; +import im.conversations.android.database.model.AccountIdentifier; import im.conversations.android.database.model.Connection; import java.util.List; import org.jxmpp.jid.BareJid; @@ -28,6 +30,9 @@ public interface AccountDao { @Query("SELECT id,address,randomSeed FROM account WHERE id=:id AND enabled=1") ListenableFuture getEnabledAccount(long id); + @Query("SELECT id,address FROM account") + LiveData> getAccounts(); + @Query("SELECT hostname,port,directTls FROM account WHERE id=:id AND hostname != null") Connection getConnectionSettings(long id); diff --git a/app/src/main/java/im/conversations/android/database/dao/ChatDao.java b/app/src/main/java/im/conversations/android/database/dao/ChatDao.java index acd7206d5..701e1dd2f 100644 --- a/app/src/main/java/im/conversations/android/database/dao/ChatDao.java +++ b/app/src/main/java/im/conversations/android/database/dao/ChatDao.java @@ -1,5 +1,6 @@ package im.conversations.android.database.dao; +import androidx.lifecycle.LiveData; import androidx.room.Dao; import androidx.room.Insert; import androidx.room.Query; @@ -10,6 +11,7 @@ import im.conversations.android.database.model.ChatIdentifier; import im.conversations.android.database.model.ChatType; import im.conversations.android.xmpp.model.stanza.Message; import java.util.Arrays; +import java.util.List; import org.jxmpp.jid.Jid; @Dao @@ -52,4 +54,7 @@ public abstract class ChatDao { @Insert protected abstract long insert(ChatEntity chatEntity); + + @Query("SELECT name FROM `group` ORDER BY name") + public abstract LiveData> getGroups(); } diff --git a/app/src/main/java/im/conversations/android/database/dao/RosterDao.java b/app/src/main/java/im/conversations/android/database/dao/RosterDao.java index d04c851d7..2f32b7090 100644 --- a/app/src/main/java/im/conversations/android/database/dao/RosterDao.java +++ b/app/src/main/java/im/conversations/android/database/dao/RosterDao.java @@ -13,10 +13,14 @@ import im.conversations.android.database.model.Account; import im.conversations.android.xmpp.model.roster.Item; import java.util.Collection; import org.jxmpp.jid.Jid; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; @Dao public abstract class RosterDao extends GroupDao { + private static final Logger LOGGER = LoggerFactory.getLogger(RosterDao.class); + @Insert(onConflict = REPLACE) protected abstract long insert(RosterItemEntity rosterItem); @@ -32,6 +36,7 @@ public abstract class RosterDao extends GroupDao { @Transaction public void set( final Account account, final String version, final Collection rosterItems) { + LOGGER.info("items: " + rosterItems); clear(account.id); for (final Item item : rosterItems) { final long id = insert(RosterItemEntity.of(account.id, item)); diff --git a/app/src/main/java/im/conversations/android/database/model/Account.java b/app/src/main/java/im/conversations/android/database/model/Account.java index 2398552b6..2c0ebfa72 100644 --- a/app/src/main/java/im/conversations/android/database/model/Account.java +++ b/app/src/main/java/im/conversations/android/database/model/Account.java @@ -12,30 +12,14 @@ import java.util.Arrays; import java.util.UUID; import org.jxmpp.jid.BareJid; -public class Account { +public class Account extends AccountIdentifier { - public final long id; - @NonNull public final BareJid address; @NonNull public final byte[] randomSeed; - @Override - public String toString() { - return "Account{" - + "id=" - + id - + ", address=" - + address - + ", randomSeed=" - + Arrays.toString(randomSeed) - + '}'; - } - public Account(final long id, @NonNull final BareJid address, @NonNull byte[] randomSeed) { - Preconditions.checkNotNull(address, "Account can not be instantiated without an address"); + super(id, address); Preconditions.checkArgument( randomSeed.length == 32, "RandomSeed must have exactly 32 bytes"); - this.id = id; - this.address = address; this.randomSeed = randomSeed; } @@ -43,15 +27,14 @@ public class Account { public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; Account account = (Account) o; - return id == account.id - && Objects.equal(address, account.address) - && Arrays.equals(randomSeed, account.randomSeed); + return Arrays.equals(randomSeed, account.randomSeed); } @Override public int hashCode() { - return Objects.hashCode(id, address, randomSeed); + return Objects.hashCode(super.hashCode(), randomSeed); } public boolean isOnion() { diff --git a/app/src/main/java/im/conversations/android/database/model/AccountIdentifier.java b/app/src/main/java/im/conversations/android/database/model/AccountIdentifier.java new file mode 100644 index 000000000..f1bb641f6 --- /dev/null +++ b/app/src/main/java/im/conversations/android/database/model/AccountIdentifier.java @@ -0,0 +1,31 @@ +package im.conversations.android.database.model; + +import androidx.annotation.NonNull; +import com.google.common.base.Objects; +import com.google.common.base.Preconditions; +import org.jxmpp.jid.BareJid; + +public class AccountIdentifier { + + public final long id; + @NonNull public final BareJid address; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AccountIdentifier that = (AccountIdentifier) o; + return id == that.id && Objects.equal(address, that.address); + } + + @Override + public int hashCode() { + return Objects.hashCode(id, address); + } + + public AccountIdentifier(long id, @NonNull BareJid address) { + Preconditions.checkNotNull(address, "Account can not be instantiated without an address"); + this.id = id; + this.address = address; + } +} diff --git a/app/src/main/java/im/conversations/android/repository/AccountRepository.java b/app/src/main/java/im/conversations/android/repository/AccountRepository.java index dadf99859..fd83611dd 100644 --- a/app/src/main/java/im/conversations/android/repository/AccountRepository.java +++ b/app/src/main/java/im/conversations/android/repository/AccountRepository.java @@ -2,6 +2,7 @@ package im.conversations.android.repository; import android.content.Context; import androidx.annotation.NonNull; +import androidx.lifecycle.LiveData; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; @@ -9,11 +10,13 @@ import im.conversations.android.IDs; import im.conversations.android.database.CredentialStore; import im.conversations.android.database.entity.AccountEntity; import im.conversations.android.database.model.Account; +import im.conversations.android.database.model.AccountIdentifier; import im.conversations.android.xmpp.ConnectionPool; import im.conversations.android.xmpp.XmppConnection; import im.conversations.android.xmpp.manager.RegistrationManager; import java.io.IOException; import java.security.GeneralSecurityException; +import java.util.List; import org.jxmpp.jid.BareJid; public class AccountRepository extends AbstractRepository { @@ -107,6 +110,10 @@ public class AccountRepository extends AbstractRepository { ConnectionPool.getInstance(context).reconnect(account); } + public LiveData> getAccounts() { + return database.accountDao().getAccounts(); + } + public static class AccountAlreadyExistsException extends IllegalStateException { public AccountAlreadyExistsException(BareJid address) { super(String.format("The account %s has already been setup", address)); diff --git a/app/src/main/java/im/conversations/android/repository/ChatRepository.java b/app/src/main/java/im/conversations/android/repository/ChatRepository.java new file mode 100644 index 000000000..83d668a54 --- /dev/null +++ b/app/src/main/java/im/conversations/android/repository/ChatRepository.java @@ -0,0 +1,16 @@ +package im.conversations.android.repository; + +import android.content.Context; +import androidx.lifecycle.LiveData; +import java.util.List; + +public class ChatRepository extends AbstractRepository { + + public ChatRepository(Context context) { + super(context); + } + + public LiveData> getGroups() { + return this.database.chatDao().getGroups(); + } +} diff --git a/app/src/main/java/im/conversations/android/ui/activity/MainActivity.java b/app/src/main/java/im/conversations/android/ui/activity/MainActivity.java index 7329d533d..6b7a58b4d 100644 --- a/app/src/main/java/im/conversations/android/ui/activity/MainActivity.java +++ b/app/src/main/java/im/conversations/android/ui/activity/MainActivity.java @@ -2,7 +2,11 @@ package im.conversations.android.ui.activity; import android.os.Bundle; import androidx.appcompat.app.AppCompatActivity; +import androidx.databinding.DataBindingUtil; +import im.conversations.android.R; +import im.conversations.android.databinding.ActivityMainBinding; import im.conversations.android.service.ForegroundService; +import im.conversations.android.ui.Activities; public class MainActivity extends AppCompatActivity { @@ -10,5 +14,8 @@ public class MainActivity extends AppCompatActivity { protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); ForegroundService.start(this); + final ActivityMainBinding binding = + DataBindingUtil.setContentView(this, R.layout.activity_main); + Activities.setStatusAndNavigationBarColors(this, binding.getRoot()); } } diff --git a/app/src/main/java/im/conversations/android/ui/fragment/main/OverviewFragment.java b/app/src/main/java/im/conversations/android/ui/fragment/main/OverviewFragment.java new file mode 100644 index 000000000..aaf281fbd --- /dev/null +++ b/app/src/main/java/im/conversations/android/ui/fragment/main/OverviewFragment.java @@ -0,0 +1,102 @@ +package im.conversations.android.ui.fragment.main; + +import android.content.Intent; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import androidx.annotation.NonNull; +import androidx.databinding.DataBindingUtil; +import androidx.fragment.app.Fragment; +import androidx.lifecycle.ViewModelProvider; +import com.google.android.material.elevation.SurfaceColors; +import com.google.android.material.search.SearchView; +import im.conversations.android.R; +import im.conversations.android.database.model.AccountIdentifier; +import im.conversations.android.databinding.FragmentOverviewBinding; +import im.conversations.android.ui.activity.SetupActivity; +import im.conversations.android.ui.model.OverviewViewModel; +import java.util.List; + +public class OverviewFragment extends Fragment { + + private FragmentOverviewBinding binding; + + private OverviewViewModel overviewViewModel; + + @Override + public View onCreateView( + @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + super.onCreateView(inflater, container, savedInstanceState); + this.binding = + DataBindingUtil.inflate(inflater, R.layout.fragment_overview, container, false); + final ViewModelProvider viewModelProvider = + new ViewModelProvider(this, getDefaultViewModelProviderFactory()); + this.overviewViewModel = viewModelProvider.get(OverviewViewModel.class); + binding.setLifecycleOwner(getViewLifecycleOwner()); + binding.searchBar.setNavigationOnClickListener( + view -> { + binding.drawerLayout.open(); + }); + binding.searchView.addTransitionListener( + (searchView, previousState, newState) -> { + final var activity = requireActivity(); + final var window = activity.getWindow(); + if (newState == SearchView.TransitionState.SHOWN) { + window.setStatusBarColor(SurfaceColors.SURFACE_4.getColor(activity)); + } else if (newState == SearchView.TransitionState.SHOWING + || newState == SearchView.TransitionState.HIDING) { + window.setStatusBarColor(SurfaceColors.SURFACE_1.getColor(activity)); + } else { + window.setStatusBarColor(SurfaceColors.SURFACE_0.getColor(activity)); + } + }); + binding.navigationView.setNavigationItemSelectedListener( + item -> { + if (item.getItemId() == R.id.add_account) { + startActivity(new Intent(requireContext(), SetupActivity.class)); + } + return true; + }); + binding.navigationView.setCheckedItem(R.id.chats); + this.overviewViewModel + .getAccounts() + .observe(getViewLifecycleOwner(), this::onAccountsUpdated); + this.overviewViewModel.getGroups().observe(getViewLifecycleOwner(), this::onGroupsUpdated); + return binding.getRoot(); + } + + private void onGroupsUpdated(final List groups) { + final var menu = this.binding.navigationView.getMenu(); + final var menuItemSpaces = menu.findItem(R.id.spaces); + if (groups.isEmpty()) { + menuItemSpaces.setVisible(false); + return; + } + menuItemSpaces.setVisible(true); + final var subMenu = menuItemSpaces.getSubMenu(); + subMenu.clear(); + for (final String group : groups) { + final var menuItemSpace = subMenu.add(group); + menuItemSpace.setCheckable(true); + menuItemSpace.setIcon(R.drawable.ic_workspaces_24dp); + } + } + + private void onAccountsUpdated(List accounts) { + final var menu = this.binding.navigationView.getMenu(); + final var menuItemAccounts = menu.findItem(R.id.accounts); + if (accounts.size() <= 1) { + menuItemAccounts.setVisible(false); + return; + } + menuItemAccounts.setVisible(true); + final var subMenu = menuItemAccounts.getSubMenu(); + subMenu.clear(); + for (final AccountIdentifier account : accounts) { + final var menuItemAccount = subMenu.add(account.address); + menuItemAccount.setCheckable(true); + menuItemAccount.setIcon(R.drawable.ic_person_24dp); + } + } +} diff --git a/app/src/main/java/im/conversations/android/ui/model/OverviewViewModel.java b/app/src/main/java/im/conversations/android/ui/model/OverviewViewModel.java new file mode 100644 index 000000000..97ee35059 --- /dev/null +++ b/app/src/main/java/im/conversations/android/ui/model/OverviewViewModel.java @@ -0,0 +1,31 @@ +package im.conversations.android.ui.model; + +import android.app.Application; +import androidx.annotation.NonNull; +import androidx.lifecycle.AndroidViewModel; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.Transformations; +import im.conversations.android.database.model.AccountIdentifier; +import im.conversations.android.repository.AccountRepository; +import im.conversations.android.repository.ChatRepository; +import java.util.List; + +public class OverviewViewModel extends AndroidViewModel { + + private final AccountRepository accountRepository; + private final ChatRepository chatRepository; + + public OverviewViewModel(@NonNull Application application) { + super(application); + this.accountRepository = new AccountRepository(application); + this.chatRepository = new ChatRepository(application); + } + + public LiveData> getAccounts() { + return Transformations.distinctUntilChanged(this.accountRepository.getAccounts()); + } + + public LiveData> getGroups() { + return Transformations.distinctUntilChanged(this.chatRepository.getGroups()); + } +} diff --git a/app/src/main/java/im/conversations/android/util/ConsistentColorGeneration.java b/app/src/main/java/im/conversations/android/util/ConsistentColorGeneration.java new file mode 100644 index 000000000..16965bf05 --- /dev/null +++ b/app/src/main/java/im/conversations/android/util/ConsistentColorGeneration.java @@ -0,0 +1,66 @@ +/* + * Copyright 2019-2021 Daniel Gultsch + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.conversations.android.util; + +import android.content.Context; +import androidx.annotation.ColorInt; +import androidx.annotation.IntRange; +import com.google.android.material.color.MaterialColors; +import com.google.common.base.Charsets; +import com.google.common.hash.Hashing; +import org.hsluv.HUSLColorConverter; +import org.jxmpp.jid.BareJid; + +public final class ConsistentColorGeneration { + + private ConsistentColorGeneration() { + throw new IllegalStateException("This is a Utility class"); + } + + @SuppressWarnings("deprecation") + private static double angle(final String input) { + final byte[] digest = Hashing.sha1().hashString(input, Charsets.UTF_8).asBytes(); + final int angle = ((int) (digest[0]) & 0xff) + ((int) (digest[1]) & 0xff) * 256; + return angle / 65536.0; + } + + @ColorInt + public static int rgb(final String input) { + final double[] rgb = + HUSLColorConverter.hsluvToRgb(new double[] {angle(input) * 360, 100, 50}); + return rgb( + (int) Math.round(rgb[0] * 255), + (int) Math.round(rgb[1] * 255), + (int) Math.round(rgb[2] * 255)); + } + + @ColorInt + public static int harmonized(final Context context, final String input) { + return MaterialColors.harmonizeWithPrimary(context, rgb(input)); + } + + public static int harmonized(final Context context, final BareJid jid) { + return harmonized(context, jid.toString()); + } + + @ColorInt + private static int rgb( + @IntRange(from = 0, to = 255) int red, + @IntRange(from = 0, to = 255) int green, + @IntRange(from = 0, to = 255) int blue) { + return 0xff000000 | (red << 16) | (green << 8) | blue; + } +} diff --git a/app/src/main/java/im/conversations/android/xmpp/XmppConnection.java b/app/src/main/java/im/conversations/android/xmpp/XmppConnection.java index ac5847bfc..1a2552904 100644 --- a/app/src/main/java/im/conversations/android/xmpp/XmppConnection.java +++ b/app/src/main/java/im/conversations/android/xmpp/XmppConnection.java @@ -2014,8 +2014,7 @@ public class XmppConnection implements Runnable { public boolean fromAccount(final Stanza stanza) { final Jid from = stanza.getFrom(); - // TODO null is valid too?! - return from != null && from.asBareJid().equals(connectionAddress.asBareJid()); + return from == null || from.asBareJid().equals(connectionAddress.asBareJid()); } public boolean toAccount(final Stanza stanza) { diff --git a/app/src/main/java/im/conversations/android/xmpp/model/roster/Group.java b/app/src/main/java/im/conversations/android/xmpp/model/roster/Group.java index ec0d3a717..9f36efae7 100644 --- a/app/src/main/java/im/conversations/android/xmpp/model/roster/Group.java +++ b/app/src/main/java/im/conversations/android/xmpp/model/roster/Group.java @@ -1,7 +1,9 @@ package im.conversations.android.xmpp.model.roster; +import im.conversations.android.annotation.XmlElement; import im.conversations.android.xmpp.model.Extension; +@XmlElement public class Group extends Extension { public Group() { diff --git a/app/src/main/java/im/conversations/android/xmpp/processor/IqProcessor.java b/app/src/main/java/im/conversations/android/xmpp/processor/IqProcessor.java index d8439a1dd..16b177bd6 100644 --- a/app/src/main/java/im/conversations/android/xmpp/processor/IqProcessor.java +++ b/app/src/main/java/im/conversations/android/xmpp/processor/IqProcessor.java @@ -69,6 +69,7 @@ public class IqProcessor extends XmppConnection.Delegate implements Consumer } final var extensionIds = packet.getExtensionIds(); + LOGGER.info("Received from {} type {}", packet.getFrom(), type); LOGGER.info("Could not handle {}. Sending feature-not-implemented", extensionIds); connection.sendErrorFor(packet, new Condition.FeatureNotImplemented()); } diff --git a/app/src/main/res/drawable/ic_chat_24dp.xml b/app/src/main/res/drawable/ic_chat_24dp.xml new file mode 100644 index 000000000..649809399 --- /dev/null +++ b/app/src/main/res/drawable/ic_chat_24dp.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/drawable/ic_manage_accounts_24dp.xml b/app/src/main/res/drawable/ic_manage_accounts_24dp.xml new file mode 100644 index 000000000..c9a63905a --- /dev/null +++ b/app/src/main/res/drawable/ic_manage_accounts_24dp.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_menu_24dp.xml b/app/src/main/res/drawable/ic_menu_24dp.xml new file mode 100644 index 000000000..ada350030 --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_24dp.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_person_24dp.xml b/app/src/main/res/drawable/ic_person_24dp.xml new file mode 100644 index 000000000..07eeb5ad8 --- /dev/null +++ b/app/src/main/res/drawable/ic_person_24dp.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_person_add_24dp.xml b/app/src/main/res/drawable/ic_person_add_24dp.xml new file mode 100644 index 000000000..111621988 --- /dev/null +++ b/app/src/main/res/drawable/ic_person_add_24dp.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_workspaces_24dp.xml b/app/src/main/res/drawable/ic_workspaces_24dp.xml new file mode 100644 index 000000000..762adf02e --- /dev/null +++ b/app/src/main/res/drawable/ic_workspaces_24dp.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 000000000..5f3d4bd06 --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,32 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_overview.xml b/app/src/main/res/layout/fragment_overview.xml new file mode 100644 index 000000000..0a963ce9b --- /dev/null +++ b/app/src/main/res/layout/fragment_overview.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_navigation_header.xml b/app/src/main/res/layout/item_navigation_header.xml new file mode 100644 index 000000000..6b7480c97 --- /dev/null +++ b/app/src/main/res/layout/item_navigation_header.xml @@ -0,0 +1,16 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/fragment_overview.xml b/app/src/main/res/menu/fragment_overview.xml new file mode 100644 index 000000000..34d97bacd --- /dev/null +++ b/app/src/main/res/menu/fragment_overview.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/main_navigation.xml b/app/src/main/res/navigation/main_navigation.xml new file mode 100644 index 000000000..e2d4e5c99 --- /dev/null +++ b/app/src/main/res/navigation/main_navigation.xml @@ -0,0 +1,28 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 27b3498ef..3f7d63d1c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1011,5 +1011,10 @@ Welcome No account? Register Account settings + Search in chats + Start chat + All chats + Accounts + Spaces