Project

General

Profile

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

magiccube / src / main / java / org / distorted / scores / RubikScores.java @ c8249cf6

1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2020 Leszek Koltunski                                                               //
3
//                                                                                               //
4
// This file is part of Magic Cube.                                                              //
5
//                                                                                               //
6
// Magic Cube is free software: you can redistribute it and/or modify                            //
7
// it under the terms of the GNU General Public License as published by                          //
8
// the Free Software Foundation, either version 2 of the License, or                             //
9
// (at your option) any later version.                                                           //
10
//                                                                                               //
11
// Magic Cube is distributed in the hope that it will be useful,                                 //
12
// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
13
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
14
// GNU General Public License for more details.                                                  //
15
//                                                                                               //
16
// You should have received a copy of the GNU General Public License                             //
17
// along with Magic Cube.  If not, see <http://www.gnu.org/licenses/>.                           //
18
///////////////////////////////////////////////////////////////////////////////////////////////////
19

    
20
package org.distorted.scores;
21

    
22
import android.content.Context;
23
import android.content.SharedPreferences;
24
import android.telephony.TelephonyManager;
25

    
26
import com.google.firebase.crashlytics.FirebaseCrashlytics;
27

    
28
import org.distorted.main.BuildConfig;
29
import org.distorted.objects.ObjectList;
30

    
31
import java.util.UUID;
32

    
33
import static org.distorted.objects.ObjectList.MAX_NUM_OBJECTS;
34
import static org.distorted.objects.ObjectList.NUM_OBJECTS;
35
import static org.distorted.objects.ObjectList.MAX_LEVEL;
36

    
37
///////////////////////////////////////////////////////////////////////////////////////////////////
38
// hold my own scores, and some other statistics.
39

    
40
public class RubikScores
41
  {
42
  public static final long NO_RECORD = Long.MAX_VALUE;
43
  private static RubikScores mThis;
44

    
45
  private long[][][] mRecords;
46
  private int [][][] mSubmitted;
47

    
48
  private String mName, mCountry;
49
  private boolean mNameIsVerified;
50
  private int mNumRuns;
51
  private int mNumPlays;
52
  private int mNumReviews;
53
  private int mDeviceID;
54

    
55
///////////////////////////////////////////////////////////////////////////////////////////////////
56

    
57
  private RubikScores()
58
    {
59
    mRecords   = new long[NUM_OBJECTS][MAX_NUM_OBJECTS][MAX_LEVEL];
60
    mSubmitted = new int [NUM_OBJECTS][MAX_NUM_OBJECTS][MAX_LEVEL];
61

    
62
    for(int i=0; i<NUM_OBJECTS; i++)
63
      for(int j=0; j<MAX_NUM_OBJECTS; j++)
64
        for(int k=0; k<MAX_LEVEL; k++)
65
          {
66
          mRecords[i][j][k]   = NO_RECORD;
67
          mSubmitted[i][j][k] = 0;
68
          }
69

    
70
    mName = "";
71
    mCountry = "un";
72

    
73
    mNameIsVerified = false;
74

    
75
    mNumPlays   = -1;
76
    mNumRuns    = -1;
77
    mDeviceID   = -1;
78
    mNumReviews =  0;
79
    }
80

    
81
///////////////////////////////////////////////////////////////////////////////////////////////////
82

    
83
  private int privateGetDeviceID()
84
    {
85
    int id;
86

    
87
    try
88
      {
89
      String s = UUID.randomUUID().toString();
90
      id = s.hashCode();
91
      }
92
    catch(Exception ex)
93
      {
94
      id = 0;
95
      android.util.Log.e("scores", "Exception in getDeviceID()");
96
      }
97

    
98
    return id<0 ? -id : id;
99
    }
100

    
101
///////////////////////////////////////////////////////////////////////////////////////////////////
102

    
103
  synchronized void successfulSubmit()
104
    {
105
    mNameIsVerified = true;
106

    
107
    for(int i=0; i<NUM_OBJECTS; i++)
108
      for(int j=0; j<MAX_NUM_OBJECTS; j++)
109
        for(int k=0; k<MAX_LEVEL; k++)
110
          {
111
          mSubmitted[i][j][k]=1;
112
          }
113
    }
114

    
115
///////////////////////////////////////////////////////////////////////////////////////////////////
116

    
117
  int getDeviceID()
118
    {
119
    return mDeviceID;
120
    }
121

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

    
124
  boolean isVerified()
125
    {
126
    return mNameIsVerified;
127
    }
128

    
129
///////////////////////////////////////////////////////////////////////////////////////////////////
130

    
131
  synchronized boolean thereAreUnsubmittedRecords()
132
    {
133
    ObjectList list;
134
    int length;
135

    
136
    for(int object=0; object<NUM_OBJECTS; object++)
137
      {
138
      list = ObjectList.getObject(object);
139
      length = list.getSizes().length;
140

    
141
      for(int size=0; size<length; size++)
142
        for(int level=0; level<MAX_LEVEL; level++)
143
          {
144
          if( mSubmitted[object][size][level]==0 && mRecords[object][size][level]<NO_RECORD )
145
            {
146
            return true;
147
            }
148
          }
149
      }
150

    
151
    return false;
152
    }
153

    
154
///////////////////////////////////////////////////////////////////////////////////////////////////
155

    
156
  synchronized String getRecordList(String strObj, String strLvl, String strTim)
157
    {
158
    ObjectList list;
159
    StringBuilder builderObj = new StringBuilder();
160
    StringBuilder builderLvl = new StringBuilder();
161
    StringBuilder builderTim = new StringBuilder();
162
    boolean first = true;
163
    int[] sizes;
164
    int length;
165

    
166
    for(int object=0; object<NUM_OBJECTS; object++)
167
      {
168
      list = ObjectList.getObject(object);
169
      sizes = list.getSizes();
170
      length = sizes.length;
171

    
172
      for(int size=0; size<length; size++)
173
        {
174
        for(int level=0; level<MAX_LEVEL; level++)
175
          {
176
          if( mSubmitted[object][size][level]==0 && mRecords[object][size][level]<NO_RECORD )
177
            {
178
            if( !first )
179
              {
180
              builderObj.append(',');
181
              builderLvl.append(',');
182
              builderTim.append(',');
183
              }
184
            else
185
              {
186
              first=false;
187
              }
188

    
189
            builderObj.append(list.name());
190
            builderObj.append("_");
191
            builderObj.append(sizes[size]);
192
            builderLvl.append(level);
193
            builderTim.append(mRecords[object][size][level]);
194
            }
195
          }
196
        }
197
      }
198

    
199
    return strObj+builderObj.toString()+strLvl+builderLvl.toString()+strTim+builderTim.toString();
200
    }
201

    
202
///////////////////////////////////////////////////////////////////////////////////////////////////
203
// Public API
204
///////////////////////////////////////////////////////////////////////////////////////////////////
205

    
206
  public synchronized long getRecord(int object, int size, int level)
207
    {
208
    int maxsize = ObjectList.getObject(object).getSizes().length;
209

    
210
    if( object>=0 && object<NUM_OBJECTS && size>=0 && size<maxsize && level>=0 && level<MAX_LEVEL )
211
      {
212
      return mRecords[object][size][level];
213
      }
214

    
215
    return -1;
216
    }
217

    
218
///////////////////////////////////////////////////////////////////////////////////////////////////
219

    
220
  public synchronized boolean isSubmitted(int object, int size, int level)
221
    {
222
    int maxsize = ObjectList.getObject(object).getSizes().length;
223

    
224
    if( object>=0 && object<NUM_OBJECTS && size>=0 && size<maxsize && level>=0 && level<MAX_LEVEL )
225
      {
226
      return mSubmitted[object][size][level]==1;
227
      }
228

    
229
    return false;
230
    }
231

    
232
///////////////////////////////////////////////////////////////////////////////////////////////////
233

    
234
  public int getNumPlays()
235
    {
236
    return mNumPlays;
237
    }
238

    
239
///////////////////////////////////////////////////////////////////////////////////////////////////
240

    
241
  public int getNumRuns()
242
    {
243
    return mNumRuns;
244
    }
245

    
246
///////////////////////////////////////////////////////////////////////////////////////////////////
247

    
248
  public int getNumReviews()
249
    {
250
    return mNumReviews;
251
    }
252

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

    
255
  public String getName()
256
    {
257
    return mName;
258
    }
259

    
260
///////////////////////////////////////////////////////////////////////////////////////////////////
261

    
262
  public String getCountry()
263
    {
264
    return mCountry;
265
    }
266

    
267
///////////////////////////////////////////////////////////////////////////////////////////////////
268

    
269
  public void incrementNumPlays()
270
    {
271
    mNumPlays++;
272
    }
273

    
274
///////////////////////////////////////////////////////////////////////////////////////////////////
275

    
276
  public void incrementNumRuns()
277
    {
278
    mNumRuns++;
279
    }
280

    
281
///////////////////////////////////////////////////////////////////////////////////////////////////
282

    
283
  public void incrementNumReviews()
284
    {
285
    mNumReviews++;
286
    }
287

    
288
///////////////////////////////////////////////////////////////////////////////////////////////////
289

    
290
  public void setName(String newName)
291
    {
292
    mName = newName;
293
    }
294

    
295
///////////////////////////////////////////////////////////////////////////////////////////////////
296

    
297
  public synchronized boolean isSolved(int object, int size, int level)
298
    {
299
    int maxsize = ObjectList.getObject(object).getSizes().length;
300

    
301
    if( object>=0 && object<NUM_OBJECTS && size>=0 && size<maxsize && level>=0 && level<MAX_LEVEL )
302
      {
303
      return mRecords[object][size][level]<NO_RECORD;
304
      }
305

    
306
    return false;
307
    }
308

    
309
///////////////////////////////////////////////////////////////////////////////////////////////////
310

    
311
  public void setCountry(Context context)
312
    {
313
    TelephonyManager tM =((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE));
314

    
315
    if( tM!=null )
316
      {
317
      mCountry = tM.getSimCountryIso();
318

    
319
      if( mCountry==null || mCountry.length()<=1 )
320
        {
321
        mCountry = tM.getNetworkCountryIso();
322
        }
323
      }
324

    
325
    // Special case: Dominicana. Its ISO-3166-alpha-2 country code is 'do' which we can't have here
326
    // because we later on map this to a resource name (the flag) and 'do' is a reserved Java keyword
327
    // and can't be a resource name.
328

    
329
    if( mCountry.equals("do") ) mCountry = "dm";
330
    }
331

    
332
///////////////////////////////////////////////////////////////////////////////////////////////////
333

    
334
  public void setCountry(String country)
335
    {
336
    mCountry = country;
337

    
338
    if( mCountry.equals("do") ) mCountry = "dm";  // see above
339
    }
340

    
341
///////////////////////////////////////////////////////////////////////////////////////////////////
342

    
343
  public static RubikScores getInstance()
344
    {
345
    if( mThis==null )
346
      {
347
      mThis = new RubikScores();
348
      }
349

    
350
    return mThis;
351
    }
352

    
353
///////////////////////////////////////////////////////////////////////////////////////////////////
354

    
355
  public synchronized void savePreferences(SharedPreferences.Editor editor)
356
    {
357
    StringBuilder builder = new StringBuilder();
358
    ObjectList list;
359
    int[] sizes;
360
    int length;
361

    
362
    for(int level=0; level<MAX_LEVEL; level++)
363
      {
364
      builder.setLength(0);
365

    
366
      for(int object=0; object<NUM_OBJECTS; object++)
367
        {
368
        list = ObjectList.getObject(object);
369
        sizes = list.getSizes();
370
        length = sizes.length;
371

    
372
        for(int size=0; size<length; size++)
373
          {
374
          builder.append(list.name());
375
          builder.append("_");
376
          builder.append(sizes[size]);
377
          builder.append("=");
378
          builder.append(mRecords[object][size][level]);
379
          builder.append(",");
380
          builder.append(mSubmitted[object][size][level]);
381
          builder.append(" ");
382
          }
383
        }
384

    
385
      editor.putString("scores_record"+level, builder.toString());
386
      }
387

    
388
    editor.putString("scores_name"  , mName  );
389
    editor.putBoolean("scores_isVerified", mNameIsVerified);
390
    editor.putInt("scores_numPlays", mNumPlays);
391
    editor.putInt("scores_numRuns" , mNumRuns);
392
    editor.putInt("scores_deviceid", mDeviceID);
393
    editor.putInt("scores_review"  , mNumReviews);
394
    }
395

    
396
///////////////////////////////////////////////////////////////////////////////////////////////////
397

    
398
  public synchronized void restorePreferences(SharedPreferences preferences)
399
    {
400
    String recordStr, subStr, nameStr, sizeStr, timeStr, submStr, errorStr="";
401
    int start, end, equals, underscore, comma;
402
    int object, sizeIndex, subm;
403
    long time;
404
    boolean thereWasError = false;
405

    
406
    for(int level=0; level<MAX_LEVEL; level++)
407
      {
408
      start = end = 0;
409
      recordStr = preferences.getString("scores_record"+level, "");
410

    
411
      while( end!=-1 )
412
        {
413
        end = recordStr.indexOf(" ", start);
414

    
415
        if( end==-1 ) subStr = recordStr.substring(start);
416
        else          subStr = recordStr.substring(start,end);
417

    
418
        start = end+1;
419

    
420
        underscore = subStr.indexOf("_");
421
        equals = subStr.indexOf("=");
422
        comma = subStr.indexOf(",");
423

    
424
        if( underscore>=0 && equals>=0 && comma>=0 )
425
          {
426
          nameStr = subStr.substring(0,underscore);
427
          sizeStr = subStr.substring(underscore+1, equals);
428
          timeStr = subStr.substring(equals+1,comma);
429
          submStr = subStr.substring(comma+1);
430

    
431
          object = ObjectList.getOrdinal(nameStr);
432

    
433
          if( object>=0 && object< NUM_OBJECTS )
434
            {
435
            sizeIndex = ObjectList.getSizeIndex(object,Integer.parseInt(sizeStr));
436
            time = Long.parseLong(timeStr);
437
            subm = Integer.parseInt(submStr);
438

    
439
            if( sizeIndex>=0 && sizeIndex<MAX_NUM_OBJECTS && subm>=0 && subm<=1 )
440
              {
441
              mRecords  [object][sizeIndex][level] = time;
442
              mSubmitted[object][sizeIndex][level] = subm;
443
              }
444
            else
445
              {
446
              errorStr += ("error1: size="+sizeIndex+" subm="+subm+" obj: "+nameStr+" size: "+sizeStr+"\n");
447
              thereWasError= true;
448
              }
449
            }
450
          else
451
            {
452
            errorStr += ("error2: object="+object+" obj: "+nameStr+" size: "+sizeStr+"\n");
453
            thereWasError = true;
454
            }
455
          }
456
        }
457
      }
458

    
459
    mName           = preferences.getString("scores_name"  , "" );
460
    mNameIsVerified = preferences.getBoolean("scores_isVerified", false);
461
    mNumPlays       = preferences.getInt("scores_numPlays", 0);
462
    mNumRuns        = preferences.getInt("scores_numRuns" , 0);
463
    mDeviceID       = preferences.getInt("scores_deviceid",-1);
464
    mNumReviews     = preferences.getInt("scores_review"  ,-3);
465

    
466
    if( mDeviceID==-1 ) mDeviceID = privateGetDeviceID();
467

    
468
    if( thereWasError ) recordDBError(errorStr);
469
    }
470

    
471
///////////////////////////////////////////////////////////////////////////////////////////////////
472

    
473
  public void recordDBError(String message)
474
    {
475
    if( BuildConfig.DEBUG )
476
      {
477
      android.util.Log.e("scores", message);
478
      }
479
    else
480
      {
481
      Exception ex = new Exception(message);
482
      FirebaseCrashlytics crashlytics = FirebaseCrashlytics.getInstance();
483
      crashlytics.setCustomKey("scores" , message);
484
      crashlytics.recordException(ex);
485
      }
486
    }
487

    
488
///////////////////////////////////////////////////////////////////////////////////////////////////
489

    
490
  public synchronized boolean setRecord(int object, int size, int level, long timeTaken)
491
    {
492
    int maxsize = ObjectList.getObject(object).getSizes().length;
493

    
494
    if( object>=0 && object<NUM_OBJECTS && size>=0 && size<maxsize && level>=1 && level<=MAX_LEVEL )
495
      {
496
      if( mRecords[object][size][level-1]> timeTaken )
497
        {
498
        mRecords  [object][size][level-1] = timeTaken;
499
        mSubmitted[object][size][level-1] = 0;
500
        return true;
501
        }
502
      }
503

    
504
    return false;
505
    }
506
  }
(1-1/2)