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