Project

General

Profile

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

magiccube / src / main / java / org / distorted / play / PlayLibInterface.java @ f85956a8

1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2023 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.play;
11

    
12
import static org.distorted.helpers.RubikScores.RECORD_FIRST;
13
import static org.distorted.helpers.RubikScores.RECORD_NEW;
14
import static org.distorted.helpers.RubikScores.RECORD_NOT_NEW;
15

    
16
import android.os.Bundle;
17

    
18
import androidx.annotation.NonNull;
19

    
20
import java.lang.ref.WeakReference;
21

    
22
import com.google.android.play.core.review.ReviewInfo;
23
import com.google.android.play.core.review.ReviewManager;
24
import com.google.android.play.core.review.ReviewManagerFactory;
25
import com.google.android.gms.tasks.OnCompleteListener;
26
import com.google.android.gms.tasks.OnFailureListener;
27
import com.google.android.gms.tasks.Task;
28
import com.google.firebase.analytics.FirebaseAnalytics;
29
import com.google.firebase.crashlytics.FirebaseCrashlytics;
30

    
31
import org.distorted.dialogs.DialogNewRecord;
32
import org.distorted.dialogs.DialogScoresView;
33
import org.distorted.dialogs.DialogSolved;
34
import org.distorted.helpers.RubikNetwork;
35
import org.distorted.helpers.RubikScores;
36
import org.distorted.library.message.EffectMessageSender;
37
import org.distorted.objectlib.helpers.BlockController;
38
import org.distorted.objectlib.helpers.ObjectLibInterface;
39
import org.distorted.objectlib.main.ObjectControl;
40
import org.distorted.objectlib.main.TwistyObject;
41
import org.distorted.objects.RubikObject;
42
import org.distorted.objects.RubikObjectList;
43
import org.distorted.main.BuildConfig;
44

    
45
///////////////////////////////////////////////////////////////////////////////////////////////////
46

    
47
public class PlayLibInterface implements ObjectLibInterface
48
{
49
  private final WeakReference<PlayActivity> mAct;
50
  private int mIsNewRecord;
51
  private int mNewRecord;
52
  private boolean mReviewAsked;
53
  private int mNumRotations, mNumScrambles;
54

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

    
57
  PlayLibInterface(PlayActivity act)
58
    {
59
    mAct = new WeakReference<>(act);
60
    mReviewAsked = false;
61
    mNumRotations = 0;
62
    mNumScrambles = 0;
63
    }
64

    
65
///////////////////////////////////////////////////////////////////////////////////////////////////
66

    
67
  public void onObjectCreated(long time) { }
68
  public void onStickerTouched(int cubit, int face) { }
69
  public void onStickerUntouched() { }
70

    
71
///////////////////////////////////////////////////////////////////////////////////////////////////
72

    
73
  private void analyticsReport(PlayActivity act, String message, String name, long timeBegin)
74
    {
75
    long elapsed = System.currentTimeMillis() - timeBegin;
76
    String msg = message+" startTime: "+timeBegin+" elapsed: "+elapsed+" name: "+name;
77

    
78
    if( BuildConfig.DEBUG )
79
      {
80
      android.util.Log.d("libInterface", msg);
81
      }
82
    else
83
      {
84
      FirebaseAnalytics analytics = act.getAnalytics();
85

    
86
      if( analytics!=null )
87
        {
88
        Bundle bundle = new Bundle();
89
        bundle.putString(FirebaseAnalytics.Param.CONTENT_TYPE, msg);
90
        analytics.logEvent(FirebaseAnalytics.Event.SHARE, bundle);
91
        }
92
      }
93
    }
94

    
95
///////////////////////////////////////////////////////////////////////////////////////////////////
96

    
97
  private void reportRecord(PlayActivity act, long startTime, long endTime, int scrambleNum)
98
    {
99
    RubikScores scores  = RubikScores.getInstance();
100
    int objectOrdinal = act.getObjectOrdinal();
101
    String name = scores.getName();
102
    RubikObject obj = RubikObjectList.getObject(objectOrdinal);
103
    String objName = obj==null ? "NULL" : obj.getUpperName();
104

    
105
    String record = objName+" time "+mNewRecord+" isNew: "+mIsNewRecord+" scrambleNum: "+scrambleNum;
106

    
107
    if( BuildConfig.DEBUG )
108
      {
109
      android.util.Log.e("libInterface", name);
110
      android.util.Log.e("libInterface", record);
111
      }
112
    else
113
      {
114
      if( scrambleNum>=9 && mNewRecord<300*scrambleNum )
115
        {
116
        long timeNow = System.currentTimeMillis();
117
        long elapsed = timeNow - startTime;
118
        String suspicious ="start"+startTime+"end"+endTime+"elapsed"+elapsed+"obj"+objName+"record"+mNewRecord+"scrambles"+scrambleNum;
119
        RubikNetwork network = RubikNetwork.getInstance();
120
        network.suspicious(suspicious,act);
121
        }
122

    
123
      FirebaseAnalytics analytics = act.getAnalytics();
124

    
125
      if( analytics!=null )
126
        {
127
        Bundle bundle = new Bundle();
128
        bundle.putString(FirebaseAnalytics.Param.CHARACTER, name);
129
        bundle.putString(FirebaseAnalytics.Param.LEVEL, record);
130
        analytics.logEvent(FirebaseAnalytics.Event.LEVEL_UP, bundle);
131
        }
132
      }
133
    }
134

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

    
137
  private Bundle createDialogBundle()
138
    {
139
    Bundle bundle = new Bundle();
140
    String arg = DialogScoresView.formatRecord(mNewRecord);
141
    bundle.putString("argument", arg );
142
    return bundle;
143
    }
144

    
145
///////////////////////////////////////////////////////////////////////////////////////////////////
146

    
147
  private void requestReview(PlayActivity act)
148
    {
149
    android.util.Log.e("D", "ASKING FOR REVIEW");
150

    
151
    mReviewAsked = true;
152
    final String name = RubikScores.getInstance().getName();
153
    final long timeBegin = System.currentTimeMillis();
154
    final ReviewManager manager = ReviewManagerFactory.create(act);
155
    Task<ReviewInfo> request = manager.requestReviewFlow();
156

    
157
    request.addOnCompleteListener(new OnCompleteListener<ReviewInfo>()
158
      {
159
      @Override
160
      public void onComplete (@NonNull Task<ReviewInfo> task)
161
        {
162
        if (task.isSuccessful())
163
          {
164
          ReviewInfo reviewInfo = task.getResult();
165
          Task<Void> flow = manager.launchReviewFlow(act, reviewInfo);
166

    
167
          flow.addOnFailureListener(new OnFailureListener()
168
            {
169
            @Override
170
            public void onFailure(Exception e)
171
            {
172
            analyticsReport(act,"Failed", name, timeBegin);
173
            }
174
            });
175

    
176
          flow.addOnCompleteListener(new OnCompleteListener<Void>()
177
            {
178
            @Override
179
            public void onComplete(@NonNull Task<Void> task)
180
              {
181
              analyticsReport(act,"Complete", name, timeBegin);
182
              }
183
            });
184
          }
185
        else analyticsReport(act,"Not Successful", name, timeBegin);
186
        }
187
      });
188
    }
189

    
190
///////////////////////////////////////////////////////////////////////////////////////////////////
191

    
192
  public void onScrambleEffectFinished()
193
    {
194
    if( ScreenList.getCurrentScreen()==ScreenList.SCRA )
195
      {
196
      PlayActivity act = mAct.get();
197
      RubikScores.getInstance().incrementNumPlays();
198

    
199
      act.runOnUiThread(new Runnable()
200
        {
201
        @Override
202
        public void run()
203
          {
204
          ScreenList.switchScreen(act,ScreenList.READ);
205
          ObjectControl control = act.getControl();
206
          control.unblockEverything();
207
          }
208
        });
209
      }
210

    
211
    mNumScrambles++;
212

    
213
    if( mNumScrambles==10 && !mReviewAsked )
214
      {
215
      PlayActivity act = mAct.get();
216
      requestReview(act);
217
      }
218
    }
219

    
220
///////////////////////////////////////////////////////////////////////////////////////////////////
221

    
222
  public void onRemoveRotation(int axis, int rowBitmap, int degrees)
223
    {
224
    mNumRotations++;
225
    PlayActivity act = mAct.get();
226

    
227
    if( act!=null )
228
      {
229
      ScreenList screen = ScreenList.getCurrentScreen();
230

    
231
      if( screen==ScreenList.FREE ||
232
          screen==ScreenList.SOLV  ) ((ScreenBase)screen.getScreenClass()).addMove(act,axis,rowBitmap,degrees);
233
      }
234

    
235
    if( mNumRotations==40 && !mReviewAsked )
236
      {
237
      requestReview(act);
238
      }
239
    }
240

    
241
///////////////////////////////////////////////////////////////////////////////////////////////////
242

    
243
  public void onBeginRotation()
244
    {
245
    if( ScreenList.getCurrentScreen()==ScreenList.READ )
246
      {
247
      ScreenSolving solving = (ScreenSolving) ScreenList.SOLV.getScreenClass();
248
      solving.resetElapsed();
249
      PlayActivity act = mAct.get();
250
      ScreenList.switchScreen( act,ScreenList.SOLV );
251
      }
252
    }
253

    
254
///////////////////////////////////////////////////////////////////////////////////////////////////
255

    
256
  public void failedToDrag()
257
    {
258
    PlayActivity act = mAct.get();
259

    
260
    if( act!=null )
261
      {
262
      ScreenList screen = ScreenList.getCurrentScreen();
263

    
264
      if( screen==ScreenList.FREE ||
265
          screen==ScreenList.SOLV ||
266
          screen==ScreenList.SCRA  ) ((ScreenBase)screen.getScreenClass()).reddenLock(act);
267
      }
268
    }
269

    
270
///////////////////////////////////////////////////////////////////////////////////////////////////
271

    
272
  public void onSolved()
273
    {
274
    if( ScreenList.getCurrentScreen()==ScreenList.SOLV )
275
      {
276
      PlayActivity act = mAct.get();
277
      ObjectControl control = act.getControl();
278
      TwistyObject obj = control.getObject();
279
      boolean submittable = obj.isSubmittable();
280
      int objectOrdinal = act.getObjectOrdinal();
281
      ScreenSolving solving = (ScreenSolving)ScreenList.SOLV.getScreenClass();
282
      mNewRecord = solving.stopTimerAndGetRecord();
283
      mIsNewRecord = submittable ? solving.setRecord(objectOrdinal) : RECORD_NOT_NEW;
284
      }
285
    }
286

    
287
///////////////////////////////////////////////////////////////////////////////////////////////////
288

    
289
  public void onWinEffectFinished(long startTime, long endTime, int scrambleNum)
290
    {
291
    if( ScreenList.getCurrentScreen()==ScreenList.SOLV )
292
      {
293
      PlayActivity act = mAct.get();
294
      ObjectControl control = act.getControl();
295
      TwistyObject obj = control.getObject();
296
      boolean submittable = obj.isSubmittable();
297

    
298
      if( submittable ) reportRecord(act,startTime,endTime,scrambleNum);
299

    
300
      RubikScores scores = RubikScores.getInstance();
301
      int numWins = scores.incrementNumWins();
302
      int numRuns = scores.getNumRuns();
303

    
304
      if( numRuns==3 || numRuns==6 || numWins==4 || numWins==20 || numWins==50 || numWins==80 || numWins==100)
305
        {
306
        requestReview(act);
307
        }
308

    
309
      switch(mIsNewRecord)
310
        {
311
        case RECORD_FIRST  :
312
        case RECORD_NEW    : Bundle byes = createDialogBundle();
313
                             DialogNewRecord dyes = new DialogNewRecord();
314
                             dyes.setArguments(byes);
315
                             dyes.show( act.getSupportFragmentManager(), DialogNewRecord.getDialogTag());
316
                             break;
317
        case RECORD_NOT_NEW: Bundle bno = createDialogBundle();
318
                             DialogSolved dno = new DialogSolved();
319
                             dno.setArguments(bno);
320
                             dno.show( act.getSupportFragmentManager(), DialogSolved.getDialogTag());
321
        break;
322
        }
323

    
324
      act.runOnUiThread(new Runnable()
325
        {
326
        @Override
327
        public void run()
328
          {
329
          ScreenList.switchScreen( act,ScreenList.DONE );
330
          }
331
        });
332
      }
333
    }
334

    
335
///////////////////////////////////////////////////////////////////////////////////////////////////
336

    
337
  public void reportProblem(String problem, boolean reportException)
338
    {
339
    if( BuildConfig.DEBUG )
340
      {
341
      android.util.Log.e("interface", problem);
342
      }
343
    else
344
      {
345
      if( reportException )
346
        {
347
        Exception ex = new Exception(problem);
348
        FirebaseCrashlytics crashlytics = FirebaseCrashlytics.getInstance();
349
        crashlytics.setCustomKey("problem" , problem);
350
        crashlytics.recordException(ex);
351
        }
352
      else
353
        {
354
        FirebaseCrashlytics crashlytics = FirebaseCrashlytics.getInstance();
355
        crashlytics.log(problem);
356
        }
357
      }
358
    }
359

    
360
///////////////////////////////////////////////////////////////////////////////////////////////////
361

    
362
  private void reportScramblingProblem(int place, long pause, long resume, long time)
363
    {
364
    String error = "SCRAMBLING BLOCK "+place+" blocked for "+time;
365

    
366
    if( BuildConfig.DEBUG )
367
       {
368
       android.util.Log.e("D", error);
369
       }
370
    else
371
      {
372
      Exception ex = new Exception(error);
373
      FirebaseCrashlytics crashlytics = FirebaseCrashlytics.getInstance();
374
      crashlytics.setCustomKey("pause" , pause );
375
      crashlytics.setCustomKey("resume", resume );
376
      crashlytics.recordException(ex);
377
      }
378
    }
379

    
380
///////////////////////////////////////////////////////////////////////////////////////////////////
381

    
382
  private void reportRotationProblem(int place, long pause, long resume, long time)
383
    {
384
    String error = "ROTATION BLOCK "+place+" blocked for "+time;
385

    
386
    if( BuildConfig.DEBUG )
387
       {
388
       android.util.Log.e("D", error);
389
       }
390
    else
391
      {
392
      Exception ex = new Exception(error);
393
      FirebaseCrashlytics crashlytics = FirebaseCrashlytics.getInstance();
394
      crashlytics.setCustomKey("pause" , pause );
395
      crashlytics.setCustomKey("resume", resume);
396
      crashlytics.recordException(ex);
397
      }
398
    }
399

    
400
///////////////////////////////////////////////////////////////////////////////////////////////////
401

    
402
  private void reportThreadProblem(int place, long pause, long resume, long time)
403
    {
404
    String error = EffectMessageSender.reportState();
405

    
406
    if( BuildConfig.DEBUG )
407
       {
408
       android.util.Log.e("D", error);
409
       }
410
    else
411
      {
412
      Exception ex = new Exception(error);
413
      FirebaseCrashlytics crashlytics = FirebaseCrashlytics.getInstance();
414
      crashlytics.setCustomKey("pause" , pause  );
415
      crashlytics.setCustomKey("resume", resume );
416
      crashlytics.recordException(ex);
417
      }
418
    }
419

    
420
///////////////////////////////////////////////////////////////////////////////////////////////////
421

    
422
  public void reportBlockProblem(int type, int place, long pause, long resume, long time)
423
    {
424
    switch(type)
425
      {
426
      case BlockController.TYPE_SCRAMBLING: reportScramblingProblem(place,pause,resume,time); break;
427
      case BlockController.TYPE_ROTATION  : reportRotationProblem(place,pause,resume,time); break;
428
      case BlockController.TYPE_THREAD    : reportThreadProblem(place,pause,resume,time); break;
429
      }
430
    }
431

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

    
434
  public void reportJSONError(String error, int ordinal)
435
    {
436
    RubikObject object = RubikObjectList.getObject(ordinal);
437
    String name = object==null ? "NULL" : object.getUpperName();
438

    
439
    if( BuildConfig.DEBUG )
440
       {
441
       android.util.Log.e("libInterface", "name="+name+" JSON error: "+error);
442
       }
443
    else
444
      {
445
      Exception ex = new Exception(error);
446
      FirebaseCrashlytics crashlytics = FirebaseCrashlytics.getInstance();
447
      crashlytics.setCustomKey("name" , name );
448
      crashlytics.setCustomKey("JSONerror", error );
449
      crashlytics.recordException(ex);
450
      }
451
    }
452
}
(2-2/12)