]> Pileus Git - ~andy/spades/blob - src/Main.java
0b29dbad516662d39776170ef7b9409865c8786d
[~andy/spades] / src / Main.java
1 package org.pileus.spades;
2
3 import android.app.Activity;
4 import android.content.Intent;
5 import android.graphics.Color;
6 import android.graphics.Typeface;
7 import android.os.Bundle;
8 import android.os.Handler;
9 import android.os.Messenger;
10 import android.preference.PreferenceManager;
11 import android.text.Spannable;
12 import android.text.SpannableString;
13 import android.text.format.DateFormat;
14 import android.text.style.BackgroundColorSpan;
15 import android.text.style.ForegroundColorSpan;
16 import android.text.style.StrikethroughSpan;
17 import android.text.style.StyleSpan;
18 import android.text.style.UnderlineSpan;
19 import android.view.Menu;
20 import android.view.MenuInflater;
21 import android.view.MenuItem;
22 import android.view.View;
23 import android.widget.Button;
24 import android.widget.EditText;
25 import android.widget.LinearLayout;
26 import android.widget.ScrollView;
27 import android.widget.TabHost;
28 import android.widget.TabWidget;
29 import android.widget.TextView;
30 import android.widget.Toast;
31
32 public class Main extends Activity
33 {
34         /* Private data */
35         private Handler      handler;
36         private Messenger    messenger;
37         private Task         task;
38         private Toast        toast;
39         private boolean      running;
40         private String       topic;
41         private String       names;
42         private Cards        cards;
43         private Spades       game;
44
45         /* Widgets */
46         private TabHost      window;
47         private TabWidget    tabs;
48         private LinearLayout chat;
49         private TextView     log;
50         private EditText     input;
51         private Button       connect;
52         private Button       send;
53         private LinearLayout spades;
54         private TextView     debug;
55
56         private ScrollView   lscroll;
57         private ScrollView   dscroll;
58
59         /* Private helper methods */
60         private int hsv2rgb(int hsv)
61         {
62                 int h  = (hsv & 0xff0000) >> 16;
63                 int s  = (hsv & 0x00ff00) >>  8;
64                 int v  = (hsv & 0x0000ff) >>  0;
65
66                 int c  = (v * s) / 256;
67                 int h1 = (h * 6) / 256;
68                 int x  = c * (1 - Math.abs((h1%2)-1));
69                 int m  = v - c;
70
71                 int rgb = 0;
72
73                 if (0 <= h1 && h1 <= 1) rgb = (c << 16) | (x << 8) | 0;
74                 if (1 <= h1 && h1 <= 2) rgb = (x << 16) | (c << 8) | 0;
75                 if (2 <= h1 && h1 <= 3) rgb = (0 << 16) | (c << 8) | x;
76                 if (3 <= h1 && h1 <= 4) rgb = (0 << 16) | (x << 8) | c;
77                 if (4 <= h1 && h1 <= 5) rgb = (x << 16) | (0 << 8) | c;
78                 if (5 <= h1 && h1 <= 6) rgb = (c << 16) | (0 << 8) | x;
79
80                 return rgb + (m << 16) + (m << 8) + m;
81         }
82
83         private void notice(String text)
84         {
85                 String    msg  = "*** " + text + "\n";
86                 Spannable span = new SpannableString(msg);
87                 span.setSpan(new StyleSpan(Typeface.BOLD), 0, msg.length(), 0);
88                 this.log.append(span);
89         }
90
91         private void display(Message msg)
92         {
93                 String date = DateFormat.format("hh:mm:ss", msg.time).toString();
94                 String text = String.format("(%s) %s: %s\n", date, msg.from, msg.msg);
95                 Spannable span = new SpannableString(text);
96
97                 // Determin positions
98                 int de  = 1 + date.length() + 1;
99                 int ne  = de + 1 + msg.from.length() + 1;
100                 int pos = ne + 1;
101
102                 // Get user color
103                 int hash  = msg.from.hashCode();
104                 int color = this.hsv2rgb(hash | 0x8080) | 0xff000000;
105
106                 // Format date and name
107                 span.setSpan(new ForegroundColorSpan(0xffffff88), 0,    de, 0);
108                 span.setSpan(new ForegroundColorSpan(color),      de+1, ne, 0);
109
110                 // Format IRC Colors
111                 for (Message.Format fmt : msg.parts) {
112                         int len = fmt.txt.length();
113
114                         // Bold/italics
115                         if (fmt.bold && fmt.italic)
116                                 span.setSpan(new StyleSpan(Typeface.BOLD_ITALIC), pos, pos+len, 0);
117                         else if (fmt.bold)
118                                 span.setSpan(new StyleSpan(Typeface.BOLD), pos, pos+len, 0);
119                         else if (fmt.italic)
120                                 span.setSpan(new StyleSpan(Typeface.ITALIC), pos, pos+len, 0);
121
122                         // Striketrough / underline
123                         if (fmt.strike)
124                                 span.setSpan(new StrikethroughSpan(), pos, pos+len, 0);
125                         if (fmt.underline)
126                                 span.setSpan(new UnderlineSpan(), pos, pos+len, 0);
127
128                         // Colors (reverse not supported)
129                         if (fmt.fg!=null)
130                                 span.setSpan(new ForegroundColorSpan(fmt.fg.color), pos, pos+len, 0);
131                         if (fmt.bg!=null)
132                                 span.setSpan(new BackgroundColorSpan(fmt.bg.color), pos, pos+len, 0);
133
134                         pos += len;
135                 }
136
137                 // Append the message
138                 this.log.append(span);
139         }
140
141         private void update(boolean running)
142         {
143                 this.running = running;
144                 this.connect.setVisibility(running ? View.GONE : View.VISIBLE);
145                 this.send.setVisibility(running ? View.VISIBLE : View.GONE);
146         }
147
148         /* Private handler methods */
149         private void onRegister(Task task)
150         {
151                 Os.debug("Main: onRegister");
152                 this.task      = task;
153                 this.game.task = task;
154                 this.update(this.task.isRunning());
155                 this.log.setText("");
156                 this.debug.setText("");
157                 for (Object obj : this.task.getLog()) {
158                         if (String.class.isInstance(obj))
159                                 this.notice((String)obj);
160                         if (Message.class.isInstance(obj))
161                                 this.onMessage((Message)obj);
162                 }
163         }
164
165         private void onMessage(Message msg)
166         {
167                 // Debug
168                 this.debug.append("> " + msg.line + "\n");
169                 this.dscroll.smoothScrollTo(0, this.debug.getBottom());
170
171                 // Chat
172                 switch (msg.type) {
173                         case PRIVMSG:
174                                 this.display(msg);
175                                 this.game.onMessage(msg);
176                                 break;
177                         case TOPIC:
178                                 if (!msg.txt.equals(this.topic))
179                                         this.notice("Topic for " + msg.arg + ": " + msg.txt);
180                                 this.topic = msg.txt;
181                                 break;
182                         case NAMES:
183                                 if (!msg.txt.equals(this.names))
184                                         this.notice("Users in " + msg.arg + ": " + msg.txt);
185                                 this.names = msg.txt;
186                                 break;
187                         case ERROR:
188                                 this.notice("Error: " + msg.txt);
189                                 break;
190                         case AUTHOK:
191                                 this.notice("Authentication succeeded: " + msg.txt);
192                                 break;
193                         case AUTHFAIL:
194                                 this.notice("Authentication failed: " + msg.txt);
195                                 break;
196                 }
197                 this.lscroll.smoothScrollTo(0, this.log.getBottom());
198         }
199
200         private void onNotify(String text)
201         {
202                 Os.debug("Main: onNotify - " + text);
203                 this.notice(text);
204                 this.toast.setText(text);
205                 this.toast.show();
206         }
207
208         /* Private service methods */
209         private void register()
210         {
211                 Os.debug("Main: register");
212                 startService(new Intent(this, Task.class)
213                                 .putExtra("Command",   Task.REGISTER)
214                                 .putExtra("Messenger", this.messenger));
215         }
216
217         private void connect()
218         {
219                 Os.debug("Main: connect");
220                 startService(new Intent(this, Task.class)
221                                 .putExtra("Command", Task.CONNECT));
222                 this.update(true);
223         }
224
225         private void disconnect()
226         {
227                 Os.debug("Main: disconnect");
228                 startService(new Intent(this, Task.class)
229                                 .putExtra("Command", Task.DISCONNECT));
230                 this.update(false);
231         }
232
233         private void quit()
234         {
235                 this.log.setText("");
236                 this.debug.setText("");
237                 stopService(new Intent(this, Task.class));
238                 Intent intent = new Intent(Intent.ACTION_MAIN);
239                 intent.addCategory(Intent.CATEGORY_HOME);
240                 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
241                 startActivity(intent);
242         }
243
244         /* Widget callback functions */
245         public void onConnect(View btn)
246         {
247                 this.connect();
248         }
249
250         public void onSend(View btn)
251         {
252                 if (this.task == null)
253                         return;
254                 String  txt = this.input.getText().toString();
255                 Message msg = this.task.send(txt);
256                 if (msg == null)
257                         return;
258                 this.input.setText("");
259         }
260
261         /* Activity Methods */
262         @Override
263         public void onCreate(Bundle savedInstanceState)
264         {
265                 try {
266                         super.onCreate(savedInstanceState);
267                         Os.debug("Main: onCreate");
268
269                         // Setup preferences
270                         PreferenceManager.setDefaultValues(this, R.xml.prefs, false);
271
272                         // Setup main layout
273                         this.setContentView(R.layout.main);
274
275                         // Setup toast
276                         this.toast     = Toast.makeText(this, "", Toast.LENGTH_SHORT);
277
278                         // Setup communication
279                         this.handler   = new MainHandler();
280                         this.messenger = new Messenger(this.handler);
281
282                         // Find widgets
283                         this.window    = (TabHost)      findViewById(android.R.id.tabhost);
284                         this.tabs      = (TabWidget)    findViewById(android.R.id.tabs);
285                         this.chat      = (LinearLayout) findViewById(R.id.chat);
286                         this.log       = (TextView)     findViewById(R.id.log);
287                         this.input     = (EditText)     findViewById(R.id.input);
288                         this.connect   = (Button)       findViewById(R.id.connect);
289                         this.send      = (Button)       findViewById(R.id.send);
290                         this.spades    = (LinearLayout) findViewById(R.id.spades);
291                         this.debug     = (TextView)     findViewById(R.id.debug);
292
293                         this.lscroll   = (ScrollView)   findViewById(R.id.log_scroll);
294                         this.dscroll   = (ScrollView)   findViewById(R.id.debug_scroll);
295
296                         // Add window tabs
297                         this.window.setup();
298
299                         this.window.addTab(this.window
300                                         .newTabSpec("chat")
301                                         .setIndicator("Chat")
302                                         .setContent(R.id.chat));
303                         this.window.addTab(this.window
304                                         .newTabSpec("spades")
305                                         .setIndicator("Spades")
306                                         .setContent(R.id.spades));
307                         this.window.addTab(this.window
308                                         .newTabSpec("debug")
309                                         .setIndicator("Debug")
310                                         .setContent(R.id.debug));
311
312                         // Setup Spades game and cards view
313                         this.game  = new Spades(PreferenceManager
314                                         .getDefaultSharedPreferences(this)
315                                         .getString("pref_referee", "rhawk"));
316                         this.cards = new Cards(this);
317
318                         this.game.cards = this.cards;
319                         this.cards.game = this.game;
320
321                         this.spades.addView(cards);
322
323                         // Attach to background service
324                         this.register();
325
326                 } catch (Exception e) {
327                         Os.debug("Error setting content view", e);
328                         return;
329                 }
330         }
331
332         @Override
333         public void onStart()
334         {
335                 super.onStart();
336                 this.register();
337                 Os.debug("Main: onStart");
338         }
339
340         @Override
341         public void onResume()
342         {
343                 super.onResume();
344                 Os.debug("Main: onResume");
345         }
346
347         @Override
348         public void onPause()
349         {
350                 super.onPause();
351                 Os.debug("Main: onPause");
352         }
353
354         @Override
355         public void onStop()
356         {
357                 super.onStop();
358                 Os.debug("Main: onStop");
359         }
360
361         @Override
362         public void onRestart()
363         {
364                 super.onRestart();
365                 Os.debug("Main: onRestart");
366         }
367
368         @Override
369         public void onDestroy()
370         {
371                 super.onDestroy();
372                 Os.debug("Main: onDestroy");
373         }
374
375         @Override
376         public boolean onCreateOptionsMenu(Menu menu)
377         {
378                 MenuInflater inflater = getMenuInflater();
379                 inflater.inflate(R.menu.main, menu);
380                 return true;
381         }
382
383         @Override
384         public boolean onPrepareOptionsMenu(Menu menu)
385         {
386                 menu.findItem(R.id.connect).setVisible(!this.running);
387                 menu.findItem(R.id.disconnect).setVisible(this.running);
388                 return true;
389         }
390
391         @Override
392         public boolean onOptionsItemSelected(MenuItem item)
393         {
394                 switch (item.getItemId()) {
395                         case R.id.connect:
396                                 this.connect();
397                                 return true;
398                         case R.id.disconnect:
399                                 this.disconnect();
400                                 return true;
401                         case R.id.settings:
402                                 this.startActivity(new Intent(this, Prefs.class));
403                                 return true;
404                         case R.id.quit:
405                                 this.quit();
406                                 return true;
407                         default:
408                                 return false;
409                 }
410         }
411
412         /* Handler class */
413         class MainHandler extends Handler
414         {
415                 public void handleMessage(android.os.Message msg)
416                 {
417                         switch (msg.what) {
418                                 case Task.REGISTER:
419                                         Main.this.onRegister((Task)msg.obj);
420                                         break;
421                                 case Task.MESSAGE:
422                                         Main.this.onMessage((Message)msg.obj);
423                                         break;
424                                 case Task.CONNECT:
425                                         Main.this.update(true);
426                                         Main.this.game.onConnect();
427                                         break;
428                                 case Task.DISCONNECT:
429                                         Main.this.update(false);
430                                         break;
431                                 case Task.NOTIFY:
432                                         Main.this.onNotify((String)msg.obj);
433                                         break;
434                                 default:
435                                         Os.debug("Main: unknown message - " + msg.what);
436                                         break;
437                         }
438                 }
439         }
440 }