Project

General

Profile

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

magiccube / src / main / java / org / distorted / network / RubikScores.java @ 318c0a7d

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.UUID;
23

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

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

    
30
import static org.distorted.objectlib.main.ObjectType.NUM_OBJECTS;
31
import static org.distorted.objectlib.main.ObjectType.MAX_LEVEL;
32

    
33
import org.distorted.objectlib.main.ObjectType;
34

    
35
import org.distorted.main.BuildConfig;
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 final long[][] mRecords;
46
  private final int [][] mSubmitted;
47

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

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

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

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

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

    
72
    mNameIsVerified = false;
73

    
74
    mNumPlays= -1;
75
    mNumRuns = -1;
76
    mDeviceID= -1;
77
    mNumWins =  0;
78
    }
79

    
80
///////////////////////////////////////////////////////////////////////////////////////////////////
81

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

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

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

    
100
///////////////////////////////////////////////////////////////////////////////////////////////////
101

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

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

    
113
///////////////////////////////////////////////////////////////////////////////////////////////////
114

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

    
120
///////////////////////////////////////////////////////////////////////////////////////////////////
121

    
122
  synchronized boolean thereAreUnsubmittedRecords()
123
    {
124
    for(int object=0; object<NUM_OBJECTS; object++)
125
      {
126
      for(int level=0; level<MAX_LEVEL; level++)
127
        {
128
        if( mSubmitted[object][level]==0 && mRecords[object][level]<NO_RECORD ) return true;
129
        }
130
      }
131

    
132
    return false;
133
    }
134

    
135
///////////////////////////////////////////////////////////////////////////////////////////////////
136

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

    
144
    for(int object=0; object<NUM_OBJECTS; object++)
145
      {
146
      String name = ObjectType.getObject(object).name();
147

    
148
      for(int level=0; level<MAX_LEVEL; level++)
149
        {
150
        if( mSubmitted[object][level]==0 && mRecords[object][level]<NO_RECORD )
151
          {
152
          if( !first )
153
            {
154
            builderObj.append(',');
155
            builderLvl.append(',');
156
            builderTim.append(',');
157
            }
158
          else
159
            {
160
            first=false;
161
            }
162

    
163
          builderObj.append(name);
164
          builderLvl.append(level);
165
          builderTim.append(mRecords[object][level]);
166
          }
167
        }
168
      }
169

    
170
    return strObj+builderObj.toString()+strLvl+builderLvl.toString()+strTim+builderTim.toString();
171
    }
172

    
173
///////////////////////////////////////////////////////////////////////////////////////////////////
174
// Public API
175
///////////////////////////////////////////////////////////////////////////////////////////////////
176

    
177
  public synchronized long getRecord(int obj, int level)
178
    {
179
    return (obj>=0 && obj<NUM_OBJECTS && level>=0 && level<MAX_LEVEL) ? mRecords[obj][level] : -1;
180
    }
181

    
182
///////////////////////////////////////////////////////////////////////////////////////////////////
183

    
184
  public synchronized boolean isSubmitted(int obj, int level)
185
    {
186
    return obj>=0 && obj<NUM_OBJECTS && level>=0 && level<MAX_LEVEL && mSubmitted[obj][level]==1;
187
    }
188

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

    
191
  public boolean isVerified()
192
    {
193
    return mNameIsVerified;
194
    }
195

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

    
198
  public int getNumPlays()
199
    {
200
    return mNumPlays;
201
    }
202

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

    
205
  public int getNumRuns()
206
    {
207
    return mNumRuns;
208
    }
209

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

    
212
  public String getName()
213
    {
214
    return mName;
215
    }
216

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

    
219
  public String getCountry()
220
    {
221
    return mCountry;
222
    }
223

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

    
226
  public void incrementNumPlays()
227
    {
228
    mNumPlays++;
229
    }
230

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

    
233
  public void incrementNumRuns()
234
    {
235
    mNumRuns++;
236
    }
237

    
238
///////////////////////////////////////////////////////////////////////////////////////////////////
239

    
240
  public int incrementNumWins()
241
    {
242
    mNumWins++;
243
    return mNumWins;
244
    }
245

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

    
248
  public void setName(String newName)
249
    {
250
    mName = newName;
251
    }
252

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

    
255
  public synchronized boolean isSolved(int obj, int level)
256
    {
257
    return obj>=0 && obj<NUM_OBJECTS && level>=0 && level<MAX_LEVEL && mRecords[obj][level]<NO_RECORD;
258
    }
259

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

    
262
  public void setCountry(Context context)
263
    {
264
    TelephonyManager tM =((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE));
265

    
266
    if( tM!=null )
267
      {
268
      mCountry = tM.getSimCountryIso();
269

    
270
      if( mCountry==null || mCountry.length()<=1 )
271
        {
272
        mCountry = tM.getNetworkCountryIso();
273
        }
274
      }
275

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

    
280
    if( mCountry.equals("do") ) mCountry = "dm";
281
    }
282

    
283
///////////////////////////////////////////////////////////////////////////////////////////////////
284

    
285
  public void setCountry(String country)
286
    {
287
    mCountry = country;
288

    
289
    if( mCountry.equals("do") ) mCountry = "dm";  // see above
290
    }
291

    
292
///////////////////////////////////////////////////////////////////////////////////////////////////
293

    
294
  public static RubikScores getInstance()
295
    {
296
    if( mThis==null )
297
      {
298
      mThis = new RubikScores();
299
      }
300

    
301
    return mThis;
302
    }
303

    
304
///////////////////////////////////////////////////////////////////////////////////////////////////
305

    
306
  public synchronized void savePreferences(SharedPreferences.Editor editor)
307
    {
308
    StringBuilder builder = new StringBuilder();
309
    String name;
310

    
311
    for(int level=0; level<MAX_LEVEL; level++)
312
      {
313
      builder.setLength(0);
314

    
315
      for(int object=0; object<NUM_OBJECTS; object++)
316
        {
317
        name = ObjectType.getObject(object).name();
318
        builder.append(name);
319
        builder.append("=");
320
        builder.append(mRecords[object][level]);
321
        builder.append(",");
322
        builder.append(mSubmitted[object][level]);
323
        builder.append(" ");
324
        }
325

    
326
      editor.putString("scores_record"+level, builder.toString());
327
      }
328

    
329
    editor.putString("scores_name"  , mName  );
330
    editor.putBoolean("scores_isVerified", mNameIsVerified);
331
    editor.putInt("scores_numPlays", mNumPlays);
332
    editor.putInt("scores_numRuns" , mNumRuns );
333
    editor.putInt("scores_deviceid", mDeviceID);
334
    editor.putInt("scores_review"  , mNumWins );
335
    }
336

    
337
///////////////////////////////////////////////////////////////////////////////////////////////////
338

    
339
  public synchronized void restorePreferences(SharedPreferences preferences)
340
    {
341
    String recordStr, subStr, nameStr, sizeStr, timeStr, submStr, errorStr="";
342
    int start, end, equals, underscore, comma, object, subm;
343
    long time;
344
    boolean thereWasError = false;
345

    
346
    for(int level=0; level<MAX_LEVEL; level++)
347
      {
348
      start = end = 0;
349
      recordStr = preferences.getString("scores_record"+level, "");
350

    
351
      while( end!=-1 )
352
        {
353
        end = recordStr.indexOf(" ", start);
354

    
355
        if( end==-1 ) subStr = recordStr.substring(start);
356
        else          subStr = recordStr.substring(start,end);
357

    
358
        start = end+1;
359

    
360
        underscore = subStr.indexOf("_");
361
        equals = subStr.indexOf("=");
362
        comma = subStr.indexOf(",");
363

    
364
        if( underscore>=0 && equals>=0 && comma>=0 )
365
          {
366
          nameStr = subStr.substring(0,underscore);
367
          sizeStr = subStr.substring(underscore+1, equals);
368
          timeStr = subStr.substring(equals+1,comma);
369
          submStr = subStr.substring(comma+1);
370

    
371
          object = ObjectType.getOrdinal(nameStr);
372

    
373
          if( object>=0 && object< NUM_OBJECTS )
374
            {
375
            time = Long.parseLong(timeStr);
376
            subm = Integer.parseInt(submStr);
377

    
378
            if( subm>=0 && subm<=1 )
379
              {
380
              mRecords  [object][level] = time;
381
              mSubmitted[object][level] = subm;
382
              }
383
            else
384
              {
385
              errorStr += ("error1: subm="+subm+" obj: "+nameStr+" size: "+sizeStr+"\n");
386
              thereWasError= true;
387
              }
388
            }
389
          else
390
            {
391
            errorStr += ("error2: object="+object+" obj: "+nameStr+" size: "+sizeStr+"\n");
392
            thereWasError = true;
393
            }
394
          }
395
        }
396
      }
397

    
398
    mName           = preferences.getString("scores_name"  , "" );
399
    mNameIsVerified = preferences.getBoolean("scores_isVerified", false);
400
    mNumPlays       = preferences.getInt("scores_numPlays", 0);
401
    mNumRuns        = preferences.getInt("scores_numRuns" , 0);
402
    mDeviceID       = preferences.getInt("scores_deviceid",-1);
403
    mNumWins        = preferences.getInt("scores_review"  , 0);
404

    
405
    if( mDeviceID==-1 ) mDeviceID = privateGetDeviceID();
406

    
407
    if( thereWasError ) recordDBError(errorStr);
408
    }
409

    
410
///////////////////////////////////////////////////////////////////////////////////////////////////
411

    
412
  public void recordDBError(String message)
413
    {
414
    if( BuildConfig.DEBUG )
415
      {
416
      android.util.Log.e("scores", message);
417
      }
418
    else
419
      {
420
      Exception ex = new Exception(message);
421
      FirebaseCrashlytics crashlytics = FirebaseCrashlytics.getInstance();
422
      crashlytics.setCustomKey("scores" , message);
423
      crashlytics.recordException(ex);
424
      }
425
    }
426

    
427
///////////////////////////////////////////////////////////////////////////////////////////////////
428

    
429
  public synchronized boolean setRecord(int object, int level, long timeTaken)
430
    {
431
    if( object>=0 && object<NUM_OBJECTS && level>=1 && level<=MAX_LEVEL && mRecords[object][level-1]>timeTaken )
432
      {
433
      mRecords  [object][level-1] = timeTaken;
434
      mSubmitted[object][level-1] = 0;
435
      return true;
436
      }
437

    
438
    return false;
439
    }
440
  }
(2-2/2)