Project

General

Profile

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

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

1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2020 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.util.HashMap;
13
import java.util.UUID;
14

    
15
import android.content.Context;
16
import android.content.SharedPreferences;
17
import android.telephony.TelephonyManager;
18

    
19
import com.google.firebase.crashlytics.FirebaseCrashlytics;
20

    
21
import org.distorted.main.BuildConfig;
22
import org.distorted.objects.RubikObject;
23
import org.distorted.objects.RubikObjectList;
24

    
25
import static org.distorted.objects.RubikObjectList.MAX_LEVEL;
26

    
27
///////////////////////////////////////////////////////////////////////////////////////////////////
28
// hold my own scores, and some other statistics.
29

    
30
public class RubikScores
31
  {
32
  public static final int MULT = 1000000;
33
  public static final long NO_RECORD = Long.MAX_VALUE;
34
  private static RubikScores mThis;
35

    
36
  private String mName, mCountry;
37
  private boolean mNameIsVerified;
38
  private int mNumRuns;
39
  private int mNumPlays;
40
  private int mNumWins;
41
  private int mDeviceID;
42

    
43
  private static class MapValue
44
    {
45
    long record;
46
    boolean submitted;
47

    
48
    MapValue(long rec,int sub)
49
      {
50
      record    = rec;
51
      submitted = sub!=0;
52
      }
53
    }
54

    
55
  private final HashMap<Integer,MapValue> mMap;
56

    
57
///////////////////////////////////////////////////////////////////////////////////////////////////
58

    
59
  private RubikScores()
60
    {
61
    mMap = new HashMap<>();
62

    
63
    mName = "";
64
    mCountry = "un";
65

    
66
    mNameIsVerified = false;
67

    
68
    mNumPlays= -1;
69
    mNumRuns = -1;
70
    mDeviceID= -1;
71
    mNumWins =  0;
72
    }
73

    
74
///////////////////////////////////////////////////////////////////////////////////////////////////
75

    
76
  private int mapKey(int object,int level)
77
    {
78
    return object*MULT + level;
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 key: mMap.keySet())
108
      {
109
      MapValue value = mMap.get(key);
110
      if( value!=null ) value.submitted = true;
111
      }
112
    }
113

    
114
///////////////////////////////////////////////////////////////////////////////////////////////////
115

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

    
121
///////////////////////////////////////////////////////////////////////////////////////////////////
122

    
123
  synchronized boolean thereAreUnsubmittedRecords()
124
    {
125
    for(int key: mMap.keySet())
126
      {
127
      MapValue value = mMap.get(key);
128
      if( value!=null && !value.submitted && value.record<NO_RECORD) return true;
129
      }
130

    
131
    return false;
132
    }
133

    
134
///////////////////////////////////////////////////////////////////////////////////////////////////
135

    
136
  synchronized String getRecordList(String strObj, String strLvl, String strTim)
137
    {
138
    StringBuilder builderObj = new StringBuilder();
139
    StringBuilder builderLvl = new StringBuilder();
140
    StringBuilder builderTim = new StringBuilder();
141
    boolean first = true;
142

    
143
    for(int key: mMap.keySet())
144
      {
145
      MapValue value = mMap.get(key);
146

    
147
      if( value!=null && !value.submitted && value.record<NO_RECORD)
148
        {
149
        if( !first )
150
          {
151
          builderObj.append(',');
152
          builderLvl.append(',');
153
          builderTim.append(',');
154
          }
155
        first=false;
156

    
157
        RubikObject object = RubikObjectList.getObject(key/MULT);
158

    
159
        if( object!=null )
160
          {
161
          builderObj.append(object.getUpperName());
162
          builderLvl.append(key%MULT);
163
          builderTim.append(value.record);
164
          }
165
        }
166
      }
167

    
168
    return strObj+builderObj.toString()+strLvl+builderLvl.toString()+strTim+builderTim.toString();
169
    }
170

    
171
///////////////////////////////////////////////////////////////////////////////////////////////////
172
// Public API
173
///////////////////////////////////////////////////////////////////////////////////////////////////
174

    
175
  public boolean isVerified()
176
    {
177
    return mNameIsVerified;
178
    }
179

    
180
///////////////////////////////////////////////////////////////////////////////////////////////////
181

    
182
  public int getNumPlays()
183
    {
184
    return mNumPlays;
185
    }
186

    
187
///////////////////////////////////////////////////////////////////////////////////////////////////
188

    
189
  public int getNumRuns()
190
    {
191
    return mNumRuns;
192
    }
193

    
194
///////////////////////////////////////////////////////////////////////////////////////////////////
195

    
196
  public String getName()
197
    {
198
    return mName;
199
    }
200

    
201
///////////////////////////////////////////////////////////////////////////////////////////////////
202

    
203
  public String getCountry()
204
    {
205
    return mCountry;
206
    }
207

    
208
///////////////////////////////////////////////////////////////////////////////////////////////////
209

    
210
  public void incrementNumPlays()
211
    {
212
    mNumPlays++;
213
    }
214

    
215
///////////////////////////////////////////////////////////////////////////////////////////////////
216

    
217
  public void incrementNumRuns()
218
    {
219
    mNumRuns++;
220
    }
221

    
222
///////////////////////////////////////////////////////////////////////////////////////////////////
223

    
224
  public int incrementNumWins()
225
    {
226
    mNumWins++;
227
    return mNumWins;
228
    }
229

    
230
///////////////////////////////////////////////////////////////////////////////////////////////////
231

    
232
  public void setName(String newName)
233
    {
234
    mName = newName;
235
    }
236

    
237
///////////////////////////////////////////////////////////////////////////////////////////////////
238

    
239
  public synchronized boolean setRecord(int object, int level, long record)
240
    {
241
    int key = mapKey(object,level);
242
    MapValue oldValue = mMap.get(key);
243

    
244
    if( oldValue==null )
245
      {
246
      MapValue value = new MapValue(record,0);
247
      mMap.put(key,value);
248
      return true;
249
      }
250

    
251
    long oldRecord = oldValue.record;
252

    
253
    if( oldRecord>record)
254
      {
255
      MapValue value = new MapValue(record,0);
256
      mMap.put(key,value);
257
      return true;
258
      }
259

    
260
    return false;
261
    }
262
///////////////////////////////////////////////////////////////////////////////////////////////////
263

    
264
  public synchronized long getRecord(int object, int level)
265
    {
266
    int key = mapKey(object,level);
267
    MapValue value = mMap.get(key);
268
    return value!=null ? value.record : NO_RECORD;
269
    }
270

    
271
///////////////////////////////////////////////////////////////////////////////////////////////////
272

    
273
  public synchronized boolean isSolved(int object, int level)
274
    {
275
    int key = mapKey(object,level);
276
    MapValue value = mMap.get(key);
277
    return value!=null && value.record<NO_RECORD;
278
    }
279

    
280
///////////////////////////////////////////////////////////////////////////////////////////////////
281

    
282
  public void setCountry(Context context)
283
    {
284
    TelephonyManager tM =((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE));
285

    
286
    if( tM!=null )
287
      {
288
      mCountry = tM.getSimCountryIso();
289

    
290
      if( mCountry==null || mCountry.length()<=1 )
291
        {
292
        mCountry = tM.getNetworkCountryIso();
293
        }
294
      }
295

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

    
300
    if( mCountry.equals("do") ) mCountry = "dm";
301
    }
302

    
303
///////////////////////////////////////////////////////////////////////////////////////////////////
304

    
305
  public void setCountry(String country)
306
    {
307
    mCountry = country;
308

    
309
    if( mCountry.equals("do") ) mCountry = "dm";  // see above
310
    }
311

    
312
///////////////////////////////////////////////////////////////////////////////////////////////////
313

    
314
  public static RubikScores getInstance()
315
    {
316
    if( mThis==null ) mThis = new RubikScores();
317
    return mThis;
318
    }
319

    
320
///////////////////////////////////////////////////////////////////////////////////////////////////
321

    
322
  public synchronized void savePreferences(SharedPreferences.Editor editor)
323
    {
324
    int numObjects = RubikObjectList.getNumObjects();
325
    StringBuilder builder = new StringBuilder();
326

    
327
    for(int level=0; level<MAX_LEVEL; level++)
328
      {
329
      builder.setLength(0);
330

    
331
      for(int object=0; object<numObjects; object++)
332
        {
333
        int key = mapKey(object,level);
334
        RubikObject obj = RubikObjectList.getObject(object);
335
        MapValue value = mMap.get(key);
336

    
337
        if( obj!=null && value!=null && value.record<NO_RECORD )
338
          {
339
          builder.append(obj.getUpperName());
340
          builder.append("=");
341
          builder.append(value.record);
342
          builder.append(",");
343
          builder.append(value.submitted ? 1:0 );
344
          builder.append(" ");
345
          }
346
        }
347

    
348
      editor.putString("scores_record"+level, builder.toString());
349
      }
350

    
351
    editor.putString("scores_name"  , mName  );
352
    editor.putBoolean("scores_isVerified", mNameIsVerified);
353
    editor.putInt("scores_numPlays", mNumPlays);
354
    editor.putInt("scores_numRuns" , mNumRuns );
355
    editor.putInt("scores_deviceid", mDeviceID);
356
    editor.putInt("scores_review"  , mNumWins );
357
    }
358

    
359
///////////////////////////////////////////////////////////////////////////////////////////////////
360

    
361
  public synchronized void restorePreferences(SharedPreferences preferences)
362
    {
363
    String recordStr, subStr, nameStr, timeStr, submStr, errorStr="";
364
    int start, end, equals, comma, object, subm;
365
    long time;
366
    boolean thereWasError = false;
367
    int numObjects = RubikObjectList.getNumObjects();
368

    
369
    for(int level=0; level<MAX_LEVEL; level++)
370
      {
371
      start = end = 0;
372
      recordStr = preferences.getString("scores_record"+level, "");
373

    
374
      while( end!=-1 )
375
        {
376
        end = recordStr.indexOf(" ", start);
377

    
378
        if( end==-1 ) subStr = recordStr.substring(start);
379
        else          subStr = recordStr.substring(start,end);
380

    
381
        start = end+1;
382

    
383
        equals = subStr.indexOf("=");
384
        comma  = subStr.indexOf(",");
385

    
386
        if( equals>=0 && comma>=0 )
387
          {
388
          nameStr = subStr.substring(0,equals);
389
          timeStr = subStr.substring(equals+1,comma);
390
          submStr = subStr.substring(comma+1);
391

    
392
          object = RubikObjectList.getOrdinal(nameStr);
393

    
394
          if( object>=0 && object<numObjects )
395
            {
396
            time = Long.parseLong(timeStr);
397
            subm = Integer.parseInt(submStr);
398

    
399
            if( subm>=0 && subm<=1 )
400
              {
401
              MapValue value = new MapValue(time,subm);
402
              int key = mapKey(object,level);
403
              mMap.put(key,value);
404
              }
405
            else
406
              {
407
              errorStr += ("error1: subm="+subm+" obj: "+nameStr+"\n");
408
              thereWasError= true;
409
              }
410
            }
411
          else
412
            {
413
            errorStr += ("error2: object="+object+" obj: "+nameStr+"\n");
414
            thereWasError = true;
415
            }
416
          }
417
        }
418
      }
419

    
420
    mName           = preferences.getString("scores_name"  , "" );
421
    mNameIsVerified = preferences.getBoolean("scores_isVerified", false);
422
    mNumPlays       = preferences.getInt("scores_numPlays", 0);
423
    mNumRuns        = preferences.getInt("scores_numRuns" , 0);
424
    mDeviceID       = preferences.getInt("scores_deviceid",-1);
425
    mNumWins        = preferences.getInt("scores_review"  , 0);
426

    
427
    if( mDeviceID==-1 ) mDeviceID = privateGetDeviceID();
428

    
429
    if( thereWasError ) recordDBError(errorStr);
430
    }
431

    
432
///////////////////////////////////////////////////////////////////////////////////////////////////
433

    
434
  public void recordDBError(String message)
435
    {
436
    if( BuildConfig.DEBUG )
437
      {
438
      android.util.Log.e("scores", message);
439
      }
440
    else
441
      {
442
      Exception ex = new Exception(message);
443
      FirebaseCrashlytics crashlytics = FirebaseCrashlytics.getInstance();
444
      crashlytics.setCustomKey("scores" , message);
445
      crashlytics.recordException(ex);
446
      }
447
    }
448
  }
(3-3/4)