├── .gitignore
├── AndroidManifest.xml
├── LICENSE
├── README.md
├── ic_launcher-web.png
├── libs
├── android-support-v4.jar
└── unclouded_v1.jar
├── proguard-project.txt
├── project.properties
├── res
├── drawable-hdpi
│ └── ic_launcher.png
├── drawable-mdpi
│ └── ic_launcher.png
├── drawable-xhdpi
│ └── ic_launcher.png
├── drawable-xxhdpi
│ └── ic_launcher.png
├── layout
│ ├── activity_messenger.xml
│ ├── activity_splash.xml
│ └── message.xml
├── menu
│ ├── messenger.xml
│ └── splash.xml
├── values-sw600dp
│ └── dimens.xml
├── values-sw720dp-land
│ └── dimens.xml
├── values-v11
│ └── styles.xml
├── values-v14
│ └── styles.xml
└── values
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
└── src
└── com
└── unclouded
└── app
└── messenger
├── MessengerActivity.java
└── Splash.java
/.gitignore:
--------------------------------------------------------------------------------
1 | # built application files
2 | *.apk
3 | *.ap_
4 |
5 | # files for the dex VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # generated files
12 | bin/
13 | gen/
14 |
15 | # Local configuration file (sdk path, etc)
16 | local.properties
17 |
18 | # Eclipse project files
19 | .classpath
20 | .project
21 |
22 | # Proguard folder generated by Eclipse
23 | proguard/
24 |
25 | # Intellij project files
26 | *.iml
27 | *.ipr
28 | *.iws
29 | .idea/
--------------------------------------------------------------------------------
/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
19 |
20 |
25 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013 unclouded-io
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Android Messenger
2 | =================
3 |
4 | A device-to-device messenger app for Android that enables you to chat with your nearby buddies without being connected to the internet.
5 |
6 | Check out the tutorial at [unclouded.io/tutorials/messenger](http://www.unclouded.io/tutorials/messenger).
7 |
--------------------------------------------------------------------------------
/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unclouded-io/android-messenger/8658ce27964600a6b88e646649f986d36ce25b75/ic_launcher-web.png
--------------------------------------------------------------------------------
/libs/android-support-v4.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unclouded-io/android-messenger/8658ce27964600a6b88e646649f986d36ce25b75/libs/android-support-v4.jar
--------------------------------------------------------------------------------
/libs/unclouded_v1.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unclouded-io/android-messenger/8658ce27964600a6b88e646649f986d36ce25b75/libs/unclouded_v1.jar
--------------------------------------------------------------------------------
/proguard-project.txt:
--------------------------------------------------------------------------------
1 | # To enable ProGuard in your project, edit project.properties
2 | # to define the proguard.config property as described in that file.
3 | #
4 | # Add project specific ProGuard rules here.
5 | # By default, the flags in this file are appended to flags specified
6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt
7 | # You can edit the include path and order by changing the ProGuard
8 | # include property in project.properties.
9 | #
10 | # For more details, see
11 | # http://developer.android.com/guide/developing/tools/proguard.html
12 |
13 | # Add any project specific keep options here:
14 |
15 | # If your project uses WebView with JS, uncomment the following
16 | # and specify the fully qualified class name to the JavaScript interface
17 | # class:
18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
19 | # public *;
20 | #}
21 |
--------------------------------------------------------------------------------
/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system edit
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 | #
10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
12 |
13 | # Project target.
14 | target=android-18
15 |
--------------------------------------------------------------------------------
/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unclouded-io/android-messenger/8658ce27964600a6b88e646649f986d36ce25b75/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unclouded-io/android-messenger/8658ce27964600a6b88e646649f986d36ce25b75/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unclouded-io/android-messenger/8658ce27964600a6b88e646649f986d36ce25b75/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unclouded-io/android-messenger/8658ce27964600a6b88e646649f986d36ce25b75/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/layout/activity_messenger.xml:
--------------------------------------------------------------------------------
1 |
11 |
12 |
17 |
18 |
19 |
24 |
25 |
32 |
33 |
34 |
35 |
36 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/res/layout/activity_splash.xml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
21 |
22 |
23 |
24 |
25 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/res/layout/message.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/res/menu/messenger.xml:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/res/menu/splash.xml:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/res/values-sw600dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/res/values-sw720dp-land/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 | 128dp
8 |
9 |
10 |
--------------------------------------------------------------------------------
/res/values-v11/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/res/values-v14/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 16dp
5 | 16dp
6 |
7 |
8 |
--------------------------------------------------------------------------------
/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Unclouded Messenger
5 | MessengerActivity
6 |
7 | Go Offline
8 | Go Online
9 |
10 | Please enter your name
11 | Enter
12 |
13 | >>
14 |
15 |
16 |
--------------------------------------------------------------------------------
/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
14 |
15 |
16 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/com/unclouded/app/messenger/MessengerActivity.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2013 Unclouded.io
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | *
22 | */
23 |
24 | package com.unclouded.app.messenger;
25 |
26 | import java.net.InetAddress;
27 | import java.util.ArrayList;
28 |
29 | import com.unclouded.android.*;
30 |
31 | import android.os.Bundle;
32 | import android.app.Activity;
33 | import android.content.Intent;
34 | import android.view.Menu;
35 | import android.view.MenuItem;
36 | import android.view.View;
37 | import android.view.View.OnClickListener;
38 | import android.widget.AbsListView;
39 | import android.widget.ArrayAdapter;
40 | import android.widget.Button;
41 | import android.widget.EditText;
42 | import android.widget.ListView;
43 |
44 | public class MessengerActivity extends Activity {
45 |
46 | // Unclouded event loop
47 | private Unclouded unclouded;
48 |
49 | // Flag to indicate whether device is connected to the network or not
50 | private boolean isOnline;
51 |
52 | // Name that is entered in the splash activity
53 | private String myName;
54 |
55 | // Adapter to update the list view with string messages
56 | private ArrayAdapter conversationArrayAdapter;
57 |
58 | // List of remote references to other devices in the network
59 | private ArrayList buddyList;
60 |
61 | @Override
62 | protected void onCreate(Bundle savedInstanceState) {
63 | super.onCreate(savedInstanceState);
64 | setContentView(R.layout.activity_messenger);
65 |
66 | buddyList = new ArrayList();
67 | isOnline = false;
68 |
69 | Intent intent = getIntent();
70 | myName = intent.getStringExtra("NAME");
71 |
72 | ListView conversationView = (ListView) findViewById(R.id.conversation_view);
73 | Button sendButton = (Button) findViewById(R.id.send_button);
74 |
75 | conversationArrayAdapter = new ArrayAdapter(this, R.layout.message);
76 | conversationView.setAdapter(conversationArrayAdapter);
77 | // Make listView to scroll down automatically
78 | conversationView.setTranscriptMode(AbsListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);
79 |
80 | // When entering a message, clear the field and broadcast the message
81 | sendButton.setOnClickListener(new OnClickListener(){
82 |
83 | @Override
84 | public void onClick(View arg0) {
85 | EditText msgField = (EditText) findViewById(R.id.msg_field);
86 | String msg = msgField.getText().toString();
87 | msgField.setText("");
88 | broadcastMessage(msg);
89 | }
90 |
91 | });
92 |
93 | // Obtain Unclouded event loop
94 | unclouded = Unclouded.getInstance();
95 |
96 | // Go online and connected to the network
97 | Network network = unclouded.goOnline();
98 |
99 | // Monitor the network connection to update the isOnline flag
100 | network.whenever(new NetworkListener(){
101 |
102 | @Override
103 | public void isOnline(InetAddress ip){
104 | isOnline = true;
105 | }
106 |
107 | @Override
108 | public void isOffline(InetAddress ip){
109 | isOnline = false;
110 | buddyList.clear();
111 | }
112 |
113 | });
114 |
115 | // MESSENGER type tag to associate with the messenger service
116 | TypeTag MESSENGER_TYPETAG = new TypeTag("MESSENGER");
117 |
118 | // New instance of the Messenger class.
119 | Messenger myMessenger = new Messenger();
120 |
121 | // myMessenger is broadcasted by reference (because Messenger implements UObject)
122 | // This makes a remote reference to this object to be spread across the network
123 | unclouded.broadcast(MESSENGER_TYPETAG, myMessenger);
124 |
125 | // Listen for remote reference associated with the MESSENGER_TYPETAG type tag
126 | unclouded.whenever(MESSENGER_TYPETAG, new ServiceListener(){
127 |
128 | String buddyName;
129 |
130 | @Override
131 | public void isDiscovered(RemoteReference remoteReference) {
132 | // When discovering a buddy, register the remote reference to its Messenger object
133 | buddyList.add(remoteReference);
134 | // Asynchronously ask for his name
135 | Promise promise = remoteReference.asyncInvoke("getName");
136 | // Listen for the name to be returned
137 | promise.when(new PromiseListener(){
138 |
139 | @Override
140 | public void isResolved(String name) {
141 | // When name is returned, store it and print message on the screen
142 | buddyName = name;
143 | printBuddyJoinedMessage(name);
144 | }
145 | });
146 | }
147 |
148 | @Override
149 | public void isDisconnected(RemoteReference remoteReference){
150 | // When disconnected, remove buddy from list
151 | buddyList.remove(remoteReference);
152 | // If name is already resolved, print disconnection message on the screen
153 | if(buddyName != null){ // Null in case disconnection occurs before name is resolved
154 | printBuddyDisconnectedMessage(buddyName);
155 | }
156 | }
157 |
158 | @Override
159 | public void isReconnected(RemoteReference remoteReference){
160 | // When reconnecting, check whether name has been resolved before
161 | if(buddyName == null){
162 | // If not, treat like a new connection
163 | isDiscovered(remoteReference);
164 | } else {
165 | // Otherwise, add reference to list and print message on the screen
166 | buddyList.add(remoteReference);
167 | printBuddyJoinedMessage(buddyName);
168 | }
169 | }
170 |
171 | });
172 |
173 |
174 | }
175 |
176 | @Override
177 | public boolean onCreateOptionsMenu(final Menu menu) {
178 | // Inflate the menu; this adds items to the action bar if it is present.
179 | getMenuInflater().inflate(R.menu.messenger, menu);
180 | return true;
181 | }
182 |
183 | @Override
184 | // Dynamically change menu depending on network status
185 | public boolean onPrepareOptionsMenu(Menu menu) {
186 | menu.clear();
187 | getMenuInflater().inflate(R.menu.messenger, menu);
188 | MenuItem item = menu.findItem(R.id.action_change_network_status);
189 | if(isOnline){
190 | // If connected to the network, show `go offline' action
191 | item.setTitle(R.string.action_go_offline);
192 | } else {
193 | // If disconnected from the network, show `go online' action
194 | item.setTitle(R.string.action_go_online);
195 | }
196 | return super.onPrepareOptionsMenu(menu);
197 | }
198 |
199 | @Override
200 | public boolean onOptionsItemSelected(MenuItem item) {
201 | switch (item.getItemId()) {
202 | case R.id.action_change_network_status:
203 | if(isOnline){
204 | // When clicked and network is online, go offline
205 | unclouded.goOffline();
206 | } else {
207 | // When clicked and network is offline, go online
208 | unclouded.goOnline();
209 | }
210 | return true;
211 | default:
212 | return super.onOptionsItemSelected(item);
213 | }
214 | }
215 |
216 |
217 | // Broadcast a message in the network
218 | private void broadcastMessage(String msg){
219 | // Loop over all discovered buddies...
220 | for(RemoteReference reference: buddyList){
221 | // ... and asynchronously invoke their receiveMsg method;
222 | // No need to wait for return value here
223 | reference.asyncInvoke("receiveMsg", myName, msg);
224 | }
225 | // Print message to the screen
226 | printMessage(myName, msg);
227 | }
228 |
229 | private void printMessage(final String name, final String msg){
230 | addToAdapter(name+": "+msg);
231 | }
232 |
233 |
234 | private void printBuddyJoinedMessage(final String name){
235 | addToAdapter(name+" has joined the conversation.");
236 | }
237 |
238 | private void printBuddyDisconnectedMessage(final String name){
239 | addToAdapter(name+" has left the conversation.");
240 | }
241 |
242 | // Main method to print something on the screen
243 | private void addToAdapter(final String msg){
244 | // Necessary because most invocations are initiated by the Unclouded event loop
245 | runOnUiThread(new Runnable(){
246 | public void run(){
247 | conversationArrayAdapter.add(msg);
248 | }
249 | });
250 | }
251 |
252 |
253 | // ------------------------------------------
254 |
255 | // Instances of the Messenger class are spread across the network
256 | // and allow other devices to share messages
257 | protected class Messenger implements UObject {
258 |
259 | public String getName(){ return myName; }
260 |
261 | public void receiveMsg(String name, String msg){
262 | printMessage(name, msg);
263 | }
264 |
265 | }
266 |
267 | }
268 |
--------------------------------------------------------------------------------
/src/com/unclouded/app/messenger/Splash.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2013 Unclouded.io
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | *
22 | */
23 |
24 | package com.unclouded.app.messenger;
25 |
26 | import android.os.Bundle;
27 | import android.app.Activity;
28 | import android.content.Intent;
29 | import android.view.Menu;
30 | import android.view.View;
31 | import android.view.View.OnClickListener;
32 | import android.widget.Button;
33 | import android.widget.EditText;
34 |
35 | public class Splash extends Activity {
36 |
37 | @Override
38 | protected void onCreate(Bundle savedInstanceState) {
39 | super.onCreate(savedInstanceState);
40 | setContentView(R.layout.activity_splash);
41 |
42 | Button button = (Button) findViewById(R.id.enter_button);
43 | button.setOnClickListener(new OnClickListener(){
44 |
45 | @Override
46 | public void onClick(View arg0) {
47 | EditText nameField = (EditText) findViewById(R.id.name_field);
48 | String name = nameField.getText().toString();
49 | startMessenger(name);
50 | }
51 | });
52 |
53 | }
54 |
55 | @Override
56 | public boolean onCreateOptionsMenu(Menu menu) {
57 | // Inflate the menu; this adds items to the action bar if it is present.
58 | getMenuInflater().inflate(R.menu.splash, menu);
59 | return true;
60 | }
61 |
62 | private void startMessenger(String name){
63 | Intent intent = new Intent(this, MessengerActivity.class);
64 | intent.putExtra("NAME", name);
65 | startActivity(intent);
66 | finish();
67 | }
68 |
69 | }
70 |
--------------------------------------------------------------------------------