Project

General

Profile

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

magiccube / src / main / java / org / distorted / network / RubikScores.java @ a7d8c3cd

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.network;
21

    
22
import java.util.HashMap;
23
import java.util.UUID;
24

    
25
import android.content.Context;
26
import android.content.SharedPreferences;
27
import android.telephony.TelephonyManager;
28

    
29
import com.google.firebase.crashlytics.FirebaseCrashlytics;
30

    
31
import static org.distorted.screens.RubikScreenPlay.MAX_LEVEL;
32
import org.distorted.main.BuildConfig;
33
import org.distorted.objects.RubikObject;
34
import org.distorted.objects.RubikObjectList;
35

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

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

    
45
  private String mName, mCountry;
46
  private boolean mNameIsVerified;
47
  private int mNumRuns;
48
  private int mNumPlays;
49
  private int mNumWins;
50
  private int mDeviceID;
51

    
52
  private static class MapValue
53
    {
54
    long record;
55
    boolean submitted;
56

    
57
    MapValue(long rec,int sub)
58
      {
59
      record    = rec;
60
      submitted = sub!=0;
61
      }
62
    }
63

    
64
  private final HashMap<Integer,MapValue> mMap;
65

    
66
///////////////////////////////////////////////////////////////////////////////////////////////////
67

    
68
  private RubikScores()
69
    {
70
    mMap = new HashMap<>();
71

    
72
    mName = "";
73
    mCountry = "un";
74

    
75
    mNameIsVerified = false;
76

    
77
    mNumPlays= -1;
78
    mNumRuns = -1;
79
    mDeviceID= -1;
80
    mNumWins =  0;
81
    }
82

    
83
///////////////////////////////////////////////////////////////////////////////////////////////////
84

    
85
  private int mapKey(int object,int level)
86
    {
87
    return object*MULT + level;
88
    }
89

    
90
///////////////////////////////////////////////////////////////////////////////////////////////////
91

    
92
  private int privateGetDeviceID()
93
    {
94
    int id;
95

    
96
    try
97
      {
98
      String s = UUID.randomUUID().toString();
99
      id = s.hashCode();
100
      }
101
    catch(Exception ex)
102
      {
103
      id = 0;
104
      android.util.Log.e("scores", "Exception in getDeviceID()");
105
      }
106

    
107
    return id<0 ? -id : id;
108
    }
109

    
110
///////////////////////////////////////////////////////////////////////////////////////////////////
111

    
112
  synchronized void successfulSubmit()
113
    {
114
    mNameIsVerified = true;
115

    
116
    for(int key: mMap.keySet())
117
      {
118
      MapValue value = mMap.get(key);
119
      if( value!=null ) value.submitted = true;
120
      }
121
    }
122

    
123
///////////////////////////////////////////////////////////////////////////////////////////////////
124

    
125
  int getDeviceID()
126
    {
127
    return mDeviceID;
128
    }
129

    
130
///////////////////////////////////////////////////////////////////////////////////////////////////
131

    
132
  synchronized boolean thereAreUnsubmittedRecords()
133
    {
134
    for(int key: mMap.keySet())
135
      {
136
      MapValue value = mMap.get(key);
137
      if( value!=null && !value.submitted && value.record<NO_RECORD) return true;
138
      }
139

    
140
    return false;
141
    }
142

    
143
///////////////////////////////////////////////////////////////////////////////////////////////////
144

    
145
  synchronized String getRecordList(String strObj, String strLvl, String strTim)
146
    {
147
    StringBuilder builderObj = new StringBuilder();
148
    StringBuilder builderLvl = new StringBuilder();
149
    StringBuilder builderTim = new StringBuilder();
150
    boolean first = true;
151

    
152
    for(int key: mMap.keySet())
153
      {
154
      MapValue value = mMap.get(key);
155

    
156
      if( value!=null && !value.submitted && value.record<NO_RECORD)
157
        {
158
        if( !first )
159
          {
160
          builderObj.append(',');
161
          builderLvl.append(',');
162
          builderTim.append(',');
163
          }
164
        first=false;
165

    
166
        RubikObject object = RubikObjectList.getObject(key/MULT);
167

    
168
        if( object!=null )
169
          {
170
          builderObj.append(object.getName());
171
          builderLvl.append(key%MULT);
172
          builderTim.append(value.record);
173
          }
174
        }
175
      }
176

    
177
    return strObj+builderObj.toString()+strLvl+builderLvl.toString()+strTim+builderTim.toString();
178
    }
179

    
180
///////////////////////////////////////////////////////////////////////////////////////////////////
181
// Public API
182
///////////////////////////////////////////////////////////////////////////////////////////////////
183

    
184
  public boolean isVerified()
185
    {
186
    return mNameIsVerified;
187
    }
188

    
189
///////////////////////////////////////////////////////////////////////////////////////////////////
190

    
191
  public int getNumPlays()
192
    {
193
    return mNumPlays;
194
    }
195

    
196
///////////////////////////////////////////////////////////////////////////////////////////////////
197

    
198
  public int getNumRuns()
199
    {
200
    return mNumRuns;
201
    }
202

    
203
///////////////////////////////////////////////////////////////////////////////////////////////////
204

    
205
  public String getName()
206
    {
207
    return mName;
208
    }
209

    
210
///////////////////////////////////////////////////////////////////////////////////////////////////
211

    
212
  public String getCountry()
213
    {
214
    return mCountry;
215
    }
216

    
217
///////////////////////////////////////////////////////////////////////////////////////////////////
218

    
219
  public void incrementNumPlays()
220
    {
221
    mNumPlays++;
222
    }
223

    
224
///////////////////////////////////////////////////////////////////////////////////////////////////
225

    
226
  public void incrementNumRuns()
227
    {
228
    mNumRuns++;
229
    }
230

    
231
///////////////////////////////////////////////////////////////////////////////////////////////////
232

    
233
  public int incrementNumWins()
234
    {
235
    mNumWins++;
236
    return mNumWins;
237
    }
238

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

    
241
  public void setName(String newName)
242
    {
243
    mName = newName;
244
    }
245

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

    
248
  public synchronized boolean setRecord(int object, int level, long record)
249
    {
250
    int key = mapKey(object,level)-1; // -1 - historical reasons; previous versions saved it like this.
251
    MapValue oldValue = mMap.get(key);
252

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

    
260
    long oldRecord = oldValue.record;
261

    
262
    if( oldRecord>record)
263
      {
264
      MapValue value = new MapValue(record,0);
265
      mMap.put(key,value);
266
      return true;
267
      }
268

    
269
    return false;
270
    }
271
///////////////////////////////////////////////////////////////////////////////////////////////////
272

    
273
  public synchronized long getRecord(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 synchronized boolean isSolved(int object, int level)
283
    {
284
    int key = mapKey(object,level);
285
    MapValue value = mMap.get(key);
286
    return value!=null && value.record<NO_RECORD;
287
    }
288

    
289
///////////////////////////////////////////////////////////////////////////////////////////////////
290

    
291
  public void setCountry(Context context)
292
    {
293
    TelephonyManager tM =((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE));
294

    
295
    if( tM!=null )
296
      {
297
      mCountry = tM.getSimCountryIso();
298

    
299
      if( mCountry==null || mCountry.length()<=1 )
300
        {
301
        mCountry = tM.getNetworkCountryIso();
302
        }
303
      }
304

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

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

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

    
314
  public void setCountry(String country)
315
    {
316
    mCountry = country;
317

    
318
    if( mCountry.equals("do") ) mCountry = "dm";  // see above
319
    }
320

    
321
///////////////////////////////////////////////////////////////////////////////////////////////////
322

    
323
  public static RubikScores getInstance()
324
    {
325
    if( mThis==null ) mThis = new RubikScores();
326
    return mThis;
327
    }
328

    
329
///////////////////////////////////////////////////////////////////////////////////////////////////
330

    
331
  public synchronized void savePreferences(SharedPreferences.Editor editor)
332
    {
333
    int numObjects = RubikObjectList.getNumObjects();
334
    StringBuilder builder = new StringBuilder();
335

    
336
    for(int level=0; level<MAX_LEVEL; level++)
337
      {
338
      builder.setLength(0);
339

    
340
      for(int object=0; object<numObjects; object++)
341
        {
342
        int key = mapKey(object,level);
343
        RubikObject obj = RubikObjectList.getObject(object);
344
        MapValue value = mMap.get(key);
345

    
346
        if( obj!=null && value!=null && value.record<NO_RECORD )
347
          {
348
          builder.append(obj.getName());
349
          builder.append("=");
350
          builder.append(value.record);
351
          builder.append(",");
352
          builder.append(value.submitted ? 1:0 );
353
          builder.append(" ");
354
          }
355
        }
356

    
357
      editor.putString("scores_record"+level, builder.toString());
358
      }
359

    
360
    editor.putString("scores_name"  , mName  );
361
    editor.putBoolean("scores_isVerified", mNameIsVerified);
362
    editor.putInt("scores_numPlays", mNumPlays);
363
    editor.putInt("scores_numRuns" , mNumRuns );
364
    editor.putInt("scores_deviceid", mDeviceID);
365
    editor.putInt("scores_review"  , mNumWins );
366
    }
367

    
368
///////////////////////////////////////////////////////////////////////////////////////////////////
369

    
370
  public synchronized void restorePreferences(SharedPreferences preferences)
371
    {
372
    String recordStr, subStr, nameStr, timeStr, submStr, errorStr="";
373
    int start, end, equals, comma, object, subm;
374
    long time;
375
    boolean thereWasError = false;
376
    int numObjects = RubikObjectList.getNumObjects();
377

    
378
    for(int level=0; level<MAX_LEVEL; level++)
379
      {
380
      start = end = 0;
381
      recordStr = preferences.getString("scores_record"+level, "");
382

    
383
      while( end!=-1 )
384
        {
385
        end = recordStr.indexOf(" ", start);
386

    
387
        if( end==-1 ) subStr = recordStr.substring(start);
388
        else          subStr = recordStr.substring(start,end);
389

    
390
        start = end+1;
391

    
392
        equals = subStr.indexOf("=");
393
        comma  = subStr.indexOf(",");
394

    
395
        if( equals>=0 && comma>=0 )
396
          {
397
          nameStr = subStr.substring(0,equals);
398
          timeStr = subStr.substring(equals+1,comma);
399
          submStr = subStr.substring(comma+1);
400

    
401
          object = RubikObjectList.getOrdinal(nameStr);
402

    
403
          if( object>=0 && object<numObjects )
404
            {
405
            time = Long.parseLong(timeStr);
406
            subm = Integer.parseInt(submStr);
407

    
408
            if( subm>=0 && subm<=1 )
409
              {
410
              MapValue value = new MapValue(time,subm);
411
              int key = mapKey(object,level);
412
              mMap.put(key,value);
413
              }
414
            else
415
              {
416
              errorStr += ("error1: subm="+subm+" obj: "+nameStr+"\n");
417
              thereWasError= true;
418
              }
419
            }
420
          else
421
            {
422
            errorStr += ("error2: object="+object+" obj: "+nameStr+"\n");
423
            thereWasError = true;
424
            }
425
          }
426
        }
427
      }
428

    
429
    mName           = preferences.getString("scores_name"  , "" );
430
    mNameIsVerified = preferences.getBoolean("scores_isVerified", false);
431
    mNumPlays       = preferences.getInt("scores_numPlays", 0);
432
    mNumRuns        = preferences.getInt("scores_numRuns" , 0);
433
    mDeviceID       = preferences.getInt("scores_deviceid",-1);
434
    mNumWins        = preferences.getInt("scores_review"  , 0);
435

    
436
    if( mDeviceID==-1 ) mDeviceID = privateGetDeviceID();
437

    
438
    if( thereWasError ) recordDBError(errorStr);
439
    }
440

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

    
443
  public void recordDBError(String message)
444
    {
445
    if( BuildConfig.DEBUG )
446
      {
447
      android.util.Log.e("scores", message);
448
      }
449
    else
450
      {
451
      Exception ex = new Exception(message);
452
      FirebaseCrashlytics crashlytics = FirebaseCrashlytics.getInstance();
453
      crashlytics.setCustomKey("scores" , message);
454
      crashlytics.recordException(ex);
455
      }
456
    }
457
  }
(2-2/2)