Project

General

Profile

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

magiccube / src / main / java / org / distorted / external / RubikScores.java @ 8f84486b

1 f0e87514 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2020 Leszek Koltunski                                                               //
3
//                                                                                               //
4
// This file is part of Magic Cube.                                                              //
5
//                                                                                               //
6 1c327853 Leszek Koltunski
// 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 f0e87514 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
9
10 acabdd83 Leszek Koltunski
package org.distorted.external;
11 f0e87514 Leszek Koltunski
12 a7d8c3cd Leszek Koltunski
import java.util.HashMap;
13 3f7a4363 Leszek Koltunski
import java.util.UUID;
14
15 82ce8e64 Leszek Koltunski
import android.content.Context;
16 f0e87514 Leszek Koltunski
import android.content.SharedPreferences;
17 82ce8e64 Leszek Koltunski
import android.telephony.TelephonyManager;
18 1c90c64a Leszek Koltunski
19 25445dcf Leszek Koltunski
import com.google.firebase.crashlytics.FirebaseCrashlytics;
20
21 3f7a4363 Leszek Koltunski
import org.distorted.main.BuildConfig;
22 a7d8c3cd Leszek Koltunski
import org.distorted.objects.RubikObject;
23
import org.distorted.objects.RubikObjectList;
24 0c233a9a Leszek Koltunski
import org.distorted.screens.RubikScreenPlay;
25 f0e87514 Leszek Koltunski
26 5bda8973 Leszek Koltunski
import static org.distorted.objectlib.main.ObjectType.MAX_SCRAMBLES;
27 d433b50e Leszek Koltunski
28 f0e87514 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
29 82ce8e64 Leszek Koltunski
// hold my own scores, and some other statistics.
30 f0e87514 Leszek Koltunski
31
public class RubikScores
32
  {
33 0c233a9a Leszek Koltunski
  public static final int RECORD_FIRST   = 0;
34
  public static final int RECORD_NEW     = 1;
35
  public static final int RECORD_NOT_NEW = 2;
36
37 5bda8973 Leszek Koltunski
  public static final int MAX_RECORD = 10;
38 a7d8c3cd Leszek Koltunski
  public static final int MULT = 1000000;
39 1c90c64a Leszek Koltunski
  public static final long NO_RECORD = Long.MAX_VALUE;
40 714292f1 Leszek Koltunski
  private static RubikScores mThis;
41
42 82ce8e64 Leszek Koltunski
  private String mName, mCountry;
43 c3ffcf58 Leszek Koltunski
  private boolean mNameIsVerified;
44
  private int mNumRuns;
45
  private int mNumPlays;
46 59aee296 Leszek Koltunski
  private int mNumWins;
47 82ce8e64 Leszek Koltunski
  private int mDeviceID;
48 0c233a9a Leszek Koltunski
  private int mNumStars;
49 f0e87514 Leszek Koltunski
50 a7d8c3cd Leszek Koltunski
  private static class MapValue
51
    {
52
    long record;
53
    boolean submitted;
54
55
    MapValue(long rec,int sub)
56
      {
57
      record    = rec;
58
      submitted = sub!=0;
59
      }
60
    }
61
62
  private final HashMap<Integer,MapValue> mMap;
63
64 f0e87514 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
65
66 714292f1 Leszek Koltunski
  private RubikScores()
67 f0e87514 Leszek Koltunski
    {
68 a7d8c3cd Leszek Koltunski
    mMap = new HashMap<>();
69 c3ffcf58 Leszek Koltunski
70 f895e77a Leszek Koltunski
    mName = "";
71 82ce8e64 Leszek Koltunski
    mCountry = "un";
72
73 c3ffcf58 Leszek Koltunski
    mNameIsVerified = false;
74 82ce8e64 Leszek Koltunski
75 59aee296 Leszek Koltunski
    mNumPlays= -1;
76
    mNumRuns = -1;
77
    mDeviceID= -1;
78
    mNumWins =  0;
79 0c233a9a Leszek Koltunski
    mNumStars=  0;
80 f0e87514 Leszek Koltunski
    }
81
82 a7d8c3cd Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
83
84
  private int mapKey(int object,int level)
85
    {
86
    return object*MULT + level;
87
    }
88
89 17f9a695 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
90
91
  private int privateGetDeviceID()
92
    {
93
    int id;
94
95
    try
96
      {
97
      String s = UUID.randomUUID().toString();
98 874c37b1 Leszek Koltunski
      id = s.hashCode();
99 17f9a695 Leszek Koltunski
      }
100
    catch(Exception ex)
101
      {
102
      id = 0;
103
      android.util.Log.e("scores", "Exception in getDeviceID()");
104
      }
105
106
    return id<0 ? -id : id;
107
    }
108
109
///////////////////////////////////////////////////////////////////////////////////////////////////
110 e06e1b7e Leszek Koltunski
111
  synchronized void successfulSubmit()
112
    {
113
    mNameIsVerified = true;
114
115 a7d8c3cd Leszek Koltunski
    for(int key: mMap.keySet())
116
      {
117
      MapValue value = mMap.get(key);
118
      if( value!=null ) value.submitted = true;
119
      }
120 e06e1b7e Leszek Koltunski
    }
121
122 714292f1 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
123
124 e06e1b7e Leszek Koltunski
  int getDeviceID()
125 714292f1 Leszek Koltunski
    {
126 e06e1b7e Leszek Koltunski
    return mDeviceID;
127
    }
128 714292f1 Leszek Koltunski
129 f0e87514 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
130
131 e06e1b7e Leszek Koltunski
  synchronized boolean thereAreUnsubmittedRecords()
132 f0e87514 Leszek Koltunski
    {
133 a7d8c3cd Leszek Koltunski
    for(int key: mMap.keySet())
134
      {
135
      MapValue value = mMap.get(key);
136
      if( value!=null && !value.submitted && value.record<NO_RECORD) return true;
137
      }
138 c3ffcf58 Leszek Koltunski
139 e06e1b7e Leszek Koltunski
    return false;
140 f0e87514 Leszek Koltunski
    }
141
142
///////////////////////////////////////////////////////////////////////////////////////////////////
143
144 e06e1b7e Leszek Koltunski
  synchronized String getRecordList(String strObj, String strLvl, String strTim)
145 f0e87514 Leszek Koltunski
    {
146 e06e1b7e Leszek Koltunski
    StringBuilder builderObj = new StringBuilder();
147
    StringBuilder builderLvl = new StringBuilder();
148
    StringBuilder builderTim = new StringBuilder();
149
    boolean first = true;
150 f0e87514 Leszek Koltunski
151 a7d8c3cd Leszek Koltunski
    for(int key: mMap.keySet())
152 f0e87514 Leszek Koltunski
      {
153 a7d8c3cd Leszek Koltunski
      MapValue value = mMap.get(key);
154 f0e87514 Leszek Koltunski
155 a7d8c3cd Leszek Koltunski
      if( value!=null && !value.submitted && value.record<NO_RECORD)
156 f0e87514 Leszek Koltunski
        {
157 a7d8c3cd Leszek Koltunski
        if( !first )
158 f0e87514 Leszek Koltunski
          {
159 a7d8c3cd Leszek Koltunski
          builderObj.append(',');
160
          builderLvl.append(',');
161
          builderTim.append(',');
162
          }
163
        first=false;
164 7ac0ee88 Leszek Koltunski
165 a7d8c3cd Leszek Koltunski
        RubikObject object = RubikObjectList.getObject(key/MULT);
166
167
        if( object!=null )
168
          {
169 84d746d7 Leszek Koltunski
          builderObj.append(object.getUpperName());
170 a7d8c3cd Leszek Koltunski
          builderLvl.append(key%MULT);
171
          builderTim.append(value.record);
172 f0e87514 Leszek Koltunski
          }
173
        }
174
      }
175 c3ffcf58 Leszek Koltunski
176 e06e1b7e Leszek Koltunski
    return strObj+builderObj.toString()+strLvl+builderLvl.toString()+strTim+builderTim.toString();
177
    }
178 82ce8e64 Leszek Koltunski
179 e06e1b7e Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
180
// Public API
181 ee4e7896 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
182
183
  public boolean isVerified()
184
    {
185
    return mNameIsVerified;
186
    }
187
188 c3ffcf58 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
189
190 e06e1b7e Leszek Koltunski
  public int getNumPlays()
191 c3ffcf58 Leszek Koltunski
    {
192 e06e1b7e Leszek Koltunski
    return mNumPlays;
193 c3ffcf58 Leszek Koltunski
    }
194
195
///////////////////////////////////////////////////////////////////////////////////////////////////
196
197 e06e1b7e Leszek Koltunski
  public int getNumRuns()
198 c3ffcf58 Leszek Koltunski
    {
199 e06e1b7e Leszek Koltunski
    return mNumRuns;
200 c3ffcf58 Leszek Koltunski
    }
201
202
///////////////////////////////////////////////////////////////////////////////////////////////////
203
204 e06e1b7e Leszek Koltunski
  public String getName()
205 c3ffcf58 Leszek Koltunski
    {
206 e06e1b7e Leszek Koltunski
    return mName;
207
    }
208 1c90c64a Leszek Koltunski
209 e06e1b7e Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
210
211
  public String getCountry()
212
    {
213
    return mCountry;
214 1c90c64a Leszek Koltunski
    }
215
216 286d73ae Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
217
218 e06e1b7e Leszek Koltunski
  public void incrementNumPlays()
219 286d73ae Leszek Koltunski
    {
220 e06e1b7e Leszek Koltunski
    mNumPlays++;
221
    }
222 286d73ae Leszek Koltunski
223 e06e1b7e Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
224 82ce8e64 Leszek Koltunski
225 e06e1b7e Leszek Koltunski
  public void incrementNumRuns()
226
    {
227
    mNumRuns++;
228
    }
229 82ce8e64 Leszek Koltunski
230 e06e1b7e Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
231 82ce8e64 Leszek Koltunski
232 59aee296 Leszek Koltunski
  public int incrementNumWins()
233 e06e1b7e Leszek Koltunski
    {
234 59aee296 Leszek Koltunski
    mNumWins++;
235
    return mNumWins;
236 e06e1b7e Leszek Koltunski
    }
237
238 0c233a9a Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
239
240
  public void changeNumStars(int stars)
241
    {
242
    mNumStars += stars;
243
    }
244
245
///////////////////////////////////////////////////////////////////////////////////////////////////
246
247
  public int getNumStars()
248
    {
249
    return mNumStars;
250
    }
251
252 e06e1b7e Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
253
254
  public void setName(String newName)
255
    {
256
    mName = newName;
257 82ce8e64 Leszek Koltunski
    }
258
259 d7e539d0 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
260
261 0c233a9a Leszek Koltunski
  public synchronized int setRecord(int object, int level, long record)
262 a7d8c3cd Leszek Koltunski
    {
263 83018ac4 Leszek Koltunski
    int key = mapKey(object,level);
264 a7d8c3cd Leszek Koltunski
    MapValue oldValue = mMap.get(key);
265
266
    if( oldValue==null )
267
      {
268
      MapValue value = new MapValue(record,0);
269
      mMap.put(key,value);
270 0c233a9a Leszek Koltunski
      return RECORD_FIRST;
271 a7d8c3cd Leszek Koltunski
      }
272
273
    long oldRecord = oldValue.record;
274
275
    if( oldRecord>record)
276
      {
277
      MapValue value = new MapValue(record,0);
278
      mMap.put(key,value);
279 0c233a9a Leszek Koltunski
      return RECORD_NEW;
280 a7d8c3cd Leszek Koltunski
      }
281
282 0c233a9a Leszek Koltunski
    return RECORD_NOT_NEW;
283 a7d8c3cd Leszek Koltunski
    }
284 0c233a9a Leszek Koltunski
285 a7d8c3cd Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
286
287
  public synchronized long getRecord(int object, int level)
288
    {
289
    int key = mapKey(object,level);
290
    MapValue value = mMap.get(key);
291
    return value!=null ? value.record : NO_RECORD;
292
    }
293
294
///////////////////////////////////////////////////////////////////////////////////////////////////
295
296
  public synchronized boolean isSolved(int object, int level)
297 d7e539d0 Leszek Koltunski
    {
298 a7d8c3cd Leszek Koltunski
    int key = mapKey(object,level);
299
    MapValue value = mMap.get(key);
300
    return value!=null && value.record<NO_RECORD;
301 d7e539d0 Leszek Koltunski
    }
302
303 714292f1 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
304
305 e06e1b7e Leszek Koltunski
  public void setCountry(Context context)
306 714292f1 Leszek Koltunski
    {
307 e06e1b7e Leszek Koltunski
    TelephonyManager tM =((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE));
308 714292f1 Leszek Koltunski
309 e06e1b7e Leszek Koltunski
    if( tM!=null )
310 714292f1 Leszek Koltunski
      {
311 e06e1b7e Leszek Koltunski
      mCountry = tM.getSimCountryIso();
312
313
      if( mCountry==null || mCountry.length()<=1 )
314
        {
315
        mCountry = tM.getNetworkCountryIso();
316
        }
317 714292f1 Leszek Koltunski
      }
318
319 e06e1b7e Leszek Koltunski
    // Special case: Dominicana. Its ISO-3166-alpha-2 country code is 'do' which we can't have here
320
    // because we later on map this to a resource name (the flag) and 'do' is a reserved Java keyword
321
    // and can't be a resource name.
322
323
    if( mCountry.equals("do") ) mCountry = "dm";
324 714292f1 Leszek Koltunski
    }
325 c3ffcf58 Leszek Koltunski
326 c8249cf6 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
327
328
  public void setCountry(String country)
329
    {
330
    mCountry = country;
331
332
    if( mCountry.equals("do") ) mCountry = "dm";  // see above
333
    }
334
335 286d73ae Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
336
337 e06e1b7e Leszek Koltunski
  public static RubikScores getInstance()
338 286d73ae Leszek Koltunski
    {
339 9333086d Leszek Koltunski
    if( mThis==null ) mThis = new RubikScores();
340 e06e1b7e Leszek Koltunski
    return mThis;
341 286d73ae Leszek Koltunski
    }
342
343 17f9a695 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
344
345 e06e1b7e Leszek Koltunski
  public synchronized void savePreferences(SharedPreferences.Editor editor)
346 17f9a695 Leszek Koltunski
    {
347 a7d8c3cd Leszek Koltunski
    int numObjects = RubikObjectList.getNumObjects();
348 e06e1b7e Leszek Koltunski
    StringBuilder builder = new StringBuilder();
349 1c90c64a Leszek Koltunski
350 5bda8973 Leszek Koltunski
    for(int level=0; level<=MAX_RECORD; level++)
351 e06e1b7e Leszek Koltunski
      {
352
      builder.setLength(0);
353 1c90c64a Leszek Koltunski
354 a7d8c3cd Leszek Koltunski
      for(int object=0; object<numObjects; object++)
355 e06e1b7e Leszek Koltunski
        {
356 a7d8c3cd Leszek Koltunski
        int key = mapKey(object,level);
357
        RubikObject obj = RubikObjectList.getObject(object);
358
        MapValue value = mMap.get(key);
359
360
        if( obj!=null && value!=null && value.record<NO_RECORD )
361
          {
362 84d746d7 Leszek Koltunski
          builder.append(obj.getUpperName());
363 a7d8c3cd Leszek Koltunski
          builder.append("=");
364
          builder.append(value.record);
365
          builder.append(",");
366
          builder.append(value.submitted ? 1:0 );
367
          builder.append(" ");
368
          }
369 e06e1b7e Leszek Koltunski
        }
370 c3ffcf58 Leszek Koltunski
371 e06e1b7e Leszek Koltunski
      editor.putString("scores_record"+level, builder.toString());
372
      }
373 c3ffcf58 Leszek Koltunski
374 e06e1b7e Leszek Koltunski
    editor.putString("scores_name"  , mName  );
375
    editor.putBoolean("scores_isVerified", mNameIsVerified);
376
    editor.putInt("scores_numPlays", mNumPlays);
377 59aee296 Leszek Koltunski
    editor.putInt("scores_numRuns" , mNumRuns );
378 e06e1b7e Leszek Koltunski
    editor.putInt("scores_deviceid", mDeviceID);
379 0c233a9a Leszek Koltunski
    editor.putInt("scores_review"  , mNumWins );   // legacy name
380
    editor.putInt("scores_numStars", mNumStars );
381 c3ffcf58 Leszek Koltunski
    }
382
383
///////////////////////////////////////////////////////////////////////////////////////////////////
384
385 e06e1b7e Leszek Koltunski
  public synchronized void restorePreferences(SharedPreferences preferences)
386 c3ffcf58 Leszek Koltunski
    {
387 9333086d Leszek Koltunski
    String recordStr, subStr, nameStr, timeStr, submStr, errorStr="";
388 5bda8973 Leszek Koltunski
    int start, end, equals, comma, ordinal, subm;
389 e06e1b7e Leszek Koltunski
    long time;
390 25445dcf Leszek Koltunski
    boolean thereWasError = false;
391 a7d8c3cd Leszek Koltunski
    int numObjects = RubikObjectList.getNumObjects();
392 c3ffcf58 Leszek Koltunski
393 5bda8973 Leszek Koltunski
    for(int level=0; level<=MAX_SCRAMBLES; level++)
394 e06e1b7e Leszek Koltunski
      {
395 5bda8973 Leszek Koltunski
      recordStr = preferences.getString("scores_record"+level, null);
396
      if( recordStr==null ) continue;
397 e06e1b7e Leszek Koltunski
      start = end = 0;
398 c3ffcf58 Leszek Koltunski
399 e06e1b7e Leszek Koltunski
      while( end!=-1 )
400
        {
401
        end = recordStr.indexOf(" ", start);
402 4895fff6 Leszek Koltunski
403 e06e1b7e Leszek Koltunski
        if( end==-1 ) subStr = recordStr.substring(start);
404
        else          subStr = recordStr.substring(start,end);
405 4895fff6 Leszek Koltunski
406 e06e1b7e Leszek Koltunski
        start = end+1;
407 4895fff6 Leszek Koltunski
408 e06e1b7e Leszek Koltunski
        equals = subStr.indexOf("=");
409 9333086d Leszek Koltunski
        comma  = subStr.indexOf(",");
410 4895fff6 Leszek Koltunski
411 9333086d Leszek Koltunski
        if( equals>=0 && comma>=0 )
412 4c0cd600 Leszek Koltunski
          {
413 9333086d Leszek Koltunski
          nameStr = subStr.substring(0,equals);
414 e06e1b7e Leszek Koltunski
          timeStr = subStr.substring(equals+1,comma);
415
          submStr = subStr.substring(comma+1);
416
417 5bda8973 Leszek Koltunski
          ordinal = RubikObjectList.getOrdinal(nameStr);
418 e06e1b7e Leszek Koltunski
419 5bda8973 Leszek Koltunski
          if( ordinal>=0 && ordinal<numObjects )
420 4895fff6 Leszek Koltunski
            {
421 e06e1b7e Leszek Koltunski
            time = Long.parseLong(timeStr);
422
            subm = Integer.parseInt(submStr);
423
424 7ac0ee88 Leszek Koltunski
            if( subm>=0 && subm<=1 )
425 e06e1b7e Leszek Koltunski
              {
426 a7d8c3cd Leszek Koltunski
              MapValue value = new MapValue(time,subm);
427 5bda8973 Leszek Koltunski
              int key = mapKey(ordinal,level);
428 a7d8c3cd Leszek Koltunski
              mMap.put(key,value);
429 e06e1b7e Leszek Koltunski
              }
430
            else
431
              {
432 9333086d Leszek Koltunski
              errorStr += ("error1: subm="+subm+" obj: "+nameStr+"\n");
433 25445dcf Leszek Koltunski
              thereWasError= true;
434 e06e1b7e Leszek Koltunski
              }
435
            }
436
          else
437
            {
438 5bda8973 Leszek Koltunski
            errorStr += ("error2: object="+ordinal+" obj: "+nameStr+"\n");
439 25445dcf Leszek Koltunski
            thereWasError = true;
440 4895fff6 Leszek Koltunski
            }
441 4c0cd600 Leszek Koltunski
          }
442 e06e1b7e Leszek Koltunski
        }
443 4895fff6 Leszek Koltunski
      }
444
445 e06e1b7e Leszek Koltunski
    mName           = preferences.getString("scores_name"  , "" );
446
    mNameIsVerified = preferences.getBoolean("scores_isVerified", false);
447
    mNumPlays       = preferences.getInt("scores_numPlays", 0);
448
    mNumRuns        = preferences.getInt("scores_numRuns" , 0);
449
    mDeviceID       = preferences.getInt("scores_deviceid",-1);
450 59aee296 Leszek Koltunski
    mNumWins        = preferences.getInt("scores_review"  , 0);
451 0c233a9a Leszek Koltunski
    mNumStars       = preferences.getInt("scores_numStars", 0);
452 e06e1b7e Leszek Koltunski
453
    if( mDeviceID==-1 ) mDeviceID = privateGetDeviceID();
454 25445dcf Leszek Koltunski
455
    if( thereWasError ) recordDBError(errorStr);
456
    }
457
458 0c233a9a Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
459
460
  public int numberOfSolvedMAXes()
461
    {
462
    int numObjects = RubikObjectList.getNumObjects();
463
    int ret=0, level = RubikScreenPlay.LEVELS_SHOWN;
464
465
    for(int obj=0; obj<numObjects; obj++)
466
      {
467
      if( isSolved(obj,level) ) ret++;
468
      }
469
470
    return ret;
471
    }
472
473 25445dcf Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
474
475
  public void recordDBError(String message)
476
    {
477
    if( BuildConfig.DEBUG )
478
      {
479
      android.util.Log.e("scores", message);
480
      }
481
    else
482
      {
483
      Exception ex = new Exception(message);
484
      FirebaseCrashlytics crashlytics = FirebaseCrashlytics.getInstance();
485
      crashlytics.setCustomKey("scores" , message);
486
      crashlytics.recordException(ex);
487
      }
488 4895fff6 Leszek Koltunski
    }
489 f0e87514 Leszek Koltunski
  }