]> Pileus Git - ~andy/spades/blob - src/Main.java
Show turn in title bar
[~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                 // Update title
200                 if (this.cards.turn  != null && this.cards.turn  != "" &&
201                     this.cards.state != null && this.cards.state != "") {
202                         this.setTitle("Spades - " + this.cards.turn + "'s " + this.cards.state);
203                 }
204         }
205
206         private void onNotify(String text)
207         {
208                 Os.debug("Main: onNotify - " + text);
209                 this.notice(text);
210                 this.toast.setText(text);
211                 this.toast.show();
212         }
213
214         /* Private service methods */
215         private void register()
216         {
217                 Os.debug("Main: register");
218                 startService(new Intent(this, Task.class)
219                                 .putExtra("Command",   Task.REGISTER)
220                                 .putExtra("Messenger", this.messenger));
221         }
222
223         private void connect()
224         {
225                 Os.debug("Main: connect");
226                 startService(new Intent(this, Task.class)
227                                 .putExtra("Command", Task.CONNECT));
228                 this.update(true);
229         }
230
231         private void disconnect()
232         {
233                 Os.debug("Main: disconnect");
234                 startService(new Intent(this, Task.class)
235                                 .putExtra("Command", Task.DISCONNECT));
236                 this.update(false);
237         }
238
239         private void quit()
240         {
241                 this.log.setText("");
242                 this.debug.setText("");
243                 stopService(new Intent(this, Task.class));
244                 Intent intent = new Intent(Intent.ACTION_MAIN);
245                 intent.addCategory(Intent.CATEGORY_HOME);
246                 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
247                 startActivity(intent);
248         }
249
250         /* Widget callback functions */
251         public void onConnect(View btn)
252         {
253                 this.connect();
254         }
255
256         public void onSend(View btn)
257         {
258                 if (this.task == null)
259                         return;
260                 String  txt = this.input.getText().toString();
261                 Message msg = this.task.send(txt);
262                 if (msg == null)
263                         return;
264                 this.input.setText("");
265         }
266
267         /* Activity Methods */
268         @Override
269         public void onCreate(Bundle savedInstanceState)
270         {
271                 try {
272                         super.onCreate(savedInstanceState);
273                         Os.debug("Main: onCreate");
274
275                         // Setup preferences
276                         PreferenceManager.setDefaultValues(this, R.xml.prefs, false);
277
278                         // Setup main layout
279                         this.setContentView(R.layout.main);
280
281                         // Setup toast
282                         this.toast     = Toast.makeText(this, "", Toast.LENGTH_SHORT);
283
284                         // Setup communication
285                         this.handler   = new MainHandler();
286                         this.messenger = new Messenger(this.handler);
287
288                         // Find widgets
289                         this.window    = (TabHost)      findViewById(android.R.id.tabhost);
290                         this.tabs      = (TabWidget)    findViewById(android.R.id.tabs);
291                         this.chat      = (LinearLayout) findViewById(R.id.chat);
292                         this.log       = (TextView)     findViewById(R.id.log);
293                         this.input     = (EditText)     findViewById(R.id.input);
294                         this.connect   = (Button)       findViewById(R.id.connect);
295                         this.send      = (Button)       findViewById(R.id.send);
296                         this.spades    = (LinearLayout) findViewById(R.id.spades);
297                         this.debug     = (TextView)     findViewById(R.id.debug);
298
299                         this.lscroll   = (ScrollView)   findViewById(R.id.log_scroll);
300                         this.dscroll   = (ScrollView)   findViewById(R.id.debug_scroll);
301
302                         // Add window tabs
303                         this.window.setup();
304
305                         this.window.addTab(this.window
306                                         .newTabSpec("chat")
307                                         .setIndicator("Chat")
308                                         .setContent(R.id.chat));
309                         this.window.addTab(this.window
310                                         .newTabSpec("spades")
311                                         .setIndicator("Spades")
312                                         .setContent(R.id.spades));
313                         this.window.addTab(this.window
314                                         .newTabSpec("debug")
315                                         .setIndicator("Debug")
316                                         .setContent(R.id.debug));
317
318                         // Setup Spades game and cards view
319                         this.game  = new Spades(PreferenceManager
320                                         .getDefaultSharedPreferences(this)
321                                         .getString("pref_referee", "rhawk"));
322                         this.cards = new Cards(this);
323
324                         this.game.cards = this.cards;
325                         this.cards.game = this.game;
326
327                         this.spades.addView(cards);
328
329                         // Attach to background service
330                         this.register();
331
332                 } catch (Exception e) {
333                         Os.debug("Error setting content view", e);
334                         return;
335                 }
336         }
337
338         @Override
339         public void onStart()
340         {
341                 super.onStart();
342                 this.register();
343                 Os.debug("Main: onStart");
344         }
345
346         @Override
347         public void onResume()
348         {
349                 super.onResume();
350                 Os.debug("Main: onResume");
351         }
352
353         @Override
354         public void onPause()
355         {
356                 super.onPause();
357                 Os.debug("Main: onPause");
358         }
359
360         @Override
361         public void onStop()
362         {
363                 super.onStop();
364                 Os.debug("Main: onStop");
365         }
366
367         @Override
368         public void onRestart()
369         {
370                 super.onRestart();
371                 Os.debug("Main: onRestart");
372         }
373
374         @Override
375         public void onDestroy()
376         {
377                 super.onDestroy();
378                 Os.debug("Main: onDestroy");
379         }
380
381         @Override
382         public boolean onCreateOptionsMenu(Menu menu)
383         {
384                 MenuInflater inflater = getMenuInflater();
385                 inflater.inflate(R.menu.main, menu);
386                 return true;
387         }
388
389         @Override
390         public boolean onPrepareOptionsMenu(Menu menu)
391         {
392                 menu.findItem(R.id.connect).setVisible(!this.running);
393                 menu.findItem(R.id.disconnect).setVisible(this.running);
394                 return true;
395         }
396
397         @Override
398         public boolean onOptionsItemSelected(MenuItem item)
399         {
400                 switch (item.getItemId()) {
401                         case R.id.connect:
402                                 this.connect();
403                                 return true;
404                         case R.id.disconnect:
405                                 this.disconnect();
406                                 return true;
407                         case R.id.settings:
408                                 this.startActivity(new Intent(this, Prefs.class));
409                                 return true;
410                         case R.id.quit:
411                                 this.quit();
412                                 return true;
413                         default:
414                                 return false;
415                 }
416         }
417
418         /* Handler class */
419         class MainHandler extends Handler
420         {
421                 public void handleMessage(android.os.Message msg)
422                 {
423                         switch (msg.what) {
424                                 case Task.REGISTER:
425                                         Main.this.onRegister((Task)msg.obj);
426                                         break;
427                                 case Task.MESSAGE:
428                                         Main.this.onMessage((Message)msg.obj);
429                                         break;
430                                 case Task.CONNECT:
431                                         Main.this.update(true);
432                                         Main.this.game.onConnect();
433                                         break;
434                                 case Task.DISCONNECT:
435                                         Main.this.update(false);
436                                         break;
437                                 case Task.NOTIFY:
438                                         Main.this.onNotify((String)msg.obj);
439                                         break;
440                                 default:
441                                         Os.debug("Main: unknown message - " + msg.what);
442                                         break;
443                         }
444                 }
445         }
446 }