├── .gitignore
├── .gitmodules
├── .idea
├── .name
├── codeStyleSettings.xml
├── codeStyles
│ ├── Project.xml
│ └── codeStyleConfig.xml
├── compiler.xml
├── copyright
│ └── profiles_settings.xml
├── encodings.xml
├── gradle.xml
├── misc.xml
├── modules.xml
├── runConfigurations.xml
└── vcs.xml
├── LICENSE.md
├── README.md
├── ServalChat.iml
├── app
├── .gitignore
├── app.iml
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── main
│ ├── .gitignore
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── org
│ │ │ └── servalproject
│ │ │ ├── mid
│ │ │ ├── AbstractFutureList.java
│ │ │ ├── AbstractGrowingList.java
│ │ │ ├── ActivityList.java
│ │ │ ├── CallbackHandler.java
│ │ │ ├── Config.java
│ │ │ ├── FeedList.java
│ │ │ ├── IObservableList.java
│ │ │ ├── IObserverSet.java
│ │ │ ├── Identities.java
│ │ │ ├── Identity.java
│ │ │ ├── IdentityFeed.java
│ │ │ ├── Interface.java
│ │ │ ├── KnownPeers.java
│ │ │ ├── ListObserver.java
│ │ │ ├── ListObserverSet.java
│ │ │ ├── MessageFeed.java
│ │ │ ├── MessageList.java
│ │ │ ├── Messaging.java
│ │ │ ├── Observer.java
│ │ │ ├── ObserverProxy.java
│ │ │ ├── ObserverSet.java
│ │ │ ├── Peer.java
│ │ │ ├── Rhizome.java
│ │ │ ├── SelfUpdater.java
│ │ │ ├── Serval.java
│ │ │ ├── Server.java
│ │ │ └── networking
│ │ │ │ ├── AbstractListObserver.java
│ │ │ │ ├── FlightModeObserver.java
│ │ │ │ ├── Hotspot.java
│ │ │ │ ├── NetworkInfo.java
│ │ │ │ ├── Networks.java
│ │ │ │ ├── WifiAware.java
│ │ │ │ ├── WifiClient.java
│ │ │ │ ├── WifiHotspotChanges.java
│ │ │ │ ├── WifiNetworkChanges.java
│ │ │ │ └── bluetooth
│ │ │ │ ├── BlueToothControl.java
│ │ │ │ ├── BlueToothInfo.java
│ │ │ │ ├── BluetoothNetworkChanges.java
│ │ │ │ ├── Connector.java
│ │ │ │ ├── PeerReader.java
│ │ │ │ ├── PeerState.java
│ │ │ │ ├── Scanner.java
│ │ │ │ └── SocketListener.java
│ │ │ └── servalchat
│ │ │ ├── App.java
│ │ │ ├── CustomFileProvider.java
│ │ │ ├── ForegroundService.java
│ │ │ ├── Notifications.java
│ │ │ ├── SampleData.java
│ │ │ ├── feeds
│ │ │ ├── ActivityAdapter.java
│ │ │ ├── BlockList.java
│ │ │ ├── Contacts.java
│ │ │ ├── FeedAdapter.java
│ │ │ ├── FeedListAdapter.java
│ │ │ ├── MyFeed.java
│ │ │ ├── MyFeedPresenter.java
│ │ │ ├── PeerFeed.java
│ │ │ ├── PeerFeedPresenter.java
│ │ │ ├── PublicFeedsList.java
│ │ │ └── PublicFeedsPresenter.java
│ │ │ ├── identity
│ │ │ ├── ConversationList.java
│ │ │ ├── IdentityDetails.java
│ │ │ ├── IdentityDetailsPresenter.java
│ │ │ └── IdentityList.java
│ │ │ ├── navigation
│ │ │ ├── CountTitle.java
│ │ │ ├── HistoryItem.java
│ │ │ ├── IContainerView.java
│ │ │ ├── IHaveMenu.java
│ │ │ ├── ILifecycle.java
│ │ │ ├── INavigate.java
│ │ │ ├── INavigatorHost.java
│ │ │ ├── IOnBack.java
│ │ │ ├── IRootContainer.java
│ │ │ ├── Id1.java
│ │ │ ├── Id2.java
│ │ │ ├── Id3.java
│ │ │ ├── Id4.java
│ │ │ ├── MainActivity.java
│ │ │ ├── NavHistory.java
│ │ │ ├── NavPageAdapter.java
│ │ │ ├── NavTabStrip.java
│ │ │ ├── NavTitle.java
│ │ │ ├── Navigation.java
│ │ │ ├── RootAppbar.java
│ │ │ ├── RootSidebar.java
│ │ │ └── ViewState.java
│ │ │ ├── network
│ │ │ └── NetworkList.java
│ │ │ ├── peer
│ │ │ ├── PeerDetails.java
│ │ │ ├── PeerHolder.java
│ │ │ ├── PeerList.java
│ │ │ ├── PeerMap.java
│ │ │ ├── PrivateMessaging.java
│ │ │ └── PrivateMessagingPresenter.java
│ │ │ └── views
│ │ │ ├── BackgroundWorker.java
│ │ │ ├── BasicViewHolder.java
│ │ │ ├── DisplayError.java
│ │ │ ├── FutureList.java
│ │ │ ├── Identicon.java
│ │ │ ├── MessageViewHolder.java
│ │ │ ├── ObservedRecyclerView.java
│ │ │ ├── Presenter.java
│ │ │ ├── PresenterFactory.java
│ │ │ ├── RecyclerHelper.java
│ │ │ ├── ScrollingAdapter.java
│ │ │ ├── SimpleRecyclerView.java
│ │ │ ├── SpinnerViewHolder.java
│ │ │ ├── TimestampView.java
│ │ │ └── UIObserver.java
│ ├── jni
│ │ ├── Android.mk
│ │ ├── Application.mk
│ │ └── chat_features.c
│ └── res
│ │ ├── drawable
│ │ ├── ic_add_account.xml
│ │ ├── ic_add_contact.xml
│ │ ├── ic_block_contact.xml
│ │ ├── ic_contacts.xml
│ │ ├── ic_remove_contact.xml
│ │ ├── progress.xml
│ │ └── side_nav_bar.xml
│ │ ├── layout
│ │ ├── ack_message.xml
│ │ ├── activity_message.xml
│ │ ├── block_list.xml
│ │ ├── contacts.xml
│ │ ├── conversation_item.xml
│ │ ├── conversation_list.xml
│ │ ├── error.xml
│ │ ├── feed.xml
│ │ ├── feed_list.xml
│ │ ├── feed_message.xml
│ │ ├── identity.xml
│ │ ├── identity_details.xml
│ │ ├── identity_list.xml
│ │ ├── main.xml
│ │ ├── main_sidebar.xml
│ │ ├── main_tabs.xml
│ │ ├── message_ack.xml
│ │ ├── message_list.xml
│ │ ├── my_feed.xml
│ │ ├── my_message.xml
│ │ ├── nav_header.xml
│ │ ├── network.xml
│ │ ├── networking.xml
│ │ ├── peer.xml
│ │ ├── peer_details.xml
│ │ ├── peer_feed.xml
│ │ ├── peer_list.xml
│ │ ├── peer_map.xml
│ │ ├── placeholder.xml
│ │ ├── progress.xml
│ │ └── their_message.xml
│ │ ├── mipmap-hdpi
│ │ └── serval_head.png
│ │ ├── mipmap-mdpi
│ │ └── serval_head.png
│ │ ├── mipmap-xhdpi
│ │ └── serval_head.png
│ │ ├── mipmap-xxhdpi
│ │ └── serval_head.png
│ │ ├── mipmap-xxxhdpi
│ │ └── serval_head.png
│ │ ├── values-w820dp
│ │ └── dimens.xml
│ │ ├── values
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ │ └── xml
│ │ └── network_security_config.xml
│ └── test
│ └── java
│ └── org
│ └── servalproject
│ └── servalchat
│ └── ExampleUnitTest.java
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | /local.properties
3 | /.idea/workspace.xml
4 | /.idea/libraries/
5 | /.idea/caches/
6 | .DS_Store
7 | /build
8 | /captures
9 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "app/src/main/jni/serval-dna"]
2 | path = app/src/main/jni/serval-dna
3 | url = ../serval-dna.git
4 | [submodule "app/src/main/jni/libsodium"]
5 | path = app/src/main/jni/libsodium
6 | url = ../libsodium.git
7 |
--------------------------------------------------------------------------------
/.idea/.name:
--------------------------------------------------------------------------------
1 | ServalChat
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Serval Chat, Copyright (C) 2016 Flinders University
2 |
3 | This program is free software; you can redistribute it and/or modify
4 | it under the terms of the GNU General Public License version 3 as
5 | published by the Free Software Foundation.
6 | See http://www.gnu.org/licenses/
7 |
8 | This program is distributed in the hope that it will be useful,
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | GNU General Public License for more details.
12 |
--------------------------------------------------------------------------------
/ServalChat.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | build
2 | *.apk
3 | .externalNativeBuild
4 | .cxx
5 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /usr/local/etc/android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/app/src/main/.gitignore:
--------------------------------------------------------------------------------
1 | obj
2 | libs
3 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/mid/AbstractFutureList.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.mid;
2 |
3 | import org.servalproject.json.JsonParser;
4 | import org.servalproject.servaldna.HttpJsonSerialiser;
5 | import org.servalproject.servaldna.ServalDInterfaceException;
6 |
7 | import java.io.IOException;
8 |
9 | /**
10 | * Created by jeremy on 30/01/17.
11 | */
12 |
13 | public abstract class AbstractFutureList
14 | extends AbstractGrowingList{
15 | private final ListObserverSet observeFuture;
16 | private HttpJsonSerialiser futureList;
17 | private boolean polling = false;
18 | protected T last;
19 |
20 | protected AbstractFutureList(Serval serval) {
21 | super(serval);
22 | this.observeFuture = new ListObserverSet<>(serval);
23 | }
24 | protected void start() {
25 | if (polling || !observeFuture.hasObservers())
26 | return;
27 | polling = true;
28 | serval.runOnThreadPool(readFuture);
29 | }
30 |
31 | public void observe(ListObserver observer) {
32 | observeFuture.add(observer);
33 | start();
34 | }
35 |
36 | @Override
37 | public void stopObserving(ListObserver observer) {
38 | observeFuture.remove(observer);
39 | if (!observeFuture.hasObservers()) {
40 | polling = false;
41 | closeFuture();
42 | }
43 | }
44 |
45 | protected abstract HttpJsonSerialiser openFuture() throws ServalDInterfaceException, E, IOException, JsonParser.JsonParseException;
46 |
47 | @Override
48 | protected void addingPastItem(T item) {
49 | if (last == null) {
50 | last = item;
51 | start();
52 | }
53 | super.addingPastItem(item);
54 | }
55 |
56 | @SuppressWarnings("unchecked")
57 | protected void addingFutureItem(T item) {
58 | boolean isLatest = true;
59 |
60 | if (last != null && item instanceof Comparable>)
61 | isLatest = ((Comparable)item).compareTo(last) <0;
62 | if (isLatest)
63 | last = item;
64 |
65 | observeFuture.onAdd(item);
66 | }
67 |
68 | private Runnable readFuture = new Runnable() {
69 | @Override
70 | public void run() {
71 | while (polling) {
72 | try {
73 | HttpJsonSerialiser list = futureList = openFuture();
74 | if (list != null) {
75 | T item;
76 | while (polling && (item = list.next()) != null) {
77 | addingFutureItem(item);
78 | }
79 | // on graceful close from the server, restart
80 | list.close();
81 | }
82 | if (futureList == list)
83 | futureList = null;
84 | } catch (IOException |
85 | JsonParser.JsonParseException e) {
86 | // ignore if we caused this deliberately in another thread.
87 | if (polling)
88 | throw new IllegalStateException(e);
89 | } catch (RuntimeException e) {
90 | throw e;
91 | } catch (Exception e) {
92 | throw new IllegalStateException(e);
93 | }
94 | }
95 | }
96 | };
97 |
98 | private void closeFuture(){
99 | HttpJsonSerialiser list = futureList;
100 | if (list != null) {
101 | try {
102 | list.close();
103 | } catch (IOException e) {
104 | }
105 | futureList = null;
106 | }
107 | }
108 |
109 | @Override
110 | public void close() {
111 | super.close();
112 | polling = false;
113 | closeFuture();
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/mid/AbstractGrowingList.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.mid;
2 |
3 | import org.servalproject.json.JsonParser;
4 | import org.servalproject.servaldna.HttpJsonSerialiser;
5 | import org.servalproject.servaldna.ServalDInterfaceException;
6 |
7 | import java.io.IOException;
8 |
9 | /**
10 | * Created by jeremy on 11/10/16.
11 | */
12 | public abstract class AbstractGrowingList
13 | implements IObservableList{
14 | protected final Serval serval;
15 | protected boolean hasMore = true;
16 | private boolean closed = false;
17 | private HttpJsonSerialiser pastList;
18 |
19 | protected AbstractGrowingList(Serval serval) {
20 | this.serval = serval;
21 | }
22 |
23 | protected abstract HttpJsonSerialiser openPast() throws ServalDInterfaceException, E, IOException, JsonParser.JsonParseException;
24 |
25 | protected void addingPastItem(T item) {
26 | }
27 |
28 | @Override
29 | public T next() throws ServalDInterfaceException, E, IOException, JsonParser.JsonParseException {
30 | if (!hasMore)
31 | return null;
32 | if (pastList == null)
33 | pastList = openPast();
34 |
35 | T item = null;
36 | if (pastList != null)
37 | item = pastList.next();
38 | if (item == null) {
39 | hasMore = false;
40 | if (pastList != null)
41 | pastList.close();
42 | pastList = null;
43 | }
44 | addingPastItem(item);
45 | return item;
46 | }
47 |
48 | @Override
49 | public void close() {
50 | hasMore = false;
51 | closed = true;
52 | if (pastList != null) {
53 | try {
54 | pastList.close();
55 | } catch (IOException e) {
56 | }
57 | pastList = null;
58 | }
59 | }
60 |
61 | @Override
62 | public void observe(ListObserver observer) {
63 | // NOOP
64 | }
65 |
66 | @Override
67 | public void stopObserving(ListObserver observer) {
68 | // NOOP
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/mid/ActivityList.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.mid;
2 |
3 | import org.servalproject.servaldna.HttpJsonSerialiser;
4 | import org.servalproject.servaldna.ServalDInterfaceException;
5 | import org.servalproject.servaldna.meshmb.MeshMBActivityMessage;
6 |
7 | import java.io.IOException;
8 |
9 | /**
10 | * Created by jeremy on 30/01/17.
11 | */
12 |
13 | public class ActivityList extends AbstractFutureList {
14 | private final Identity identity;
15 |
16 | ActivityList(Serval serval, Identity identity) {
17 | super(serval);
18 | this.identity = identity;
19 | }
20 |
21 | @Override
22 | protected void start() {
23 | if (last == null && hasMore)
24 | return;
25 | super.start();
26 | }
27 |
28 | private void updatePeer(MeshMBActivityMessage item){
29 | Peer p = serval.knownPeers.getPeer(item.subscriber);
30 | p.updateFeedName(item.name);
31 | }
32 |
33 | @Override
34 | protected void addingPastItem(MeshMBActivityMessage item) {
35 | if (item!=null)
36 | updatePeer(item);
37 | super.addingPastItem(item);
38 | }
39 |
40 | @Override
41 | protected void addingFutureItem(MeshMBActivityMessage item) {
42 | updatePeer(item);
43 | super.addingFutureItem(item);
44 | }
45 |
46 | @Override
47 | protected HttpJsonSerialiser openPast() throws ServalDInterfaceException, IOException, IOException {
48 | return serval.getResultClient().meshmbActivity(identity.subscriber);
49 | }
50 |
51 | @Override
52 | protected HttpJsonSerialiser openFuture() throws ServalDInterfaceException, IOException, IOException {
53 | return serval.getResultClient().meshmbActivity(identity.subscriber, last == null ? "" : last.token);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/mid/CallbackHandler.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.mid;
2 |
3 | import android.os.Handler;
4 | import android.os.Looper;
5 | import android.os.Message;
6 |
7 | /**
8 | * Created by jeremy on 11/05/16.
9 | */
10 | public class CallbackHandler extends Handler {
11 |
12 | CallbackHandler(Looper looper) {
13 | super(looper);
14 | }
15 |
16 | @Override
17 | public void handleMessage(Message msg) {
18 | Object c = msg.obj;
19 | if (c != null){
20 | if (c instanceof CallbackMessage>) {
21 | CallbackMessage> m = (CallbackMessage>) c;
22 | m.handleMessage(msg);
23 | return;
24 | }else if(c instanceof Callback){
25 | ((Callback)c).handleMessage(msg);
26 | return;
27 | }
28 | }
29 | super.dispatchMessage(msg);
30 | }
31 |
32 | public void sendEmptyMessage(Callback callback, int what){
33 | Message msg = obtainMessage(what, callback);
34 | sendMessage(msg);
35 | }
36 |
37 | public void sendMessage(MessageHandler h, T obj, int what) {
38 | if (isOnThread()) {
39 | // call immediately if already in the right thread
40 | h.handleMessage(obj, what);
41 | } else {
42 | // TODO reuse CallbackMessage instances?
43 | CallbackMessage m = new CallbackMessage<>(h, obj);
44 | Message msg = obtainMessage(what, m);
45 | sendMessage(msg);
46 | }
47 | }
48 |
49 | public boolean isOnThread() {
50 | return Thread.currentThread() == this.getLooper().getThread();
51 | }
52 |
53 | private class CallbackMessage {
54 | final MessageHandler handler;
55 | final T obj;
56 |
57 | CallbackMessage(MessageHandler handler, T obj) {
58 | this.handler = handler;
59 | this.obj = obj;
60 | }
61 |
62 | void handleMessage(Message msg) {
63 | handler.handleMessage(obj, msg.what);
64 | }
65 | }
66 |
67 | public interface MessageHandler {
68 | void handleMessage(T obj, int what);
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/mid/Config.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.mid;
2 |
3 | import org.servalproject.servaldna.ServalDCommand;
4 | import org.servalproject.servaldna.ServalDFailureException;
5 |
6 | import java.util.ArrayList;
7 | import java.util.HashMap;
8 | import java.util.List;
9 | import java.util.Map;
10 |
11 | /**
12 | * Created by jeremy on 3/05/16.
13 | */
14 |
15 | public class Config {
16 |
17 | private Map values = null;
18 | private Map pending = new HashMap<>();
19 | private static final String deleteFlag = "deleteme";
20 |
21 | private static final String TAG = "Config";
22 |
23 | void set(String name, String value) {
24 | pending.put(name, value);
25 | }
26 |
27 | String get(String name) {
28 | String value = null;
29 | if (pending.containsKey(name)) {
30 | value = pending.get(name);
31 | // don't use .equals() here
32 | if (value == deleteFlag)
33 | value = null;
34 | } else if (values != null)
35 | value = values.get(name);
36 | return value;
37 | }
38 |
39 | void delete(String name) {
40 | pending.put(name, deleteFlag);
41 | }
42 |
43 | void load() throws ServalDFailureException {
44 | ServalDCommand.ConfigItems items = ServalDCommand.getConfig();
45 | values = items.values;
46 | }
47 |
48 | void reset(){
49 | for(String key : values.keySet())
50 | delete(key);
51 | }
52 |
53 | public void sync() throws ServalDFailureException {
54 | if (pending.isEmpty())
55 | return;
56 |
57 | List changes = new ArrayList<>();
58 | for (String key : pending.keySet()) {
59 | String value = pending.get(key);
60 | // don't use .equals() here
61 | if (value == deleteFlag) {
62 | changes.add("del");
63 | changes.add(key);
64 | } else {
65 | changes.add("set");
66 | changes.add(key);
67 | changes.add(value);
68 | }
69 | }
70 | changes.add("sync");
71 | ServalDCommand.configActions(changes.toArray(new Object[changes.size()]));
72 | load();
73 | pending.clear();
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/mid/FeedList.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.mid;
2 |
3 | import org.servalproject.servaldna.HttpJsonSerialiser;
4 | import org.servalproject.servaldna.ServalDInterfaceException;
5 | import org.servalproject.servaldna.Subscriber;
6 | import org.servalproject.servaldna.meshmb.MeshMBCommon;
7 | import org.servalproject.servaldna.rhizome.RhizomeBundleList;
8 | import org.servalproject.servaldna.rhizome.RhizomeListBundle;
9 |
10 | import java.io.IOException;
11 |
12 | /**
13 | * Created by jeremy on 11/10/16.
14 | */
15 | public class FeedList extends AbstractFutureList {
16 | private static final String TAG = "FeedList";
17 | public final String search;
18 |
19 | public FeedList(Serval serval, String search) {
20 | super(serval);
21 | this.search = search;
22 | }
23 |
24 | @Override
25 | protected void start() {
26 | if (last == null && hasMore)
27 | return;
28 | super.start();
29 | }
30 |
31 | @Override
32 | protected HttpJsonSerialiser openPast() throws ServalDInterfaceException, IOException {
33 | RhizomeBundleList list = new RhizomeBundleList(serval.getResultClient());
34 | list.setServiceFilter(MeshMBCommon.SERVICE);
35 | if (search!=null)
36 | list.setNameFilter(search);
37 | list.connect();
38 | return list;
39 | }
40 |
41 | @Override
42 | protected HttpJsonSerialiser openFuture() throws ServalDInterfaceException, IOException {
43 | RhizomeBundleList list = new RhizomeBundleList(serval.getResultClient(), last == null? "" : last.token);
44 | list.setServiceFilter(MeshMBCommon.SERVICE);
45 | if (search!=null && !"".equals(search))
46 | list.setNameFilter(search);
47 | list.connect();
48 | return list;
49 | }
50 |
51 | private void updatePeer(RhizomeListBundle item) {
52 | // TODO verify that the sender and id are for the same identity!
53 | // for now we can assume this, but we might break this rule in a future version
54 | if (item.author == null && item.manifest.sender == null)
55 | return;
56 | Subscriber subscriber = new Subscriber(
57 | item.author != null ? item.author : item.manifest.sender,
58 | item.manifest.id, true);
59 | Peer p = serval.knownPeers.getPeer(subscriber);
60 | p.updateFeedName(item.manifest.name);
61 | }
62 |
63 | @Override
64 | protected void addingFutureItem(RhizomeListBundle item) {
65 | updatePeer(item);
66 | super.addingFutureItem(item);
67 | }
68 |
69 | @Override
70 | protected void addingPastItem(RhizomeListBundle item) {
71 | if (item != null)
72 | updatePeer(item);
73 | super.addingPastItem(item);
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/mid/IObservableList.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.mid;
2 |
3 | import org.servalproject.json.JsonParser;
4 | import org.servalproject.servaldna.ServalDInterfaceException;
5 |
6 | import java.io.IOException;
7 |
8 | /**
9 | * Created by jeremy on 8/08/16.
10 | */
11 | public interface IObservableList{
12 | T next() throws ServalDInterfaceException, E, IOException, JsonParser.JsonParseException;
13 | void close();
14 | void observe(ListObserver observer);
15 | void stopObserving(ListObserver observer);
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/mid/IObserverSet.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.mid;
2 |
3 | public interface IObserverSet {
4 | void addUI(Observer observer);
5 |
6 | void removeUI(Observer observer);
7 |
8 | void addBackground(Observer observer);
9 |
10 | void removeBackground(Observer observer);
11 |
12 | T getObj();
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/mid/Identities.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.mid;
2 |
3 | import org.servalproject.json.JsonParser;
4 | import org.servalproject.servaldna.ServalDInterfaceException;
5 | import org.servalproject.servaldna.SigningKey;
6 | import org.servalproject.servaldna.Subscriber;
7 | import org.servalproject.servaldna.SubscriberId;
8 | import org.servalproject.servaldna.keyring.KeyringIdentity;
9 | import org.servalproject.servaldna.keyring.KeyringIdentityList;
10 |
11 | import java.io.IOException;
12 | import java.util.ArrayList;
13 | import java.util.HashMap;
14 | import java.util.List;
15 | import java.util.Map;
16 |
17 | /**
18 | * Created by jeremy on 6/06/16.
19 | */
20 | public class Identities {
21 | private static final String TAG = "Identities";
22 | private final Serval serval;
23 | private boolean loaded = false;
24 | public final ListObserverSet listObservers;
25 | private final List identityList = new ArrayList<>();
26 | private final Map identities = new HashMap<>();
27 | private Identity selected;
28 |
29 | Identities(Serval serval) {
30 | this.serval = serval;
31 | listObservers = new ListObserverSet<>(serval);
32 | }
33 |
34 | void onStart() {
35 | enterPin(null);
36 | }
37 |
38 | private Identity addId(KeyringIdentity id) {
39 | Identity i = identities.get(id.subscriber);
40 | if (i == null) {
41 | i = new Identity(serval, id.subscriber);
42 | identities.put(id.subscriber, i);
43 | identityList.add(i);
44 | if (selected == null)
45 | selected = i;
46 | i.update(id);
47 | listObservers.onAdd(i);
48 | } else {
49 | i.update(id);
50 | listObservers.onUpdate(i);
51 | }
52 | return i;
53 | }
54 |
55 | public List getIdentities() {
56 | return identityList;
57 | }
58 |
59 | public boolean isLoaded() {
60 | return loaded;
61 | }
62 |
63 | public Identity getIdentity(SigningKey key) {
64 | for (Identity id : identityList) {
65 | if (id.subscriber.signingKey.equals(key))
66 | return id;
67 | }
68 | return null;
69 | }
70 |
71 | public void updateIdentity(final Identity id, final String did, final String name, final String pin) throws ServalDInterfaceException, IOException {
72 | addId(serval.getResultClient().keyringSetDidName(id.subscriber, did, name, pin));
73 | }
74 |
75 | public Identity addIdentity(final String did, final String name, final String pin) throws ServalDInterfaceException, IOException {
76 | return addId(serval.getResultClient().keyringAdd(did, name, pin));
77 | }
78 |
79 | void enterPin(final String pin) {
80 | serval.runOnThreadPool(new Runnable() {
81 | @Override
82 | public void run() {
83 | try {
84 | KeyringIdentityList list = serval.getResultClient().keyringListIdentities(pin);
85 | KeyringIdentity id = null;
86 | while ((id = list.next()) != null)
87 | addId(id);
88 | if (pin == null)
89 | loaded = true;
90 | listObservers.onReset();
91 | } catch (ServalDInterfaceException |
92 | JsonParser.JsonParseException |
93 | IOException e) {
94 | throw new IllegalStateException(e);
95 | }
96 | }
97 | });
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/mid/Identity.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.mid;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.Canvas;
5 |
6 | import org.servalproject.servalchat.views.Identicon;
7 | import org.servalproject.servaldna.ServalDInterfaceException;
8 | import org.servalproject.servaldna.Subscriber;
9 | import org.servalproject.servaldna.keyring.KeyringIdentity;
10 | import org.servalproject.servaldna.meshmb.MeshMBCommon;
11 |
12 | import java.io.IOException;
13 |
14 | /**
15 | * Created by jeremy on 6/06/16.
16 | */
17 | public class Identity {
18 | private final Serval serval;
19 | public final Subscriber subscriber;
20 | public final ObserverSet observers;
21 | private KeyringIdentity identity;
22 | private Identicon icon;
23 |
24 | private static long nextId = 0;
25 | private final long id;
26 | public final Messaging messaging;
27 |
28 | public Identity(Serval serval, Subscriber subscriber) {
29 | this.serval = serval;
30 | this.subscriber = subscriber;
31 | id = nextId++;
32 | if (serval == null) {
33 | // dummy object for ui design
34 | messaging = null;
35 | observers = null;
36 | } else {
37 | observers = new ObserverSet<>(serval, this);
38 | this.messaging = new Messaging(serval, this);
39 | }
40 | }
41 |
42 | public IdentityFeed getFeed() {
43 | return new IdentityFeed(serval, this);
44 | }
45 |
46 | public FeedList getAllFeeds(String search) {
47 | return new FeedList(serval, search);
48 | }
49 |
50 | public ActivityList getActivity() {
51 | return new ActivityList(serval, this);
52 | }
53 |
54 | public void alterSubscription(MeshMBCommon.SubscriptionAction action, Peer peer) throws ServalDInterfaceException, IOException {
55 | serval.getResultClient().meshmbAlterSubscription(subscriber, action, peer.getSubscriber(), peer.getFeedName());
56 | messaging.subscriptionAltered(action, peer);
57 | }
58 |
59 | public void update(KeyringIdentity id) {
60 | this.identity = id;
61 | if (observers != null)
62 | observers.onUpdate();
63 | }
64 |
65 | public Identicon getIcon(){
66 | if (icon == null)
67 | icon = new Identicon(subscriber.signingKey);
68 | return icon;
69 | }
70 |
71 | private Bitmap iconBitmap;
72 | public Bitmap getBitmap(){
73 | if (iconBitmap == null) {
74 | iconBitmap = Bitmap.createBitmap(48, 48, Bitmap.Config.ARGB_8888);
75 | Canvas canvas = new Canvas(iconBitmap);
76 | getIcon().draw(canvas);
77 | }
78 | return iconBitmap;
79 | }
80 |
81 |
82 | public String getName() {
83 | return identity == null ? null : identity.name;
84 | }
85 |
86 | public String getDid() {
87 | return identity == null ? null : identity.did;
88 | }
89 |
90 | public long getId() {
91 | return id;
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/mid/IdentityFeed.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.mid;
2 |
3 | import org.servalproject.servaldna.ServalDInterfaceException;
4 | import org.servalproject.servaldna.meshms.MeshMSException;
5 |
6 | import java.io.IOException;
7 |
8 | /**
9 | * Created by jeremy on 11/10/16.
10 | */
11 | public class IdentityFeed extends MessageFeed {
12 | public final Identity id;
13 |
14 | IdentityFeed(Serval serval, Identity id) {
15 | super(serval, id.subscriber);
16 | this.id = id;
17 | }
18 |
19 | public void sendMessage(String message) throws ServalDInterfaceException, IOException, MeshMSException {
20 | if (serval.uiHandler.isOnThread())
21 | throw new IllegalStateException();
22 | serval.getResultClient().meshmbSendMessage(id.subscriber.signingKey, message);
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/mid/Interface.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.mid;
2 |
3 | /**
4 | * Created by jeremy on 24/10/16.
5 | */
6 | public class Interface {
7 | public final int id;
8 | public final String name;
9 |
10 | public Interface(int id, String name) {
11 | this.id = id;
12 | this.name = name;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/mid/ListObserver.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.mid;
2 |
3 | /**
4 | * Created by jeremy on 11/05/16.
5 | */
6 | public interface ListObserver {
7 | void added(T obj);
8 |
9 | void removed(T obj);
10 |
11 | void updated(T obj);
12 |
13 | void reset();
14 | }
15 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/mid/ListObserverSet.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.mid;
2 |
3 | import java.util.ArrayList;
4 | import java.util.HashSet;
5 | import java.util.List;
6 | import java.util.Set;
7 |
8 | /**
9 | * Created by jeremy on 11/05/16.
10 | */
11 | public class ListObserverSet {
12 | private final Set> observers = new HashSet<>();
13 | private final Set> backgroundObservers = new HashSet<>();
14 |
15 | private static final int ADD = 1;
16 | private static final int REMOVE = 2;
17 | private static final int UPDATE = 3;
18 | private static final int RESET = 4;
19 | private int generation = 0;
20 |
21 | private final CallbackHandler uiHandler;
22 | private final CallbackHandler backgroundHandler;
23 |
24 | public ListObserverSet(Serval serval) {
25 | this.uiHandler = serval.uiHandler;
26 | this.backgroundHandler = serval.backgroundHandler;
27 | }
28 |
29 | public int add(ListObserver observer) {
30 | observers.add(observer);
31 | return generation;
32 | }
33 |
34 | public int remove(ListObserver observer) {
35 | observers.remove(observer);
36 | return generation;
37 | }
38 |
39 | public int addBackground(ListObserver observer) {
40 | backgroundObservers.add(observer);
41 | return generation;
42 | }
43 |
44 | public int removeBackground(ListObserver observer) {
45 | backgroundObservers.remove(observer);
46 | return generation;
47 | }
48 |
49 | public boolean hasObservers() {
50 | return !observers.isEmpty();
51 | }
52 |
53 | private void onChange(T t, int what){
54 | generation++;
55 | if (!observers.isEmpty())
56 | uiHandler.sendMessage(uiMessageHandler, t, what);
57 | if (!backgroundObservers.isEmpty())
58 | backgroundHandler.sendMessage(backgroundMessageHandler, t, what);
59 | }
60 |
61 | public void onAdd(T t) {
62 | onChange(t, ADD);
63 | }
64 |
65 | public void onRemove(T t) {
66 | onChange(t, REMOVE);
67 | }
68 |
69 | public void onUpdate(T t) {
70 | onChange(t, UPDATE);
71 | }
72 |
73 | public void onReset() {
74 | onChange(null, RESET);
75 | }
76 |
77 | private CallbackHandler.MessageHandler uiMessageHandler = new CallbackHandler.MessageHandler() {
78 | @Override
79 | public void handleMessage(T obj, int what) {
80 | handle(observers, obj, what);
81 | }
82 | };
83 |
84 | private CallbackHandler.MessageHandler backgroundMessageHandler = new CallbackHandler.MessageHandler() {
85 | @Override
86 | public void handleMessage(T obj, int what) {
87 | handle(backgroundObservers, obj, what);
88 | }
89 | };
90 |
91 | private void handle(Set> observers, T obj, int what) {
92 | if (observers.isEmpty())
93 | return;
94 | // clone the list so handlers can remove while we are iterating
95 | List> notify = new ArrayList<>(observers);
96 | for (ListObserver observer : notify) {
97 | switch (what) {
98 | case ADD:
99 | observer.added(obj);
100 | break;
101 | case REMOVE:
102 | observer.removed(obj);
103 | break;
104 | case UPDATE:
105 | observer.updated(obj);
106 | break;
107 | case RESET:
108 | observer.reset();
109 | break;
110 | }
111 | }
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/mid/MessageFeed.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.mid;
2 |
3 | import org.servalproject.json.JsonParser;
4 | import org.servalproject.servaldna.HttpJsonSerialiser;
5 | import org.servalproject.servaldna.ServalDInterfaceException;
6 | import org.servalproject.servaldna.Subscriber;
7 | import org.servalproject.servaldna.meshmb.MeshMBCommon;
8 | import org.servalproject.servaldna.meshmb.MessagePlyList;
9 | import org.servalproject.servaldna.meshmb.PlyMessage;
10 | import org.servalproject.servaldna.rhizome.RhizomeBundleList;
11 | import org.servalproject.servaldna.rhizome.RhizomeListBundle;
12 |
13 | import java.io.IOException;
14 |
15 | /**
16 | * Created by jeremy on 3/08/16.
17 | */
18 | public class MessageFeed extends AbstractFutureList {
19 | private Subscriber id;
20 | private Peer peer;
21 | private String name;
22 |
23 | MessageFeed(Serval serval, Peer peer) {
24 | super(serval);
25 | this.peer = peer;
26 | Subscriber peerId = peer.getSubscriber();
27 | if (peerId.signingKey!=null)
28 | this.id = peerId;
29 | }
30 |
31 | MessageFeed(Serval serval, Subscriber id) {
32 | super(serval);
33 | if (id == null || id.signingKey == null)
34 | throw new IllegalStateException();
35 | this.id = id;
36 | }
37 |
38 | public Peer getPeer(){
39 | return peer;
40 | }
41 |
42 | public Subscriber getId(){
43 | if (id == null && peer != null){
44 | // might have discovered the key some other way in the mean time
45 | Subscriber peerId = peer.getSubscriber();
46 | if (peerId.signingKey != null)
47 | id = peerId;
48 | }
49 | return id;
50 | }
51 |
52 | @Override
53 | protected void start() {
54 | if (last == null && hasMore)
55 | return;
56 | super.start();
57 | }
58 |
59 | private void findKey() throws IOException, ServalDInterfaceException, JsonParser.JsonParseException {
60 | if (id!=null)
61 | return;
62 | if (peer == null)
63 | throw new IllegalStateException();
64 |
65 | // might have discovered the key some other way in the mean time
66 | Subscriber peerId = peer.getSubscriber();
67 | if (peerId.signingKey != null){
68 | id = peerId;
69 | return;
70 | }
71 | // look for a feed in rhizome
72 | RhizomeBundleList list = new RhizomeBundleList(serval.getResultClient());
73 | try {
74 | list.setServiceFilter(MeshMBCommon.SERVICE);
75 | list.setSenderFilter(peer.getSubscriber().sid);
76 | list.connect();
77 | RhizomeListBundle bundle = list.next();
78 | if (bundle != null){
79 | id = new Subscriber(peerId.sid, bundle.manifest.id, true);
80 | peer.updateSubscriber(id);
81 | }
82 | } finally {
83 | list.close();
84 | }
85 | }
86 |
87 | @Override
88 | protected HttpJsonSerialiser openPast() throws ServalDInterfaceException, IOException, JsonParser.JsonParseException {
89 | findKey();
90 | if (id == null)
91 | return null;
92 | MessagePlyList list = serval.getResultClient().meshmbListMessages(id.signingKey);
93 | this.name = list.getName();
94 | if (peer != null)
95 | peer.updateFeedName(name);
96 | return list;
97 | }
98 |
99 | @Override
100 | protected HttpJsonSerialiser openFuture() throws ServalDInterfaceException, IOException, JsonParser.JsonParseException {
101 | findKey();
102 | if (id == null)
103 | return null;
104 | MessagePlyList list = serval.getResultClient().meshmbListMessagesSince(id.signingKey, last==null?"":last.token);
105 | this.name = list.getName();
106 | if (peer != null)
107 | peer.updateFeedName(name);
108 | return list;
109 | }
110 |
111 | public String getName(){
112 | String peerName = name;
113 | if (peer != null) {
114 | if (peerName == null)
115 | peerName = peer.getFeedName();
116 | if (peerName == null)
117 | peerName = peer.getName();
118 | }
119 | return peerName;
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/mid/MessageList.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.mid;
2 |
3 | import org.servalproject.servaldna.HttpJsonSerialiser;
4 | import org.servalproject.servaldna.ServalDInterfaceException;
5 | import org.servalproject.servaldna.Subscriber;
6 | import org.servalproject.servaldna.meshms.MeshMSConversation;
7 | import org.servalproject.servaldna.meshms.MeshMSException;
8 | import org.servalproject.servaldna.meshms.MeshMSMessage;
9 |
10 | import java.io.IOException;
11 |
12 | /**
13 | * Created by jeremy on 11/07/16.
14 | */
15 | public class MessageList extends AbstractFutureList {
16 | private final Messaging messaging;
17 | public final Subscriber self;
18 | public final Subscriber peer;
19 |
20 | MessageList(Serval serval, Messaging messaging, Subscriber self, Subscriber peer) {
21 | super(serval);
22 | this.messaging = messaging;
23 | this.self = self;
24 | this.peer = peer;
25 | }
26 |
27 | @Override
28 | protected void start() {
29 | if (last == null && hasMore)
30 | return;
31 | super.start();
32 | }
33 |
34 | @Override
35 | protected HttpJsonSerialiser openPast() throws ServalDInterfaceException, IOException {
36 | return serval.getResultClient().meshmsListMessages(self.sid, peer.sid);
37 | }
38 |
39 | @Override
40 | protected HttpJsonSerialiser openFuture() throws ServalDInterfaceException, IOException {
41 | return serval.getResultClient().meshmsListMessagesSince(self.sid, peer.sid, last==null?"":last.token);
42 | }
43 |
44 | public void sendMessage(String message) throws ServalDInterfaceException, MeshMSException, IOException {
45 | if (serval.uiHandler.isOnThread())
46 | throw new IllegalStateException();
47 | serval.getResultClient().meshmsSendMessage(self.sid, peer.sid, message);
48 | }
49 |
50 | public boolean isRead(){
51 | MeshMSConversation conv = messaging.getPrivateConversation(peer);
52 | return conv == null || conv.isRead;
53 | }
54 |
55 | public void markRead() throws ServalDInterfaceException, MeshMSException, IOException {
56 | if (serval.uiHandler.isOnThread())
57 | throw new IllegalStateException();
58 | serval.getResultClient().meshmsMarkAllMessagesRead(self.sid, peer.sid);
59 | messaging.refresh();
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/mid/Observer.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.mid;
2 |
3 | public interface Observer {
4 | void updated(T obj);
5 | }
6 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/mid/ObserverProxy.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.mid;
2 |
3 | import android.util.Log;
4 |
5 | import java.util.HashSet;
6 | import java.util.Set;
7 |
8 | public class ObserverProxy implements IObserverSet, Observer {
9 | private final IObserverSet source;
10 | private final I item;
11 | private final Set> UIobservers = new HashSet<>();
12 | private final Set> backgroundObservers = new HashSet<>();
13 | private final CallbackHandler uiHandler;
14 |
15 | public ObserverProxy(CallbackHandler uiHandler, IObserverSet source, I item){
16 | this.uiHandler = uiHandler;
17 | this.source = source;
18 | this.item = item;
19 | }
20 |
21 | @Override
22 | public void addUI(Observer observer) {
23 | if (UIobservers.isEmpty())
24 | source.addUI(this);
25 | UIobservers.add(observer);
26 | }
27 |
28 | @Override
29 | public void removeUI(Observer observer) {
30 | UIobservers.remove(observer);
31 | if (UIobservers.isEmpty())
32 | source.removeUI(this);
33 | }
34 |
35 | @Override
36 | public void addBackground(Observer observer) {
37 | if (backgroundObservers.isEmpty())
38 | source.addBackground(this);
39 | backgroundObservers.add(observer);
40 | }
41 |
42 | @Override
43 | public void removeBackground(Observer observer) {
44 | backgroundObservers.remove(observer);
45 | if (backgroundObservers.isEmpty())
46 | source.removeBackground(this);
47 | }
48 |
49 | @Override
50 | public I getObj() {
51 | return item;
52 | }
53 |
54 | @Override
55 | public void updated(S obj) {
56 | Set> observers = (uiHandler.isOnThread())?UIobservers:backgroundObservers;
57 | for(Observer o : observers)
58 | o.updated(item);
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/mid/ObserverSet.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.mid;
2 |
3 | import android.os.Handler;
4 | import android.os.Message;
5 |
6 | import java.util.HashSet;
7 | import java.util.Set;
8 |
9 | /**
10 | * Created by jeremy on 4/05/16.
11 | */
12 | public class ObserverSet implements Handler.Callback, IObserverSet {
13 | private final Set> UIobservers = new HashSet<>();
14 | private final Set> backgroundObservers = new HashSet<>();
15 |
16 | private final CallbackHandler UIhandler;
17 | private final CallbackHandler backgroundHandler;
18 | private final T obj;
19 | private static final String TAG = "ObserverSet";
20 |
21 | private static final int UICallbacks = 1;
22 | private static final int BackgroundCallbacks = 2;
23 |
24 | public ObserverSet(Serval serval, T obj) {
25 | this.UIhandler = serval.uiHandler;
26 | this.backgroundHandler = serval.backgroundHandler;
27 | this.obj = obj;
28 | }
29 |
30 | public T getObj(){
31 | return obj;
32 | }
33 |
34 | @Override
35 | public void addUI(Observer observer) {
36 | UIobservers.add(observer);
37 | }
38 |
39 | @Override
40 | public void removeUI(Observer observer) {
41 | UIobservers.remove(observer);
42 | }
43 |
44 | @Override
45 | public void addBackground(Observer observer) {
46 | backgroundObservers.add(observer);
47 | }
48 |
49 | @Override
50 | public void removeBackground(Observer observer) {
51 | backgroundObservers.remove(observer);
52 | }
53 |
54 | public void onUpdate() {
55 | if (!UIobservers.isEmpty())
56 | UIhandler.sendEmptyMessage(this, UICallbacks);
57 | if (!backgroundObservers.isEmpty())
58 | backgroundHandler.sendEmptyMessage(this, BackgroundCallbacks);
59 | }
60 |
61 | @Override
62 | public boolean handleMessage(Message message) {
63 | Set> observers;
64 | switch (message.what){
65 | case UICallbacks:
66 | observers = UIobservers;
67 | break;
68 | case BackgroundCallbacks:
69 | observers = backgroundObservers;
70 | break;
71 | default:
72 | return false;
73 | }
74 | for (Observer observer : observers)
75 | observer.updated(obj);
76 | return true;
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/mid/Peer.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.mid;
2 |
3 | import android.util.Log;
4 |
5 | import org.servalproject.servaldna.RouteLink;
6 | import org.servalproject.servaldna.ServalDCommand;
7 | import org.servalproject.servaldna.Subscriber;
8 | import org.servalproject.servaldna.SubscriberId;
9 |
10 | /**
11 | * Created by jeremy on 4/05/16.
12 | */
13 | public final class Peer implements Comparable {
14 | private static long nextId = 0;
15 | private final long id;
16 |
17 | Peer(Serval serval, Subscriber subscriber) {
18 | this.subscriber = subscriber;
19 | observers = new ObserverSet<>(serval, this);
20 | id = nextId++;
21 | }
22 |
23 | public final ObserverSet observers;
24 | private Subscriber subscriber;
25 |
26 | public Subscriber getSubscriber() {
27 | return subscriber;
28 | }
29 |
30 | public void updateSubscriber(Subscriber subscriber) {
31 | if (this.subscriber.sid.equals(subscriber.sid)
32 | && this.subscriber.signingKey == null) {
33 | this.subscriber = subscriber;
34 | observers.onUpdate();
35 | }
36 | }
37 |
38 | ServalDCommand.LookupResult lookup;
39 |
40 | public String getDid() {
41 | return lookup == null ? null : lookup.did;
42 | }
43 |
44 | public String getName() {
45 | return lookup == null ? null : lookup.name;
46 | }
47 |
48 | void update(ServalDCommand.LookupResult result) {
49 | lookup = result;
50 | observers.onUpdate();
51 | }
52 |
53 | RouteLink link;
54 | Interface netInterface;
55 | Peer priorHop;
56 |
57 | public boolean isReachable() {
58 | return link != null;
59 | }
60 |
61 | public int getHopCount(){
62 | return link != null ? link.hop_count : -1;
63 | }
64 |
65 | public Peer getPriorHop(){
66 | return isReachable() ? priorHop : null;
67 | }
68 |
69 | public Interface getNetInterface(){
70 | return netInterface;
71 | }
72 |
73 | public boolean isContact() {
74 | return false;
75 | }
76 |
77 | public boolean isBlocked() {
78 | return false;
79 | }
80 |
81 | void update(RouteLink route, Interface netInterface, Peer priorHop) {
82 | link = route.isReachable() ? route : null;
83 | this.netInterface = netInterface;
84 | this.priorHop = priorHop;
85 | observers.onUpdate();
86 | }
87 |
88 | public long getId() {
89 | // return a stable id, for UI list binding.
90 | return id;
91 | }
92 |
93 | @Override
94 | public String toString() {
95 | return "Peer{" +
96 | "subscriber=" + subscriber +
97 | ", lookup=" + lookup +
98 | ", link=" + link +
99 | ", interface=" + (netInterface==null?"None":netInterface.name) +
100 | ", prior=" + (priorHop==null?"None":priorHop.subscriber.sid.abbreviation()) +
101 | '}';
102 | }
103 |
104 | private String feedName;
105 | public String getFeedName(){
106 | return feedName;
107 | }
108 |
109 | public String displayName() {
110 | String n = feedName;
111 | if (n == null || "".equals(n))
112 | n = getName();
113 | if (n == null || "".equals(n))
114 | n = getDid();
115 | if (n == null || "".equals(n))
116 | n = subscriber.sid.abbreviation();
117 | return n;
118 | }
119 |
120 | public void updateFeedName(String name) {
121 | if (feedName == null && name == null)
122 | return;
123 | if (name != null && name.equals(feedName))
124 | return;
125 | feedName = name;
126 | observers.onUpdate();
127 | }
128 |
129 | public MessageFeed getFeed() {
130 | return new MessageFeed(Serval.getInstance(), this);
131 | }
132 |
133 | @Override
134 | public int compareTo(Peer another) {
135 | int r = this.displayName().compareTo(another.displayName());
136 | if (r!=0)
137 | return r;
138 | return subscriber.sid.toHex().compareTo(another.subscriber.sid.toHex());
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/mid/networking/AbstractListObserver.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.mid.networking;
2 |
3 | import org.servalproject.mid.ListObserver;
4 |
5 | public abstract class AbstractListObserver implements ListObserver {
6 | @Override
7 | public void added(T obj) {
8 |
9 | }
10 |
11 | @Override
12 | public void removed(T obj) {
13 |
14 | }
15 |
16 | @Override
17 | public void updated(T obj) {
18 |
19 | }
20 |
21 | @Override
22 | public void reset() {
23 |
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/mid/networking/FlightModeObserver.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.mid.networking;
2 |
3 | import android.content.ContentResolver;
4 | import android.database.ContentObserver;
5 | import android.net.Uri;
6 | import android.os.Build;
7 | import android.os.Handler;
8 | import android.provider.Settings;
9 | import android.util.Log;
10 |
11 | /**
12 | * Created by jeremy on 31/05/17.
13 | */
14 |
15 | public class FlightModeObserver extends ContentObserver {
16 | private final Networks networks;
17 | private final ContentResolver resolver;
18 | boolean flightMode;
19 | String flightModeRadios = "";
20 | String flightModeToggleable= "";
21 |
22 | private static final String TAG = "FlightMode";
23 |
24 | public FlightModeObserver(Networks networks, ContentResolver resolver, Handler handler) {
25 | super(handler);
26 | this.networks = networks;
27 | this.resolver = resolver;
28 | }
29 |
30 | void register(){
31 | if (Build.VERSION.SDK_INT >= 17) {
32 | resolver.registerContentObserver(Settings.Global.CONTENT_URI, true, this);
33 | }else {
34 | resolver.registerContentObserver(Settings.System.CONTENT_URI, true, this);
35 | }
36 | onChange(false, null);
37 | }
38 |
39 | void unregister(){
40 | resolver.unregisterContentObserver(this);
41 | }
42 |
43 | @Override
44 | public void onChange(boolean selfChange) {
45 | onChange(selfChange, null);
46 | }
47 |
48 | @SuppressWarnings("deprecation")
49 | @Override
50 | public void onChange(boolean selfChange, Uri uri) {
51 | boolean airplaneMode;
52 | String airplaneRadios;
53 | String airplaneToggleable;
54 |
55 | try {
56 | if (Build.VERSION.SDK_INT >= 17){
57 | airplaneMode = Settings.Global.getInt(resolver, Settings.Global.AIRPLANE_MODE_ON) !=0;
58 | airplaneRadios = Settings.Global.getString(resolver, Settings.Global.AIRPLANE_MODE_RADIOS);
59 | airplaneToggleable= Settings.Global.getString(resolver, "airplane_mode_toggleable_radios");
60 | }else {
61 | airplaneMode = Settings.System.getInt(resolver, Settings.System.AIRPLANE_MODE_ON) !=0;
62 | airplaneRadios = Settings.System.getString(resolver, Settings.System.AIRPLANE_MODE_RADIOS);
63 | airplaneToggleable = Settings.System.getString(resolver, "airplane_mode_toggleable_radios");
64 | }
65 | } catch (Settings.SettingNotFoundException e) {
66 | throw new IllegalStateException(e);
67 | }
68 |
69 | Log.v(TAG, airplaneMode+", ["+airplaneRadios+"], ["+airplaneToggleable+"]");
70 |
71 | if (airplaneMode == this.flightMode
72 | && this.flightModeRadios.equals(airplaneRadios)
73 | && this.flightModeToggleable.equals(airplaneToggleable))
74 | return;
75 |
76 | this.flightMode = airplaneMode;
77 | this.flightModeRadios = airplaneRadios;
78 | this.flightModeToggleable = airplaneToggleable;
79 | networks.onFlightModeChanged();
80 | }
81 | }
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/mid/networking/NetworkInfo.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.mid.networking;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.util.Log;
6 |
7 | import org.servalproject.mid.ObserverSet;
8 | import org.servalproject.mid.Serval;
9 | import org.servalproject.servalchat.R;
10 |
11 | /**
12 | * Created by jeremy on 2/11/16.
13 | */
14 | public abstract class NetworkInfo {
15 | private static final String TAG = "NetworkInfo";
16 | public enum State{
17 | Off(R.string.stopped),
18 | Starting(R.string.starting),
19 | Stopping(R.string.stopping),
20 | On(R.string.started),
21 | Error(R.string.unknown_error);
22 |
23 | public final int stringResource;
24 | State(int stringResource){
25 | this.stringResource = stringResource;
26 | }
27 |
28 | public String getString(Context context){
29 | if (stringResource == 0)
30 | throw new IllegalArgumentException();
31 | return context.getString(stringResource);
32 | }
33 | }
34 |
35 | public final ObserverSet observers;
36 | protected final Serval serval;
37 |
38 | public NetworkInfo(Serval serval){
39 | this.serval = serval;
40 | observers = new ObserverSet<>(serval, this);
41 | }
42 |
43 | public abstract String getName(Context context);
44 | public abstract void enable(Context context);
45 | public abstract void disable(Context context);
46 | public abstract Intent getIntent(Context context);
47 | public abstract String getRadioName();
48 |
49 | public boolean isUsable(){
50 | return isOn();
51 | }
52 |
53 | public boolean isOn(){
54 | switch (getState()){
55 | case On:
56 | return true;
57 | default:
58 | return false;
59 | }
60 | }
61 |
62 | public void toggle(Context context){
63 | if (isUsable())
64 | disable(context);
65 | else
66 | enable(context);
67 | }
68 |
69 | public String getStatus(Context context){
70 | return getState().getString(context);
71 | }
72 |
73 | private State state = State.Error;
74 | protected void setState(State state){
75 | if (state == this.state)
76 | return;
77 | Log.v(TAG, getName(serval.context)+" changed to "+state);
78 | this.state = state;
79 | observers.onUpdate();
80 | }
81 |
82 | public State getState(){
83 | return state;
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/mid/networking/WifiClient.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.mid.networking;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.net.wifi.WifiManager;
6 | import android.util.Log;
7 |
8 | import org.servalproject.mid.Serval;
9 | import org.servalproject.servalchat.R;
10 |
11 | /**
12 | * Created by jeremy on 2/11/16.
13 | */
14 | public class WifiClient extends NetworkInfo{
15 | private WifiManager manager;
16 | private static final String TAG = "WifiClient";
17 |
18 | protected WifiClient(Serval serval) {
19 | super(serval);
20 | manager = (WifiManager) serval.context.getSystemService(Context.WIFI_SERVICE);
21 | setState(statusToState(manager.getWifiState()));
22 | }
23 |
24 | @Override
25 | public String getName(Context context) {
26 | return context.getString(R.string.wifi_client);
27 | }
28 |
29 | @Override
30 | public String getStatus(Context context) {
31 | if (getState()==State.Off && Networks.getInstance().getGoal() == Networks.WifiGoal.ClientOn)
32 | return context.getString(R.string.queued);
33 | return super.getStatus(context);
34 | }
35 |
36 | void setEnabled(boolean enabled){
37 | manager.setWifiEnabled(enabled);
38 | }
39 |
40 | @Override
41 | public void enable(Context context) {
42 | Networks.getInstance().setWifiGoal(Networks.WifiGoal.ClientOn);
43 | }
44 |
45 | @Override
46 | public void disable(Context context) {
47 | Networks.getInstance().setWifiGoal(Networks.WifiGoal.Off);
48 | }
49 |
50 | @Override
51 | public Intent getIntent(Context context) {
52 | return new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK);
53 | }
54 |
55 | @Override
56 | public String getRadioName() {
57 | return "wifi";
58 | }
59 |
60 | public static State statusToState(int state) {
61 | switch (state) {
62 | case WifiManager.WIFI_STATE_DISABLED:
63 | return State.Off;
64 | case WifiManager.WIFI_STATE_DISABLING:
65 | return State.Stopping;
66 | case WifiManager.WIFI_STATE_ENABLED:
67 | return State.On;
68 | case WifiManager.WIFI_STATE_ENABLING:
69 | return State.Starting;
70 | default:
71 | Log.v(TAG, "Unknown state: "+state);
72 | return State.Error;
73 | }
74 | }
75 |
76 | public void onStateChanged(Intent intent) {
77 | setState(statusToState(intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, -1)));
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/mid/networking/WifiHotspotChanges.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.mid.networking;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.Context;
5 | import android.content.Intent;
6 |
7 | import org.servalproject.mid.Serval;
8 |
9 | /**
10 | * Created by jeremy on 2/11/16.
11 | */
12 | public class WifiHotspotChanges extends BroadcastReceiver {
13 | public static final String WIFI_AP_STATE_CHANGED_ACTION = "android.net.wifi.WIFI_AP_STATE_CHANGED";
14 |
15 | @Override
16 | public void onReceive(Context context, Intent intent) {
17 | String action = intent.getAction();
18 | Hotspot hotspot = Networks.getInstance().wifiHotspot;
19 | if (hotspot == null)
20 | return;
21 |
22 | if (action.equals(WIFI_AP_STATE_CHANGED_ACTION)) {
23 | hotspot.onStateChanged(intent);
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/mid/networking/WifiNetworkChanges.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.mid.networking;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.net.wifi.WifiManager;
7 |
8 | import org.servalproject.mid.Serval;
9 |
10 | public class WifiNetworkChanges extends BroadcastReceiver {
11 | @Override
12 | public void onReceive(Context context, Intent intent) {
13 | String action = intent.getAction();
14 | WifiClient wifiClient = Networks.getInstance().wifiClient;
15 |
16 | if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
17 | wifiClient.onStateChanged(intent);
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/mid/networking/bluetooth/BlueToothInfo.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.mid.networking.bluetooth;
2 |
3 | import android.bluetooth.BluetoothAdapter;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.provider.Settings;
7 |
8 | import org.servalproject.mid.Serval;
9 | import org.servalproject.mid.networking.NetworkInfo;
10 | import org.servalproject.servalchat.R;
11 |
12 | /**
13 | * Created by jeremy on 8/11/16.
14 | */
15 |
16 | public class BlueToothInfo extends NetworkInfo {
17 | private final BlueToothControl control;
18 |
19 | BlueToothInfo(BlueToothControl control, Serval serval) {
20 | super(serval);
21 | this.control = control;
22 | }
23 | @Override
24 | public String getName(Context context) {
25 | return context.getString(R.string.bluetooth);
26 | }
27 |
28 | @Override
29 | public String getStatus(Context context) {
30 | if (getState()==State.On && !control.isDiscoverable())
31 | return context.getString(R.string.not_discoverable);
32 | return super.getStatus(context);
33 | }
34 |
35 | @Override
36 | public void enable(Context context) {
37 | control.requestDiscoverable(context);
38 | }
39 |
40 | @Override
41 | public void disable(Context context) {
42 | if (control.isEnabled())
43 | control.adapter.disable();
44 | }
45 |
46 | @Override
47 | public Intent getIntent(Context context) {
48 | return new Intent(Settings.ACTION_BLUETOOTH_SETTINGS);
49 | }
50 |
51 | @Override
52 | public String getRadioName() {
53 | return "bluetooth";
54 | }
55 |
56 | @Override
57 | public boolean isUsable() {
58 | return control.isDiscoverable();
59 | }
60 |
61 | static State statusToState(int status){
62 | switch (status){
63 | case BluetoothAdapter.STATE_ON:
64 | return State.On;
65 | case BluetoothAdapter.STATE_OFF:
66 | return State.Off;
67 | case BluetoothAdapter.STATE_TURNING_ON:
68 | return State.Starting;
69 | case BluetoothAdapter.STATE_TURNING_OFF:
70 | return State.Stopping;
71 | default:
72 | return State.Error;
73 | }
74 | }
75 |
76 | void setState(int state) {
77 | setState(statusToState(state));
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/mid/networking/bluetooth/BluetoothNetworkChanges.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.mid.networking.bluetooth;
2 |
3 | import android.bluetooth.BluetoothAdapter;
4 | import android.bluetooth.BluetoothDevice;
5 | import android.content.BroadcastReceiver;
6 | import android.content.Context;
7 | import android.content.Intent;
8 |
9 | import org.servalproject.mid.Serval;
10 | import org.servalproject.mid.networking.Networks;
11 |
12 | public class BluetoothNetworkChanges extends BroadcastReceiver {
13 | @Override
14 | public void onReceive(Context context, Intent intent) {
15 | BlueToothControl blueTooth = Networks.getInstance().blueTooth;
16 | if (blueTooth == null)
17 | return;
18 | String action = intent.getAction();
19 | if (action.equals(BluetoothDevice.ACTION_FOUND)) {
20 | blueTooth.onFound(intent);
21 | } else if (action.equals(BluetoothDevice.ACTION_NAME_CHANGED)) {
22 | blueTooth.onRemoteNameChanged(intent);
23 | } else if (action.equals(BluetoothAdapter.ACTION_DISCOVERY_STARTED)) {
24 | blueTooth.scanner.onDiscoveryStarted();
25 | } else if (action.equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)) {
26 | blueTooth.scanner.onDiscoveryFinished();
27 | } else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
28 | blueTooth.onStateChange(intent);
29 | } else if (action.equals(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED)) {
30 | blueTooth.scanner.onScanModeChanged(intent);
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/mid/networking/bluetooth/Connector.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.mid.networking.bluetooth;
2 |
3 | import android.bluetooth.BluetoothAdapter;
4 | import android.bluetooth.BluetoothSocket;
5 | import android.os.Build;
6 | import android.os.SystemClock;
7 | import android.util.Log;
8 |
9 | import java.io.IOException;
10 |
11 | /**
12 | * Created by jeremy on 7/04/15.
13 | */
14 | class Connector implements Runnable{
15 | private final BlueToothControl control;
16 | private final BluetoothAdapter adapter;
17 | public final PeerState peer;
18 | private final boolean paired;
19 | private long connectionStarted=0;
20 | private boolean connecting = false;
21 | private BluetoothSocket socket = null;
22 |
23 | private static final String TAG = "Connector";
24 |
25 | Connector(BlueToothControl control, PeerState peer, boolean paired) {
26 | this.control = control;
27 | this.adapter = control.adapter;
28 | this.peer = peer;
29 | this.paired = paired;
30 | }
31 |
32 | @Override
33 | public void run() {
34 | connectionStarted = SystemClock.elapsedRealtime();
35 |
36 | try {
37 | if (paired)
38 | socket = peer.device.createRfcommSocketToServiceRecord(BlueToothControl.SECURE_UUID);
39 | else if (Build.VERSION.SDK_INT >= 10) {
40 | socket = peer.device.createInsecureRfcommSocketToServiceRecord(BlueToothControl.INSECURE_UUID);
41 | }
42 | } catch (IOException e){
43 | Log.v(TAG, "Failed to create socket", e);
44 | }
45 |
46 | if (socket!=null) {
47 | try {
48 | socket.connect();
49 | Log.v(TAG, "Connected to " + peer);
50 | int bias = peer.device.getName().toLowerCase().compareTo(control.adapter.getName().toLowerCase()) * 500;
51 | peer.onConnected(socket, paired, bias);
52 | } catch (IOException e) {
53 | try {
54 | socket.close();
55 | } catch (IOException e1) {
56 | }
57 | Log.v(TAG, "Connection failed to " + peer);
58 | peer.onConnectionFailed();
59 | }
60 | socket = null;
61 | }
62 | connecting = false;
63 | control.remove(this);
64 | }
65 |
66 | public void connect() {
67 | connecting = true;
68 | control.serval.runOnThreadPool(this);
69 | }
70 |
71 | public void cancel() {
72 | BluetoothSocket s = socket;
73 | if (s == null)
74 | return;
75 | try {
76 | Log.v(TAG, "Cancelling connection to " + peer);
77 | s.close();
78 | } catch (IOException e) {
79 | }
80 | }
81 |
82 | public synchronized void moveNext() {
83 | if (!connecting)
84 | connect();
85 | else if (connectionStarted!=0 && SystemClock.elapsedRealtime() - connectionStarted > 5000)
86 | cancel();
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/mid/networking/bluetooth/PeerReader.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.mid.networking.bluetooth;
2 |
3 | import android.bluetooth.BluetoothSocket;
4 | import android.os.SystemClock;
5 | import android.util.Log;
6 |
7 | import java.io.EOFException;
8 | import java.io.IOException;
9 | import java.io.InputStream;
10 |
11 | /**
12 | * Created by jeremy on 7/04/15.
13 | */
14 | class PeerReader implements Runnable, Comparable {
15 | private final BlueToothControl control;
16 | public final boolean secure;
17 | public final BluetoothSocket socket;
18 | private final PeerState peer;
19 | private final int bias;
20 | private Thread thread;
21 | public final String name;
22 | long lastReceived;
23 | long lastWritten;
24 |
25 | private static int __id = 0;
26 |
27 | PeerReader(BlueToothControl control, PeerState peer, BluetoothSocket socket, boolean secure, int bias) {
28 | name = "PeerReader" + (__id++);
29 | this.control = control;
30 | this.peer = peer;
31 | this.socket = socket;
32 | this.secure = secure;
33 | this.bias = bias;
34 | lastReceived = SystemClock.elapsedRealtime();
35 | }
36 |
37 | public void start() {
38 | if (thread != null)
39 | return;
40 | thread = new Thread(this, "Reader" + peer.device.getAddress());
41 | thread.start();
42 | }
43 |
44 | public boolean isRunning() {
45 | return thread != null;
46 | }
47 |
48 | @Override
49 | public void run() {
50 | try {
51 | InputStream in = socket.getInputStream();
52 | byte buff[] = new byte[BlueToothControl.MTU + 2];
53 | int offset = 0;
54 | while (thread == Thread.currentThread()) {
55 | if (offset >= 2) {
56 | int msgLen = (buff[0] & 0xFF) | ((buff[1] & 0xFF) << 8);
57 | if (msgLen > buff.length - 2 || msgLen < 0)
58 | throw new IllegalStateException(msgLen + " is greater than the link MTU");
59 | if (offset >= msgLen + 2) {
60 | control.receivedPacket(peer.addrBytes, buff, 2, msgLen);
61 | if (offset > msgLen + 2)
62 | System.arraycopy(buff, msgLen + 2, buff, 0, offset - (msgLen + 2));
63 | offset -= msgLen + 2;
64 | continue;
65 | }
66 | }
67 | int len = in.read(buff, offset, buff.length - offset);
68 | if (len < 0)
69 | throw new EOFException();
70 | lastReceived = SystemClock.elapsedRealtime();
71 | offset += len;
72 | }
73 | } catch (IOException e) {
74 | Log.e(name, e.getMessage(), e);
75 | }finally {
76 | if (thread == Thread.currentThread())
77 | thread = null;
78 |
79 | peer.onClosed(this);
80 | }
81 | }
82 |
83 | @Override
84 | public int compareTo(PeerReader peerReader) {
85 | if (peerReader == this)
86 | return 0;
87 | if (this.bias + this.lastReceived < peerReader.bias + peerReader.lastReceived)
88 | return -1;
89 | return 1;
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/mid/networking/bluetooth/Scanner.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.mid.networking.bluetooth;
2 |
3 | import android.bluetooth.BluetoothAdapter;
4 | import android.content.Intent;
5 | import android.os.SystemClock;
6 | import android.util.Log;
7 |
8 | import org.servalproject.mid.networking.NetworkInfo;
9 |
10 | public class Scanner{
11 | private final BlueToothControl control;
12 | private final BluetoothAdapter adapter;
13 | private int scanMode;
14 | private long lastScanStart=0;
15 | private long lastPeerScanned=0;
16 | private long lastScanEnd=1;
17 |
18 | private static final String TAG = "BTScanner";
19 |
20 | Scanner(BlueToothControl control, BluetoothAdapter adapter){
21 | this.control = control;
22 | this.adapter = adapter;
23 | setState();
24 | }
25 |
26 | private void setState(int newMode){
27 | if (scanMode == newMode)
28 | return;
29 | scanMode = newMode;
30 | Log.v(TAG, "Scan mode changed; " + scanMode + " " + adapter.isEnabled());
31 | lastScanStart=0;
32 | lastPeerScanned=0;
33 | lastScanEnd=1;
34 | if (adapter.isEnabled() && adapter.isDiscovering())
35 | lastScanStart = SystemClock.elapsedRealtime();
36 | }
37 |
38 | void setState(){
39 | setState(adapter.getScanMode());
40 | }
41 |
42 | public void onScanModeChanged(Intent intent) {
43 | setState(intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE, 0));
44 | }
45 |
46 | public void onDiscoveryStarted() {
47 | lastScanStart = SystemClock.elapsedRealtime();
48 | Log.v(TAG, "Discovery Started");
49 | // TODO set alarm to cancel / restart bluetooth
50 | }
51 |
52 | public void onDiscoveryFinished() {
53 | lastScanEnd = SystemClock.elapsedRealtime();
54 | Log.v(TAG, "Discovery finished");
55 | control.runNext();
56 | }
57 |
58 | public void onPeerScanned(){
59 | lastPeerScanned = SystemClock.elapsedRealtime();
60 | }
61 |
62 | public boolean isDiscovering() {
63 | return adapter.isDiscovering();
64 | }
65 |
66 | public void startDiscovery() {
67 | if (control.networkInfo.getState() != NetworkInfo.State.On || !adapter.isEnabled())
68 | return;
69 | if (adapter.startDiscovery())
70 | lastScanStart = SystemClock.elapsedRealtime();
71 | }
72 |
73 | private static final int RESCAN_INTERVAL = 30000;
74 | private static final int SCAN_IDLE_TIME = 5000;
75 | private static final int CANCEL_BROKEN = 10000;
76 | private static final int POLL_INTERVAL = 1000;
77 |
78 | public int nextScanAction(Connector item){
79 | long now = SystemClock.elapsedRealtime();
80 |
81 | if (lastScanStart < lastScanEnd){
82 | if (item!=null) {
83 | item.moveNext();
84 | return POLL_INTERVAL;
85 | }
86 |
87 | long nextScanDelay = lastScanEnd + RESCAN_INTERVAL - now;
88 | if (nextScanDelay <=0) {
89 | startDiscovery();
90 | return POLL_INTERVAL;
91 | }
92 | return (int) nextScanDelay;
93 | }
94 |
95 | int delay = (item == null) ? RESCAN_INTERVAL : SCAN_IDLE_TIME;
96 |
97 | long cancelAfter = Math.max(lastScanStart, lastPeerScanned) + delay - now;
98 | if (cancelAfter <=0){
99 | cancelDiscovery();
100 | if (cancelAfter <= - CANCEL_BROKEN && cancelAfter >= - RESCAN_INTERVAL){
101 | if (item == null)
102 | startDiscovery();
103 | else {
104 | item.moveNext();
105 | }
106 | }
107 | return POLL_INTERVAL;
108 | }
109 | return (int) cancelAfter;
110 | }
111 |
112 | public void cancelDiscovery(){
113 | if (control.networkInfo.getState() != NetworkInfo.State.On || !adapter.isEnabled())
114 | return;
115 | if (adapter.isDiscovering())
116 | adapter.cancelDiscovery();
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/mid/networking/bluetooth/SocketListener.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.mid.networking.bluetooth;
2 |
3 | import android.bluetooth.BluetoothServerSocket;
4 | import android.bluetooth.BluetoothSocket;
5 | import android.util.Log;
6 |
7 | import java.io.IOException;
8 | import java.util.UUID;
9 |
10 | class SocketListener extends Thread {
11 | private BlueToothControl blueToothControl;
12 | private final BluetoothServerSocket socket;
13 | private final boolean secure;
14 | private boolean running = true;
15 |
16 | private static final String appName = "Serval";
17 | private static final String TAG = "BTListener";
18 |
19 | private SocketListener(BlueToothControl blueToothControl, BluetoothServerSocket socket, boolean secure, UUID uuid) {
20 | super(secure ? "BluetoothSL" : "BluetoothISL");
21 | this.blueToothControl = blueToothControl;
22 | this.secure = secure;
23 | this.socket = socket;
24 | this.start();
25 | Log.v(TAG, "Listening for; " + uuid);
26 | }
27 |
28 | public static SocketListener create(BlueToothControl blueToothControl, boolean secure, UUID uuid) throws IOException {
29 | if (!blueToothControl.adapter.isEnabled())
30 | return null;
31 | BluetoothServerSocket socket;
32 | if (secure){
33 | socket = blueToothControl.adapter.listenUsingRfcommWithServiceRecord(appName, uuid);
34 | }else{
35 | socket = blueToothControl.adapter.listenUsingInsecureRfcommWithServiceRecord(appName, uuid);
36 | }
37 | return new SocketListener(blueToothControl, socket, secure, uuid);
38 | }
39 |
40 | public void close() {
41 | try {
42 | running = false;
43 | socket.close();
44 | } catch (IOException e) {
45 | Log.e(TAG, e.getMessage(), e);
46 | }
47 | }
48 |
49 | @Override
50 | public void run() {
51 | while (running && blueToothControl.adapter.isEnabled()) {
52 | try {
53 | BluetoothSocket client = socket.accept();
54 | Log.v(TAG, "Incoming connection from " + client.getRemoteDevice().getAddress());
55 | PeerState peer = blueToothControl.getPeer(client.getRemoteDevice());
56 | int bias = peer.device.getName().toLowerCase().compareTo(blueToothControl.adapter.getName().toLowerCase()) * -500;
57 | peer.onConnected(client, secure, bias);
58 | } catch (Exception e) {
59 | Log.e(TAG, e.getMessage(), e);
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/ForegroundService.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat;
2 |
3 | import android.app.PendingIntent;
4 | import android.app.Service;
5 | import android.content.Intent;
6 | import android.os.IBinder;
7 | import android.support.annotation.Nullable;
8 | import android.support.v4.app.NotificationCompat;
9 |
10 | import org.servalproject.servalchat.navigation.MainActivity;
11 | import org.servalproject.servalchat.navigation.Navigation;
12 |
13 | /**
14 | * Created by jeremy on 19/10/16.
15 | * Create a notification so that android wont just kill our process while a network is viable
16 | */
17 | public class ForegroundService extends Service {
18 |
19 | @Nullable
20 | @Override
21 | public IBinder onBind(Intent intent) {
22 | return null;
23 | }
24 |
25 | @Override
26 | public int onStartCommand(Intent intent, int flags, int startId) {
27 | if (intent != null && intent.getBooleanExtra("foreground", false)) {
28 | Intent navIntent = MainActivity.getIntentFor(this, Navigation.Networking, null, null, null);
29 | PendingIntent pending = PendingIntent.getActivity(this, 0, navIntent, PendingIntent.FLAG_UPDATE_CURRENT);
30 |
31 | NotificationCompat.Builder builder =
32 | new NotificationCompat.Builder(this, App.CHANNEL_ID)
33 | .setSmallIcon(R.mipmap.serval_head)
34 | .setContentTitle(getString(R.string.foreground_title))
35 | .setContentText(getString(R.string.foreground_text))
36 | .setContentIntent(pending);
37 | this.startForeground(-1, builder.build());
38 | return START_STICKY;
39 | } else {
40 | stopForeground(true);
41 | stopSelf();
42 | return START_NOT_STICKY;
43 | }
44 | }
45 |
46 | @Override
47 | public void onDestroy() {
48 | super.onDestroy();
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/SampleData.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat;
2 |
3 | import org.servalproject.mid.Identity;
4 | import org.servalproject.mid.IdentityFeed;
5 | import org.servalproject.mid.ListObserver;
6 | import org.servalproject.mid.MessageList;
7 | import org.servalproject.mid.Observer;
8 | import org.servalproject.mid.Peer;
9 | import org.servalproject.mid.Serval;
10 | import org.servalproject.mid.Server;
11 | import org.servalproject.mid.networking.AbstractListObserver;
12 | import org.servalproject.servaldna.ServalDInterfaceException;
13 | import org.servalproject.servaldna.meshmb.MeshMBCommon;
14 | import org.servalproject.servaldna.meshms.MeshMSException;
15 |
16 | import java.io.IOException;
17 | import java.util.ArrayList;
18 | import java.util.List;
19 |
20 | /**
21 | * Created by jeremy on 1/05/17.
22 | */
23 |
24 | public class SampleData {
25 | private static SampleData instance;
26 |
27 | private final Serval serval;
28 | public SampleData(final Serval serval) {
29 | this.serval = serval;
30 | serval.identities.listObservers.addBackground(new AbstractListObserver() {
31 | @Override
32 | public void reset() {
33 | createTestData(serval);
34 | }
35 | });
36 | }
37 |
38 | public static void createTestData(Serval serval){
39 | try {
40 | if (!serval.identities.isLoaded()
41 | || serval.identities.getIdentities().size()>0)
42 | return;
43 |
44 | List identities = new ArrayList<>();
45 | List peers = new ArrayList<>();
46 | for (int i=0;i<4;i++) {
47 | Identity id = serval.identities.addIdentity("", "Sample User " + i, "");
48 | peers.add(serval.knownPeers.getPeer(id.subscriber));
49 | identities.add(id);
50 | }
51 | for (int i=0;i
28 | implements ILifecycle, INavigate, ListObserver {
29 | private MainActivity activity;
30 | private final SortedList list;
31 | private Identity identity;
32 | private int generation = -1;
33 |
34 | public BlockList(Context context, @Nullable AttributeSet attrs) {
35 | super(context, attrs, R.string.empty_blocklist);
36 | listAdapter.setHasStableIds(true);
37 | setHasFixedSize(true);
38 | RecyclerHelper.createLayoutManager(this, true, false);
39 | RecyclerHelper.createDivider(this);
40 | SortedListAdapterCallback listCallback = new SortedListAdapterCallback(listAdapter) {
41 | @Override
42 | public int compare(Peer o1, Peer o2) {
43 | return o1.compareTo(o2);
44 | }
45 |
46 | @Override
47 | public boolean areContentsTheSame(Peer oldItem, Peer newItem) {
48 | return oldItem.equals(newItem);
49 | }
50 |
51 | @Override
52 | public boolean areItemsTheSame(Peer item1, Peer item2) {
53 | return item1.equals(item2);
54 | }
55 | };
56 | list = new SortedList(Peer.class, listCallback);
57 | }
58 |
59 | @Override
60 | protected PeerHolder createHolder(ViewGroup parent, int viewType) {
61 | return new PeerHolder(activity, parent);
62 | }
63 |
64 | @Override
65 | protected void bind(PeerHolder holder, Peer item) {
66 | holder.bind(item);
67 | }
68 |
69 | @Override
70 | protected Peer get(int position) {
71 | return list.get(position);
72 | }
73 |
74 | @Override
75 | protected int getCount() {
76 | return list.size();
77 | }
78 |
79 | @Override
80 | public void added(Peer obj) {
81 | list.add(obj);
82 | }
83 |
84 | @Override
85 | public void removed(Peer obj) {
86 | list.remove(obj);
87 | }
88 |
89 | @Override
90 | public void updated(Peer obj) {
91 | }
92 |
93 | @Override
94 | public void reset() {
95 | list.beginBatchedUpdates();
96 | list.clear();
97 | list.addAll(identity.messaging.getBlockList());
98 | list.endBatchedUpdates();
99 | }
100 |
101 | @Override
102 | public void onVisible() {
103 | int g = identity.messaging.observeBlockList.add(this);
104 | if (g != generation)
105 | reset();
106 | }
107 |
108 | @Override
109 | public void onHidden() {
110 | generation = identity.messaging.observeBlockList.remove(this);
111 | }
112 |
113 | @Override
114 | public void onDetach(boolean configChanging) {
115 |
116 | }
117 |
118 | @Override
119 | public ILifecycle onAttach(MainActivity activity, Navigation n, Identity id, Peer peer, Bundle args) {
120 | this.activity = activity;
121 | this.identity = id;
122 | return this;
123 | }
124 |
125 | }
126 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/feeds/Contacts.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat.feeds;
2 |
3 | import android.content.Context;
4 | import android.os.Bundle;
5 | import android.support.annotation.Nullable;
6 | import android.support.v7.util.SortedList;
7 | import android.support.v7.widget.util.SortedListAdapterCallback;
8 | import android.util.AttributeSet;
9 | import android.util.Log;
10 | import android.view.ViewGroup;
11 |
12 | import org.servalproject.mid.Identity;
13 | import org.servalproject.mid.ListObserver;
14 | import org.servalproject.mid.Peer;
15 | import org.servalproject.servalchat.R;
16 | import org.servalproject.servalchat.navigation.ILifecycle;
17 | import org.servalproject.servalchat.navigation.INavigate;
18 | import org.servalproject.servalchat.navigation.MainActivity;
19 | import org.servalproject.servalchat.navigation.Navigation;
20 | import org.servalproject.servalchat.peer.PeerHolder;
21 | import org.servalproject.servalchat.views.RecyclerHelper;
22 | import org.servalproject.servalchat.views.SimpleRecyclerView;
23 |
24 | /**
25 | * Created by jeremy on 30/05/17.
26 | */
27 |
28 | public class Contacts extends SimpleRecyclerView
29 | implements ILifecycle, INavigate, ListObserver {
30 | private MainActivity activity;
31 | private final SortedList list;
32 | private Identity identity;
33 | private int generation = -1;
34 | private static final String TAG = "Contacts";
35 |
36 | public Contacts(Context context, @Nullable AttributeSet attrs) {
37 | super(context, attrs, R.string.empty_contacts);
38 | listAdapter.setHasStableIds(true);
39 | setHasFixedSize(true);
40 | RecyclerHelper.createLayoutManager(this, true, false);
41 | RecyclerHelper.createDivider(this);
42 | SortedListAdapterCallback listCallback = new SortedListAdapterCallback(listAdapter) {
43 | @Override
44 | public int compare(Peer o1, Peer o2) {
45 | return o1.compareTo(o2);
46 | }
47 |
48 | @Override
49 | public boolean areContentsTheSame(Peer oldItem, Peer newItem) {
50 | return oldItem.equals(newItem);
51 | }
52 |
53 | @Override
54 | public boolean areItemsTheSame(Peer item1, Peer item2) {
55 | return item1.equals(item2);
56 | }
57 | };
58 | list = new SortedList(Peer.class, listCallback);
59 | }
60 |
61 | @Override
62 | protected PeerHolder createHolder(ViewGroup parent, int viewType) {
63 | return new PeerHolder(activity, parent);
64 | }
65 |
66 | @Override
67 | protected void bind(PeerHolder holder, Peer item) {
68 | holder.bind(item);
69 | }
70 |
71 | @Override
72 | protected Peer get(int position) {
73 | return list.get(position);
74 | }
75 |
76 | @Override
77 | protected int getCount() {
78 | Log.v(TAG, "getCount "+list.size());
79 | return list.size();
80 | }
81 |
82 | @Override
83 | public void added(Peer obj) {
84 | Log.v(TAG, "Adding "+obj.displayName());
85 | list.add(obj);
86 | }
87 |
88 | @Override
89 | public void removed(Peer obj) {
90 | Log.v(TAG, "Removing "+obj.displayName());
91 | list.remove(obj);
92 | }
93 |
94 | @Override
95 | public void updated(Peer obj) {
96 | Log.v(TAG, "Updated "+obj.displayName());
97 | }
98 |
99 | @Override
100 | public void reset() {
101 | Log.v(TAG, "Reset");
102 | list.beginBatchedUpdates();
103 | list.clear();
104 | list.addAll(identity.messaging.contacts);
105 | list.endBatchedUpdates();
106 | }
107 |
108 | @Override
109 | public void onVisible() {
110 | int g = identity.messaging.observeContacts.add(this);
111 | if (g != generation)
112 | reset();
113 | }
114 |
115 | @Override
116 | public void onHidden() {
117 | generation = identity.messaging.observeContacts.remove(this);
118 | }
119 |
120 | @Override
121 | public void onDetach(boolean configChanging) {
122 |
123 | }
124 |
125 | @Override
126 | public ILifecycle onAttach(MainActivity activity, Navigation n, Identity id, Peer peer, Bundle args) {
127 | this.activity = activity;
128 | this.identity = id;
129 | return this;
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/feeds/FeedAdapter.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat.feeds;
2 |
3 | import android.support.v7.widget.RecyclerView;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.TextView;
8 |
9 | import org.servalproject.mid.MessageFeed;
10 | import org.servalproject.servalchat.R;
11 | import org.servalproject.servalchat.navigation.MainActivity;
12 | import org.servalproject.servalchat.views.BasicViewHolder;
13 | import org.servalproject.servalchat.views.ScrollingAdapter;
14 | import org.servalproject.servalchat.views.TimestampView;
15 | import org.servalproject.servaldna.meshmb.PlyMessage;
16 |
17 | /**
18 | * Created by jeremy on 8/08/16.
19 | */
20 | public abstract class FeedAdapter extends ScrollingAdapter {
21 | public FeedAdapter(MessageFeed feed) {
22 | super(feed, R.string.empty_feed);
23 | }
24 |
25 | @Override
26 | public MessageHolder create(ViewGroup parent, int viewType) {
27 | LayoutInflater inflater = LayoutInflater.from(parent.getContext());
28 | return new MessageHolder(inflater.inflate(R.layout.feed_message, parent, false));
29 | }
30 |
31 | @Override
32 | protected void bind(MessageHolder holder, PlyMessage item) {
33 | }
34 |
35 | @Override
36 | protected void bindItem(MessageHolder holder, int position) {
37 | holder.bind(getItem(position), position>0?getItem(position - 1):null);
38 | }
39 |
40 | @Override
41 | public void insertedItem(PlyMessage item, int position) {
42 | super.insertedItem(item, position);
43 | if (position+1 {
28 |
29 | private final PublicFeedsPresenter presenter;
30 | private HashSet bundles = new HashSet<>();
31 |
32 | public FeedListAdapter(FeedList list, PublicFeedsPresenter presenter) {
33 | super(list, R.string.empty_feed_list);
34 | this.presenter = presenter;
35 | }
36 |
37 | private void removeItem(BundleId id) {
38 | // find the old item and remove it.
39 | for (int i = 0; i < items.size(); i++) {
40 | if (items.get(i).manifest.id.equals(id)) {
41 | items.remove(i);
42 | return;
43 | }
44 | }
45 | }
46 |
47 | @Override
48 | protected void addItem(int index, RhizomeListBundle item) {
49 | if (item.author == null && item.manifest.sender == null)
50 | return;
51 | Subscriber subscriber = new Subscriber(
52 | item.author != null ? item.author : item.manifest.sender,
53 | item.manifest.id, true);
54 | BundleId id = item.manifest.id;
55 | Messaging.SubscriptionState state = presenter.identity.messaging.getSubscriptionState(subscriber);
56 | if (state == Messaging.SubscriptionState.Blocked)
57 | return;
58 | if (bundles.contains(id)) {
59 | if (index == items.size())
60 | return;
61 | removeItem(id);
62 | }else{
63 | bundles.add(id);
64 | }
65 | super.addItem(index, item);
66 | }
67 |
68 | @Override
69 | protected MainActivity getActivity() {
70 | return presenter.getActivity();
71 | }
72 |
73 | @Override
74 | protected void bind(FeedHolder holder, RhizomeListBundle item) {
75 | holder.bind(item);
76 | }
77 |
78 | @Override
79 | public FeedHolder create(ViewGroup parent, int viewType) {
80 | LayoutInflater inflater = LayoutInflater.from(parent.getContext());
81 | return new FeedHolder(inflater.inflate(R.layout.feed, parent, false));
82 | }
83 |
84 | public class FeedHolder extends BasicViewHolder implements View.OnClickListener {
85 | private TextView name;
86 | private ImageView icon;
87 | private Subscriber subscriber;
88 |
89 | public FeedHolder(View itemView) {
90 | super(itemView);
91 | this.name = (TextView) this.itemView.findViewById(R.id.name);
92 | this.icon = (ImageView) this.itemView.findViewById(R.id.identicon);
93 | this.itemView.setOnClickListener(this);
94 | }
95 |
96 | public void bind(RhizomeListBundle item) {
97 | subscriber = new Subscriber(
98 | item.author != null ? item.author : item.manifest.sender,
99 | item.manifest.id, true);
100 | this.icon.setImageDrawable(new Identicon(item.manifest.id));
101 | if (item.manifest.name == null || "".equals(item.manifest.name))
102 | name.setText(subscriber.sid.abbreviation());
103 | else
104 | name.setText(item.manifest.name);
105 | }
106 |
107 | @Override
108 | public void onClick(View v) {
109 | presenter.openFeed(subscriber);
110 | }
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/feeds/MyFeed.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat.feeds;
2 |
3 | import android.content.Context;
4 | import android.os.Bundle;
5 | import android.support.v7.widget.RecyclerView;
6 | import android.util.AttributeSet;
7 | import android.view.View;
8 | import android.widget.Button;
9 | import android.widget.EditText;
10 | import android.widget.RelativeLayout;
11 |
12 | import org.servalproject.mid.Identity;
13 | import org.servalproject.mid.Peer;
14 | import org.servalproject.servalchat.R;
15 | import org.servalproject.servalchat.navigation.ILifecycle;
16 | import org.servalproject.servalchat.navigation.INavigate;
17 | import org.servalproject.servalchat.navigation.MainActivity;
18 | import org.servalproject.servalchat.navigation.Navigation;
19 | import org.servalproject.servalchat.views.RecyclerHelper;
20 |
21 | /**
22 | * Created by jeremy on 8/08/16.
23 | */
24 | public class MyFeed extends RelativeLayout
25 | implements INavigate, View.OnClickListener {
26 | Button post;
27 | EditText message;
28 | RecyclerView list;
29 | MyFeedPresenter presenter;
30 | MainActivity activity;
31 |
32 | public MyFeed(Context context, AttributeSet attrs) {
33 | super(context, attrs);
34 | }
35 |
36 | @Override
37 | public ILifecycle onAttach(MainActivity activity, Navigation n, Identity id, Peer peer, Bundle args) {
38 | this.activity = activity;
39 | this.post = (Button) findViewById(R.id.post);
40 | this.post.setOnClickListener(this);
41 | this.message = (EditText) findViewById(R.id.message);
42 | this.list = (RecyclerView) findViewById(R.id.activity);
43 | RecyclerHelper.createLayoutManager(list, true, false);
44 | RecyclerHelper.createDivider(list);
45 | return presenter = MyFeedPresenter.factory.getPresenter(this, id, peer, args);
46 | }
47 |
48 | @Override
49 | public void onClick(View v) {
50 | switch (v.getId()) {
51 | case R.id.post:
52 | presenter.post();
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/feeds/MyFeedPresenter.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat.feeds;
2 |
3 | import android.os.Bundle;
4 |
5 | import org.servalproject.mid.Identity;
6 | import org.servalproject.mid.IdentityFeed;
7 | import org.servalproject.mid.Peer;
8 | import org.servalproject.mid.Serval;
9 | import org.servalproject.servalchat.App;
10 | import org.servalproject.servalchat.navigation.MainActivity;
11 | import org.servalproject.servalchat.navigation.Navigation;
12 | import org.servalproject.servalchat.views.BackgroundWorker;
13 | import org.servalproject.servalchat.views.Presenter;
14 | import org.servalproject.servalchat.views.PresenterFactory;
15 | import org.servalproject.servaldna.Subscriber;
16 |
17 | /**
18 | * Created by jeremy on 8/08/16.
19 | */
20 | public class MyFeedPresenter extends Presenter {
21 | private final IdentityFeed feed;
22 | private ActivityAdapter adapter;
23 |
24 | protected MyFeedPresenter(PresenterFactory factory, String key, Identity identity) {
25 | super(factory, key, identity);
26 | this.feed = identity.getFeed();
27 | }
28 |
29 | public static final PresenterFactory factory = new PresenterFactory() {
30 | @Override
31 | protected MyFeedPresenter create(String key, Identity id, Peer peer) {
32 | return new MyFeedPresenter(this, key, id);
33 | }
34 | };
35 |
36 | private boolean posting = false;
37 |
38 | private void setEnabled() {
39 | MyFeed view = getView();
40 | if (view == null)
41 | return;
42 | view.post.setEnabled(!posting);
43 | view.message.setEnabled(!posting);
44 | }
45 |
46 | @Override
47 | protected void restore(Bundle config) {
48 | super.restore(config);
49 | adapter = new ActivityAdapter(identity.getActivity(), this);
50 | }
51 |
52 | @Override
53 | protected void bind(MyFeed view) {
54 | view.list.setAdapter(adapter);
55 | setEnabled();
56 | if (App.isTesting() && "".equals(view.message.getText().toString()))
57 | view.message.setText("Sample Post \uD83D\uDE00");
58 | }
59 |
60 | public void post() {
61 | MyFeed view = getView();
62 | if (view == null)
63 | return;
64 | final String message = view.message.getText().toString();
65 | if ("".equals(message))
66 | return;
67 |
68 | posting = true;
69 | setEnabled();
70 | new BackgroundWorker() {
71 | @Override
72 | protected void onBackGround() throws Exception {
73 | feed.sendMessage(message);
74 | }
75 |
76 | @Override
77 | protected void onComplete(Throwable t) {
78 | posting = false;
79 | MyFeed view = getView();
80 | if (view != null) {
81 | if (t == null)
82 | view.message.setText("");
83 | else
84 | view.activity.showError(t);
85 | setEnabled();
86 | }else
87 | rethrow(t);
88 | }
89 | }.execute();
90 | }
91 |
92 | public void openFeed(Subscriber subscriber, long offset) {
93 | MyFeed view = getView();
94 | if (view == null || subscriber.equals(identity.subscriber))
95 | return;
96 | Peer peer = Serval.getInstance().knownPeers.getPeer(subscriber);
97 | Bundle args = new Bundle();
98 | args.putLong("offset", offset);
99 | view.activity.go(Navigation.PeerFeed, peer, args);
100 | }
101 |
102 | protected MainActivity getActivity() {
103 | MyFeed view = getView();
104 | return view == null ? null : view.activity;
105 | }
106 |
107 |
108 | @Override
109 | public void onVisible() {
110 | super.onVisible();
111 | adapter.onVisible();
112 | }
113 |
114 | @Override
115 | public void onHidden() {
116 | super.onHidden();
117 | adapter.onHidden();
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/feeds/PeerFeed.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat.feeds;
2 |
3 | import android.content.Context;
4 | import android.os.Bundle;
5 | import android.support.annotation.Nullable;
6 | import android.support.design.widget.Snackbar;
7 | import android.support.v7.widget.LinearLayoutManager;
8 | import android.support.v7.widget.RecyclerView;
9 | import android.util.AttributeSet;
10 | import android.view.Menu;
11 | import android.view.MenuItem;
12 | import android.widget.LinearLayout;
13 |
14 | import org.servalproject.mid.Identity;
15 | import org.servalproject.mid.Messaging;
16 | import org.servalproject.mid.Peer;
17 | import org.servalproject.servalchat.R;
18 | import org.servalproject.servalchat.navigation.IHaveMenu;
19 | import org.servalproject.servalchat.navigation.ILifecycle;
20 | import org.servalproject.servalchat.navigation.INavigate;
21 | import org.servalproject.servalchat.navigation.MainActivity;
22 | import org.servalproject.servalchat.navigation.Navigation;
23 | import org.servalproject.servalchat.views.RecyclerHelper;
24 | import org.servalproject.servaldna.meshmb.MeshMBCommon;
25 |
26 | /**
27 | * Created by jeremy on 3/08/16.
28 | */
29 | public class PeerFeed extends LinearLayout
30 | implements INavigate {
31 |
32 | RecyclerView list;
33 | PeerFeedPresenter presenter;
34 | MainActivity activity;
35 |
36 | public PeerFeed(Context context, @Nullable AttributeSet attrs) {
37 | super(context, attrs);
38 | }
39 |
40 | @Override
41 | public ILifecycle onAttach(MainActivity activity, Navigation n, Identity id, Peer peer, Bundle args) {
42 | this.activity = activity;
43 | this.list = (RecyclerView) findViewById(R.id.list);
44 | RecyclerHelper.createLayoutManager(list, true, false);
45 | return presenter = PeerFeedPresenter.factory.getPresenter(this, id, peer, args);
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/feeds/PeerFeedPresenter.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat.feeds;
2 |
3 | import android.os.Bundle;
4 |
5 | import org.servalproject.mid.Identity;
6 | import org.servalproject.mid.MessageFeed;
7 | import org.servalproject.mid.Observer;
8 | import org.servalproject.mid.Peer;
9 | import org.servalproject.servalchat.navigation.MainActivity;
10 | import org.servalproject.servalchat.views.Presenter;
11 | import org.servalproject.servalchat.views.PresenterFactory;
12 |
13 | /**
14 | * Created by jeremy on 3/08/16.
15 | */
16 | public class PeerFeedPresenter extends Presenter {
17 |
18 | private FeedAdapter adapter;
19 | private final Peer peer;
20 | private MessageFeed feed;
21 |
22 | private Observer peerObserver = new Observer() {
23 | @Override
24 | public void updated(Peer obj) {
25 | // if we discover a peer signing key, reset our adapter
26 | if (obj.getSubscriber().signingKey != null && (feed == null || feed.getId() == null)){
27 | feed = peer.getFeed();
28 | adapter = new FeedAdapter(feed) {
29 | @Override
30 | protected MainActivity getActivity() {
31 | return PeerFeedPresenter.this.getActivity();
32 | }
33 | };
34 | PeerFeed view = getView();
35 | if (view != null) {
36 | view.list.setAdapter(adapter);
37 | view.activity.supportInvalidateOptionsMenu();
38 | }
39 | }
40 | }
41 | };
42 |
43 | protected PeerFeedPresenter(PresenterFactory factory, String key, Identity identity, Peer peer) {
44 | super(factory, key, identity);
45 | this.peer = peer;
46 | }
47 |
48 | public static PresenterFactory factory
49 | = new PresenterFactory() {
50 |
51 | @Override
52 | protected PeerFeedPresenter create(String key, Identity id, Peer peer) {
53 | return new PeerFeedPresenter(this, key, id, peer);
54 | }
55 |
56 | };
57 |
58 | @Override
59 | protected void bind(PeerFeed feed) {
60 | feed.list.setAdapter(adapter);
61 | }
62 |
63 | @Override
64 | protected void restore(Bundle config) {
65 | feed = peer.getFeed();
66 | adapter = new FeedAdapter(feed) {
67 | @Override
68 | protected MainActivity getActivity() {
69 | return PeerFeedPresenter.this.getActivity();
70 | }
71 | };
72 | }
73 |
74 | protected MainActivity getActivity() {
75 | PeerFeed view = getView();
76 | return view == null ? null : view.activity;
77 | }
78 |
79 | @Override
80 | public void onVisible() {
81 | super.onVisible();
82 | adapter.onVisible();
83 | peer.observers.addUI(this.peerObserver);
84 | }
85 |
86 | @Override
87 | public void onHidden() {
88 | super.onHidden();
89 | adapter.onHidden();
90 | peer.observers.removeUI(this.peerObserver);
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/feeds/PublicFeedsList.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat.feeds;
2 |
3 | import android.content.Context;
4 | import android.os.Bundle;
5 | import android.support.annotation.Nullable;
6 | import android.support.v7.widget.LinearLayoutManager;
7 | import android.support.v7.widget.RecyclerView;
8 | import android.text.Editable;
9 | import android.text.TextWatcher;
10 | import android.util.AttributeSet;
11 | import android.widget.EditText;
12 | import android.widget.RelativeLayout;
13 |
14 | import org.servalproject.mid.Identity;
15 | import org.servalproject.mid.Peer;
16 | import org.servalproject.servalchat.R;
17 | import org.servalproject.servalchat.navigation.ILifecycle;
18 | import org.servalproject.servalchat.navigation.INavigate;
19 | import org.servalproject.servalchat.navigation.MainActivity;
20 | import org.servalproject.servalchat.navigation.Navigation;
21 | import org.servalproject.servalchat.views.RecyclerHelper;
22 |
23 | /**
24 | * Created by jeremy on 11/10/16.
25 | */
26 | public class PublicFeedsList extends RelativeLayout implements INavigate {
27 | PublicFeedsPresenter presenter;
28 | MainActivity activity;
29 | EditText search;
30 | RecyclerView feedList;
31 |
32 | public PublicFeedsList(Context context, @Nullable AttributeSet attrs) {
33 | super(context, attrs);
34 | }
35 |
36 | @Override
37 | public ILifecycle onAttach(MainActivity activity, Navigation n, Identity id, Peer peer, Bundle args) {
38 | this.activity = activity;
39 | search = findViewById(R.id.search);
40 | feedList = findViewById(R.id.feed_list);
41 | search.addTextChangedListener(new TextWatcher() {
42 | @Override
43 | public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
44 |
45 | }
46 |
47 | @Override
48 | public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
49 | presenter.search(charSequence);
50 | }
51 |
52 | @Override
53 | public void afterTextChanged(Editable editable) {
54 |
55 | }
56 | });
57 | RecyclerHelper.createLayoutManager(feedList, true, false);
58 | RecyclerHelper.createDivider(feedList);
59 | presenter = PublicFeedsPresenter.factory.getPresenter(this, id, peer, args);
60 | return presenter;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/feeds/PublicFeedsPresenter.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat.feeds;
2 |
3 | import android.os.Bundle;
4 | import android.util.Log;
5 |
6 | import org.servalproject.mid.FeedList;
7 | import org.servalproject.mid.Identity;
8 | import org.servalproject.mid.KnownPeers;
9 | import org.servalproject.mid.Peer;
10 | import org.servalproject.mid.Serval;
11 | import org.servalproject.servalchat.navigation.MainActivity;
12 | import org.servalproject.servalchat.navigation.Navigation;
13 | import org.servalproject.servalchat.views.Presenter;
14 | import org.servalproject.servalchat.views.PresenterFactory;
15 | import org.servalproject.servaldna.Subscriber;
16 |
17 | /**
18 | * Created by jeremy on 11/10/16.
19 | */
20 | public class PublicFeedsPresenter extends Presenter {
21 | FeedList list;
22 | FeedListAdapter adapter;
23 |
24 | private static final String TAG = "PublicFeedsPresenter";
25 |
26 | protected PublicFeedsPresenter(PresenterFactory factory, String key, Identity identity) {
27 | super(factory, key, identity);
28 | }
29 |
30 | public static PresenterFactory factory
31 | = new PresenterFactory() {
32 |
33 | @Override
34 | protected PublicFeedsPresenter create(String key, Identity id, Peer peer) {
35 | return new PublicFeedsPresenter(this, key, id);
36 | }
37 | };
38 |
39 | @Override
40 | protected void bind(PublicFeedsList view) {
41 | view.feedList.setAdapter(adapter);
42 | }
43 |
44 | @Override
45 | protected void restore(Bundle config) {
46 | list = identity.getAllFeeds(null);
47 | adapter = new FeedListAdapter(list, this);
48 | }
49 |
50 | public void openFeed(Subscriber subscriber) {
51 | PublicFeedsList view = getView();
52 | if (view == null)
53 | return;
54 | if (identity.subscriber.equals(subscriber)) {
55 | view.activity.go(Navigation.MyFeed);
56 | } else {
57 | Peer peer = Serval.getInstance().knownPeers.getPeer(subscriber);
58 | view.activity.go(Navigation.PeerFeed, peer, null);
59 | }
60 | }
61 |
62 | protected MainActivity getActivity() {
63 | PublicFeedsList view = getView();
64 | return view == null ? null : view.activity;
65 | }
66 |
67 | @Override
68 | public void onVisible() {
69 | super.onVisible();
70 | adapter.onVisible();
71 | }
72 |
73 | @Override
74 | public void onHidden() {
75 | super.onHidden();
76 | adapter.onHidden();
77 | }
78 |
79 | public void search(CharSequence search) {
80 | String srch = (search == null || search.equals("")) ? null : "%"+search+"%";
81 | if (list.search == null && srch == null)
82 | return;
83 | if (srch != null && srch.equals(list.search))
84 | return;
85 |
86 | Log.v(TAG, "Replacing adapter for "+srch);
87 | FeedList list = identity.getAllFeeds(srch);
88 | FeedListAdapter adapter = new FeedListAdapter(list, this);
89 | getView().feedList.setAdapter(adapter);
90 | this.adapter.clear();
91 | this.list = list;
92 | this.adapter = adapter;
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/identity/IdentityDetails.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat.identity;
2 |
3 | import android.content.Context;
4 | import android.os.Bundle;
5 | import android.util.AttributeSet;
6 | import android.view.View;
7 | import android.widget.Button;
8 | import android.widget.EditText;
9 | import android.widget.ImageView;
10 | import android.widget.LinearLayout;
11 | import android.widget.TextView;
12 |
13 | import org.servalproject.mid.Identity;
14 | import org.servalproject.mid.Peer;
15 | import org.servalproject.servalchat.R;
16 | import org.servalproject.servalchat.navigation.ILifecycle;
17 | import org.servalproject.servalchat.navigation.INavigate;
18 | import org.servalproject.servalchat.navigation.MainActivity;
19 | import org.servalproject.servalchat.navigation.Navigation;
20 |
21 | /**
22 | * Created by jeremy on 7/06/16.
23 | */
24 | public class IdentityDetails extends LinearLayout
25 | implements INavigate, View.OnClickListener {
26 | MainActivity activity;
27 | ImageView icon;
28 | TextView sidLabel;
29 | TextView sid;
30 | EditText name;
31 | Button update;
32 | IdentityDetailsPresenter presenter;
33 |
34 | public IdentityDetails(Context context, AttributeSet attrs) {
35 | super(context, attrs);
36 | }
37 |
38 | @Override
39 | public ILifecycle onAttach(MainActivity activity, Navigation n, Identity id, Peer peer, Bundle args) {
40 | this.activity = activity;
41 | icon = (ImageView) findViewById(R.id.identicon);
42 | sidLabel = (TextView) findViewById(R.id.sid_label);
43 | sid = (TextView) findViewById(R.id.sid);
44 | name = (EditText) findViewById(R.id.name);
45 | update = (Button) findViewById(R.id.update);
46 | update.setOnClickListener(this);
47 |
48 | presenter = IdentityDetailsPresenter.factory.getPresenter(this, id, peer, args);
49 | return presenter;
50 | }
51 |
52 | @Override
53 | public void onClick(View v) {
54 | switch (v.getId()) {
55 | case R.id.update:
56 | presenter.update();
57 | }
58 | }
59 |
60 | }
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/identity/IdentityDetailsPresenter.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat.identity;
2 |
3 | import android.view.View;
4 |
5 | import org.servalproject.mid.Identity;
6 | import org.servalproject.mid.Peer;
7 | import org.servalproject.mid.Serval;
8 | import org.servalproject.servalchat.App;
9 | import org.servalproject.servalchat.R;
10 | import org.servalproject.servalchat.navigation.Navigation;
11 | import org.servalproject.servalchat.views.BackgroundWorker;
12 | import org.servalproject.servalchat.views.Presenter;
13 | import org.servalproject.servalchat.views.PresenterFactory;
14 |
15 | /**
16 | * Created by jeremy on 20/07/16.
17 | */
18 | public class IdentityDetailsPresenter extends Presenter {
19 |
20 | public static PresenterFactory factory
21 | = new PresenterFactory() {
22 | @Override
23 | protected IdentityDetailsPresenter create(String key, Identity id, Peer peer) {
24 | return new IdentityDetailsPresenter(this, key, id);
25 | }
26 | };
27 |
28 | private boolean updating = false;
29 |
30 | private IdentityDetailsPresenter(PresenterFactory factory, String key, Identity id) {
31 | super(factory, key, id);
32 | }
33 |
34 | @Override
35 | protected void bind(IdentityDetails view) {
36 | if (identity != null) {
37 | view.name.setText(identity.getName());
38 | view.sid.setText(identity.subscriber.sid.toHex());
39 | view.icon.setImageDrawable(identity.getIcon());
40 | view.update.setText(R.string.identity_update);
41 | view.sidLabel.setVisibility(View.VISIBLE);
42 | view.sid.setVisibility(View.VISIBLE);
43 | view.icon.setVisibility(View.VISIBLE);
44 | } else {
45 | view.sidLabel.setVisibility(View.GONE);
46 | view.sid.setVisibility(View.GONE);
47 | view.icon.setVisibility(View.GONE);
48 | view.update.setText(R.string.add_identity);
49 | if (App.isTesting())
50 | view.name.setText("Test User");
51 | }
52 | view.update.setEnabled(!updating);
53 | }
54 |
55 | @Override
56 | public void onVisible() {
57 | IdentityDetails view = getView();
58 | if (view != null)
59 | bind(view);
60 | }
61 |
62 | public void update() {
63 | if (updating)
64 | return;
65 |
66 | IdentityDetails view = getView();
67 | if (view == null)
68 | return;
69 |
70 | updating = true;
71 | view.update.setEnabled(!updating);
72 | final String name = view.name.getText().toString();
73 |
74 | new BackgroundWorker() {
75 | private Identity result;
76 |
77 | @Override
78 | protected void onBackGround() throws Exception {
79 | Serval serval = Serval.getInstance();
80 | if (identity == null) {
81 | result = serval.identities.addIdentity("", name, "");
82 | } else {
83 | serval.identities.updateIdentity(
84 | identity, "", name, "");
85 | result = identity;
86 | }
87 | }
88 |
89 | @Override
90 | protected void onComplete(Throwable t) {
91 | updating = false;
92 | IdentityDetails view = getView();
93 | if (t != null) {
94 | if (view == null) {
95 | rethrow(t);
96 | } else {
97 | view.activity.showError(t);
98 | }
99 | } else if (view != null && view.activity != null){
100 | if (identity == null)
101 | view.activity.go(Navigation.MyFeed, result, null, null, true);
102 | else
103 | view.activity.go(Navigation.MyFeed);
104 | }
105 | }
106 | }.execute();
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/navigation/CountTitle.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat.navigation;
2 |
3 | import android.graphics.drawable.Drawable;
4 |
5 | import org.servalproject.mid.Messaging;
6 | import org.servalproject.mid.ObserverProxy;
7 | import org.servalproject.mid.ObserverSet;
8 | import org.servalproject.mid.Serval;
9 | import org.servalproject.servalchat.navigation.NavTitle;
10 |
11 | public abstract class CountTitle extends NavTitle {
12 | private final CharSequence prefix;
13 |
14 | public CountTitle(ObserverSet observer, CharSequence prefix) {
15 | observers = new ObserverProxy<>(Serval.getInstance().uiHandler, observer, this);
16 | this.prefix = prefix;
17 | }
18 |
19 | @Override
20 | public Drawable getIcon() {
21 | return null;
22 | }
23 |
24 | public abstract int unreadCount();
25 |
26 | @Override
27 | public CharSequence getMenuLabel() {
28 | return prefix+" ("+unreadCount()+")";
29 | }
30 |
31 | @Override
32 | public CharSequence getTitle() {
33 | return prefix;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/navigation/HistoryItem.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat.navigation;
2 |
3 | import android.os.Bundle;
4 |
5 | import org.servalproject.servaldna.SigningKey;
6 | import org.servalproject.servaldna.Subscriber;
7 |
8 | /**
9 | * Created by jeremy on 20/07/16.
10 | */
11 | public class HistoryItem {
12 | public final Navigation key;
13 | public final SigningKey identity;
14 | public final Subscriber peer;
15 | public final Bundle args;
16 |
17 | public HistoryItem(Navigation key, SigningKey identity, Subscriber peer, Bundle args) {
18 | this.key = key;
19 | this.args = args;
20 | this.identity = identity;
21 | this.peer = peer;
22 | }
23 |
24 | @Override
25 | public boolean equals(Object o) {
26 | if (this == o) return true;
27 | if (o == null || getClass() != o.getClass()) return false;
28 |
29 | HistoryItem that = (HistoryItem) o;
30 |
31 | // TODO compare arg values?
32 | return key.equals(that.key) && args == that.args;
33 | }
34 |
35 | @Override
36 | public int hashCode() {
37 | return key.hashCode();
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/navigation/IContainerView.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat.navigation;
2 |
3 | import android.os.Bundle;
4 |
5 | import org.servalproject.mid.Identity;
6 | import org.servalproject.mid.Peer;
7 |
8 | /**
9 | * Created by jeremy on 14/06/16.
10 | */
11 | public interface IContainerView {
12 | void deactivate(ViewState state, boolean configChange, boolean visible);
13 | ViewState activate(Navigation n, Identity identity, Peer peer, Bundle args, boolean visible);
14 | }
15 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/navigation/IHaveMenu.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat.navigation;
2 |
3 | import android.view.Menu;
4 |
5 | /**
6 | * Created by jeremy on 20/06/16.
7 | */
8 | public interface IHaveMenu {
9 | void populateItems(Menu menu);
10 | }
11 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/navigation/ILifecycle.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat.navigation;
2 |
3 | /**
4 | * Created by jeremy on 14/06/16.
5 | */
6 | public interface ILifecycle {
7 |
8 | void onDetach(boolean configChange);
9 |
10 | void onVisible();
11 |
12 | void onHidden();
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/navigation/INavigate.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat.navigation;
2 |
3 | import android.os.Bundle;
4 |
5 | import org.servalproject.mid.Identity;
6 | import org.servalproject.mid.Peer;
7 |
8 | /**
9 | * Created by jeremy on 14/06/16.
10 | */
11 | public interface INavigate {
12 | ILifecycle onAttach(MainActivity activity, Navigation n, Identity id, Peer peer, Bundle args);
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/navigation/INavigatorHost.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat.navigation;
2 |
3 | import android.view.View;
4 |
5 | /**
6 | * Created by jeremy on 15/06/16.
7 | */
8 | public interface INavigatorHost extends IContainerView {
9 | void navigated(boolean backEnabled, boolean upEnabled);
10 |
11 | void rebuildMenu();
12 |
13 | void showSnack(CharSequence message, int length, CharSequence actionLabel, View.OnClickListener action);
14 | }
15 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/navigation/IOnBack.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat.navigation;
2 |
3 | /**
4 | * Created by jeremy on 6/11/17.
5 | */
6 |
7 | public interface IOnBack {
8 | boolean onBack();
9 | }
10 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/navigation/IRootContainer.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat.navigation;
2 |
3 | import android.support.design.widget.CoordinatorLayout;
4 | import android.support.v7.widget.Toolbar;
5 | import android.view.MenuItem;
6 |
7 | /**
8 | * Created by jeremy on 6/11/17.
9 | */
10 |
11 | public interface IRootContainer extends IContainerView{
12 | Toolbar getToolbar();
13 | CoordinatorLayout getCoordinator();
14 | void updateToolbar(boolean canGoBack);
15 | boolean onOptionsItemSelected(MenuItem item);
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/navigation/Id1.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat.navigation;
2 |
3 | /**
4 | * Created by jeremy on 25/07/16.
5 | */
6 | public class Id1 extends MainActivity {
7 | // this class only exists so we can have multiple tasks pre SDK 21
8 | }
9 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/navigation/Id2.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat.navigation;
2 |
3 | /**
4 | * Created by jeremy on 25/07/16.
5 | */
6 | public class Id2 extends MainActivity {
7 | // this class only exists so we can have multiple tasks pre SDK 21
8 | }
9 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/navigation/Id3.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat.navigation;
2 |
3 | /**
4 | * Created by jeremy on 25/07/16.
5 | */
6 | public class Id3 extends MainActivity {
7 | // this class only exists so we can have multiple tasks pre SDK 21
8 | }
9 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/navigation/Id4.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat.navigation;
2 |
3 | /**
4 | * Created by jeremy on 25/07/16.
5 | */
6 | public class Id4 extends MainActivity {
7 | // this class only exists so we can have multiple tasks pre SDK 21
8 | }
9 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/navigation/NavTabStrip.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat.navigation;
2 |
3 | import android.content.Context;
4 | import android.os.Bundle;
5 | import android.support.design.widget.TabLayout;
6 | import android.support.v4.view.ViewPager;
7 | import android.util.AttributeSet;
8 | import android.widget.LinearLayout;
9 |
10 | import org.servalproject.mid.Identity;
11 | import org.servalproject.mid.Peer;
12 | import org.servalproject.servalchat.R;
13 | import org.servalproject.servaldna.SigningKey;
14 | import org.servalproject.servaldna.Subscriber;
15 |
16 | /**
17 | * Created by jeremy on 7/06/16.
18 | */
19 | public class NavTabStrip extends LinearLayout implements IContainerView, INavigate {
20 | NavPageAdapter adapter;
21 | private ViewPager pager;
22 |
23 | public NavTabStrip(Context context, AttributeSet attrs) {
24 | super(context, attrs);
25 | }
26 |
27 | @Override
28 | public void deactivate(ViewState viewState, boolean configChange, boolean visible) {
29 | // Noop, handled by viewpager
30 | }
31 |
32 | @Override
33 | public ViewState activate(Navigation n, Identity identity, Peer peer, Bundle args, boolean visible) {
34 | for (int i = 0; i < adapter.screens.length; i++) {
35 | HistoryItem screen = adapter.screens[i];
36 | if (screen.key.equals(n)) {
37 | if (pager.getCurrentItem() != i)
38 | pager.setCurrentItem(i);
39 | return adapter.getViewState(i);
40 | }
41 | }
42 | throw new IllegalStateException("Cannot locate " + n.name);
43 | }
44 |
45 | @Override
46 | public ILifecycle onAttach(MainActivity activity, Navigation n, Identity id, Peer peer, Bundle args) {
47 | HistoryItem items[] = new HistoryItem[n.children.size()];
48 | // use the same arguments for all tabs
49 | SigningKey key = id == null ? null : id.subscriber.signingKey;
50 | Subscriber subscriber = peer == null ? null : peer.getSubscriber();
51 | for (int i = 0; i < n.children.size(); i++)
52 | items[i] = new HistoryItem(n.children.get(i), key, subscriber, args);
53 | adapter = new NavPageAdapter(activity, id, peer, items);
54 | pager = (ViewPager) findViewById(R.id.pager);
55 | adapter.setViewPager(pager);
56 | TabLayout tabs = (TabLayout) findViewById(R.id.sliding_tabs);
57 | tabs.setupWithViewPager(pager);
58 | return adapter;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/navigation/NavTitle.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat.navigation;
2 |
3 | import android.graphics.drawable.Drawable;
4 |
5 | import org.servalproject.mid.IObserverSet;
6 |
7 | public abstract class NavTitle {
8 | protected IObserverSet observers;
9 |
10 | public abstract Drawable getIcon();
11 |
12 | public CharSequence getMenuLabel(){
13 | return getTitle();
14 | }
15 |
16 | public abstract CharSequence getTitle();
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/navigation/RootAppbar.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat.navigation;
2 |
3 | import android.app.ActivityManager;
4 | import android.content.Context;
5 | import android.os.Build;
6 | import android.os.Bundle;
7 | import android.support.design.widget.CoordinatorLayout;
8 | import android.support.v7.app.ActionBar;
9 | import android.support.v7.widget.Toolbar;
10 | import android.util.AttributeSet;
11 | import android.view.MenuItem;
12 | import android.view.ViewGroup;
13 | import android.widget.LinearLayout;
14 |
15 | import org.servalproject.mid.Identity;
16 | import org.servalproject.mid.Peer;
17 | import org.servalproject.servalchat.R;
18 |
19 | /**
20 | * Created by jeremy on 6/11/17.
21 | */
22 |
23 | public class RootAppbar extends CoordinatorLayout implements IRootContainer, INavigate {
24 | private ViewGroup rootLayout;
25 | Toolbar toolbar;
26 | private MainActivity activity;
27 | private static final String TAG = "RootAppbar";
28 |
29 | public RootAppbar(Context context, AttributeSet attrs) {
30 | this(context, attrs, android.R.attr.dialogPreferenceStyle);
31 | }
32 |
33 | public RootAppbar(Context context, AttributeSet attrs, int defStyleAttr) {
34 | super(context, attrs, defStyleAttr);
35 | }
36 |
37 | @Override
38 | public void deactivate(ViewState state, boolean configChange, boolean visible) {
39 | for(ILifecycle l : state.getLifecycle()) {
40 | if (visible)
41 | l.onHidden();
42 | l.onDetach(configChange);
43 | }
44 | rootLayout.removeView(state.view);
45 | }
46 |
47 | @Override
48 | public ViewState activate(Navigation n, Identity identity, Peer peer, Bundle args, boolean visible) {
49 | ViewState ret = ViewState.Inflate(activity, n, identity, peer, args);
50 | ret.view.setLayoutParams(new LinearLayout.LayoutParams(
51 | ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
52 | rootLayout.addView(ret.view);
53 | for(ILifecycle l : ret.getLifecycle())
54 | l.onVisible();
55 |
56 | activity.setSupportActionBar(toolbar);
57 | NavTitle title = n.getTitle(activity, identity, peer);
58 | toolbar.setTitle(title.getTitle());
59 | if (Build.VERSION.SDK_INT>=21) {
60 | activity.setTaskDescription(new ActivityManager.TaskDescription(title.toString(), identity == null ? null : identity.getBitmap()));
61 | }
62 |
63 | return ret;
64 | }
65 |
66 | @Override
67 | public ILifecycle onAttach(MainActivity activity, Navigation n, Identity id, Peer peer, Bundle args) {
68 | this.activity = activity;
69 | rootLayout = (ViewGroup) findViewById(R.id.root_layout);
70 | toolbar = (Toolbar) findViewById(R.id.app_toolbar);
71 | return null;
72 | }
73 |
74 | @Override
75 | public Toolbar getToolbar() {
76 | return toolbar;
77 | }
78 |
79 | @Override
80 | public CoordinatorLayout getCoordinator() {
81 | return (CoordinatorLayout)findViewById(R.id.coordinator);
82 | }
83 |
84 | @Override
85 | public void updateToolbar(boolean canGoBack) {
86 | ActionBar bar = activity.getSupportActionBar();
87 | bar.setDisplayOptions(
88 | ActionBar.DISPLAY_SHOW_HOME | (canGoBack ? ActionBar.DISPLAY_HOME_AS_UP : 0),
89 | ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_HOME_AS_UP);
90 | }
91 |
92 | @Override
93 | public boolean onOptionsItemSelected(MenuItem item) {
94 | switch (item.getItemId()) {
95 | case android.R.id.home:
96 | return activity.goBack();
97 | }
98 | return false;
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/navigation/ViewState.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat.navigation;
2 |
3 | import android.os.Bundle;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 |
8 | import org.servalproject.mid.Identity;
9 | import org.servalproject.mid.Peer;
10 |
11 | import java.util.ArrayList;
12 | import java.util.List;
13 |
14 | /**
15 | * Created by jeremy on 19/07/16.
16 | */
17 | public class ViewState {
18 | public final Navigation key;
19 | private IContainerView container;
20 | private List lifecycle = new ArrayList<>();
21 | public final View view;
22 | private View firstInput = null;
23 |
24 | public IContainerView getContainer() {
25 | return container;
26 | }
27 |
28 | public List getLifecycle() {
29 | return lifecycle;
30 | }
31 |
32 | public View getTextInput(){
33 | return firstInput;
34 | }
35 |
36 | public interface ViewVisitor{
37 | boolean visit(View view);
38 | }
39 |
40 | public boolean visitViews(View view, ViewVisitor visitor){
41 | if (visitor.visit(view))
42 | return true;
43 | if (view instanceof ViewGroup) {
44 | ViewGroup g = (ViewGroup) view;
45 | for (int i = 0; i < g.getChildCount(); i++) {
46 | if (visitViews(g.getChildAt(i), visitor))
47 | return true;
48 | }
49 | }
50 | return false;
51 | }
52 |
53 | public boolean visit(ViewVisitor visitor){
54 | return visitViews(view, visitor);
55 | }
56 |
57 | public static ViewState Inflate(final MainActivity activity, final Navigation key, final Identity identity, final Peer peer, final Bundle args) {
58 | LayoutInflater inflater = LayoutInflater.from(activity);
59 | View view = inflater.inflate(key.layoutResource, null);
60 | final ViewState ret = new ViewState(key, view);
61 | ret.visit(new ViewVisitor() {
62 | @Override
63 | public boolean visit(View view) {
64 | if (view instanceof INavigate) {
65 | INavigate navigate = (INavigate) view;
66 | ILifecycle l = navigate.onAttach(activity, key, identity, peer, args);
67 | if (l!=null)
68 | ret.lifecycle.add(l);
69 | }
70 | if (view.onCheckIsTextEditor() && ret.firstInput == null)
71 | ret.firstInput = view;
72 | if (view instanceof IContainerView && ret.container == null)
73 | ret.container = (IContainerView) view;
74 | return false;
75 | }
76 | });
77 | return ret;
78 | }
79 |
80 | private ViewState(Navigation key, View view) {
81 | this.key = key;
82 | this.view = view;
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/peer/PeerDetails.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat.peer;
2 |
3 | import android.content.Context;
4 | import android.os.Bundle;
5 | import android.support.design.widget.Snackbar;
6 | import android.util.AttributeSet;
7 | import android.view.Menu;
8 | import android.view.MenuItem;
9 | import android.widget.ImageView;
10 | import android.widget.LinearLayout;
11 | import android.widget.TextView;
12 |
13 | import org.servalproject.mid.Identity;
14 | import org.servalproject.mid.Observer;
15 | import org.servalproject.mid.Peer;
16 | import org.servalproject.mid.Serval;
17 | import org.servalproject.servalchat.R;
18 | import org.servalproject.servalchat.navigation.IHaveMenu;
19 | import org.servalproject.servalchat.navigation.ILifecycle;
20 | import org.servalproject.servalchat.navigation.INavigate;
21 | import org.servalproject.servalchat.navigation.MainActivity;
22 | import org.servalproject.servalchat.navigation.Navigation;
23 | import org.servalproject.servalchat.views.Identicon;
24 | import org.servalproject.servaldna.AbstractId;
25 | import org.servalproject.servaldna.SigningKey;
26 |
27 | /**
28 | * Created by jeremy on 1/08/16.
29 | */
30 | public class PeerDetails extends LinearLayout
31 | implements INavigate, ILifecycle, Observer {
32 |
33 | private MainActivity activity;
34 | private ImageView icon;
35 | private TextView name;
36 | private TextView number;
37 | private TextView numberLabel;
38 | private TextView sid;
39 | private Peer peer;
40 |
41 | public PeerDetails(Context context, AttributeSet attrs) {
42 | super(context, attrs);
43 | }
44 |
45 | @Override
46 | public ILifecycle onAttach(MainActivity activity, Navigation n, Identity id, Peer peer, Bundle args) {
47 | this.activity = activity;
48 | icon = (ImageView) findViewById(R.id.identicon);
49 | name = (TextView) findViewById(R.id.name);
50 | number = (TextView) findViewById(R.id.number);
51 | numberLabel = (TextView) findViewById(R.id.number_label);
52 | sid = (TextView) findViewById(R.id.sid);
53 | this.peer = peer;
54 | updated(this.peer);
55 |
56 | return this;
57 | }
58 |
59 | @Override
60 | public void onDetach(boolean configChange) {
61 |
62 | }
63 |
64 | @Override
65 | public void onVisible() {
66 | peer.observers.addUI(this);
67 | updated(peer);
68 | }
69 |
70 | @Override
71 | public void onHidden() {
72 | peer.observers.removeUI(this);
73 | }
74 |
75 | @Override
76 | public void updated(Peer obj) {
77 | name.setText(peer.displayName());
78 | String did =peer.getDid();
79 | number.setText(did);
80 | SigningKey key = peer.getSubscriber().signingKey;
81 | if (icon.getDrawable() == null && key!=null)
82 | icon.setImageDrawable(new Identicon(key));
83 | number.setVisibility( (did==null || "".equals(did)) ? GONE : VISIBLE);
84 | numberLabel.setVisibility( (did==null || "".equals(did)) ? GONE : VISIBLE);
85 |
86 | sid.setText(peer.getSubscriber().sid.toHex());
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/peer/PeerHolder.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat.peer;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.widget.RecyclerView;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.ImageView;
9 | import android.widget.TextView;
10 |
11 | import org.servalproject.mid.KnownPeers;
12 | import org.servalproject.mid.Peer;
13 | import org.servalproject.servalchat.R;
14 | import org.servalproject.servalchat.navigation.MainActivity;
15 | import org.servalproject.servalchat.navigation.Navigation;
16 | import org.servalproject.servalchat.views.BasicViewHolder;
17 | import org.servalproject.servalchat.views.Identicon;
18 | import org.servalproject.servaldna.SigningKey;
19 |
20 | /**
21 | * Created by jeremy on 30/05/17.
22 | */
23 | public class PeerHolder extends BasicViewHolder implements View.OnClickListener {
24 | private final ImageView icon;
25 | private final TextView name;
26 | private final MainActivity activity;
27 | private Peer peer;
28 |
29 | public PeerHolder(MainActivity activity, ViewGroup parent) {
30 | super(LayoutInflater.from(parent.getContext()).inflate(R.layout.peer, parent, false));
31 | this.activity = activity;
32 | //avatar = (ImageView)this.itemView.findViewById(R.id.avatar);
33 | name = (TextView) this.itemView.findViewById(R.id.name);
34 | icon = (ImageView) this.itemView.findViewById(R.id.identicon);
35 | this.itemView.setOnClickListener(this);
36 | }
37 |
38 | public void bind(Peer peer) {
39 | this.peer = peer;
40 | this.name.setText(peer.displayName());
41 | SigningKey key = peer.getSubscriber().signingKey;
42 | if (key == null){
43 | this.icon.setVisibility(View.INVISIBLE);
44 | }else{
45 | this.icon.setImageDrawable(new Identicon(key));
46 | this.icon.setVisibility(View.VISIBLE);
47 | }
48 |
49 | if (peer.isReachable()) {
50 | // Show green dot?
51 | }
52 | }
53 |
54 | @Override
55 | public void onClick(View v) {
56 | activity.go(Navigation.PeerDetails, peer, null);
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/peer/PeerList.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat.peer;
2 |
3 | import android.content.Context;
4 | import android.support.annotation.Nullable;
5 | import android.util.AttributeSet;
6 | import android.view.ViewGroup;
7 |
8 | import org.servalproject.mid.ListObserverSet;
9 | import org.servalproject.mid.Peer;
10 | import org.servalproject.mid.Serval;
11 | import org.servalproject.servalchat.R;
12 | import org.servalproject.servalchat.views.ObservedRecyclerView;
13 | import org.servalproject.servalchat.views.RecyclerHelper;
14 | import org.servalproject.servaldna.SubscriberId;
15 |
16 | import java.util.ArrayList;
17 | import java.util.Collections;
18 | import java.util.HashSet;
19 | import java.util.List;
20 | import java.util.Set;
21 |
22 | /**
23 | * Created by jeremy on 31/05/16.
24 | */
25 | public class PeerList extends ObservedRecyclerView{
26 | private Serval serval;
27 | private static final String TAG = "PeerList";
28 | private List items = new ArrayList();
29 |
30 | private static ListObserverSet getObserver() {
31 | Serval serval = Serval.getInstance();
32 | if (serval == null)
33 | return null;
34 | return serval.knownPeers.peerListObservers;
35 | }
36 |
37 | public PeerList(Context context, @Nullable AttributeSet attrs) {
38 | super(getObserver(), context, attrs, R.string.empty_peer_list);
39 | listAdapter.setHasStableIds(true);
40 | serval = Serval.getInstance();
41 | }
42 |
43 | @Override
44 | protected void onFinishInflate() {
45 | super.onFinishInflate();
46 | setHasFixedSize(true);
47 | RecyclerHelper.createLayoutManager(this, true, false);
48 | RecyclerHelper.createDivider(this);
49 | }
50 |
51 | @Override
52 | public PeerHolder createHolder(ViewGroup parent, int viewType) {
53 | return new PeerHolder(activity, parent);
54 | }
55 |
56 | @Override
57 | public void bind(PeerHolder holder, Peer item) {
58 | holder.bind(item);
59 | }
60 |
61 | @Override
62 | public long getId(Peer item) {
63 | return item.getId();
64 | }
65 |
66 | private static final int MAP = 1;
67 |
68 | private boolean sorted = false;
69 | private final Set addedPeers = new HashSet<>();
70 |
71 | @Override
72 | public void onVisible() {
73 | addedPeers.clear();
74 | items.clear();
75 | if (serval != null) {
76 | for (Peer p : serval.knownPeers.getReachablePeers())
77 | add(p);
78 | }
79 | super.onVisible();
80 | }
81 |
82 | @Override
83 | public void onHidden() {
84 | super.onHidden();
85 | addedPeers.clear();
86 | items.clear();
87 | }
88 |
89 | private boolean add(Peer p) {
90 | if (!p.isReachable())
91 | return false;
92 | if (addedPeers.contains(p.getSubscriber().sid))
93 | return false;
94 | addedPeers.add(p.getSubscriber().sid);
95 | items.add(p);
96 | sorted = false;
97 | return true;
98 | }
99 |
100 | @Override
101 | protected Peer get(int position) {
102 | sort();
103 | return items.get(position);
104 | }
105 |
106 | @Override
107 | protected int getCount() {
108 | return items.size();
109 | }
110 |
111 | private void sort() {
112 | if (sorted)
113 | return;
114 | Collections.sort(items);
115 | sorted = true;
116 | }
117 |
118 | @Override
119 | public void added(Peer obj) {
120 | if (add(obj))
121 | super.added(obj);
122 | }
123 |
124 | @Override
125 | public void updated(Peer obj) {
126 | add(obj);
127 | sorted = false;
128 | super.updated(obj);
129 | }
130 |
131 | }
132 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/peer/PrivateMessaging.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat.peer;
2 |
3 | import android.content.Context;
4 | import android.os.Bundle;
5 | import android.support.v7.widget.LinearLayoutManager;
6 | import android.support.v7.widget.RecyclerView;
7 | import android.util.AttributeSet;
8 | import android.view.View;
9 | import android.widget.Button;
10 | import android.widget.EditText;
11 | import android.widget.RelativeLayout;
12 |
13 | import org.servalproject.mid.Identity;
14 | import org.servalproject.mid.Peer;
15 | import org.servalproject.servalchat.R;
16 | import org.servalproject.servalchat.navigation.ILifecycle;
17 | import org.servalproject.servalchat.navigation.INavigate;
18 | import org.servalproject.servalchat.navigation.MainActivity;
19 | import org.servalproject.servalchat.navigation.Navigation;
20 | import org.servalproject.servalchat.views.RecyclerHelper;
21 |
22 | /**
23 | * Created by jeremy on 27/07/16.
24 | */
25 | public class PrivateMessaging extends RelativeLayout
26 | implements INavigate, View.OnClickListener {
27 | MainActivity activity;
28 | EditText message;
29 | Button send;
30 | RecyclerView list;
31 | PrivateMessagingPresenter presenter;
32 |
33 | public PrivateMessaging(Context context, AttributeSet attrs) {
34 | super(context, attrs);
35 | }
36 |
37 | @Override
38 | public ILifecycle onAttach(MainActivity activity, Navigation n, Identity id, Peer peer, Bundle args) {
39 | this.activity = activity;
40 | this.message = (EditText) findViewById(R.id.message);
41 | this.send = (Button) findViewById(R.id.send);
42 | this.list = (RecyclerView) findViewById(R.id.message_list);
43 | RecyclerHelper.createLayoutManager(list, true, true);
44 | send.setOnClickListener(this);
45 | presenter = PrivateMessagingPresenter.factory.getPresenter(this, id, peer, args);
46 | return presenter;
47 | }
48 |
49 | @Override
50 | public void onClick(View v) {
51 | if (v.getId() == R.id.send)
52 | presenter.send();
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/views/BackgroundWorker.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat.views;
2 |
3 | import org.servalproject.mid.Serval;
4 | import org.servalproject.servaldna.ServalDInterfaceException;
5 | import org.servalproject.servaldna.meshms.MeshMSException;
6 |
7 | import java.io.IOException;
8 |
9 | /**
10 | * Created by jeremy on 15/11/17.
11 | */
12 |
13 | // A simple background worker, with simpler exception handling than using AsncTask
14 | public abstract class BackgroundWorker implements Runnable{
15 | private final Serval serval;
16 | private int state = IDLE;
17 | private Throwable ex;
18 | static final int IDLE=0;
19 | static final int RUNNING=1;
20 |
21 | protected BackgroundWorker(){
22 | this.serval = Serval.getInstance();
23 | }
24 |
25 | protected abstract void onBackGround() throws Exception;
26 | protected abstract void onComplete(Throwable t);
27 |
28 | public boolean isRunning(){
29 | return state == RUNNING;
30 | }
31 |
32 | protected void rethrow(Throwable t){
33 | if (t == null)
34 | return;
35 | if (t instanceof RuntimeException)
36 | throw (RuntimeException) t;
37 | throw new IllegalStateException(t);
38 | }
39 |
40 | @Override
41 | public void run() {
42 | if (state!=RUNNING)
43 | throw new IllegalStateException();
44 | if (serval.uiHandler.isOnThread()){
45 | state = IDLE;
46 | Throwable e = this.ex;
47 | ex = null;
48 | onComplete(e);
49 | }else{
50 | try {
51 | onBackGround();
52 | }catch (Throwable t){
53 | ex = t;
54 | }
55 | serval.uiHandler.post(this);
56 | }
57 | }
58 |
59 | public void execute(){
60 | if (state != IDLE)
61 | throw new IllegalStateException();
62 | state = RUNNING;
63 | serval.runOnThreadPool(this);
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/views/BasicViewHolder.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat.views;
2 |
3 | import android.support.v7.widget.RecyclerView;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 |
8 | /**
9 | * Created by jeremy on 30/01/17.
10 | */
11 |
12 | public abstract class BasicViewHolder extends RecyclerView.ViewHolder {
13 | public BasicViewHolder(View itemView) {
14 | super(itemView);
15 | }
16 | public BasicViewHolder(LayoutInflater inflater, int layout_id, ViewGroup parent){
17 | this(inflater.inflate(layout_id, parent, false));
18 | }
19 | public BasicViewHolder(int layout_id, ViewGroup parent){
20 | this(LayoutInflater.from(parent.getContext()), layout_id, parent);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/views/DisplayError.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat.views;
2 |
3 | import android.content.Context;
4 | import android.os.Bundle;
5 | import android.support.annotation.Nullable;
6 | import android.support.v7.widget.AppCompatTextView;
7 | import android.util.AttributeSet;
8 |
9 | import org.servalproject.mid.Identity;
10 | import org.servalproject.mid.Peer;
11 | import org.servalproject.servalchat.navigation.ILifecycle;
12 | import org.servalproject.servalchat.navigation.INavigate;
13 | import org.servalproject.servalchat.navigation.MainActivity;
14 | import org.servalproject.servalchat.navigation.Navigation;
15 |
16 | /**
17 | * Created by jeremy on 8/01/18.
18 | */
19 |
20 | public class DisplayError extends AppCompatTextView implements INavigate {
21 |
22 | public DisplayError(Context context, @Nullable AttributeSet attrs) {
23 | super(context, attrs);
24 | }
25 |
26 | public static final String MESSAGE = "message";
27 |
28 | @Override
29 | public ILifecycle onAttach(MainActivity activity, Navigation n, Identity id, Peer peer, Bundle args) {
30 | if (args!=null){
31 | int res = args.getInt(MESSAGE, -1);
32 | if (res!=-1)
33 | setText(res);
34 | }
35 | return null;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/views/FutureList.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat.views;
2 |
3 | import android.support.v7.widget.RecyclerView;
4 |
5 | import java.util.AbstractList;
6 | import java.util.ArrayList;
7 | import java.util.Collections;
8 | import java.util.List;
9 |
10 | /**
11 | * Supposed to provide fast random access and grow efficiently from both ends
12 | * notifying an adapter when items are added.
13 | * Created by jeremy on 31/01/17.
14 | */
15 |
16 | public class FutureList
17 | extends AbstractList{
18 |
19 | private ScrollingAdapter adapter;
20 | private final List past = new ArrayList<>();
21 | private final List future = new ArrayList<>();
22 |
23 | public FutureList(ScrollingAdapter adapter){
24 | this.adapter = adapter;
25 | }
26 |
27 | public T get(int position){
28 | if (position<0)
29 | return null;
30 | int futureSize = future.size();
31 | if (position>=0 && position < futureSize)
32 | return future.get(futureSize - 1 - position);
33 | position -= futureSize;
34 | if (position < past.size())
35 | return past.get(position);
36 | return null;
37 | }
38 |
39 | @Override
40 | public int size() {
41 | return past.size() + future.size();
42 | }
43 |
44 | @Override
45 | public void add(int index, T item){
46 | int futureSize = future.size();
47 | if (index <= futureSize){
48 | future.add(futureSize - index, item);
49 | }else{
50 | past.add(index - futureSize, item);
51 | }
52 | adapter.insertedItem(item, index);
53 | }
54 |
55 | @Override
56 | public boolean add(T item){
57 | past.add(item);
58 | adapter.insertedItem(item, size() - 1);
59 | return true;
60 | }
61 |
62 | public int find(T item){
63 | int index = Collections.binarySearch((List extends Comparable super T>>) this, item);
64 | if (index<0)
65 | index = (-index)-1;
66 | return index;
67 | }
68 |
69 | @Override
70 | public T remove(int index) {
71 | T ret = null;
72 | if (index>0){
73 | int futureSize = future.size();
74 | if (index>0 && index < futureSize) {
75 | ret = future.remove(futureSize - 1 - index);
76 | }else{
77 | index -= futureSize;
78 | if (index< past.size())
79 | ret = past.remove(index);
80 | }
81 | if (ret!=null)
82 | adapter.notifyItemRemoved(index);
83 | }
84 | return ret;
85 | }
86 |
87 | public boolean removeItem(T item) {
88 | int index = Collections.binarySearch((List extends Comparable super T>>) this, item);
89 | if (index<0)
90 | return false;
91 | remove(index);
92 | return true;
93 | }
94 |
95 | @Override
96 | public void clear() {
97 | past.clear();
98 | future.clear();
99 | adapter.notifyDataSetChanged();
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/views/Identicon.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat.views;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.Canvas;
5 | import android.graphics.Color;
6 | import android.graphics.ColorFilter;
7 | import android.graphics.Paint;
8 | import android.graphics.PixelFormat;
9 | import android.graphics.Rect;
10 | import android.graphics.drawable.Drawable;
11 | import android.support.annotation.IntRange;
12 | import android.support.annotation.NonNull;
13 | import android.support.annotation.Nullable;
14 | import android.util.Log;
15 |
16 | import org.servalproject.mid.Identity;
17 | import org.servalproject.servaldna.AbstractId;
18 |
19 | import java.nio.ByteBuffer;
20 | import java.security.MessageDigest;
21 | import java.security.NoSuchAlgorithmException;
22 |
23 | /**
24 | * Created by jeremy on 26/06/17.
25 | */
26 |
27 | public class Identicon extends Drawable {
28 | private final Bitmap bitPattern;
29 | private final Paint bitmapPaint;
30 | private final Rect dest = new Rect();
31 | private static final String TAG = "Identicon";
32 |
33 | public Identicon(AbstractId id){
34 | this(id.getBinary());
35 | }
36 |
37 | private static final int patterns[] = new int[]{
38 | 0b00101, 0b00110, 0b01001, 0b01010,
39 | 0b01011, 0b01100, 0b01101, 0b01110,
40 | 0b10010, 0b10011, 0b10100, 0b10101,
41 | 0b10110, 0b10111, 0b11001, 0b11010
42 | };
43 |
44 | public Identicon(byte[] value) {
45 |
46 | try {
47 | MessageDigest md = MessageDigest.getInstance("SHA-256");
48 | md.update(value);
49 | value = md.digest();
50 | } catch (NoSuchAlgorithmException e) {
51 | throw new IllegalStateException(e);
52 | }
53 | ByteBuffer b = ByteBuffer.wrap(value);
54 |
55 | int H = b.get() & 0xFF;
56 | byte val = b.get();
57 | int S = val &0xF;
58 | int V = val >>4 &0xF;
59 |
60 | int fg = Color.HSVToColor(new float[]{H/255f * 360f, S/31f + 0.5f, V/31f + 0.5f});
61 | int bg = 0;
62 |
63 | int colors[] = new int[25];
64 |
65 | for (int i=0; i<3; i++){
66 | val = ((i&1)==0) ? b.get() : (byte) (val >> 4);
67 | // use 4 bits to pick a 5 bit pattern so we never get all on or all off
68 | int bitPattern = patterns[val & 0xf];
69 | colors[i] = ((bitPattern & 1) !=0) ? fg : bg;
70 | colors[i+5] = ((bitPattern & 2) !=0) ? fg : bg;
71 | colors[i+10] = ((bitPattern & 4) !=0) ? fg : bg;
72 | colors[i+15] = ((bitPattern & 8) !=0) ? fg : bg;
73 | colors[i+20] = ((bitPattern & 16) !=0) ? fg : bg;
74 | }
75 | // copy column 1 to 5 and 2 to 4
76 | for (int i=0;i<25;i+=5){
77 | colors[i+4] = colors[i];
78 | colors[i+3] = colors[i+1];
79 | }
80 |
81 | bitPattern = Bitmap.createBitmap(colors, 5, 5, Bitmap.Config.ARGB_8888);
82 | bitmapPaint = new Paint();
83 | bitmapPaint.setAntiAlias(false);
84 | }
85 |
86 | @Override
87 | public void draw(@NonNull Canvas canvas) {
88 | dest.set(0,0,canvas.getWidth(),canvas.getHeight());
89 | dest.inset(canvas.getWidth() / 10,canvas.getHeight() / 10);
90 | canvas.drawColor(0xFFF0F0F0);
91 | canvas.drawBitmap(bitPattern, null, dest, bitmapPaint);
92 | }
93 |
94 | @Override
95 | public void setAlpha(@IntRange(from = 0, to = 255) int i) {
96 |
97 | }
98 |
99 | @Override
100 | public void setColorFilter(@Nullable ColorFilter colorFilter) {
101 |
102 | }
103 |
104 | @Override
105 | public int getOpacity() {
106 | return PixelFormat.UNKNOWN;
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/views/MessageViewHolder.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat.views;
2 |
3 | import android.view.ViewGroup;
4 |
5 | import org.servalproject.servalchat.R;
6 |
7 | /**
8 | * Created by jeremy on 10/01/18.
9 | */
10 |
11 | public class MessageViewHolder extends BasicViewHolder {
12 | public MessageViewHolder(int textResource, ViewGroup parent) {
13 | super(R.layout.error, parent);
14 | DisplayError e = (DisplayError)itemView.findViewById(R.id.error);
15 | e.setText(textResource);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/views/ObservedRecyclerView.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat.views;
2 |
3 | import android.content.Context;
4 | import android.os.Bundle;
5 | import android.support.annotation.Nullable;
6 | import android.support.v7.widget.RecyclerView;
7 | import android.util.AttributeSet;
8 |
9 | import org.servalproject.mid.Identity;
10 | import org.servalproject.mid.ListObserver;
11 | import org.servalproject.mid.ListObserverSet;
12 | import org.servalproject.mid.Peer;
13 | import org.servalproject.servalchat.navigation.ILifecycle;
14 | import org.servalproject.servalchat.navigation.INavigate;
15 | import org.servalproject.servalchat.navigation.MainActivity;
16 | import org.servalproject.servalchat.navigation.Navigation;
17 |
18 | /**
19 | * Created by jeremy on 20/06/16.
20 | */
21 | public abstract class ObservedRecyclerView
22 | extends SimpleRecyclerView
23 | implements ILifecycle, INavigate, ListObserver {
24 |
25 | protected MainActivity activity;
26 | protected Identity identity;
27 | protected Peer peer;
28 | private ListObserverSet observerSet;
29 | private int generation = -1;
30 |
31 | @Override
32 | public void added(T obj) {
33 | notifyChanged();
34 | }
35 |
36 | @Override
37 | public void removed(T obj) {
38 | notifyChanged();
39 | }
40 |
41 | @Override
42 | public void updated(T obj) {
43 | notifyChanged();
44 | }
45 |
46 | @Override
47 | public void reset() {
48 | notifyChanged();
49 | }
50 |
51 | public ObservedRecyclerView(ListObserverSet observerSet, Context context, @Nullable AttributeSet attrs, int emptyResource) {
52 | super(context, attrs, emptyResource);
53 | this.observerSet = observerSet;
54 | }
55 |
56 | public void setObserverSet(ListObserverSet observerSet) {
57 | this.observerSet = observerSet;
58 | }
59 |
60 | @Override
61 | public void onVisible() {
62 | if (observerSet != null) {
63 | int g = observerSet.add(this);
64 | if (g != generation)
65 | notifyChanged();
66 | }
67 | }
68 |
69 | @Override
70 | public void onHidden() {
71 | if (observerSet != null)
72 | generation = observerSet.remove(this);
73 | }
74 |
75 | @Override
76 | public void onDetach(boolean configChanging) {
77 |
78 | }
79 |
80 | @Override
81 | public ILifecycle onAttach(MainActivity activity, Navigation n, Identity id, Peer peer, Bundle args) {
82 | this.activity = activity;
83 | this.identity = id;
84 | this.peer = peer;
85 | return this;
86 | }
87 |
88 | }
89 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/views/Presenter.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat.views;
2 |
3 | import android.os.Bundle;
4 | import android.view.View;
5 |
6 | import org.servalproject.mid.Identity;
7 | import org.servalproject.servalchat.navigation.ILifecycle;
8 |
9 | /**
10 | * Created by jeremy on 20/07/16.
11 | */
12 | public abstract class Presenter implements ILifecycle {
13 | private V view;
14 |
15 | public final String key;
16 | public final Identity identity;
17 | private PresenterFactory factory;
18 |
19 | protected Presenter(PresenterFactory factory, String key, Identity identity) {
20 | this.key = key;
21 | this.identity = identity;
22 | this.factory = factory;
23 | }
24 |
25 | protected final V getView() {
26 | return view;
27 | }
28 |
29 | public final void takeView(V view) {
30 | this.view = view;
31 | if (view != null)
32 | bind(view);
33 | }
34 |
35 | protected void bind(V view) {
36 | }
37 |
38 | protected void save(Bundle config) {
39 |
40 | }
41 |
42 | protected void restore(Bundle config) {
43 |
44 | }
45 |
46 | protected void onDestroy() {
47 |
48 | }
49 |
50 | @Override
51 | public void onDetach(boolean changingConfig) {
52 | if (factory == null)
53 | return;
54 | takeView(null);
55 | if (!changingConfig) {
56 | factory.release(this);
57 | factory = null;
58 | onDestroy();
59 | }
60 | }
61 |
62 | @Override
63 | public void onVisible() {
64 |
65 | }
66 |
67 | @Override
68 | public void onHidden() {
69 |
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/views/PresenterFactory.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat.views;
2 |
3 | import android.os.Bundle;
4 | import android.view.View;
5 |
6 | import org.servalproject.mid.Identity;
7 | import org.servalproject.mid.Peer;
8 |
9 | import java.util.HashMap;
10 | import java.util.Map;
11 |
12 | /**
13 | * Created by jeremy on 20/07/16.
14 | */
15 | public abstract class PresenterFactory> {
16 |
17 | private Map presenters = new HashMap<>();
18 |
19 | protected String getKey(V view, Identity id, Peer peer, Bundle savedState) {
20 | if (id == null)
21 | return "null";
22 | if (peer == null)
23 | return id.subscriber.signingKey.toHex();
24 | return id.subscriber.signingKey.toHex() + peer.getSubscriber().sid.toHex();
25 | }
26 |
27 | public final P getPresenter(V view, Identity id, Peer peer, Bundle savedState) {
28 | String key = getKey(view, id, peer, savedState);
29 | P ret = presenters.get(key);
30 | if (ret == null) {
31 | ret = create(key, id, peer);
32 | ret.restore(savedState);
33 | }
34 | ret.takeView(view);
35 | presenters.put(key, ret);
36 | return ret;
37 | }
38 |
39 | public void release(Presenter> presenter) {
40 | presenters.remove(presenter.key);
41 | }
42 |
43 | protected abstract P create(String key, Identity id, Peer peer);
44 | }
45 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/views/RecyclerHelper.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat.views;
2 |
3 | import android.support.v7.widget.DividerItemDecoration;
4 | import android.support.v7.widget.LinearLayoutManager;
5 | import android.support.v7.widget.RecyclerView;
6 |
7 | /**
8 | * Created by jeremy on 14/11/16.
9 | */
10 |
11 | public class RecyclerHelper {
12 | private RecyclerHelper(){}
13 |
14 | public static void createLayoutManager(RecyclerView view, boolean vertical, boolean reverse){
15 | LinearLayoutManager layoutManager = new LinearLayoutManager(
16 | view.getContext(),
17 | vertical?LinearLayoutManager.VERTICAL:LinearLayoutManager.HORIZONTAL,
18 | reverse);
19 | view.setLayoutManager(layoutManager);
20 | }
21 |
22 | public static void createDivider(RecyclerView view){
23 | LinearLayoutManager layoutManager = (LinearLayoutManager)view.getLayoutManager();
24 | view.addItemDecoration(new DividerItemDecoration(view.getContext(), layoutManager.getOrientation()));
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/views/SimpleRecyclerView.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat.views;
2 |
3 | import android.content.Context;
4 | import android.support.annotation.Nullable;
5 | import android.support.v7.widget.DividerItemDecoration;
6 | import android.support.v7.widget.LinearLayoutManager;
7 | import android.support.v7.widget.RecyclerView;
8 | import android.util.AttributeSet;
9 | import android.view.ViewGroup;
10 |
11 | /**
12 | * Created by jeremy on 11/07/16.
13 | */
14 | public abstract class SimpleRecyclerView
15 | extends RecyclerView {
16 | protected final ListAdapter listAdapter;
17 | private final int emptyResource;
18 |
19 | public SimpleRecyclerView(Context context, @Nullable AttributeSet attrs) {
20 | this(context, attrs, -1);
21 | }
22 |
23 | public SimpleRecyclerView(Context context, @Nullable AttributeSet attrs, int emptyResource) {
24 | super(context, attrs);
25 | this.emptyResource = emptyResource;
26 | listAdapter = new ListAdapter();
27 | }
28 |
29 | @Override
30 | protected void onAttachedToWindow() {
31 | setAdapter(listAdapter);
32 | super.onAttachedToWindow();
33 | }
34 |
35 | protected int getItemType(T item) {
36 | return 0;
37 | }
38 |
39 | abstract protected H createHolder(ViewGroup parent, int viewType);
40 |
41 | abstract protected void bind(H holder, T item);
42 |
43 | protected void unBind(H holder, T item) {
44 | }
45 |
46 | protected long getId(T item) {
47 | return -1;
48 | }
49 |
50 | abstract protected T get(int position);
51 |
52 | abstract protected int getCount();
53 |
54 | protected void notifyChanged() {
55 | listAdapter.notifyDataSetChanged();
56 | }
57 |
58 | public class ListAdapter extends RecyclerView.Adapter {
59 | @Override
60 | public BasicViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
61 | if (viewType == 0)
62 | return new MessageViewHolder(emptyResource, parent);
63 | return createHolder(parent, viewType -1);
64 | }
65 |
66 | @Override
67 | public void onViewRecycled(BasicViewHolder holder) {
68 | if (holder instanceof MessageViewHolder)
69 | return;
70 | int position = holder.getAdapterPosition();
71 | if (position != NO_POSITION)
72 | //noinspection unchecked
73 | unBind((H)holder, get(position));
74 | }
75 |
76 | @Override
77 | public void onBindViewHolder(BasicViewHolder holder, int position) {
78 | if (holder instanceof MessageViewHolder)
79 | return;
80 | //noinspection unchecked
81 | bind((H)holder, get(position));
82 | }
83 |
84 | @Override
85 | public int getItemCount() {
86 | int count = getCount();
87 | if (count == 0 && emptyResource!=-1)
88 | return 1;
89 | return getCount();
90 | }
91 |
92 | public long getItemId(int position) {
93 | if (getCount()==0)
94 | return -1;
95 | return getId(get(position));
96 | }
97 |
98 | @Override
99 | public int getItemViewType(int position) {
100 | if (getCount()==0)
101 | return 0;
102 | return getItemType(get(position))+1;
103 | }
104 | }
105 |
106 | }
107 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/views/SpinnerViewHolder.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat.views;
2 |
3 | import android.view.ViewGroup;
4 |
5 | import org.servalproject.servalchat.R;
6 |
7 | /**
8 | * Created by jeremy on 30/01/17.
9 | */
10 |
11 | public class SpinnerViewHolder extends BasicViewHolder {
12 | public SpinnerViewHolder(ViewGroup parent) {
13 | super(R.layout.progress, parent);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/views/TimestampView.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat.views;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.View;
6 | import android.widget.TextView;
7 |
8 | import java.text.DateFormat;
9 | import java.util.Date;
10 |
11 | /**
12 | * Created by jeremy on 29/03/17.
13 | */
14 |
15 | public class TimestampView extends TextView {
16 | public TimestampView(Context context) {
17 | super(context);
18 | }
19 |
20 | public TimestampView(Context context, AttributeSet attrs) {
21 | super(context, attrs);
22 | }
23 |
24 | public TimestampView(Context context, AttributeSet attrs, int defStyleAttr) {
25 | super(context, attrs, defStyleAttr);
26 | }
27 |
28 | private static DateFormat df = DateFormat.getDateInstance();
29 | private static DateFormat tf = DateFormat.getTimeInstance(DateFormat.SHORT);
30 |
31 | public void setDates(Date date, Date previous){
32 | if (date.getTime()<=0){
33 | setVisibility(View.GONE);
34 | } else {
35 | String thisDate = df.format(date);
36 | if (!thisDate.equals(df.format(previous==null ? new Date() : previous))) {
37 | setText(thisDate+" "+tf.format(date));
38 | setVisibility(View.VISIBLE);
39 | } else if (previous==null || previous.getTime() - date.getTime() >= 30 * 60 * 1000) {
40 | setText(tf.format(date));
41 | setVisibility(View.VISIBLE);
42 | } else {
43 | setVisibility(View.GONE);
44 | }
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/app/src/main/java/org/servalproject/servalchat/views/UIObserver.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat.views;
2 |
3 | import android.util.Log;
4 |
5 | import org.servalproject.mid.Observer;
6 | import org.servalproject.mid.IObserverSet;
7 | import org.servalproject.servalchat.navigation.ILifecycle;
8 |
9 | public abstract class UIObserver implements ILifecycle, Observer {
10 | private final IObserverSet source;
11 |
12 | protected UIObserver(IObserverSet source){
13 | this.source = source;
14 | }
15 |
16 | @Override
17 | public abstract void updated(T obj);
18 |
19 | @Override
20 | public void onDetach(boolean configChange) {
21 |
22 | }
23 |
24 | @Override
25 | public void onVisible() {
26 | source.addUI(this);
27 | updated(source.getObj());
28 | }
29 |
30 | @Override
31 | public void onHidden() {
32 | source.removeUI(this);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/app/src/main/jni/Android.mk:
--------------------------------------------------------------------------------
1 | LOCAL_PATH:= $(call my-dir)
2 |
3 | SODIUM_ARCH_FOLDER := $(APP_ABI)
4 | ifeq ($(SODIUM_ARCH_FOLDER),armeabi-v7a)
5 | SODIUM_ARCH_FOLDER = armv7-a
6 | endif
7 | ifeq ($(SODIUM_ARCH_FOLDER),arm64-v8a)
8 | SODIUM_ARCH_FOLDER = armv8-a
9 | endif
10 | ifeq ($(SODIUM_ARCH_FOLDER),x86)
11 | SODIUM_ARCH_FOLDER = i686
12 | endif
13 | ifeq ($(SODIUM_ARCH_FOLDER),x86_64)
14 | SODIUM_ARCH_FOLDER = westmere
15 | endif
16 |
17 | SODIUM_BASE := libsodium/libsodium-android-$(SODIUM_ARCH_FOLDER)
18 | SODIUM_INCLUDE := $(LOCAL_PATH)/$(SODIUM_BASE)/include
19 |
20 | include $(CLEAR_VARS)
21 | LOCAL_MODULE:= sodium
22 | LOCAL_SRC_FILES:= $(SODIUM_BASE)/lib/libsodium.a
23 | include $(PREBUILT_STATIC_LIBRARY)
24 |
25 | include $(CLEAR_VARS)
26 | LOCAL_STATIC_LIBRARIES := servaldstatic
27 | LOCAL_C_INCLUDES += $(LOCAL_PATH)/serval-dna
28 | LOCAL_SRC_FILES := $(LOCAL_PATH)/chat_features.c
29 | LOCAL_MODULE := servaldaemon
30 | LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog
31 | include $(BUILD_SHARED_LIBRARY)
32 |
33 | # Build serval library
34 | include $(CLEAR_VARS)
35 | include $(LOCAL_PATH)/serval-dna/Android.mk
36 |
--------------------------------------------------------------------------------
/app/src/main/jni/Application.mk:
--------------------------------------------------------------------------------
1 | APP_PLATFORM := android-16
2 | APP_ABI := armeabi-v7a arm64-v8a x86 x86_64
3 |
--------------------------------------------------------------------------------
/app/src/main/jni/chat_features.c:
--------------------------------------------------------------------------------
1 | /*
2 | Serval DNA daemon features
3 | Copyright (C) 2016 Flinders University
4 |
5 | This program is free software; you can redistribute it and/or
6 | modify it under the terms of the GNU General Public License
7 | as published by the Free Software Foundation; either version 2
8 | of the License, or (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program; if not, write to the Free Software
17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 | */
19 |
20 | #include "feature.h"
21 |
22 | void servald_features()
23 | {
24 | USE_FEATURE(cli_config);
25 |
26 | USE_FEATURE(mdp_binding_MDP_PORT_ECHO);
27 | USE_FEATURE(mdp_binding_MDP_PORT_TRACE);
28 | USE_FEATURE(mdp_binding_MDP_PORT_KEYMAPREQUEST);
29 | USE_FEATURE(mdp_binding_MDP_PORT_DNALOOKUP);
30 | USE_FEATURE(mdp_binding_MDP_PORT_PROBE);
31 | USE_FEATURE(mdp_binding_MDP_PORT_STUN);
32 | USE_FEATURE(mdp_binding_MDP_PORT_STUNREQ);
33 | USE_FEATURE(mdp_binding_MDP_PORT_LINKSTATE);
34 | USE_FEATURE(mdp_binding_MDP_PORT_RHIZOME_SYNC);
35 | USE_FEATURE(mdp_binding_MDP_PORT_RHIZOME_SYNC_KEYS);
36 | USE_FEATURE(mdp_binding_MDP_PORT_RHIZOME_REQUEST);
37 | USE_FEATURE(mdp_binding_MDP_PORT_RHIZOME_RESPONSE);
38 | USE_FEATURE(mdp_binding_MDP_PORT_RHIZOME_MANIFEST_REQUEST);
39 |
40 | USE_FEATURE(http_server);
41 | USE_FEATURE(http_rhizome);
42 | USE_FEATURE(http_rest_keyring);
43 | USE_FEATURE(http_rest_rhizome);
44 | USE_FEATURE(http_rest_meshms);
45 | USE_FEATURE(http_rest_meshmb);
46 |
47 | USE_FEATURE(log_output_file);
48 | USE_FEATURE(log_output_android);
49 |
50 | USE_FEATURE(jni_commandline);
51 | USE_FEATURE(jni_server);
52 | }
53 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_add_account.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_add_contact.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_block_contact.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_contacts.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_remove_contact.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/progress.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/side_nav_bar.xml:
--------------------------------------------------------------------------------
1 |
3 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/ack_message.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_message.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
21 |
22 |
26 |
27 |
34 |
35 |
36 |
43 |
44 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/block_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/contacts.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/conversation_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
14 |
15 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/conversation_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/error.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/feed.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
14 |
15 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/feed_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
16 |
17 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/feed_message.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
15 |
16 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/identity.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
14 |
15 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/identity_details.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
18 |
19 |
26 |
27 |
31 |
32 |
38 |
39 |
45 |
46 |
53 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/identity_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
15 |
16 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/main_sidebar.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
17 |
18 |
23 |
24 |
31 |
32 |
33 |
34 |
35 |
36 |
44 |
45 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/main_tabs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
12 |
17 |
18 |
19 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/message_ack.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/message_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
16 |
17 |
24 |
25 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/my_feed.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
17 |
18 |
26 |
27 |
37 |
38 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/my_message.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
14 |
15 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/nav_header.xml:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
19 |
20 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/network.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
17 |
18 |
25 |
26 |
31 |
32 |
33 |
34 |
39 |
40 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/networking.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/peer.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
17 |
18 |
29 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/peer_details.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
18 |
19 |
25 |
26 |
30 |
31 |
37 |
38 |
43 |
44 |
50 |
55 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/peer_feed.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/peer_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/peer_map.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/placeholder.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/progress.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/their_message.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
17 |
18 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/serval_head.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/servalproject/serval_chat/f71e8344827795f6e42975a049076fce8faece81/app/src/main/res/mipmap-hdpi/serval_head.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/serval_head.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/servalproject/serval_chat/f71e8344827795f6e42975a049076fce8faece81/app/src/main/res/mipmap-mdpi/serval_head.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/serval_head.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/servalproject/serval_chat/f71e8344827795f6e42975a049076fce8faece81/app/src/main/res/mipmap-xhdpi/serval_head.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/serval_head.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/servalproject/serval_chat/f71e8344827795f6e42975a049076fce8faece81/app/src/main/res/mipmap-xxhdpi/serval_head.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/serval_head.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/servalproject/serval_chat/f71e8344827795f6e42975a049076fce8faece81/app/src/main/res/mipmap-xxxhdpi/serval_head.png
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #a0ea74
4 | #83e14c
5 | #69d528
6 | #4ab70a
7 | #369100
8 | #64c8a7
9 | #1fa277
10 | #006e4a
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 | 16dp
7 | 160dp
8 | 16dp
9 | 200dp
10 | 200dp
11 | 16dp
12 | 16dp
13 | 48dp
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/network_security_config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 127.0.0.1
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/test/java/org/servalproject/servalchat/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package org.servalproject.servalchat;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * To work on unit tests, switch the Test Artifact in the Build Variants view.
9 | */
10 | public class ExampleUnitTest {
11 | @Test
12 | public void addition_isCorrect() throws Exception {
13 | assertEquals(4, 2 + 2);
14 | }
15 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | google()
7 | }
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:3.6.1'
10 |
11 | // NOTE: Do not place your application dependencies here; they belong
12 | // in the individual module build.gradle files
13 | }
14 | }
15 |
16 | allprojects {
17 | repositories {
18 | jcenter()
19 | google()
20 | }
21 | }
22 |
23 | task clean(type: Delete) {
24 | delete rootProject.buildDir
25 | }
26 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
19 |
20 | alpha.manifest.author=8C3C8F77E334EE909F06307DC75400CF1C7DFF5905A8B1D25AACE6B35900A814
21 | alpha.manifest.id=9D419753A093FDD3329FB88D00ECF30D08E92CDC8AF09A62F6D14191085FD8DD
22 | alpha.manifest.bk=4169DFEEC2D8DC239346A827CA1231C0B5C1F7BD1FB75F67210C84B1A8E9BC81
23 |
24 | beta.manifest.author=8C3C8F77E334EE909F06307DC75400CF1C7DFF5905A8B1D25AACE6B35900A814
25 | beta.manifest.id=295EAB1289C0D5FEF6CA5471B04DEED4F0ED7E1952E63238911BA567AF0DEF3E
26 | beta.manifest.bk=DD528FDC276D4B004FD91CCD46F571B73BA9ED3E0D2067823D7BF7BB4A4A491D
27 |
28 | release.manifest.author=8C3C8F77E334EE909F06307DC75400CF1C7DFF5905A8B1D25AACE6B35900A814
29 | release.manifest.id=86FAC7C5B97375D9A3A068BCA067D5A58B6E917A80FCF1AF3D8C70367DF2ACD2
30 | release.manifest.bk=83DB691B103599E73903084E6701192903D8FB57B3EED2A4F744C3C708A4E70B
31 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/servalproject/serval_chat/f71e8344827795f6e42975a049076fce8faece81/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-bin.zip
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStorePath=wrapper/dists
5 | zipStoreBase=GRADLE_USER_HOME
6 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------