diff --git a/app/build.gradle b/app/build.gradle index dc2a506526a2b67124ded6d192678f11284e7ee6..ab27eff0253103e8975bdc150ed7e9904b14a297 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -82,6 +82,9 @@ android { versionCode versionBuild versionName rawVersionName + " Build #" + versionBuild } + dataBinding { + enabled = true + } buildTypes { release { minifyEnabled true diff --git a/app/src/main/java/de/kuschku/libquassel/QuasselClient.java b/app/src/main/java/de/kuschku/libquassel/QuasselClient.java index 4bb6059b72be5ed2dd5ae0180d35bd7d3eed0a5a..318c46da0893d149b5e03028923178444e60d776 100644 --- a/app/src/main/java/de/kuschku/libquassel/QuasselClient.java +++ b/app/src/main/java/de/kuschku/libquassel/QuasselClient.java @@ -28,6 +28,7 @@ import de.kuschku.libquassel.client.ClientData; import de.kuschku.libquassel.localtypes.backlogstorage.BacklogStorage; import de.kuschku.libquassel.ssl.CertificateManager; import de.kuschku.util.accounts.ServerAddress; +import de.kuschku.util.buffermetadata.BufferMetaDataManager; import static de.kuschku.util.AndroidAssert.assertNotNull; @@ -44,7 +45,7 @@ public class QuasselClient { private final ClientData data; public CoreConnection connection; - public QuasselClient(@NonNull BusProvider provider, @NonNull ClientData data, @NonNull CertificateManager certificateManager, @NonNull BacklogStorage backlogStorage) { + public QuasselClient(@NonNull BusProvider provider, @NonNull ClientData data, @NonNull CertificateManager certificateManager, @NonNull BacklogStorage backlogStorage, @NonNull BufferMetaDataManager metaDataManager, String coreId) { assertNotNull(provider); assertNotNull(data); assertNotNull(certificateManager); @@ -53,7 +54,7 @@ public class QuasselClient { this.provider = provider; this.data = data; this.certificateManager = certificateManager; - this.client = new Client(provider, backlogStorage); + this.client = new Client(provider, backlogStorage, metaDataManager, coreId); this.handler = new ProtocolHandler(provider, this.client); } diff --git a/app/src/main/java/de/kuschku/libquassel/client/Client.java b/app/src/main/java/de/kuschku/libquassel/client/Client.java index 21104c93b9470e5be9dd5e3d9bcc5a6bd60d2971..0bb973ed72f3a51fb5c3e167bd7c504377994801 100644 --- a/app/src/main/java/de/kuschku/libquassel/client/Client.java +++ b/app/src/main/java/de/kuschku/libquassel/client/Client.java @@ -64,6 +64,7 @@ import de.kuschku.libquassel.syncables.types.interfaces.QBufferViewManager; import de.kuschku.libquassel.syncables.types.interfaces.QIgnoreListManager; import de.kuschku.libquassel.syncables.types.interfaces.QNetwork; import de.kuschku.libquassel.syncables.types.interfaces.QNetworkConfig; +import de.kuschku.util.buffermetadata.BufferMetaDataManager; import static de.kuschku.util.AndroidAssert.assertNotNull; @@ -97,8 +98,11 @@ public class Client extends AClient { private CoreInfo coreInfo; private long latency; private ConnectionChangeEvent.Status connectionStatus; + private BufferMetaDataManager metaDataManager; + private String coreId; - public Client(@NonNull BusProvider provider, @NonNull BacklogStorage backlogStorage) { + public Client(@NonNull BusProvider provider, @NonNull BacklogStorage backlogStorage, @NonNull BufferMetaDataManager metaDataManager, String coreId) { + this.coreId = coreId; this.provider = provider; this.networkManager = new NetworkManager(this); this.bufferManager = new BufferManager(this); @@ -109,6 +113,7 @@ public class Client extends AClient { this.backlogManager.init("", provider, this); this.notificationManager = new NotificationManager(this); this.initialized = true; + this.metaDataManager = metaDataManager; } public QBufferViewManager bufferViewManager() { @@ -471,4 +476,12 @@ public class Client extends AClient { public BusProvider provider() { return provider; } + + public BufferMetaDataManager metaDataManager() { + return metaDataManager; + } + + public String coreId() { + return coreId; + } } diff --git a/app/src/main/java/de/kuschku/libquassel/localtypes/BacklogFilter.java b/app/src/main/java/de/kuschku/libquassel/localtypes/BacklogFilter.java index 83c60019cbe65f6866895fbaf9f2474b97c7e87f..9a2fce8c11c5f2538e781c65b8ff1c7a0f085120 100644 --- a/app/src/main/java/de/kuschku/libquassel/localtypes/BacklogFilter.java +++ b/app/src/main/java/de/kuschku/libquassel/localtypes/BacklogFilter.java @@ -59,6 +59,7 @@ public class BacklogFilter implements UICallback { this.unfiltered = unfiltered; this.filtered = filtered; this.bus.register(this); + setFiltersInternal(client.metaDataManager().hiddendata(client.coreId(), bufferId)); } @Override @@ -184,6 +185,11 @@ public class BacklogFilter implements UICallback { } public void setFilters(int filters) { + setFiltersInternal(filters); + client.metaDataManager().setHiddendata(client.coreId(), bufferId, filters); + } + + private void setFiltersInternal(int filters) { Set<Message.Type> removed = new HashSet<>(); for (Message.Type type : filteredTypes) { if ((filters & type.value) == 0) diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/types/impl/BacklogManager.java b/app/src/main/java/de/kuschku/libquassel/syncables/types/impl/BacklogManager.java index 74f910c850e381db04d093c196574ccca6605504..c52f6d53773cdefe4ab60559651d6ee02d04a9c9 100644 --- a/app/src/main/java/de/kuschku/libquassel/syncables/types/impl/BacklogManager.java +++ b/app/src/main/java/de/kuschku/libquassel/syncables/types/impl/BacklogManager.java @@ -52,7 +52,7 @@ public class BacklogManager extends ABacklogManager<BacklogManager> { private final Set<Integer> waiting = new HashSet<>(); private int waitingMax = 0; @IntRange(from = -1) - private int openBuffer; + private int openBuffer = -1; public BacklogManager(Client client, BacklogStorage storage) { this.client = client; @@ -156,14 +156,21 @@ public class BacklogManager extends ABacklogManager<BacklogManager> { return storage.getFiltered(id); } + @Override + public void setOpen(int openBuffer) { + assertNotNull(provider); + + this.openBuffer = openBuffer; + } + @Override public void open(int bufferId) { assertNotNull(provider); - openBuffer = bufferId; - if (bufferId != -1) + setOpen(bufferId); + if (bufferId != -1 && client.bufferSyncer() != null) client.bufferSyncer().requestMarkBufferAsRead(bufferId); - provider.event.postSticky(new BufferChangeEvent()); + provider.sendEvent(new BufferChangeEvent()); } @Override diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/types/interfaces/QBacklogManager.java b/app/src/main/java/de/kuschku/libquassel/syncables/types/interfaces/QBacklogManager.java index b5904855f8b3952dbc97e2cb9a765d3c9014cbcf..9ab862b607f35db80fc4e380b026c8fbab57cd1f 100644 --- a/app/src/main/java/de/kuschku/libquassel/syncables/types/interfaces/QBacklogManager.java +++ b/app/src/main/java/de/kuschku/libquassel/syncables/types/interfaces/QBacklogManager.java @@ -65,6 +65,7 @@ public interface QBacklogManager<T extends QSyncableObject<T>> extends QSyncable @NonNull ObservableComparableSortedList<Message> filtered(int id); + void setOpen(int bufferId); void open(int bufferId); int open(); diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/service/ClientBackgroundThread.java b/app/src/main/java/de/kuschku/quasseldroid_ng/service/ClientBackgroundThread.java index 7beba23f47c1f2ebab4531de9af826172a9f9f5e..82a79a6f91a253ca1c6e85857f0e4127dcba2cde 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/service/ClientBackgroundThread.java +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/service/ClientBackgroundThread.java @@ -39,7 +39,7 @@ import de.kuschku.quasseldroid_ng.ui.chat.Settings; import de.kuschku.util.CompatibilityUtils; import de.kuschku.util.accounts.Account; import de.kuschku.util.accounts.AccountManager; -import de.kuschku.util.accounts.ServerAddress; +import de.kuschku.util.buffermetadata.SQLiteBufferMetaDataManager; import de.kuschku.util.certificates.SQLiteCertificateManager; public class ClientBackgroundThread implements Runnable { @@ -57,14 +57,16 @@ public class ClientBackgroundThread implements Runnable { private final Settings settings; private final AccountManager manager; - public ClientBackgroundThread(@NonNull BusProvider provider, @NonNull ServerAddress address, @NonNull Context context) { + public ClientBackgroundThread(@NonNull BusProvider provider, @NonNull Account account, @NonNull Context context) { this.client = new QuasselClient( provider, CLIENT_DATA, new SQLiteCertificateManager(context), - new MemoryBacklogStorage() + new MemoryBacklogStorage(), + new SQLiteBufferMetaDataManager(context), + account.id.toString() ); - this.client.connect(address); + this.client.connect(account.toAddress()); this.client.provider.event.registerSticky(this); settings = new Settings(context); diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/service/QuasselService.java b/app/src/main/java/de/kuschku/quasseldroid_ng/service/QuasselService.java index 1ba3d35b23bd4fa0e95328950b3574d05da912f6..b4494939cb74f6f3b1c04fa31a38a36b299ef19f 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/service/QuasselService.java +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/service/QuasselService.java @@ -32,7 +32,7 @@ import java.util.HashSet; import java.util.Set; import de.kuschku.libquassel.BusProvider; -import de.kuschku.util.accounts.ServerAddress; +import de.kuschku.util.accounts.Account; import de.kuschku.util.backports.Consumer; public class QuasselService extends Service { @@ -55,8 +55,8 @@ public class QuasselService extends Service { } public class LocalBinder extends Binder { - public void startBackgroundThread(@NonNull BusProvider provider, @NonNull ServerAddress address) { - bgThread = new ClientBackgroundThread(provider, address, QuasselService.this); + public void startBackgroundThread(@NonNull BusProvider provider, @NonNull Account account) { + bgThread = new ClientBackgroundThread(provider, account, QuasselService.this); new Thread(bgThread).start(); notify(bgThread); } diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/MainActivity.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/MainActivity.java index a423cbd9399d0b487b07bc3deb5091de7b631a20..7e425be5dbcfd06e00e7de965560c7e728969fe3 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/MainActivity.java +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/MainActivity.java @@ -48,14 +48,21 @@ import java.util.List; import butterknife.Bind; import butterknife.ButterKnife; import de.kuschku.libquassel.client.Client; +import de.kuschku.libquassel.events.BufferChangeEvent; import de.kuschku.libquassel.events.ConnectionChangeEvent; import de.kuschku.libquassel.events.GeneralErrorEvent; import de.kuschku.libquassel.events.LoginRequireEvent; import de.kuschku.libquassel.events.UnknownCertificateEvent; import de.kuschku.libquassel.localtypes.BacklogFilter; +import de.kuschku.libquassel.localtypes.buffers.Buffer; +import de.kuschku.libquassel.localtypes.buffers.ChannelBuffer; +import de.kuschku.libquassel.localtypes.buffers.QueryBuffer; import de.kuschku.libquassel.message.Message; +import de.kuschku.libquassel.syncables.types.interfaces.QBacklogManager; import de.kuschku.libquassel.syncables.types.interfaces.QBufferViewConfig; import de.kuschku.libquassel.syncables.types.interfaces.QBufferViewManager; +import de.kuschku.libquassel.syncables.types.interfaces.QIrcChannel; +import de.kuschku.libquassel.syncables.types.interfaces.QIrcUser; import de.kuschku.quasseldroid_ng.R; import de.kuschku.quasseldroid_ng.service.ClientBackgroundThread; import de.kuschku.quasseldroid_ng.ui.chat.drawer.BufferItem; @@ -107,15 +114,20 @@ public class MainActivity extends BoundActivity { /** * This object encapsulates the current status of the activity – opened bufferview, for example */ - Status status = new Status(); - BufferViewConfigItem currentConfig; + private Status status = new Status(); + + private BufferViewConfigItem currentConfig; + private AccountManager manager; + private ToolbarWrapper toolbarWrapper; + @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); + toolbarWrapper = new ToolbarWrapper(toolbar); setSupportActionBar(toolbar); layoutHelper = ActivityImplFactory.of(getResources().getBoolean(R.bool.isTablet), this); accountHeader = buildAccountHeader(); @@ -134,19 +146,24 @@ public class MainActivity extends BoundActivity { replaceFragment(new LoadingFragment()); + if (savedInstanceState != null) + status.onRestoreInstanceState(savedInstanceState); + manager = new AccountManager(this); } @Override protected void onPause() { - if (context.client() != null) - context.client().backlogManager().open(-1); super.onPause(); + if (context.client() != null) + context.client().backlogManager().setOpen(-1); } @Override protected void onResume() { super.onResume(); + if (context.client() != null) + context.client().backlogManager().open(status.bufferId); } private void replaceFragment(Fragment fragment) { @@ -218,6 +235,8 @@ public class MainActivity extends BoundActivity { if (status == ConnectionChangeEvent.Status.CONNECTED) { replaceFragment(new ChatFragment()); updateBufferViewConfigs(); + context.client().backlogManager().open(this.status.bufferId); + accountHeader.setActiveProfile(this.status.bufferViewConfigId, true); } else if (status == ConnectionChangeEvent.Status.DISCONNECTED) { Toast.makeText(getApplication(), context.themeUtil().translations.statusDisconnected, Toast.LENGTH_LONG).show(); reauth(); @@ -228,6 +247,43 @@ public class MainActivity extends BoundActivity { Toast.makeText(getApplication(), event.exception.getClass().getSimpleName() + ": " + event.debugInfo, Toast.LENGTH_LONG).show(); } + public void onEventMainThread(BufferChangeEvent event) { + Client client = context.client(); + if (client != null) { + QBacklogManager<? extends QBacklogManager> backlogManager = client.backlogManager(); + int id = backlogManager.open(); + status.bufferId = id; + updateBuffer(id); + } + } + + private void updateBuffer(int id) { + Client client = context.client(); + if (client != null) { + Buffer buffer = client.bufferManager().buffer(id); + if (buffer != null) { + toolbarWrapper.setTitle(buffer.getName()); + if (buffer instanceof QueryBuffer) { + QIrcUser user = ((QueryBuffer) buffer).getUser(); + if (user == null) { + toolbarWrapper.setSubtitle(null); + } else { + toolbarWrapper.setSubtitle(user.hostmask() + " | " + user.realName()); + } + } else if (buffer instanceof ChannelBuffer) { + QIrcChannel channel = ((ChannelBuffer) buffer).getChannel(); + if (channel == null) { + toolbarWrapper.setSubtitle(null); + } else { + toolbarWrapper.setSubtitle(channel.topic()); + } + } else { + toolbarWrapper.setSubtitle(null); + } + } + } + } + private void selectBufferViewConfig(@IntRange(from = -1) int bufferViewConfigId) { assertNotNull(drawerLeft); assertNotNull(accountHeader); @@ -275,7 +331,9 @@ public class MainActivity extends BoundActivity { connectToServer(manager.account(context.settings().lastAccount.get())); else { if (context.client() != null) { + context.client().backlogManager().init("", context.provider(), context.client()); context.client().backlogManager().open(status.bufferId); + updateBuffer(context.client().backlogManager().open()); accountHeader.setActiveProfile(status.bufferViewConfigId, true); } } @@ -338,9 +396,7 @@ public class MainActivity extends BoundActivity { new MaterialDialog.Builder(this) .content(context.themeUtil().translations.warningCertificate + "\n" + CertificateUtils.certificateToFingerprint(event.certificate, "")) .title("Unknown Certificate") - .onPositive((dialog, which) -> { - new SQLiteCertificateManager(this).addCertificate(event.certificate, event.address); - }) + .onPositive((dialog, which) -> new SQLiteCertificateManager(this).addCertificate(event.certificate, event.address)) .negativeColor(context.themeUtil().res.colorForeground) .positiveText("Yes") .negativeText("No") diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ToolbarWrapper.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ToolbarWrapper.java index 5ec6cac6d94730decdd981902749c22a6bd9b983..1ad1f2fdfb6523c34c3707091284a5994185bba8 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ToolbarWrapper.java +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ToolbarWrapper.java @@ -21,6 +21,7 @@ package de.kuschku.quasseldroid_ng.ui.chat; +import android.support.annotation.Nullable; import android.support.annotation.StringRes; import android.support.v7.widget.Toolbar; import android.view.View; @@ -43,7 +44,7 @@ public class ToolbarWrapper { title.setText(id); } - public void setTitle(CharSequence text) { + public void setTitle(@Nullable CharSequence text) { title.setText(text); } @@ -51,8 +52,9 @@ public class ToolbarWrapper { subtitle.setText(id); } - public void setSubtitle(CharSequence text) { + public void setSubtitle(@Nullable CharSequence text) { subtitle.setText(text); + subtitle.setVisibility(text == null ? View.GONE : View.VISIBLE); } public void setOnClickListener(View.OnClickListener listener) { diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/fragment/ChatFragment.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/fragment/ChatFragment.java index 54baafec773998d6cef7246c4f78cfc53533173d..1bff0eb3909d0cb8582d6a95a981b5f4f254b12f 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/fragment/ChatFragment.java +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/fragment/ChatFragment.java @@ -27,7 +27,6 @@ import android.support.v4.widget.SwipeRefreshLayout; import android.support.v7.widget.DefaultItemAnimator; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; -import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -42,6 +41,7 @@ import de.kuschku.libquassel.events.BufferChangeEvent; import de.kuschku.libquassel.message.Message; import de.kuschku.libquassel.syncables.types.interfaces.QBacklogManager; import de.kuschku.quasseldroid_ng.R; +import de.kuschku.quasseldroid_ng.service.ClientBackgroundThread; import de.kuschku.quasseldroid_ng.ui.chat.chatview.MessageAdapter; import de.kuschku.quasseldroid_ng.ui.chat.util.SlidingPanelHandler; import de.kuschku.util.observables.AutoScroller; @@ -104,15 +104,6 @@ public class ChatFragment extends BoundFragment { swipeView.setEnabled(id != -1); // Load markerline - if (client.bufferSyncer() != null) { - int markerLine = client.bufferSyncer().markerLine(id); - for (int i = 0; i < messageList.size(); i++) { - if (messageList.get(i).messageId == markerLine) { - messages.scrollToPosition(i); - break; - } - } - } } else { swipeView.setEnabled(false); } @@ -125,20 +116,12 @@ public class ChatFragment extends BoundFragment { } private void setMarkerline() { - Client client = context.client(); - if (client == null) return; - - int buffer = client.backlogManager().open(); - if (buffer == -1) return; - - int messageposition = layoutManager.findFirstCompletelyVisibleItemPosition(); - if (messageposition == -1) return; - - Message message = messageAdapter.getItem(messageposition); - if (message == null) return; + } - client.bufferSyncer().requestSetMarkerLine(buffer, message.messageId); - Log.d("DEBUG", "Store: " + message.messageId + ":" + messageposition); + @Override + protected void onConnectToThread(@Nullable ClientBackgroundThread thread) { + super.onConnectToThread(thread); + onEventMainThread(new BufferChangeEvent()); } public void onEventMainThread(BacklogReceivedEvent event) { diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/util/SlidingPanelHandler.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/util/SlidingPanelHandler.java index dfe024d16e15c77dc5129fcecac300065ca39bda..03c88e169fa455df6b4fde2be304f89cd1541b73 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/util/SlidingPanelHandler.java +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/util/SlidingPanelHandler.java @@ -22,6 +22,9 @@ package de.kuschku.quasseldroid_ng.ui.chat.util; import android.app.Activity; +import android.graphics.Color; +import android.support.annotation.ColorInt; +import android.support.annotation.FloatRange; import android.support.v7.widget.ActionMenuView; import android.support.v7.widget.AppCompatEditText; import android.support.v7.widget.AppCompatImageButton; @@ -149,12 +152,42 @@ public class SlidingPanelHandler { chatline.setSelection(selectionStart, selectionEnd); } + private int combineColors(@ColorInt int colora, @ColorInt int colorb, @FloatRange(from = 0.0, to = 1.0) float offset) { + float invOffset = 1 - offset; + + double alphaA = Math.pow(Color.alpha(colora), 2); + double alphaB = Math.pow(Color.alpha(colorb), 2); + + double redA = Math.pow(Color.red(colora), 2); + double redB = Math.pow(Color.red(colorb), 2); + + double greenA = Math.pow(Color.green(colora), 2); + double greenB = Math.pow(Color.green(colorb), 2); + + double blueA = Math.pow(Color.blue(colora), 2); + double blueB = Math.pow(Color.blue(colorb), 2); + + return Color.argb( + (int) Math.sqrt(alphaA * invOffset + alphaB * offset), + (int) Math.sqrt(redA * invOffset + redB * offset), + (int) Math.sqrt(greenA * invOffset + greenB * offset), + (int) Math.sqrt(blueA * invOffset + blueB * offset) + ); + } + private void bindListener() { slidingLayout.setAntiDragView(R.id.card_panel); slidingLayout.setPanelSlideListener(new SlidingUpPanelLayout.PanelSlideListener() { @Override public void onPanelSlide(View panel, float slideOffset) { + /* + slidingLayoutHistory.setBackgroundColor(combineColors( + context.themeUtil().res.colorBackgroundCard, + context.themeUtil().res.colorBackground, + slideOffset + )); + */ } @Override diff --git a/app/src/main/java/de/kuschku/util/buffermetadata/BufferMetaDataHelper.java b/app/src/main/java/de/kuschku/util/buffermetadata/BufferMetaDataHelper.java index f38b7a61dbac52eef813b849f8d3f9d99ad82c7a..bfd1e38bc9be547864b8c12ca17c3211dacb09ad 100644 --- a/app/src/main/java/de/kuschku/util/buffermetadata/BufferMetaDataHelper.java +++ b/app/src/main/java/de/kuschku/util/buffermetadata/BufferMetaDataHelper.java @@ -22,8 +22,10 @@ package de.kuschku.util.buffermetadata; import android.content.Context; +import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; +import android.database.sqlite.SQLiteStatement; import android.support.annotation.NonNull; public class BufferMetaDataHelper extends SQLiteOpenHelper { @@ -48,14 +50,29 @@ public class BufferMetaDataHelper extends SQLiteOpenHelper { private static final String STATEMENT_INSERT = String.format("INSERT OR IGNORE INTO %s(%s, %s, %s, %s) VALUES (?, ?, ?, ?)", TABLE_ACCOUNTS, KEY_CORE, KEY_BUFFER, KEY_HIDDEN, KEY_MARKERLINE); + + @NonNull + private static final String STATEMENT_UPDATE_HIDDEN = + String.format("UPDATE %s SET %s=? WHERE %s=? AND %s=?", + TABLE_ACCOUNTS, KEY_HIDDEN, KEY_CORE, KEY_BUFFER); + + @NonNull + private static final String STATEMENT_UPDATE_MARKERLINE = + String.format("UPDATE %s SET %s=? WHERE %s=? AND %s=?", + TABLE_ACCOUNTS, KEY_MARKERLINE, KEY_CORE, KEY_BUFFER); @NonNull - private static final String STATEMENT_DELETE = + private static final String STATEMENT_DELETE_BUFFER = String.format("DELETE FROM %s WHERE %s = ? AND %s = ?", TABLE_ACCOUNTS, KEY_CORE, KEY_BUFFER); + @NonNull + private static final String STATEMENT_DELETE_CORE = + String.format("DELETE FROM %s WHERE %s = ?", + TABLE_ACCOUNTS, KEY_CORE); + @NonNull private static final String SPECIFIER_FIND = - String.format("%s = ? AND %s = ?", KEY_CORE, KEY_BUFFER); + String.format("%s = ? ", KEY_CORE); public BufferMetaDataHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); @@ -78,27 +95,95 @@ public class BufferMetaDataHelper extends SQLiteOpenHelper { } - public void storeMarkerline(String coreid, int bufferid, int messageid) { + public boolean storeMarkerline(String coreid, int bufferid, int messageid) { + ensureExisting(coreid, bufferid); + + SQLiteDatabase db = this.getWritableDatabase(); + SQLiteStatement statement = db.compileStatement(STATEMENT_UPDATE_MARKERLINE); + statement.bindLong(1, messageid); + statement.bindString(2, coreid); + statement.bindLong(3, bufferid); + return statement.executeUpdateDelete() > 0; + } + + public boolean storeHiddenData(String coreid, int bufferid, int hiddendata) { + ensureExisting(coreid, bufferid); + SQLiteDatabase db = this.getWritableDatabase(); + SQLiteStatement statement = db.compileStatement(STATEMENT_UPDATE_HIDDEN); + statement.bindLong(1, hiddendata); + statement.bindString(2, coreid); + statement.bindLong(3, bufferid); + return statement.executeUpdateDelete() > 0; } - public void storeHiddenData(String coreid, int bufferid, int hiddendata) { + private boolean ensureExisting(String coreid, int bufferid) { + SQLiteDatabase db = this.getWritableDatabase(); + SQLiteStatement statement = db.compileStatement(STATEMENT_INSERT); + statement.bindString(1, coreid); + statement.bindLong(2, bufferid); + statement.bindLong(3, 0); + statement.bindLong(4, -1); + // executeInsert returns -1 if unsuccessful + return statement.executeInsert() != -1; + } + private Cursor cursorFindData(String coreid) { + SQLiteDatabase db = this.getReadableDatabase(); + return db.query( + // table name + TABLE_ACCOUNTS, + // column names + new String[]{KEY_CORE, KEY_BUFFER, KEY_HIDDEN, KEY_MARKERLINE}, + // where clause + SPECIFIER_FIND, + // binds for where clause + new String[]{coreid}, + null, + null, + null, + null + ); } public int markerLine(String coreid, int bufferid) { + Cursor cursor = cursorFindData(coreid); + if (cursor.moveToFirst()) { + do { + if (cursor.getInt(1) == bufferid) { + return cursor.getInt(3); + } + } while (cursor.moveToNext()); + } return -1; } public int hiddenData(String coreid, int bufferid) { - return -1; + Cursor cursor = cursorFindData(coreid); + if (cursor.moveToFirst()) { + do { + if (cursor.getInt(1) == bufferid) { + return cursor.getInt(2); + } + } while (cursor.moveToNext()); + } + return 0; } - public void deleteCore(String coreid) { - + public boolean deleteCore(String coreid) { + SQLiteDatabase db = this.getWritableDatabase(); + SQLiteStatement statement = db.compileStatement(STATEMENT_DELETE_CORE); + statement.bindString(1, coreid); + // executeUpdateDelete returns amount of modified rows + return statement.executeUpdateDelete() > 0; } - public void deleteBuffer(String coreid, int bufferid) { - + public boolean deleteBuffer(String coreid, int bufferid) { + SQLiteDatabase db = this.getWritableDatabase(); + SQLiteStatement statement = db.compileStatement(STATEMENT_DELETE_BUFFER); + statement.bindString(1, coreid); + statement.bindLong(2, bufferid); + // executeUpdateDelete returns amount of modified rows + return statement.executeUpdateDelete() > 0; } } diff --git a/app/src/main/java/de/kuschku/util/buffermetadata/BufferMetaDataManager.java b/app/src/main/java/de/kuschku/util/buffermetadata/BufferMetaDataManager.java new file mode 100644 index 0000000000000000000000000000000000000000..592c966c4c3e478aa4b183b0cbbef4e835a97dd3 --- /dev/null +++ b/app/src/main/java/de/kuschku/util/buffermetadata/BufferMetaDataManager.java @@ -0,0 +1,36 @@ +/* + * QuasselDroid - Quassel client for Android + * Copyright (C) 2016 Janne Koschinski + * Copyright (C) 2016 Ken Børge Viktil + * Copyright (C) 2016 Magnus Fjell + * Copyright (C) 2016 Martin Sandsmark <martin.sandsmark@kde.org> + * + * This program 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, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.util.buffermetadata; + +public interface BufferMetaDataManager { + int markerline(String coreid, int bufferid); + + int hiddendata(String coreid, int bufferid); + + void setMarkerline(String coreid, int bufferid, int markerline); + + void setHiddendata(String coreid, int bufferid, int hiddendata); + + void removeBuffer(String coreid, int bufferid); + + void removeCore(String coreid); +} diff --git a/app/src/main/java/de/kuschku/util/buffermetadata/SQLiteBufferMetaDataManager.java b/app/src/main/java/de/kuschku/util/buffermetadata/SQLiteBufferMetaDataManager.java new file mode 100644 index 0000000000000000000000000000000000000000..280d7aea203302563c72a954c7a903a750dae50c --- /dev/null +++ b/app/src/main/java/de/kuschku/util/buffermetadata/SQLiteBufferMetaDataManager.java @@ -0,0 +1,62 @@ +/* + * QuasselDroid - Quassel client for Android + * Copyright (C) 2016 Janne Koschinski + * Copyright (C) 2016 Ken Børge Viktil + * Copyright (C) 2016 Magnus Fjell + * Copyright (C) 2016 Martin Sandsmark <martin.sandsmark@kde.org> + * + * This program 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, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.util.buffermetadata; + +import android.content.Context; + +public class SQLiteBufferMetaDataManager implements BufferMetaDataManager { + private BufferMetaDataHelper helper; + + public SQLiteBufferMetaDataManager(Context context) { + helper = new BufferMetaDataHelper(context); + } + + @Override + public int markerline(String coreid, int bufferid) { + return helper.markerLine(coreid, bufferid); + } + + @Override + public int hiddendata(String coreid, int bufferid) { + return helper.hiddenData(coreid, bufferid); + } + + @Override + public void setMarkerline(String coreid, int bufferid, int markerline) { + helper.storeMarkerline(coreid, bufferid, markerline); + } + + @Override + public void setHiddendata(String coreid, int bufferid, int hiddendata) { + helper.storeHiddenData(coreid, bufferid, hiddendata); + } + + @Override + public void removeBuffer(String coreid, int bufferid) { + helper.deleteBuffer(coreid, bufferid); + } + + @Override + public void removeCore(String coreid) { + helper.deleteCore(coreid); + } +} diff --git a/app/src/main/java/de/kuschku/util/irc/format/IrcFormatDeserializer.java b/app/src/main/java/de/kuschku/util/irc/format/IrcFormatDeserializer.java index 1540be165821657486b759afe28992e038441310..eeb00f40bf790e1046db09889f7e97c027e9420b 100644 --- a/app/src/main/java/de/kuschku/util/irc/format/IrcFormatDeserializer.java +++ b/app/src/main/java/de/kuschku/util/irc/format/IrcFormatDeserializer.java @@ -34,7 +34,6 @@ import android.text.style.UnderlineSpan; import java.util.Arrays; import java.util.HashSet; import java.util.Set; -import java.util.Stack; import de.kuschku.quasseldroid_ng.ui.theme.AppContext; @@ -114,27 +113,58 @@ public class IrcFormatDeserializer { @NonNull public CharSequence formatString(@NonNull String str) { SpannableStringBuilder plainText = new SpannableStringBuilder(); - Stack<FormatDescription> stack = new Stack<>(); + FormatDescription bold = null; + FormatDescription italic = null; + FormatDescription underline = null; + FormatDescription color = null; boolean colorize = context.settings().mircColors.get(); // Iterating over every character for (int i = 0; i < str.length(); i++) { char character = str.charAt(i); switch (character) { - case CODE_BOLD: - case CODE_ITALIC: - case CODE_UNDERLINE: { + case CODE_BOLD: { if (!colorize) continue; // If there is an element on stack with the same code, close it - if (!stack.empty() && stack.peek().format.id() == character) { - stack.pop().apply(plainText, plainText.length()); + if (bold != null) { + bold.apply(plainText, plainText.length()); + bold = null; + // Otherwise create a new one + } else { + IrcFormat format = fromId(character); + assertNotNull(format); + bold = new FormatDescription(plainText.length(), format); + } + } + break; + case CODE_ITALIC: { + if (!colorize) continue; + // If there is an element on stack with the same code, close it + if (italic != null) { + italic.apply(plainText, plainText.length()); + italic = null; // Otherwise create a new one } else { IrcFormat format = fromId(character); assertNotNull(format); - stack.push(new FormatDescription(plainText.length(), format)); + italic = new FormatDescription(plainText.length(), format); + } + } + break; + case CODE_UNDERLINE: { + if (!colorize) continue; + + // If there is an element on stack with the same code, close it + if (underline != null) { + underline.apply(plainText, plainText.length()); + underline = null; + // Otherwise create a new one + } else { + IrcFormat format = fromId(character); + assertNotNull(format); + underline = new FormatDescription(plainText.length(), format); } } break; @@ -155,23 +185,23 @@ public class IrcFormatDeserializer { background = readNumber(str, foregroundEnd + 1, backgroundEnd); } // If previous element was also a color element, try to reuse background - if (!stack.empty() && stack.peek().format.id() == CODE_COLOR) { + if (color != null) { // Apply old format - FormatDescription oldFormat = stack.pop(); - oldFormat.apply(plainText, plainText.length()); + color.apply(plainText, plainText.length()); // Reuse old background, if possible if (background == -1) - background = ((ColorIrcFormat) oldFormat.format).background; + background = ((ColorIrcFormat) color.format).background; } // Add new format - stack.push(new FormatDescription(plainText.length(), new ColorIrcFormat(foreground, background))); + color = new FormatDescription(plainText.length(), new ColorIrcFormat(foreground, background)); // i points in front of the next character i = ((backgroundEnd == -1) ? foregroundEnd : backgroundEnd) - 1; // Otherwise assume this is a closing tag - } else if (!stack.empty() && stack.peek().format.id() == CODE_COLOR) { - stack.pop().apply(plainText, plainText.length()); + } else if (color != null) { + color.apply(plainText, plainText.length()); + color = null; } } break; @@ -179,10 +209,9 @@ public class IrcFormatDeserializer { if (!colorize) continue; // If we have a color tag before, apply it, and create a new one with swapped colors - if (!stack.empty() && stack.peek().format.id() == CODE_COLOR) { - FormatDescription format = stack.pop(); - format.apply(plainText, plainText.length()); - stack.push(new FormatDescription(plainText.length(), ((ColorIrcFormat) format.format).copySwapped())); + if (color != null) { + color.apply(plainText, plainText.length()); + color = new FormatDescription(plainText.length(), ((ColorIrcFormat) color.format).copySwapped()); } } break; @@ -190,8 +219,21 @@ public class IrcFormatDeserializer { if (!colorize) continue; // End all formatting tags - while (!stack.empty()) { - stack.pop().apply(plainText, plainText.length()); + if (bold != null) { + bold.apply(plainText, plainText.length()); + bold = null; + } + if (italic != null) { + italic.apply(plainText, plainText.length()); + italic = null; + } + if (underline != null) { + underline.apply(plainText, plainText.length()); + underline = null; + } + if (color != null) { + color.apply(plainText, plainText.length()); + color = null; } } break; @@ -203,8 +245,17 @@ public class IrcFormatDeserializer { } // End all formatting tags - while (!stack.empty()) { - stack.pop().apply(plainText, plainText.length()); + if (bold != null) { + bold.apply(plainText, plainText.length()); + } + if (italic != null) { + italic.apply(plainText, plainText.length()); + } + if (underline != null) { + underline.apply(plainText, plainText.length()); + } + if (color != null) { + color.apply(plainText, plainText.length()); } return plainText; } diff --git a/app/src/main/java/de/kuschku/util/servicebound/BoundActivity.java b/app/src/main/java/de/kuschku/util/servicebound/BoundActivity.java index 1a7f78341b6655241be13de1109c921cf97cd694..6cecaac28dc2217be538a75a61aa8ad139b516c2 100644 --- a/app/src/main/java/de/kuschku/util/servicebound/BoundActivity.java +++ b/app/src/main/java/de/kuschku/util/servicebound/BoundActivity.java @@ -79,7 +79,7 @@ public abstract class BoundActivity extends AppCompatActivity { protected void connectToServer(Account account) { BusProvider provider = new BusProvider(); - binder.startBackgroundThread(provider, account.toAddress()); + binder.startBackgroundThread(provider, account); } protected void onConnectToThread(@Nullable ClientBackgroundThread thread) { diff --git a/app/src/main/java/de/kuschku/util/servicebound/BoundFragment.java b/app/src/main/java/de/kuschku/util/servicebound/BoundFragment.java index 5aae12208dcdb8f7b29891372d947e6b2f378152..5d45019a874deb1e75a487c674f7fc0f79ac7de7 100644 --- a/app/src/main/java/de/kuschku/util/servicebound/BoundFragment.java +++ b/app/src/main/java/de/kuschku/util/servicebound/BoundFragment.java @@ -78,7 +78,7 @@ public abstract class BoundFragment extends Fragment { protected void connectToServer(Account account) { BusProvider provider = new BusProvider(); - binder.startBackgroundThread(provider, account.toAddress()); + binder.startBackgroundThread(provider, account); } protected void onConnectToThread(@Nullable ClientBackgroundThread thread) { diff --git a/app/src/main/res/layout/widget_actionbar.xml b/app/src/main/res/layout/widget_actionbar.xml index 5d87a559f6624f1826628bfbd2fa39953dff2792..0187f872334fcb3bb69491a24399b25dcbb48479 100644 --- a/app/src/main/res/layout/widget_actionbar.xml +++ b/app/src/main/res/layout/widget_actionbar.xml @@ -32,5 +32,63 @@ android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" - app:popupTheme="@style/AppTheme.PopupOverlay" /> + app:popupTheme="@style/AppTheme.PopupOverlay"> + + <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/toolbar_action_area" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:background="?attr/selectableItemBackgroundBorderless" + android:clickable="true" + android:gravity="center_vertical|start" + android:minHeight="?attr/actionBarSize" + android:orientation="vertical" + android:paddingLeft="@dimen/action_bar_default_padding_start_material" + android:paddingStart="@dimen/action_bar_default_padding_start_material"> + + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:layout_marginTop="-2dp" + android:baselineAligned="false" + android:gravity="center_vertical"> + + <TextView + android:id="@+id/key" + style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title" + android:layout_width="16dp" + android:layout_height="16dp" + android:layout_marginEnd="2dp" + android:layout_marginRight="2dp" + android:layout_marginTop="2dp" + android:gravity="center" + android:textSize="16sp" + android:visibility="gone" /> + + <TextView + android:id="@+id/toolbar_title" + style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:ellipsize="end" + android:gravity="center_vertical" + android:singleLine="true" + android:text="@string/appName" /> + + </LinearLayout> + + <TextView + android:id="@+id/toolbar_subtitle" + style="@style/TextAppearance.AppCompat.Widget.ActionBar.Subtitle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="-3dp" + android:ellipsize="end" + android:singleLine="true" + android:visibility="gone" /> + </LinearLayout> + + </android.support.v7.widget.Toolbar> + </android.support.design.widget.AppBarLayout> diff --git a/app/src/main/res/layout/widget_slider.xml b/app/src/main/res/layout/widget_slider.xml index e26f756b9359256fde15bc9d9aeb01e88c3e8f0c..9d4a0fbb5debaed310ad33740ec894f763b953f3 100644 --- a/app/src/main/res/layout/widget_slider.xml +++ b/app/src/main/res/layout/widget_slider.xml @@ -21,7 +21,7 @@ --> <com.sothree.slidinguppanel.SlidingUpPanelLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:sothree="http://schemas.android.com/apk/res-auto" + xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/sliding_layout_history" android:layout_width="fill_parent" @@ -30,11 +30,11 @@ android:clickable="false" android:focusable="true" android:gravity="bottom" - sothree:umanoFadeColor="?attr/colorBackgroundCard" - sothree:umanoOverlay="true" - sothree:umanoPanelHeight="0dip" - sothree:umanoScrollableView="@+id/msg_history" - sothree:umanoShadowHeight="0.0dip" + app:umanoFadeColor="?attr/colorBackground" + app:umanoOverlay="true" + app:umanoPanelHeight="0dip" + app:umanoScrollableView="@+id/msg_history" + app:umanoShadowHeight="0.0dip" tools:showIn="@layout/activity_chat"> @@ -52,7 +52,8 @@ android:layout_marginBottom="16.0dip" android:layout_marginLeft="16.0dip" android:layout_marginRight="16.0dip" - android:layout_marginTop="16.0dip"> + android:layout_marginTop="16.0dip" + app:cardBackgroundColor="?attr/colorBackgroundCard"> <LinearLayout android:layout_width="fill_parent" diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 6594d1de52c7ba13099f531ac239953f5914155d..467286a158b9a2d452b77fe990bc99ef2e62722d 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -28,4 +28,6 @@ <dimen name="message_vertical">4dp</dimen> <dimen name="circular_button_size">56dp</dimen> + + <dimen name="action_bar_default_padding_start_material">0dp</dimen> </resources> diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 80a075e8fed90c44d163d50573d2d684fbbe76a9..c5a618699170840f76fa5114b43ae1ac8001b16a 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -87,6 +87,7 @@ <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> + <item name="colorAccentFocus">@color/colorAccentFocus</item> <item name="windowActionModeOverlay">true</item> </style> diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 40c4bcd4778b16efdb2591a3e3546e8c844e9e5e..ade8a8d16631cacc7cf7f1b7942d36455161838c 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -225,7 +225,7 @@ <item name="colorPrimary">?attr/colorBackgroundCard</item> <item name="colorPrimaryDark">?attr/colorBackground</item> <item name="colorAccent">#B58900</item> - <item name="colorControlHighlight">#40B58900</item> + <item name="colorAccentFocus">#40B58900</item> <item name="senderColor0">#B58900</item> <item name="senderColor1">#CB4B16</item>