Project

General

Profile

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

magiccube / src / main / java / org / distorted / external / RubikNetwork.java @ c02235d5

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
import java.util.ArrayList;
22

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

    
30
import org.distorted.objectlib.json.JsonWriter;
31
import org.distorted.objects.RubikObjectList;
32

    
33
import static org.distorted.objects.RubikObjectList.SHOW_DOWNLOADED_DEBUG;
34
import static org.distorted.external.RubikScores.LEVELS_SHOWN;
35

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

    
38
public class RubikNetwork
39
  {
40
  public interface ScoresReceiver
41
    {
42
    void receive(String[][][] country, String[][][] name, int[][][] 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
    int getType();
55
    void receiveUpdate(RubikUpdates update);
56
    void objectDownloaded(String shortName);
57
    void errorUpdate();
58
    }
59

    
60
  public interface Downloadee
61
    {
62
    void jsonDownloaded();
63
    }
64

    
65
  public static final int MAX_PLACES = 10;
66

    
67
  private static final int UPDATES_RUNNING = 1;
68
  private static final int UPDATES_SUCCESS = 2;
69
  private static final int UPDATES_FAILURE = 3;
70

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

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

    
108
  private static String[][][] mCountry;
109
  private static String[][][] mName;
110
  private static int[][][] mTime;
111
  private static int[][] mPlaces;
112

    
113
  private static RubikNetwork mThis;
114
  private static String mScores = "";
115
  private static boolean mRunning = false;
116
  private static ArrayList<Updatee> mUpdateeList;
117
  private static String mVersion;
118
  private static int mNumObjects;
119
  private static RubikUpdates mUpdates;
120
  private static int mUpdatesState;
121

    
122
///////////////////////////////////////////////////////////////////////////////////////////////////
123

    
124
  private static void initializeStatics()
125
    {
126
    int newNum = RubikObjectList.getNumObjects();
127

    
128
    if( mCountry==null || newNum!=mNumObjects ) mCountry = new String[newNum][LEVELS_SHOWN+1][MAX_PLACES];
129
    if( mName==null    || newNum!=mNumObjects ) mName    = new String[newNum][LEVELS_SHOWN+1][MAX_PLACES];
130
    if( mTime==null    || newNum!=mNumObjects ) mTime    = new    int[newNum][LEVELS_SHOWN+1][MAX_PLACES];
131
    if( mPlaces==null  || newNum!=mNumObjects ) mPlaces  = new    int[newNum][LEVELS_SHOWN+1];
132

    
133
    if( mUpdates==null ) mUpdates = new RubikUpdates();
134

    
135
    mNumObjects = newNum;
136
    }
137

    
138
///////////////////////////////////////////////////////////////////////////////////////////////////
139

    
140
  private static String computeHash(String stringToHash, byte[] salt)
141
    {
142
    String generatedPassword;
143

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

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

    
156
      generatedPassword = sb.toString();
157
      }
158
    catch (NoSuchAlgorithmException e)
159
      {
160
      return "NoSuchAlgorithm";
161
      }
162

    
163
    return generatedPassword;
164
    }
165

    
166
///////////////////////////////////////////////////////////////////////////////////////////////////
167

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

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

    
184
    for(int i=0; i<mNumObjects; i++)
185
      for(int j=0; j<=LEVELS_SHOWN; j++)
186
        {
187
        mPlaces[i][j] = 0;
188
        }
189

    
190
    while( begin<len )
191
      {
192
      end = mScores.indexOf('\n', begin+1);
193
      if( end<0 ) end = len;
194

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

    
205
      begin = end;
206
      }
207

    
208
    return true;
209
    }
210

    
211
///////////////////////////////////////////////////////////////////////////////////////////////////
212

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

    
221
    if( s5>s4 && s4>s3 && s3>s2 && s2>s1 && s1>0 )
222
      {
223
      int object = RubikObjectList.getOrdinal( row.substring(0,s1) );
224

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

    
232
        if( country.equals("do") ) country = "dm"; // see RubikScores.setCountry()
233

    
234
        if( level>LEVELS_SHOWN ) level = LEVELS_SHOWN;
235

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

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

    
253
///////////////////////////////////////////////////////////////////////////////////////////////////
254

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

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

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

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

    
285
///////////////////////////////////////////////////////////////////////////////////////////////////
286

    
287
  private String URLencode(String s)
288
    {
289
    StringBuilder sbuf = new StringBuilder();
290
    int len = s.length();
291

    
292
    for (int i = 0; i < len; i++)
293
      {
294
      int ch = s.charAt(i);
295

    
296
           if ('A' <= ch && ch <= 'Z') sbuf.append((char)ch);
297
      else if ('a' <= ch && ch <= 'z') sbuf.append((char)ch);
298
      else if ('0' <= ch && ch <= '9') sbuf.append((char)ch);
299
      else if (ch == ' '             ) sbuf.append('+');
300
      else if (ch == '-' || ch == '_'
301
            || ch == '.' || ch == '!'
302
            || ch == '~' || ch == '*'
303
            || ch == '\'' || ch == '('
304
            || ch == ')'             ) sbuf.append((char)ch);
305
      else if (ch <= 0x007f)           sbuf.append(hex[ch]);
306
      else if (ch <= 0x07FF)
307
        {
308
        sbuf.append(hex[0xc0 | (ch >> 6)]);
309
        sbuf.append(hex[0x80 | (ch & 0x3F)]);
310
        }
311
      else
312
        {
313
        sbuf.append(hex[0xe0 | (ch >> 12)]);
314
        sbuf.append(hex[0x80 | ((ch >> 6) & 0x3F)]);
315
        sbuf.append(hex[0x80 | (ch & 0x3F)]);
316
        }
317
      }
318

    
319
    return sbuf.toString();
320
    }
321

    
322
///////////////////////////////////////////////////////////////////////////////////////////////////
323

    
324
  private boolean network(String url, ScoresReceiver receiver)
325
    {
326
    try
327
      {
328
      java.net.URL connectURL = new URL(url);
329
      HttpURLConnection conn = (HttpURLConnection)connectURL.openConnection();
330

    
331
      conn.setDoInput(true);
332
      conn.setDoOutput(true);
333
      conn.setUseCaches(false);
334
      conn.setRequestMethod("GET");
335
      conn.connect();
336
      conn.getOutputStream().flush();
337

    
338
      InputStream is = conn.getInputStream();
339
      BufferedReader r = new BufferedReader(new InputStreamReader(is));
340
      StringBuilder total = new StringBuilder();
341

    
342
      for (String line; (line = r.readLine()) != null; )
343
        {
344
        total.append(line).append('\n');
345
        }
346

    
347
      mScores = total.toString();
348
      conn.disconnect();
349
      }
350
    catch( final UnknownHostException e )
351
      {
352
      receiver.message("No access to Internet");
353
      return false;
354
      }
355
    catch( final SecurityException e )
356
      {
357
      receiver.message("Application not authorized to connect to the Internet");
358
      return false;
359
      }
360
    catch( final Exception e )
361
      {
362
      receiver.message(e.getMessage());
363
      return false;
364
      }
365

    
366
    if( mScores.length()==0 )
367
      {
368
      receiver.message("Failed to download scores");
369
      return false;
370
      }
371

    
372
    return true;
373
    }
374

    
375
///////////////////////////////////////////////////////////////////////////////////////////////////
376

    
377
  private String constructSuspiciousURL(String suspURL)
378
    {
379
    RubikScores scores = RubikScores.getInstance();
380
    int deviceID       = scores.getDeviceID();
381
    String suspicious  = URLencode(suspURL);
382

    
383
    return SERVER+"suspicious.cgi?i="+deviceID+"&d="+suspicious;
384
    }
385

    
386
///////////////////////////////////////////////////////////////////////////////////////////////////
387

    
388
  private String constructTokenURL(String token)
389
    {
390
    RubikScores scores = RubikScores.getInstance();
391
    String name = URLencode(scores.getName());
392
    int deviceID= scores.getDeviceID();
393
    String country = scores.getCountry();
394
    String version = mVersion==null ? "null" : mVersion;
395
    String tkn = URLencode(token);
396

    
397
    return SERVER+"token.cgi?n="+name+"&i="+deviceID+"&e="+version+"&c="+country+"&t="+tkn;
398
    }
399

    
400
///////////////////////////////////////////////////////////////////////////////////////////////////
401

    
402
  private String constructUpdatesURL()
403
    {
404
    RubikScores sco = RubikScores.getInstance();
405
    String name     = URLencode(sco.getName());
406
    int numRuns     = sco.getNumRuns();
407
    int numPlay     = sco.getNumPlays();
408
    String country  = sco.getCountry();
409
    int objectAPI   = JsonWriter.VERSION_OBJECT_APP;
410
    int tutorialAPI = JsonWriter.VERSION_EXTRAS_APP;
411

    
412
    String url=SERVER+"updates.cgi";
413
    url += "?n="+name+"&r="+numRuns+"&p="+numPlay+"&c="+country+"&e="+mVersion+"d"+"&a="+objectAPI+"&b="+tutorialAPI;
414

    
415
    return url;
416
    }
417

    
418
///////////////////////////////////////////////////////////////////////////////////////////////////
419

    
420
  private String constructDownloadURL()
421
    {
422
    RubikScores scores = RubikScores.getInstance();
423
    String name = URLencode(scores.getName());
424
    int numRuns = scores.getNumRuns();
425
    int numPlay = scores.getNumPlays();
426
    String country = scores.getCountry();
427

    
428
    return SERVER+"download.cgi?n="+name+"&r="+numRuns+"&p="+numPlay+"&c="+country+"&e="+mVersion;
429
    }
430

    
431
///////////////////////////////////////////////////////////////////////////////////////////////////
432

    
433
  private String constructSubmitURL()
434
    {
435
    RubikScores scores = RubikScores.getInstance();
436
    String name = URLencode(scores.getName());
437
    String veri = scores.isVerified() ? "1" : "";
438
    int numRuns = scores.getNumRuns();
439
    int numPlay = scores.getNumPlays();
440
    int deviceID= scores.getDeviceID();
441
    String reclist = scores.getRecordList("&o=","&l=","&t=");
442
    String country = scores.getCountry();
443
    long epoch = System.currentTimeMillis();
444
    String salt = "cuboid";
445

    
446
    String url1=SERVER+"submit.cgi";
447
    String url2 = "n="+name+"&v="+veri+"&r="+numRuns+"&p="+numPlay+"&i="+deviceID+"&e="+mVersion;
448
    url2 += reclist+"&c="+country+"&f="+epoch;
449
    String hash = computeHash( url2, salt.getBytes() );
450

    
451
    return url1 + "?" + url2 + "&h=" + hash;
452
    }
453

    
454
///////////////////////////////////////////////////////////////////////////////////////////////////
455

    
456
  private boolean gottaDownload()
457
    {
458
    return ((mScores.length()==0) && !mRunning);
459
    }
460

    
461
///////////////////////////////////////////////////////////////////////////////////////////////////
462

    
463
  private void figureOutVersion(Activity act)
464
    {
465
    if( mVersion==null )
466
      {
467
      try
468
        {
469
        PackageInfo pInfo = act.getPackageManager().getPackageInfo( act.getPackageName(), 0);
470
        mVersion = pInfo.versionName;
471
        }
472
      catch (PackageManager.NameNotFoundException e)
473
        {
474
        mVersion = "0.9.2";
475
        }
476
      }
477
    }
478

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

    
481
  private void downloadThread(ScoresReceiver receiver)
482
    {
483
    boolean receiveValues=true;
484

    
485
    try
486
      {
487
      if( gottaDownload() )
488
        {
489
        mRunning = true;
490
        receiveValues = network(constructDownloadURL(),receiver);
491

    
492
        if( mRunning )
493
          {
494
          receiveValues = fillValuesNormal(receiver);
495
          mRunning = false;
496
          }
497
        }
498
      if( receiveValues ) receiver.receive(mCountry, mName, mTime);
499
      }
500
    catch( Exception e )
501
      {
502
      receiver.message("Exception downloading records: "+e.getMessage() );
503
      }
504
    }
505

    
506
///////////////////////////////////////////////////////////////////////////////////////////////////
507

    
508
  private void submitThread(ScoresReceiver receiver)
509
    {
510
    try
511
      {
512
      mRunning = true;
513
      RubikScores scores = RubikScores.getInstance();
514

    
515
      if( scores.thereAreUnsubmittedRecords() )
516
        {
517
        boolean receiveValues = network(constructSubmitURL(),receiver);
518

    
519
        if( mRunning )
520
          {
521
          receiveValues = fillValuesNormal(receiver);
522
          mRunning = false;
523
          }
524

    
525
        if( receiveValues )
526
          {
527
          RubikScores.getInstance().successfulSubmit();
528
          receiver.receive(mCountry, mName, mTime);
529
          }
530
        }
531
      }
532
    catch( Exception e )
533
      {
534
      receiver.message("Exception submitting records: "+e.getMessage() );
535
      }
536
    }
537

    
538
///////////////////////////////////////////////////////////////////////////////////////////////////
539

    
540
  private void updatesThread()
541
    {
542
    String url = constructUpdatesURL();
543

    
544
    try
545
      {
546
      java.net.URL connectURL = new URL(url);
547
      HttpURLConnection conn = (HttpURLConnection)connectURL.openConnection();
548

    
549
      conn.setDoInput(true);
550
      conn.setDoOutput(true);
551
      conn.setUseCaches(false);
552
      conn.setRequestMethod("GET");
553
      conn.connect();
554
      conn.getOutputStream().flush();
555

    
556
      InputStream is = conn.getInputStream();
557
      BufferedReader r = new BufferedReader(new InputStreamReader(is));
558
      StringBuilder answer = new StringBuilder();
559

    
560
      for( String line; (line = r.readLine()) != null; )
561
        {
562
        answer.append(line).append('\n');
563
        }
564

    
565
      String updates = answer.toString();
566
      conn.disconnect();
567
      mUpdates.parse(updates);
568

    
569
      if( mUpdateeList!=null )
570
        {
571
        int numUpdatees = mUpdateeList.size();
572

    
573
        for(int u=0; u<numUpdatees; u++)
574
          {
575
          Updatee upd = mUpdateeList.get(u);
576
          upd.receiveUpdate(mUpdates);
577
          }
578
        }
579

    
580
      mUpdatesState = UPDATES_SUCCESS;
581
      }
582
    catch( final Exception e )
583
      {
584
      if( mUpdateeList!=null )
585
        {
586
        int numUpdatees = mUpdateeList.size();
587

    
588
        for(int u=0; u<numUpdatees; u++)
589
          {
590
          Updatee upd = mUpdateeList.get(u);
591
          upd.errorUpdate();
592
          }
593
        }
594

    
595
      mUpdatesState = UPDATES_FAILURE;
596
      }
597
    }
598

    
599
///////////////////////////////////////////////////////////////////////////////////////////////////
600

    
601
  private void suspiciousThread(String suspURL)
602
    {
603
    String url = constructSuspiciousURL(suspURL);
604

    
605
    try
606
      {
607
      java.net.URL connectURL = new URL(url);
608
      HttpURLConnection conn = (HttpURLConnection)connectURL.openConnection();
609

    
610
      conn.setDoInput(true);
611
      conn.setDoOutput(true);
612
      conn.setUseCaches(false);
613
      conn.setRequestMethod("GET");
614
      conn.connect();
615
      conn.getOutputStream().flush();
616
      conn.getInputStream();
617
      conn.disconnect();
618
      }
619
    catch( final Exception e )
620
      {
621
      // ignore
622
      }
623
    }
624

    
625
///////////////////////////////////////////////////////////////////////////////////////////////////
626

    
627
  private void tokenThread(String token)
628
    {
629
    String url = constructTokenURL(token);
630

    
631
    try
632
      {
633
      java.net.URL connectURL = new URL(url);
634
      HttpURLConnection conn = (HttpURLConnection)connectURL.openConnection();
635

    
636
      conn.setDoInput(true);
637
      conn.setDoOutput(true);
638
      conn.setUseCaches(false);
639
      conn.setRequestMethod("GET");
640
      conn.connect();
641
      conn.getOutputStream().flush();
642
      conn.getInputStream();
643
      conn.disconnect();
644
      }
645
    catch( final Exception e )
646
      {
647
      // ignore
648
      }
649
    }
650

    
651
///////////////////////////////////////////////////////////////////////////////////////////////////
652

    
653
  private Bitmap downloadIcon(String url)
654
    {
655
    try
656
      {
657
      java.net.URL connectURL = new URL(url);
658
      HttpURLConnection conn = (HttpURLConnection) connectURL.openConnection();
659
      conn.setDoInput(true);
660
      conn.connect();
661
      InputStream input = conn.getInputStream();
662
      Bitmap icon = BitmapFactory.decodeStream(input);
663
      conn.disconnect();
664
      return icon;
665
      }
666
    catch (IOException e)
667
      {
668
      android.util.Log.e("D", "Failed to download "+url);
669
      android.util.Log.e("D", e.getMessage() );
670
      return null;
671
      }
672
    }
673

    
674
///////////////////////////////////////////////////////////////////////////////////////////////////
675

    
676
  private void iconThread(Context context, IconReceiver receiver)
677
    {
678
    int numC = mUpdates.getCompletedNumber();
679
    int numS = mUpdates.getStartedNumber();
680

    
681
    for(int c=0; c<numC; c++)
682
      {
683
      int iconPresent = mUpdates.getCompletedIconPresent(c);
684

    
685
      if( iconPresent!=0 )
686
        {
687
        boolean downloaded = false;
688
        Bitmap icon = mUpdates.getCompletedIcon(context,c);
689

    
690
        if( icon==null )
691
          {
692
          String url = mUpdates.getCompletedURL(c);
693
          icon = downloadIcon(url);
694
          downloaded = true;
695

    
696
          if( SHOW_DOWNLOADED_DEBUG ) android.util.Log.e("D", "Downloading icon "+url);
697
          }
698
        if( icon!=null )
699
          {
700
          mUpdates.setCompletedIcon(c,icon);
701
          receiver.iconDownloaded(c,icon,downloaded);
702
          }
703
        }
704
      }
705

    
706
    for(int s=0; s<numS; s++)
707
      {
708
      int iconPresent = mUpdates.getStartedIconPresent(s);
709

    
710
      if( iconPresent!=0 )
711
        {
712
        boolean downloaded = false;
713
        Bitmap icon = mUpdates.getStartedIcon(context,s);
714

    
715
        if( icon==null )
716
          {
717
          String url = mUpdates.getStartedURL(s);
718
          icon = downloadIcon(url);
719
          downloaded = true;
720

    
721
          if( SHOW_DOWNLOADED_DEBUG ) android.util.Log.e("D", "Downloading icon "+url);
722
          }
723
        if( icon!=null )
724
          {
725
          mUpdates.setStartedIcon(s,icon);
726
          receiver.iconDownloaded(numC+s,icon,downloaded);
727
          }
728
        }
729
      }
730
    }
731

    
732
///////////////////////////////////////////////////////////////////////////////////////////////////
733

    
734
  private InputStream downloadJSON(String name)
735
    {
736
    String url = mUpdates.getURL() + name;
737

    
738
    try
739
      {
740
      if( SHOW_DOWNLOADED_DEBUG ) android.util.Log.e("D", "Downloading JSON "+url);
741

    
742
      java.net.URL connectURL = new URL(url);
743
      HttpURLConnection conn = (HttpURLConnection) connectURL.openConnection();
744
      conn.setDoInput(true);
745
      conn.connect();
746
      return conn.getInputStream();
747
      }
748
    catch (IOException e)
749
      {
750
      android.util.Log.e("D", "Failed to download "+url);
751
      android.util.Log.e("D", e.getMessage() );
752
      return null;
753
      }
754
    }
755

    
756
///////////////////////////////////////////////////////////////////////////////////////////////////
757

    
758
  private void jsonThread(final RubikUpdates.UpdateInfo info, Downloadee downloadee)
759
    {
760
    if(info.mUpdateObject) info.mObjectStream = downloadJSON(info.mObjectShortName+"_object.json");
761
    if(info.mUpdateExtras) info.mExtrasStream = downloadJSON(info.mObjectShortName+"_extras.json");
762

    
763
    downloadee.jsonDownloaded();
764

    
765
    try
766
      {
767
      if( info.mObjectStream!=null ) info.mObjectStream.close();
768
      }
769
    catch(IOException ioe)
770
      {
771
      android.util.Log.e("D", "failed to close object input stream");
772
      }
773

    
774
    try
775
      {
776
      if( info.mExtrasStream!=null ) info.mExtrasStream.close();
777
      }
778
    catch(IOException ioe)
779
      {
780
      android.util.Log.e("D", "failed to close extras input stream");
781
      }
782
    }
783

    
784
///////////////////////////////////////////////////////////////////////////////////////////////////
785

    
786
  private RubikNetwork()
787
    {
788

    
789
    }
790

    
791
///////////////////////////////////////////////////////////////////////////////////////////////////
792
// PUBLIC API
793
///////////////////////////////////////////////////////////////////////////////////////////////////
794

    
795
  public static void onPause()
796
    {
797
    mRunning = false;
798
    mUpdateeList.clear();
799
    }
800

    
801
///////////////////////////////////////////////////////////////////////////////////////////////////
802

    
803
  public static RubikNetwork getInstance()
804
    {
805
    if( mThis==null ) mThis = new RubikNetwork();
806
    return mThis;
807
    }
808

    
809
///////////////////////////////////////////////////////////////////////////////////////////////////
810

    
811
  public void download(final ScoresReceiver receiver, final Activity act)
812
    {
813
    initializeStatics();
814
    figureOutVersion(act);
815

    
816
    Thread thread = new Thread()
817
      {
818
      public void run()
819
        {
820
        downloadThread(receiver);
821
        }
822
      };
823

    
824
    thread.start();
825
    }
826

    
827
///////////////////////////////////////////////////////////////////////////////////////////////////
828

    
829
  public void submit(ScoresReceiver receiver, final Activity act)
830
    {
831
    initializeStatics();
832
    figureOutVersion(act);
833

    
834
    Thread thread = new Thread()
835
      {
836
      public void run()
837
        {
838
        submitThread(receiver);
839
        }
840
      };
841

    
842
    thread.start();
843
    }
844

    
845
///////////////////////////////////////////////////////////////////////////////////////////////////
846

    
847
  public void downloadUpdates(final Activity act)
848
    {
849
    initializeStatics();
850
    figureOutVersion(act);
851
    mUpdatesState = UPDATES_RUNNING;
852

    
853
    Thread thread = new Thread()
854
      {
855
      public void run()
856
        {
857
        updatesThread();
858
        }
859
      };
860

    
861
    thread.start();
862
    }
863

    
864
///////////////////////////////////////////////////////////////////////////////////////////////////
865

    
866
  public void suspicious(final String suspicious, final Activity act)
867
    {
868
    initializeStatics();
869
    figureOutVersion(act);
870

    
871
    Thread thread = new Thread()
872
      {
873
      public void run()
874
        {
875
        suspiciousThread(suspicious);
876
        }
877
      };
878

    
879
    thread.start();
880
    }
881

    
882
///////////////////////////////////////////////////////////////////////////////////////////////////
883

    
884
  public void token(final String token)
885
    {
886
    initializeStatics();
887

    
888
    Thread thread = new Thread()
889
      {
890
      public void run()
891
        {
892
        tokenThread(token);
893
        }
894
      };
895

    
896
    thread.start();
897
    }
898

    
899
///////////////////////////////////////////////////////////////////////////////////////////////////
900

    
901
  public void signUpForUpdates(Updatee updatee)
902
    {
903
    if( mUpdateeList==null ) mUpdateeList = new ArrayList<>();
904

    
905
    int numUpdatees = mUpdateeList.size();
906
    int type = updatee.getType();
907

    
908
    for(int u=0; u<numUpdatees; u++)
909
      {
910
      Updatee upd = mUpdateeList.get(u);
911

    
912
      if( upd.getType()==type )
913
        {
914
        mUpdateeList.remove(u);
915
        break;
916
        }
917
      }
918

    
919
    mUpdateeList.add(updatee);
920

    
921
    if( mUpdatesState==UPDATES_SUCCESS )
922
      {
923
      updatee.receiveUpdate(mUpdates);
924
      }
925
    else if( mUpdatesState==UPDATES_FAILURE )
926
      {
927
      updatee.errorUpdate();
928
      }
929
    }
930

    
931
///////////////////////////////////////////////////////////////////////////////////////////////////
932

    
933
  public void downloadIcons(final Context context, final IconReceiver receiver)
934
    {
935
    initializeStatics();
936

    
937
    Thread thread = new Thread()
938
      {
939
      public void run()
940
        {
941
        iconThread(context,receiver);
942
        }
943
      };
944

    
945
    thread.start();
946
    }
947

    
948
///////////////////////////////////////////////////////////////////////////////////////////////////
949

    
950
  public void downloadJSON(final RubikUpdates.UpdateInfo info, final Downloadee downloadee)
951
    {
952
    initializeStatics();
953

    
954
    Thread thread = new Thread()
955
      {
956
      public void run()
957
        {
958
        jsonThread(info,downloadee);
959
        }
960
      };
961

    
962
    thread.start();
963
    }
964

    
965
///////////////////////////////////////////////////////////////////////////////////////////////////
966

    
967
  public void updateDone(String shortName)
968
    {
969
    mUpdates.updateDone(shortName);
970
    mScores = "";
971

    
972
    if( mUpdateeList!=null )
973
      {
974
      int numUpdatees = mUpdateeList.size();
975

    
976
      for(int u=0; u<numUpdatees; u++)
977
        {
978
        Updatee upd = mUpdateeList.get(u);
979
        upd.objectDownloaded(shortName);
980
        }
981
      }
982
    }
983
}
(2-2/4)