add stub MainActivity
This commit is contained in:
parent
c105c3420e
commit
87e33a779f
|
@ -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'
|
||||
|
||||
|
|
|
@ -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')"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Account> getEnabledAccount(long id);
|
||||
|
||||
@Query("SELECT id,address FROM account")
|
||||
LiveData<List<AccountIdentifier>> getAccounts();
|
||||
|
||||
@Query("SELECT hostname,port,directTls FROM account WHERE id=:id AND hostname != null")
|
||||
Connection getConnectionSettings(long id);
|
||||
|
||||
|
|
|
@ -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<List<String>> getGroups();
|
||||
}
|
||||
|
|
|
@ -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<Item> rosterItems) {
|
||||
LOGGER.info("items: " + rosterItems);
|
||||
clear(account.id);
|
||||
for (final Item item : rosterItems) {
|
||||
final long id = insert(RosterItemEntity.of(account.id, item));
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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<List<AccountIdentifier>> 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));
|
||||
|
|
|
@ -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<List<String>> getGroups() {
|
||||
return this.database.chatDao().getGroups();
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<String> 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<AccountIdentifier> 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<List<AccountIdentifier>> getAccounts() {
|
||||
return Transformations.distinctUntilChanged(this.accountRepository.getAccounts());
|
||||
}
|
||||
|
||||
public LiveData<List<String>> getGroups() {
|
||||
return Transformations.distinctUntilChanged(this.chatRepository.getGroups());
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -69,6 +69,7 @@ public class IqProcessor extends XmppConnection.Delegate implements Consumer<Iq>
|
|||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
|
11
app/src/main/res/drawable/ic_chat_24dp.xml
Normal file
11
app/src/main/res/drawable/ic_chat_24dp.xml
Normal file
|
@ -0,0 +1,11 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:autoMirrored="true"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M20,2L4,2c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM6,9h12v2L6,11L6,9zM14,14L6,14v-2h8v2zM18,8L6,8L6,6h12v2z" />
|
||||
</vector>
|
16
app/src/main/res/drawable/ic_manage_accounts_24dp.xml
Normal file
16
app/src/main/res/drawable/ic_manage_accounts_24dp.xml
Normal file
|
@ -0,0 +1,16 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M10,8m-4,0a4,4 0,1 1,8 0a4,4 0,1 1,-8 0" />
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M10.67,13.02C10.45,13.01 10.23,13 10,13c-2.42,0 -4.68,0.67 -6.61,1.82C2.51,15.34 2,16.32 2,17.35V20h9.26C10.47,18.87 10,17.49 10,16C10,14.93 10.25,13.93 10.67,13.02z" />
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M20.75,16c0,-0.22 -0.03,-0.42 -0.06,-0.63l1.14,-1.01l-1,-1.73l-1.45,0.49c-0.32,-0.27 -0.68,-0.48 -1.08,-0.63L18,11h-2l-0.3,1.49c-0.4,0.15 -0.76,0.36 -1.08,0.63l-1.45,-0.49l-1,1.73l1.14,1.01c-0.03,0.21 -0.06,0.41 -0.06,0.63s0.03,0.42 0.06,0.63l-1.14,1.01l1,1.73l1.45,-0.49c0.32,0.27 0.68,0.48 1.08,0.63L16,21h2l0.3,-1.49c0.4,-0.15 0.76,-0.36 1.08,-0.63l1.45,0.49l1,-1.73l-1.14,-1.01C20.72,16.42 20.75,16.22 20.75,16zM17,18c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2s2,0.9 2,2S18.1,18 17,18z" />
|
||||
</vector>
|
10
app/src/main/res/drawable/ic_menu_24dp.xml
Normal file
10
app/src/main/res/drawable/ic_menu_24dp.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M3,18h18v-2L3,16v2zM3,13h18v-2L3,11v2zM3,6v2h18L21,6L3,6z" />
|
||||
</vector>
|
10
app/src/main/res/drawable/ic_person_24dp.xml
Normal file
10
app/src/main/res/drawable/ic_person_24dp.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M12,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z" />
|
||||
</vector>
|
10
app/src/main/res/drawable/ic_person_add_24dp.xml
Normal file
10
app/src/main/res/drawable/ic_person_add_24dp.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M13,8c0,-2.21 -1.79,-4 -4,-4S5,5.79 5,8s1.79,4 4,4S13,10.21 13,8zM15,10v2h3v3h2v-3h3v-2h-3V7h-2v3H15zM1,18v2h16v-2c0,-2.66 -5.33,-4 -8,-4S1,15.34 1,18z" />
|
||||
</vector>
|
10
app/src/main/res/drawable/ic_workspaces_24dp.xml
Normal file
10
app/src/main/res/drawable/ic_workspaces_24dp.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M6,13c-2.2,0 -4,1.8 -4,4s1.8,4 4,4s4,-1.8 4,-4S8.2,13 6,13zM12,3C9.8,3 8,4.8 8,7s1.8,4 4,4s4,-1.8 4,-4S14.2,3 12,3zM18,13c-2.2,0 -4,1.8 -4,4s1.8,4 4,4s4,-1.8 4,-4S20.2,13 18,13z" />
|
||||
</vector>
|
32
app/src/main/res/layout/activity_main.xml
Normal file
32
app/src/main/res/layout/activity_main.xml
Normal file
|
@ -0,0 +1,32 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright 2019 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.
|
||||
-->
|
||||
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/nav_host_fragment"
|
||||
android:name="androidx.navigation.fragment.NavHostFragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:defaultNavHost="true"
|
||||
app:navGraph="@navigation/main_navigation" />
|
||||
|
||||
</FrameLayout>
|
||||
</layout>
|
64
app/src/main/res/layout/fragment_overview.xml
Normal file
64
app/src/main/res/layout/fragment_overview.xml
Normal file
|
@ -0,0 +1,64 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<androidx.drawerlayout.widget.DrawerLayout
|
||||
android:id="@+id/drawerLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:openDrawer="start">
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/app_bar_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:elevation="0dp"
|
||||
app:liftOnScroll="false">
|
||||
<com.google.android.material.search.SearchBar
|
||||
android:id="@+id/search_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/search_in_chats"
|
||||
app:navigationIcon="@drawable/ic_menu_24dp"/>
|
||||
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<com.google.android.material.search.SearchView
|
||||
android:id="@+id/search_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:hint="@string/search_in_chats"
|
||||
app:layout_anchor="@id/search_bar"
|
||||
app:useDrawerArrowDrawable="true">
|
||||
<!-- Search suggestions/results go here (ScrollView, RecyclerView, etc.). -->
|
||||
</com.google.android.material.search.SearchView>
|
||||
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||
android:id="@+id/extended_fab"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:text="@string/start_chat"
|
||||
app:icon="@drawable/ic_chat_24dp"/>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
|
||||
<!-- Screen content -->
|
||||
<!-- Use app:layout_behavior="@string/appbar_scrolling_view_behavior" to fit below top app bar -->
|
||||
|
||||
<com.google.android.material.navigation.NavigationView
|
||||
android:id="@+id/navigation_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="start"
|
||||
app:menu="@menu/fragment_overview"
|
||||
app:headerLayout="@layout/item_navigation_header"/>
|
||||
|
||||
</androidx.drawerlayout.widget.DrawerLayout>
|
||||
</layout>
|
16
app/src/main/res/layout/item_navigation_header.xml
Normal file
16
app/src/main/res/layout/item_navigation_header.xml
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginVertical="24dp"
|
||||
android:layout_marginHorizontal="24dp"
|
||||
android:textAppearance="?attr/textAppearanceHeadlineSmall"
|
||||
android:textColor="?attr/colorOnSurface"
|
||||
android:text="@string/app_name" />
|
||||
|
||||
</LinearLayout>
|
35
app/src/main/res/menu/fragment_overview.xml
Normal file
35
app/src/main/res/menu/fragment_overview.xml
Normal file
|
@ -0,0 +1,35 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:id="@+id/chats"
|
||||
android:checkable="true"
|
||||
android:icon="@drawable/ic_chat_24dp"
|
||||
android:title="@string/all_chats" />
|
||||
<item
|
||||
android:id="@+id/accounts"
|
||||
android:title="@string/accounts">
|
||||
<menu />
|
||||
</item>
|
||||
<item
|
||||
android:id="@+id/spaces"
|
||||
android:title="@string/spaces">
|
||||
<menu/>
|
||||
</item>
|
||||
<group android:id="@+id/navigation">
|
||||
<item
|
||||
android:id="@+id/add_account"
|
||||
android:checkable="true"
|
||||
android:icon="@drawable/ic_person_add_24dp"
|
||||
android:title="@string/action_add_account" />
|
||||
<item
|
||||
android:id="@+id/manage_accounts"
|
||||
android:checkable="true"
|
||||
android:icon="@drawable/ic_manage_accounts_24dp"
|
||||
android:title="@string/title_activity_manage_accounts" />
|
||||
<item
|
||||
android:id="@+id/settings"
|
||||
android:checkable="true"
|
||||
android:icon="@drawable/ic_settings_24dp"
|
||||
android:title="@string/action_settings" />
|
||||
</group>
|
||||
</menu>
|
28
app/src/main/res/navigation/main_navigation.xml
Normal file
28
app/src/main/res/navigation/main_navigation.xml
Normal file
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright 2019 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.
|
||||
-->
|
||||
|
||||
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/main_navigation"
|
||||
app:startDestination="@+id/overview">
|
||||
|
||||
<fragment
|
||||
android:id="@+id/overview"
|
||||
android:name="im.conversations.android.ui.fragment.main.OverviewFragment"
|
||||
tools:layout="@layout/fragment_overview" />
|
||||
|
||||
|
||||
</navigation>
|
|
@ -1011,5 +1011,10 @@
|
|||
<string name="welcome">Welcome</string>
|
||||
<string name="no_account_register">No account? Register</string>
|
||||
<string name="account_settings">Account settings</string>
|
||||
<string name="search_in_chats">Search in chats</string>
|
||||
<string name="start_chat">Start chat</string>
|
||||
<string name="all_chats">All chats</string>
|
||||
<string name="accounts">Accounts</string>
|
||||
<string name="spaces">Spaces</string>
|
||||
|
||||
</resources>
|
||||
|
|
Loading…
Reference in a new issue