Project

General

Profile

Download (29.3 KB) Statistics
| Branch: | Tag: | Revision:

magiccube / src / main / java / org / distorted / external / RubikNetwork.java @ 83018ac4

1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2019 Leszek Koltunski                                                               //
3
//                                                                                               //
4
// This file is part of Magic Cube.                                                              //
5
//                                                                                               //
6
// Magic Cube is proprietary software licensed under an EULA which you should have received      //
7
// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
8
///////////////////////////////////////////////////////////////////////////////////////////////////
9

    
10
package org.distorted.external;
11

    
12
import java.io.BufferedReader;
13
import java.io.IOException;
14
import java.io.InputStream;
15
import java.io.InputStreamReader;
16
import java.net.HttpURLConnection;
17
import java.net.URL;
18
import java.net.UnknownHostException;
19
import java.security.MessageDigest;
20
import java.security.NoSuchAlgorithmException;
21

    
22
import android.app.Activity;
23
import android.content.Context;
24
import android.content.pm.PackageInfo;
25
import android.content.pm.PackageManager;
26
import android.graphics.Bitmap;
27
import android.graphics.BitmapFactory;
28

    
29
import org.distorted.library.main.DistortedLibrary;
30
import org.distorted.objectlib.json.JsonWriter;
31
import org.distorted.objects.RubikObjectList;
32

    
33
import static org.distorted.objects.RubikObjectList.MAX_LEVEL;
34
import static org.distorted.main.RubikActivity.SHOW_DOWNLOADED_DEBUG;
35

    
36
///////////////////////////////////////////////////////////////////////////////////////////////////
37

    
38
public class RubikNetwork
39
  {
40
  public interface ScoresReceiver
41
    {
42
    void receive(String[][][] country, String[][][] name, float[][][] time);
43
    void message(String mess);
44
    void error(String error);
45
    }
46

    
47
  public interface IconReceiver
48
    {
49
    void iconDownloaded(int ordinal, Bitmap bitmap, boolean downloaded);
50
    }
51

    
52
  public interface Updatee
53
    {
54
    void receiveUpdate(RubikUpdates update);
55
    void errorUpdate();
56
    }
57

    
58
  public interface Downloadee
59
    {
60
    void jsonDownloaded();
61
    }
62

    
63
  public static final int MAX_PLACES = 10;
64

    
65
  private static final int REND_ADRENO= 0;
66
  private static final int REND_MALI  = 1;
67
  private static final int REND_POWER = 2;
68
  private static final int REND_OTHER = 3;
69

    
70
  private static final int DEBUG_RUNNING = 1;
71
  private static final int DEBUG_SUCCESS = 2;
72
  private static final int DEBUG_FAILURE = 3;
73

    
74
  private final String[] hex = {
75
    "%00", "%01", "%02", "%03", "%04", "%05", "%06", "%07",
76
    "%08", "%09", "%0a", "%0b", "%0c", "%0d", "%0e", "%0f",
77
    "%10", "%11", "%12", "%13", "%14", "%15", "%16", "%17",
78
    "%18", "%19", "%1a", "%1b", "%1c", "%1d", "%1e", "%1f",
79
    "%20", "%21", "%22", "%23", "%24", "%25", "%26", "%27",
80
    "%28", "%29", "%2a", "%2b", "%2c", "%2d", "%2e", "%2f",
81
    "%30", "%31", "%32", "%33", "%34", "%35", "%36", "%37",
82
    "%38", "%39", "%3a", "%3b", "%3c", "%3d", "%3e", "%3f",
83
    "%40", "%41", "%42", "%43", "%44", "%45", "%46", "%47",
84
    "%48", "%49", "%4a", "%4b", "%4c", "%4d", "%4e", "%4f",
85
    "%50", "%51", "%52", "%53", "%54", "%55", "%56", "%57",
86
    "%58", "%59", "%5a", "%5b", "%5c", "%5d", "%5e", "%5f",
87
    "%60", "%61", "%62", "%63", "%64", "%65", "%66", "%67",
88
    "%68", "%69", "%6a", "%6b", "%6c", "%6d", "%6e", "%6f",
89
    "%70", "%71", "%72", "%73", "%74", "%75", "%76", "%77",
90
    "%78", "%79", "%7a", "%7b", "%7c", "%7d", "%7e", "%7f",
91
    "%80", "%81", "%82", "%83", "%84", "%85", "%86", "%87",
92
    "%88", "%89", "%8a", "%8b", "%8c", "%8d", "%8e", "%8f",
93
    "%90", "%91", "%92", "%93", "%94", "%95", "%96", "%97",
94
    "%98", "%99", "%9a", "%9b", "%9c", "%9d", "%9e", "%9f",
95
    "%a0", "%a1", "%a2", "%a3", "%a4", "%a5", "%a6", "%a7",
96
    "%a8", "%a9", "%aa", "%ab", "%ac", "%ad", "%ae", "%af",
97
    "%b0", "%b1", "%b2", "%b3", "%b4", "%b5", "%b6", "%b7",
98
    "%b8", "%b9", "%ba", "%bb", "%bc", "%bd", "%be", "%bf",
99
    "%c0", "%c1", "%c2", "%c3", "%c4", "%c5", "%c6", "%c7",
100
    "%c8", "%c9", "%ca", "%cb", "%cc", "%cd", "%ce", "%cf",
101
    "%d0", "%d1", "%d2", "%d3", "%d4", "%d5", "%d6", "%d7",
102
    "%d8", "%d9", "%da", "%db", "%dc", "%dd", "%de", "%df",
103
    "%e0", "%e1", "%e2", "%e3", "%e4", "%e5", "%e6", "%e7",
104
    "%e8", "%e9", "%ea", "%eb", "%ec", "%ed", "%ee", "%ef",
105
    "%f0", "%f1", "%f2", "%f3", "%f4", "%f5", "%f6", "%f7",
106
    "%f8", "%f9", "%fa", "%fb", "%fc", "%fd", "%fe", "%ff"
107
    };
108

    
109
  private static final String SERVER="https://distorted.org/magic/cgi-bin/";
110

    
111
  private static String[][][] mCountry;
112
  private static String[][][] mName;
113
  private static float[][][] mTime;
114
  private static int[][] mPlaces;
115

    
116
  private static RubikNetwork mThis;
117
  private static String mScores = "";
118
  private static boolean mRunning = false;
119
  private static Updatee mUpdatee;
120
  private static String mVersion;
121
  private static int mNumObjects;
122
  private static RubikUpdates mUpdates;
123
  private static int mDebugState;
124

    
125
///////////////////////////////////////////////////////////////////////////////////////////////////
126

    
127
  private static void initializeStatics()
128
    {
129
    int newNum = RubikObjectList.getNumObjects();
130

    
131
    if( mCountry==null || newNum!=mNumObjects ) mCountry = new String[newNum][MAX_LEVEL][MAX_PLACES];
132
    if( mName==null    || newNum!=mNumObjects ) mName    = new String[newNum][MAX_LEVEL][MAX_PLACES];
133
    if( mTime==null    || newNum!=mNumObjects ) mTime    = new  float[newNum][MAX_LEVEL][MAX_PLACES];
134
    if( mPlaces==null  || newNum!=mNumObjects ) mPlaces  = new    int[newNum][MAX_LEVEL];
135

    
136
    if( mUpdates==null ) mUpdates = new RubikUpdates();
137

    
138
    mNumObjects = newNum;
139
    }
140

    
141
///////////////////////////////////////////////////////////////////////////////////////////////////
142

    
143
  private static String computeHash(String stringToHash, byte[] salt)
144
    {
145
    String generatedPassword;
146

    
147
    try
148
      {
149
      MessageDigest md = MessageDigest.getInstance("MD5");
150
      md.update(salt);
151
      byte[] bytes = md.digest(stringToHash.getBytes());
152
      StringBuilder sb = new StringBuilder();
153

    
154
      for (byte aByte : bytes)
155
        {
156
        sb.append(Integer.toString((aByte & 0xff) + 0x100, 16).substring(1));
157
        }
158

    
159
      generatedPassword = sb.toString();
160
      }
161
    catch (NoSuchAlgorithmException e)
162
      {
163
      return "NoSuchAlgorithm";
164
      }
165

    
166
    return generatedPassword;
167
    }
168

    
169
///////////////////////////////////////////////////////////////////////////////////////////////////
170

    
171
  private boolean fillValuesNormal(ScoresReceiver receiver)
172
    {
173
    int begin=-1 ,end, len = mScores.length();
174
    String row;
175

    
176
    if( len==0 )
177
      {
178
      receiver.error("1");
179
      return false;
180
      }
181
    else if( len<=2 )
182
      {
183
      receiver.error(mScores);
184
      return false;
185
      }
186

    
187
    for(int i=0; i<mNumObjects; i++)
188
      for(int j=0; j<MAX_LEVEL; j++)
189
        {
190
        mPlaces[i][j] = 0;
191
        }
192

    
193
    while( begin<len )
194
      {
195
      end = mScores.indexOf('\n', begin+1);
196
      if( end<0 ) end = len;
197

    
198
      try
199
        {
200
        row = mScores.substring(begin+1,end);
201
        fillRow(row);
202
        }
203
      catch(Exception ex)
204
        {
205
        // faulty row - ignore
206
        }
207

    
208
      begin = end;
209
      }
210

    
211
    return true;
212
    }
213

    
214
///////////////////////////////////////////////////////////////////////////////////////////////////
215

    
216
  private void fillRow(String row)
217
    {
218
    int s1 = row.indexOf(' ');
219
    int s2 = row.indexOf(' ',s1+1);
220
    int s3 = row.indexOf(' ',s2+1);
221
    int s4 = row.indexOf(' ',s3+1);
222
    int s5 = row.length();
223

    
224
    if( s5>s4 && s4>s3 && s3>s2 && s2>s1 && s1>0 )
225
      {
226
      int object = RubikObjectList.getOrdinal( row.substring(0,s1) );
227

    
228
      if( object>=0 && object<mNumObjects )
229
        {
230
        int level      = Integer.parseInt( row.substring(s1+1,s2) );
231
        String name    = row.substring(s2+1, s3);
232
        int time       = Integer.parseInt( row.substring(s3+1,s4) );
233
        String country = row.substring(s4+1, s5);
234

    
235
        if( country.equals("do") ) country = "dm"; // see RubikScores.setCountry()
236

    
237
        if(level>=0 && level<MAX_LEVEL)
238
          {
239
          int p = mPlaces[object][level];
240
          mPlaces[object][level]++;
241

    
242
          mCountry[object][level][p] = country;
243
          mName   [object][level][p] = name;
244
          mTime   [object][level][p] = ((float)(time/10))/100.0f;
245
          }
246
        }
247
      }
248
    else
249
      {
250
      tryDoCommand(row);
251
      }
252
    }
253

    
254
///////////////////////////////////////////////////////////////////////////////////////////////////
255

    
256
  private void tryDoCommand(String row)
257
    {
258
    if( row.startsWith("comm") )
259
      {
260
      int colon = row.indexOf(':');
261

    
262
      if( colon>0 )
263
        {
264
        String commandNumber = row.substring(4,colon);
265
        int number;
266

    
267
        try
268
          {
269
          number = Integer.parseInt(commandNumber);
270
          }
271
        catch(NumberFormatException ex)
272
          {
273
          number=0;
274
          }
275

    
276
        if(number==1)
277
          {
278
          String country = row.substring(colon+1);
279
          RubikScores scores = RubikScores.getInstance();
280
          scores.setCountry(country);
281
          }
282
        }
283
      }
284
    }
285

    
286
///////////////////////////////////////////////////////////////////////////////////////////////////
287

    
288
  private int getRendererType(String renderer)
289
    {
290
    if( renderer.contains("Adreno")  ) return REND_ADRENO;
291
    if( renderer.contains("Mali")    ) return REND_MALI;
292
    if( renderer.contains("PowerVR") ) return REND_POWER;
293

    
294
    return REND_OTHER;
295
    }
296

    
297
///////////////////////////////////////////////////////////////////////////////////////////////////
298

    
299
  private String parseRenderer(final int type, String renderer)
300
    {
301
    if( type==REND_ADRENO || type==REND_POWER )
302
      {
303
      int lastSpace = renderer.lastIndexOf(' ');
304
      String ret = renderer.substring(lastSpace+1);
305
      return URLencode(ret);
306
      }
307

    
308
    if( type==REND_MALI )
309
      {
310
      int firstHyphen = renderer.indexOf('-');
311
      String ret = renderer.substring(firstHyphen+1);
312
      return URLencode(ret);
313
      }
314

    
315
    return "other";
316
    }
317

    
318
///////////////////////////////////////////////////////////////////////////////////////////////////
319

    
320
  private String parseVersion(final int type, String version)
321
    {
322
    switch(type)
323
      {
324
      case REND_ADRENO: int aMonkey = version.indexOf('@');
325
                        int aDot = version.indexOf('.', aMonkey);
326
                        String ret1 = aDot>=3 ? version.substring(aDot-3,aDot) : "";
327
                        return URLencode(ret1);
328
      case REND_MALI  : int mV1 = version.indexOf("v1");
329
                        int mHyphen = version.indexOf('-', mV1);
330
                        String ret2 = mHyphen>mV1+3 && mV1>=0 ? version.substring(mV1+3,mHyphen) : "";
331
                        return URLencode(ret2);
332
      case REND_POWER : int pMonkey = version.indexOf('@');
333
                        int pSpace  = version.lastIndexOf(' ');
334
                        String ret3 = pSpace>=0 && pMonkey>pSpace+1 ? version.substring(pSpace+1,pMonkey) : "";
335
                        return URLencode(ret3);
336
      default         : return "";
337
      }
338
    }
339

    
340
///////////////////////////////////////////////////////////////////////////////////////////////////
341

    
342
  private String URLencode(String s)
343
    {
344
    StringBuilder sbuf = new StringBuilder();
345
    int len = s.length();
346

    
347
    for (int i = 0; i < len; i++)
348
      {
349
      int ch = s.charAt(i);
350

    
351
           if ('A' <= ch && ch <= 'Z') sbuf.append((char)ch);
352
      else if ('a' <= ch && ch <= 'z') sbuf.append((char)ch);
353
      else if ('0' <= ch && ch <= '9') sbuf.append((char)ch);
354
      else if (ch == ' '             ) sbuf.append('+');
355
      else if (ch == '-' || ch == '_'
356
            || ch == '.' || ch == '!'
357
            || ch == '~' || ch == '*'
358
            || ch == '\'' || ch == '('
359
            || ch == ')'             ) sbuf.append((char)ch);
360
      else if (ch <= 0x007f)           sbuf.append(hex[ch]);
361
      else if (ch <= 0x07FF)
362
        {
363
        sbuf.append(hex[0xc0 | (ch >> 6)]);
364
        sbuf.append(hex[0x80 | (ch & 0x3F)]);
365
        }
366
      else
367
        {
368
        sbuf.append(hex[0xe0 | (ch >> 12)]);
369
        sbuf.append(hex[0x80 | ((ch >> 6) & 0x3F)]);
370
        sbuf.append(hex[0x80 | (ch & 0x3F)]);
371
        }
372
      }
373

    
374
    return sbuf.toString();
375
    }
376

    
377
///////////////////////////////////////////////////////////////////////////////////////////////////
378

    
379
  private boolean network(String url, ScoresReceiver receiver)
380
    {
381
    try
382
      {
383
      java.net.URL connectURL = new URL(url);
384
      HttpURLConnection conn = (HttpURLConnection)connectURL.openConnection();
385

    
386
      conn.setDoInput(true);
387
      conn.setDoOutput(true);
388
      conn.setUseCaches(false);
389
      conn.setRequestMethod("GET");
390
      conn.connect();
391
      conn.getOutputStream().flush();
392

    
393
      InputStream is = conn.getInputStream();
394
      BufferedReader r = new BufferedReader(new InputStreamReader(is));
395
      StringBuilder total = new StringBuilder();
396

    
397
      for (String line; (line = r.readLine()) != null; )
398
        {
399
        total.append(line).append('\n');
400
        }
401

    
402
      mScores = total.toString();
403
      conn.disconnect();
404
      }
405
    catch( final UnknownHostException e )
406
      {
407
      receiver.message("No access to Internet");
408
      return false;
409
      }
410
    catch( final SecurityException e )
411
      {
412
      receiver.message("Application not authorized to connect to the Internet");
413
      return false;
414
      }
415
    catch( final Exception e )
416
      {
417
      receiver.message(e.getMessage());
418
      return false;
419
      }
420

    
421
    if( mScores.length()==0 )
422
      {
423
      receiver.message("Failed to download scores");
424
      return false;
425
      }
426

    
427
    return true;
428
    }
429

    
430
///////////////////////////////////////////////////////////////////////////////////////////////////
431

    
432
  private String constructSuspiciousURL(String suspURL)
433
    {
434
    RubikScores scores = RubikScores.getInstance();
435
    int deviceID       = scores.getDeviceID();
436
    String suspicious  = URLencode(suspURL);
437

    
438
    return SERVER+"suspicious.cgi?i="+deviceID+"&d="+suspicious;
439
    }
440

    
441
///////////////////////////////////////////////////////////////////////////////////////////////////
442

    
443
  private String constructTokenURL(String token)
444
    {
445
    RubikScores scores = RubikScores.getInstance();
446
    String name = URLencode(scores.getName());
447
    int deviceID= scores.getDeviceID();
448
    String country = scores.getCountry();
449
    String version = mVersion==null ? "null" : mVersion;
450
    String tkn = URLencode(token);
451

    
452
    return SERVER+"token.cgi?n="+name+"&i="+deviceID+"&e="+version+"&c="+country+"&t="+tkn;
453
    }
454

    
455
///////////////////////////////////////////////////////////////////////////////////////////////////
456

    
457
  private String constructDebugURL()
458
    {
459
    RubikScores scores = RubikScores.getInstance();
460
    String name = URLencode(scores.getName());
461
    int numRuns = scores.getNumRuns();
462
    int numPlay = scores.getNumPlays();
463
    String country = scores.getCountry();
464
    String renderer = DistortedLibrary.getDriverRenderer();
465
    String version  = DistortedLibrary.getDriverVersion();
466
    int objectAPI   = JsonWriter.VERSION_OBJECT_MAJOR;
467
    int tutorialAPI = JsonWriter.VERSION_EXTRAS_MAJOR;
468

    
469
    renderer = URLencode(renderer);
470
    version  = URLencode(version);
471

    
472
    String url=SERVER+"debugs.cgi";
473
    url += "?n="+name+"&r="+numRuns+"&p="+numPlay+"&c="+country+"&e="+mVersion+"d";
474
    url += "&d="+renderer+"&v="+version+"&a="+objectAPI+"&b="+tutorialAPI;
475

    
476
    return url;
477
    }
478

    
479
///////////////////////////////////////////////////////////////////////////////////////////////////
480

    
481
  private String constructDownloadURL()
482
    {
483
    RubikScores scores = RubikScores.getInstance();
484
    String name = URLencode(scores.getName());
485
    int numRuns = scores.getNumRuns();
486
    int numPlay = scores.getNumPlays();
487
    String country = scores.getCountry();
488

    
489
    return SERVER+"download.cgi?n="+name+"&r="+numRuns+"&p="+numPlay+"&c="+country+"&e="+mVersion;
490
    }
491

    
492
///////////////////////////////////////////////////////////////////////////////////////////////////
493

    
494
  private String constructSubmitURL()
495
    {
496
    RubikScores scores = RubikScores.getInstance();
497
    String name = URLencode(scores.getName());
498
    String veri = scores.isVerified() ? "1" : "";
499
    int numRuns = scores.getNumRuns();
500
    int numPlay = scores.getNumPlays();
501
    int deviceID= scores.getDeviceID();
502
    String reclist = scores.getRecordList("&o=","&l=","&t=");
503
    String country = scores.getCountry();
504
    long epoch = System.currentTimeMillis();
505
    String salt = "cuboid";
506

    
507
    String renderer = DistortedLibrary.getDriverRenderer();
508
    String version  = DistortedLibrary.getDriverVersion();
509

    
510
    int type = getRendererType(renderer);
511
    renderer = parseRenderer(type,renderer);
512
    version  = parseVersion(type,version);
513

    
514
    String url1=SERVER+"submit.cgi";
515
    String url2 = "n="+name+"&v="+veri+"&r="+numRuns+"&p="+numPlay+"&i="+deviceID+"&e="+mVersion;
516
    url2 += "&d="+renderer+"&s="+version+reclist+"&c="+country+"&f="+epoch;
517
    String hash = computeHash( url2, salt.getBytes() );
518

    
519
    return url1 + "?" + url2 + "&h=" + hash;
520
    }
521

    
522
///////////////////////////////////////////////////////////////////////////////////////////////////
523

    
524
  private boolean gottaDownload()
525
    {
526
    return ((mScores.length()==0) && !mRunning);
527
    }
528

    
529
///////////////////////////////////////////////////////////////////////////////////////////////////
530

    
531
  private void figureOutVersion(Activity act)
532
    {
533
    if( mVersion==null )
534
      {
535
      try
536
        {
537
        PackageInfo pInfo = act.getPackageManager().getPackageInfo( act.getPackageName(), 0);
538
        mVersion = pInfo.versionName;
539
        }
540
      catch (PackageManager.NameNotFoundException e)
541
        {
542
        mVersion = "0.9.2";
543
        }
544
      }
545
    }
546

    
547
///////////////////////////////////////////////////////////////////////////////////////////////////
548

    
549
  private void downloadThread(ScoresReceiver receiver)
550
    {
551
    boolean receiveValues=true;
552

    
553
    try
554
      {
555
      if( gottaDownload() )
556
        {
557
        mRunning = true;
558
        receiveValues = network(constructDownloadURL(),receiver);
559

    
560
        if( mRunning )
561
          {
562
          receiveValues = fillValuesNormal(receiver);
563
          mRunning = false;
564
          }
565
        }
566
      if( receiveValues ) receiver.receive(mCountry, mName, mTime);
567
      }
568
    catch( Exception e )
569
      {
570
      receiver.message("Exception downloading records: "+e.getMessage() );
571
      }
572
    }
573

    
574
///////////////////////////////////////////////////////////////////////////////////////////////////
575

    
576
  private void submitThread(ScoresReceiver receiver)
577
    {
578
    try
579
      {
580
      mRunning = true;
581
      RubikScores scores = RubikScores.getInstance();
582

    
583
      if( scores.thereAreUnsubmittedRecords() )
584
        {
585
        boolean receiveValues = network(constructSubmitURL(),receiver);
586

    
587
        if( mRunning )
588
          {
589
          receiveValues = fillValuesNormal(receiver);
590
          mRunning = false;
591
          }
592

    
593
        if( receiveValues )
594
          {
595
          RubikScores.getInstance().successfulSubmit();
596
          receiver.receive(mCountry, mName, mTime);
597
          }
598
        }
599
      }
600
    catch( Exception e )
601
      {
602
      receiver.message("Exception submitting records: "+e.getMessage() );
603
      }
604
    }
605

    
606
///////////////////////////////////////////////////////////////////////////////////////////////////
607

    
608
  private void debugThread()
609
    {
610
    String url = constructDebugURL();
611

    
612
    try
613
      {
614
      java.net.URL connectURL = new URL(url);
615
      HttpURLConnection conn = (HttpURLConnection)connectURL.openConnection();
616

    
617
      conn.setDoInput(true);
618
      conn.setDoOutput(true);
619
      conn.setUseCaches(false);
620
      conn.setRequestMethod("GET");
621
      conn.connect();
622
      conn.getOutputStream().flush();
623

    
624
      InputStream is = conn.getInputStream();
625
      BufferedReader r = new BufferedReader(new InputStreamReader(is));
626
      StringBuilder answer = new StringBuilder();
627

    
628
      for (String line; (line = r.readLine()) != null; )
629
        {
630
        answer.append(line).append('\n');
631
        }
632

    
633
      String updates = answer.toString();
634
      conn.disconnect();
635
      mUpdates.parse(updates);
636

    
637
      if( mUpdatee!=null ) mUpdatee.receiveUpdate(mUpdates);
638
      mDebugState = DEBUG_SUCCESS;
639
      }
640
    catch( final Exception e )
641
      {
642
      if( mUpdatee!=null ) mUpdatee.errorUpdate();
643
      mDebugState = DEBUG_FAILURE;
644
      }
645
    }
646

    
647
///////////////////////////////////////////////////////////////////////////////////////////////////
648

    
649
  private void suspiciousThread(String suspURL)
650
    {
651
    String url = constructSuspiciousURL(suspURL);
652

    
653
    try
654
      {
655
      java.net.URL connectURL = new URL(url);
656
      HttpURLConnection conn = (HttpURLConnection)connectURL.openConnection();
657

    
658
      conn.setDoInput(true);
659
      conn.setDoOutput(true);
660
      conn.setUseCaches(false);
661
      conn.setRequestMethod("GET");
662
      conn.connect();
663
      conn.getOutputStream().flush();
664
      conn.getInputStream();
665
      conn.disconnect();
666
      }
667
    catch( final Exception e )
668
      {
669
      // ignore
670
      }
671
    }
672

    
673
///////////////////////////////////////////////////////////////////////////////////////////////////
674

    
675
  private void tokenThread(String token)
676
    {
677
    String url = constructTokenURL(token);
678

    
679
    try
680
      {
681
      java.net.URL connectURL = new URL(url);
682
      HttpURLConnection conn = (HttpURLConnection)connectURL.openConnection();
683

    
684
      conn.setDoInput(true);
685
      conn.setDoOutput(true);
686
      conn.setUseCaches(false);
687
      conn.setRequestMethod("GET");
688
      conn.connect();
689
      conn.getOutputStream().flush();
690
      conn.getInputStream();
691
      conn.disconnect();
692
      }
693
    catch( final Exception e )
694
      {
695
      // ignore
696
      }
697
    }
698

    
699
///////////////////////////////////////////////////////////////////////////////////////////////////
700

    
701
  private Bitmap downloadIcon(String url)
702
    {
703
    try
704
      {
705
      java.net.URL connectURL = new URL(url);
706
      HttpURLConnection conn = (HttpURLConnection) connectURL.openConnection();
707
      conn.setDoInput(true);
708
      conn.connect();
709
      InputStream input = conn.getInputStream();
710
      Bitmap icon = BitmapFactory.decodeStream(input);
711
      conn.disconnect();
712
      return icon;
713
      }
714
    catch (IOException e)
715
      {
716
      android.util.Log.e("D", "Failed to download "+url);
717
      android.util.Log.e("D", e.getMessage() );
718
      return null;
719
      }
720
    }
721

    
722
///////////////////////////////////////////////////////////////////////////////////////////////////
723

    
724
  private void iconThread(Context context, IconReceiver receiver)
725
    {
726
    int numC = mUpdates.getCompletedNumber();
727
    int numS = mUpdates.getStartedNumber();
728

    
729
    for(int c=0; c<numC; c++)
730
      {
731
      int iconPresent = mUpdates.getCompletedIconPresent(c);
732

    
733
      if( iconPresent!=0 )
734
        {
735
        boolean downloaded = false;
736
        Bitmap icon = mUpdates.getCompletedIcon(context,c);
737

    
738
        if( icon==null )
739
          {
740
          String url = mUpdates.getCompletedURL(c);
741
          icon = downloadIcon(url);
742
          downloaded = true;
743

    
744
          if( SHOW_DOWNLOADED_DEBUG ) android.util.Log.e("D", "Downloading icon "+url);
745
          }
746
        if( icon!=null )
747
          {
748
          mUpdates.setCompletedIcon(c,icon);
749
          receiver.iconDownloaded(c,icon,downloaded);
750
          }
751
        }
752
      }
753

    
754
    for(int s=0; s<numS; s++)
755
      {
756
      int iconPresent = mUpdates.getStartedIconPresent(s);
757

    
758
      if( iconPresent!=0 )
759
        {
760
        boolean downloaded = false;
761
        Bitmap icon = mUpdates.getStartedIcon(context,s);
762

    
763
        if( icon==null )
764
          {
765
          String url = mUpdates.getStartedURL(s);
766
          icon = downloadIcon(url);
767
          downloaded = true;
768

    
769
          if( SHOW_DOWNLOADED_DEBUG ) android.util.Log.e("D", "Downloading icon "+url);
770
          }
771
        if( icon!=null )
772
          {
773
          mUpdates.setStartedIcon(s,icon);
774
          receiver.iconDownloaded(numC+s,icon,downloaded);
775
          }
776
        }
777
      }
778
    }
779

    
780
///////////////////////////////////////////////////////////////////////////////////////////////////
781

    
782
  private InputStream downloadJSON(String name)
783
    {
784
    String url = mUpdates.getURL() + name;
785

    
786
    try
787
      {
788
      if( SHOW_DOWNLOADED_DEBUG ) android.util.Log.e("D", "Downloading JSON "+url);
789

    
790
      java.net.URL connectURL = new URL(url);
791
      HttpURLConnection conn = (HttpURLConnection) connectURL.openConnection();
792
      conn.setDoInput(true);
793
      conn.connect();
794
      return conn.getInputStream();
795
      }
796
    catch (IOException e)
797
      {
798
      android.util.Log.e("D", "Failed to download "+url);
799
      android.util.Log.e("D", e.getMessage() );
800
      return null;
801
      }
802
    }
803

    
804
///////////////////////////////////////////////////////////////////////////////////////////////////
805

    
806
  private void jsonThread(final RubikUpdates.UpdateInfo info, Downloadee downloadee)
807
    {
808
    if(info.mUpdateObject) info.mObjectStream = downloadJSON(info.mObjectShortName+"_object.json");
809
    if(info.mUpdateExtras) info.mExtrasStream = downloadJSON(info.mObjectShortName+"_extras.json");
810

    
811
    downloadee.jsonDownloaded();
812

    
813
    try
814
      {
815
      if( info.mObjectStream!=null ) info.mObjectStream.close();
816
      }
817
    catch(IOException ioe)
818
      {
819
      android.util.Log.e("D", "failed to close object input stream");
820
      }
821

    
822
    try
823
      {
824
      if( info.mExtrasStream!=null ) info.mExtrasStream.close();
825
      }
826
    catch(IOException ioe)
827
      {
828
      android.util.Log.e("D", "failed to close extras input stream");
829
      }
830
    }
831

    
832
///////////////////////////////////////////////////////////////////////////////////////////////////
833

    
834
  private RubikNetwork()
835
    {
836

    
837
    }
838

    
839
///////////////////////////////////////////////////////////////////////////////////////////////////
840
// PUBLIC API
841
///////////////////////////////////////////////////////////////////////////////////////////////////
842

    
843
  public static void onPause()
844
    {
845
    mRunning = false;
846
    }
847

    
848
///////////////////////////////////////////////////////////////////////////////////////////////////
849

    
850
  public static RubikNetwork getInstance()
851
    {
852
    if( mThis==null ) mThis = new RubikNetwork();
853
    return mThis;
854
    }
855

    
856
///////////////////////////////////////////////////////////////////////////////////////////////////
857

    
858
  public void download(final ScoresReceiver receiver, final Activity act)
859
    {
860
    initializeStatics();
861
    figureOutVersion(act);
862

    
863
    Thread thread = new Thread()
864
      {
865
      public void run()
866
        {
867
        downloadThread(receiver);
868
        }
869
      };
870

    
871
    thread.start();
872
    }
873

    
874
///////////////////////////////////////////////////////////////////////////////////////////////////
875

    
876
  public void submit(ScoresReceiver receiver, final Activity act)
877
    {
878
    initializeStatics();
879
    figureOutVersion(act);
880

    
881
    Thread thread = new Thread()
882
      {
883
      public void run()
884
        {
885
        submitThread(receiver);
886
        }
887
      };
888

    
889
    thread.start();
890
    }
891

    
892
///////////////////////////////////////////////////////////////////////////////////////////////////
893

    
894
  public void debug(final Activity act)
895
    {
896
    initializeStatics();
897
    figureOutVersion(act);
898
    mDebugState = DEBUG_RUNNING;
899

    
900
    Thread thread = new Thread()
901
      {
902
      public void run()
903
        {
904
        debugThread();
905
        }
906
      };
907

    
908
    thread.start();
909
    }
910

    
911
///////////////////////////////////////////////////////////////////////////////////////////////////
912

    
913
  public void suspicious(final String suspicious, final Activity act)
914
    {
915
    initializeStatics();
916
    figureOutVersion(act);
917

    
918
    Thread thread = new Thread()
919
      {
920
      public void run()
921
        {
922
        suspiciousThread(suspicious);
923
        }
924
      };
925

    
926
    thread.start();
927
    }
928

    
929
///////////////////////////////////////////////////////////////////////////////////////////////////
930

    
931
  public void token(final String token)
932
    {
933
    initializeStatics();
934

    
935
    Thread thread = new Thread()
936
      {
937
      public void run()
938
        {
939
        tokenThread(token);
940
        }
941
      };
942

    
943
    thread.start();
944
    }
945

    
946
///////////////////////////////////////////////////////////////////////////////////////////////////
947
// Yes it can happen that the second Updatee registers before we sent an update to the first one
948
// and, as a result, the update never gets sent to the first one. This is not a problem (now, when
949
// there are only two updatees - the RubikStatePlay and the UpdateDialog)
950
//
951
// Yes, there is also a remote possibility that the two threads executing this function and executing
952
// the sendDebug() get swapped exactly in unlucky moment and the update never gets to the updatee.
953
// We don't care about such remote possibility, then the app simply would signal that there are no
954
// updates available.
955

    
956
  public void signUpForUpdates(Updatee updatee)
957
    {
958
         if( mDebugState==DEBUG_SUCCESS ) updatee.receiveUpdate(mUpdates);
959
    else if( mDebugState==DEBUG_FAILURE ) updatee.errorUpdate();
960
    else mUpdatee = updatee;
961
    }
962

    
963
///////////////////////////////////////////////////////////////////////////////////////////////////
964

    
965
  public void downloadIcons(final Context context, final IconReceiver receiver)
966
    {
967
    initializeStatics();
968

    
969
    Thread thread = new Thread()
970
      {
971
      public void run()
972
        {
973
        iconThread(context,receiver);
974
        }
975
      };
976

    
977
    thread.start();
978
    }
979

    
980
///////////////////////////////////////////////////////////////////////////////////////////////////
981

    
982
  public void downloadJSON(final RubikUpdates.UpdateInfo info, final Downloadee downloadee)
983
    {
984
    initializeStatics();
985

    
986
    Thread thread = new Thread()
987
      {
988
      public void run()
989
        {
990
        jsonThread(info,downloadee);
991
        }
992
      };
993

    
994
    thread.start();
995
    }
996

    
997
///////////////////////////////////////////////////////////////////////////////////////////////////
998

    
999
  public void updateDone(String shortName)
1000
    {
1001
    mUpdates.updateDone(shortName);
1002
    mScores = "";
1003
    }
1004
}
(2-2/4)