Przeglądaj źródła

游戏相关功能提交

贾艺驰 4 lat temu
rodzic
commit
45175d69f6
33 zmienionych plików z 1201 dodań i 45 usunięć
  1. 1 0
      app/build.gradle
  2. 3 0
      app/src/main/AndroidManifest.xml
  3. 7 0
      app/src/main/java/com/jyc/threegames/App.java
  4. 5 0
      app/src/main/java/com/jyc/threegames/activity/AdminActivity.java
  5. 278 11
      app/src/main/java/com/jyc/threegames/activity/GameActivity.java
  6. 24 6
      app/src/main/java/com/jyc/threegames/activity/NormalUserActivity.java
  7. 198 0
      app/src/main/java/com/jyc/threegames/activity/RingActivity.java
  8. 17 4
      app/src/main/java/com/jyc/threegames/activity/ScaleActivity.java
  9. 35 3
      app/src/main/java/com/jyc/threegames/activity/base/BaseActivity.java
  10. 17 0
      app/src/main/java/com/jyc/threegames/bean/GameAnswer.java
  11. 9 1
      app/src/main/java/com/jyc/threegames/bean/GameInfo.java
  12. 81 0
      app/src/main/java/com/jyc/threegames/bean/result/ResGameInfo.java
  13. 21 0
      app/src/main/java/com/jyc/threegames/bean/result/ResUserConfig.java
  14. 118 0
      app/src/main/java/com/jyc/threegames/controller/GameController.java
  15. 33 1
      app/src/main/java/com/jyc/threegames/controller/LoginController.java
  16. 44 0
      app/src/main/java/com/jyc/threegames/controller/UserConfigController.java
  17. 6 6
      app/src/main/java/com/jyc/threegames/net/RetrofitHelper.java
  18. 1 1
      app/src/main/java/com/jyc/threegames/net/TokenInterceptor.java
  19. 33 0
      app/src/main/java/com/jyc/threegames/net/api/GameService.java
  20. 1 0
      app/src/main/java/com/jyc/threegames/net/api/LoginService.java
  21. 16 0
      app/src/main/java/com/jyc/threegames/net/api/UserConfigService.java
  22. 55 0
      app/src/main/java/com/jyc/threegames/service/GameService.java
  23. 97 1
      app/src/main/res/layout/activity_game_one.xml
  24. 0 11
      app/src/main/res/layout/activity_main.xml
  25. 38 0
      app/src/main/res/layout/activity_normal_user.xml
  26. 41 0
      app/src/main/res/layout/activity_ring.xml
  27. 1 0
      build.gradle
  28. BIN
      key/key.jks
  29. 1 0
      output/release/ThreeGames/loginInfo
  30. BIN
      output/release/app-release.aab
  31. BIN
      output/release/app-release.apk
  32. 20 0
      output/release/output.json
  33. BIN
      output/release/release.zip

+ 1 - 0
app/build.gradle

@@ -48,4 +48,5 @@ dependencies {
     implementation 'com.jakewharton:butterknife:10.1.0'
     annotationProcessor 'com.jakewharton:butterknife-compiler:10.1.0'
 
+    implementation 'com.github.sephiroth74:NumberSlidingPicker:v.1.1.1'
 }

+ 3 - 0
app/src/main/AndroidManifest.xml

@@ -7,6 +7,7 @@
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
 
     <application
         android:name=".App"
@@ -42,6 +43,8 @@
             android:launchMode="singleTask"
             android:screenOrientation="portrait" />
         <activity android:name=".activity.NormalUserActivity" />
+        <activity android:name=".activity.RingActivity" />
+        <service android:name=".service.GameService" />
     </application>
 
 </manifest>

+ 7 - 0
app/src/main/java/com/jyc/threegames/App.java

@@ -1,16 +1,23 @@
 package com.jyc.threegames;
 
 import android.app.Application;
+import android.content.Intent;
+
+import com.jyc.threegames.service.GameService;
 
 public class App extends Application {
     public static Application app;
 
     public static final String SERVER_ADDRESS = "http://192.168.1.100:8080/";
 
+    public static boolean CAN_PLAY_GAME = true;
+
     @Override
     public void onCreate() {
         super.onCreate();
 
         app = this;
+
+        startService(new Intent(this, GameService.class));
     }
 }

+ 5 - 0
app/src/main/java/com/jyc/threegames/activity/AdminActivity.java

@@ -45,4 +45,9 @@ public class AdminActivity extends BaseActivity {
     public void goToScale(){
         startActivity(new Intent(this, ScaleActivity.class));
     }
+
+    @OnClick(R.id.logout)
+    public void clickLogout(){
+        doLogout();
+    }
 }

+ 278 - 11
app/src/main/java/com/jyc/threegames/activity/GameActivity.java

@@ -1,34 +1,52 @@
 package com.jyc.threegames.activity;
 
 import android.app.Activity;
+import android.app.Dialog;
+import android.app.ProgressDialog;
 import android.content.Intent;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
 import android.text.Html;
+import android.view.Gravity;
 import android.view.View;
 import android.widget.TextView;
 
 import androidx.annotation.NonNull;
 import androidx.constraintlayout.widget.ConstraintLayout;
 
+import com.jyc.threegames.App;
 import com.jyc.threegames.R;
 import com.jyc.threegames.activity.base.BaseActivity;
 import com.jyc.threegames.bean.GameInfo;
+import com.jyc.threegames.bean.result.ResGameInfo;
+import com.jyc.threegames.controller.GameController;
+import com.jyc.threegames.controller.LoginController;
+import com.jyc.threegames.net.SimpleRequest;
 
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
 import java.util.Locale;
 
 import butterknife.BindView;
+import butterknife.OnClick;
+import it.sephiroth.android.library.numberpicker.NumberPicker;
 
 public class GameActivity extends BaseActivity {
     private static final String PARAM_GAME_INFO = "gameInfo";
+    private static final String PARAM_RES_GAME_INFO = "resGameInfo";
 
-    private static final int PRE_GAME_COUNT_DOWN = 5;
     private int mCurrentPreGameCountDown = 0;
     private Handler mPreGameHandler;
 
+    private Integer[] mPractiseNumbers;
+    private Handler mPractiseHandler;
+    private int mCurrentPractiseIndex;
+
     private GameInfo mGameInfo;
+    private ResGameInfo mResGameInfo;
 
     private int mSpecialNumberOne;
     private int mSpecialNumberTwo;
@@ -45,27 +63,54 @@ public class GameActivity extends BaseActivity {
     @BindView(R.id.click_area)
     View mVClickArea;
 
+    @BindView(R.id.form)
+    ConstraintLayout mCLForm;
+
+    @BindView(R.id.right)
+    NumberPicker mNPRight;
+    @BindView(R.id.grasp)
+    NumberPicker mNPGrasp;
+
+    private boolean mClickable = false;
+    private long mClickTime = 0;
+    private long mCurrentTime = 0;
+
+    private List<Integer> mRecordList;
+    private List<Long> mResponseList;
+    private List<Integer> mPressRecordList;
+
+    private long mStartTime = System.currentTimeMillis();
+    private long mEndTime = 0;
+
     @Override
     protected void init(Bundle instance) {
         super.init(instance);
-        if (instance == null)
+        if (instance == null) {
             mGameInfo = getIntent().getParcelableExtra(PARAM_GAME_INFO);
-        else
+            mResGameInfo = getIntent().getParcelableExtra(PARAM_RES_GAME_INFO);
+        }
+        else {
             mGameInfo = instance.getParcelable(PARAM_GAME_INFO);
+            mResGameInfo = getIntent().getParcelableExtra(PARAM_RES_GAME_INFO);
+        }
 
         startPrePractise();
+
+        App.CAN_PLAY_GAME = false;
     }
 
     @Override
     protected void onSaveInstanceState(@NonNull Bundle outState) {
         super.onSaveInstanceState(outState);
         outState.putParcelable(PARAM_GAME_INFO, mGameInfo);
+        outState.putParcelable(PARAM_RES_GAME_INFO, mResGameInfo);
     }
 
     @Override
     protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
         super.onRestoreInstanceState(savedInstanceState);
         mGameInfo = savedInstanceState.getParcelable(PARAM_GAME_INFO);
+        mResGameInfo = savedInstanceState.getParcelable(PARAM_RES_GAME_INFO);
     }
 
     @Override
@@ -74,8 +119,53 @@ public class GameActivity extends BaseActivity {
     }
 
     @Override
-    public void onBackPressed() {
-        super.onBackPressed();
+    protected void onDestroy() {
+        super.onDestroy();
+        App.CAN_PLAY_GAME = true;
+    }
+
+    @Override
+    public void onBackPressed() {}
+
+    @OnClick(R.id.click_area)
+    public void clickNumber(){
+        if (mClickable && mClickTime == 0){
+            mClickTime = System.currentTimeMillis();
+        }
+    }
+
+    @OnClick(R.id.sure)
+    public void clickSure(){
+        if (LoginController.getInstance().isCurrentUserAdmin()){
+            goFinish();
+        }else{
+            Dialog loading = new ProgressDialog(this);
+            loading.setTitle("正在提交數據");
+            loading.show();
+            new SimpleRequest<>()
+                .request(this, GameController.getInstance().addAnswer(mResGameInfo.playGameId, mPractiseNumbers, mRecordList, mResponseList, mPressRecordList, mSpecialNumberOne, mSpecialNumberTwo, mGameInfo.gameVersion, mStartTime, mEndTime, mNPRight.getProgress(), mNPGrasp.getProgress()), "提交數據失敗!請檢查網絡狀態", loading, new SimpleRequest.Executor<Object>() {
+                    @Override
+                    public void execute(Object obj) {
+                        goFinish();
+                    }
+                });
+        }
+    }
+
+    private void goFinish(){
+        hideAllView();
+
+        mTVPreGameHint.setGravity(Gravity.CENTER);
+        mTVPreGameHint.setText("數據提交成功\n\n謝謝參與");
+        mTVPreGameHint.setVisibility(View.VISIBLE);
+
+        new Handler(Looper.getMainLooper(), new Handler.Callback() {
+            @Override
+            public boolean handleMessage(@NonNull Message message) {
+                finish();
+                return true;
+            }
+        }).sendEmptyMessageDelayed(0, 3000);
     }
 
     private void startPrePractise(){
@@ -84,25 +174,32 @@ public class GameActivity extends BaseActivity {
         mSpecialNumberOne = getRandomNumber();
         mSpecialNumberTwo = getRandomNumber(mSpecialNumberOne);
 
-        mCurrentPreGameCountDown = PRE_GAME_COUNT_DOWN;
+        mCurrentPreGameCountDown = mGameInfo.getPractiseDurationSecond();
         mPreGameHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
             @Override
             public boolean handleMessage(@NonNull Message message) {
                 if (mCurrentPreGameCountDown > 0){
                     mTVPreGameHint.setVisibility(View.VISIBLE);
+                    mTVPreGameHint.setGravity(Gravity.START);
                     mTVPreGameHint.setText(Html.fromHtml(getPrePractiseText(mSpecialNumberOne, mSpecialNumberTwo, mCurrentPreGameCountDown), Html.FROM_HTML_MODE_LEGACY));
                     mCurrentPreGameCountDown --;
                     mPreGameHandler.sendEmptyMessageDelayed(0, 1000);
                 }else{
                     mTVPreGameHint.setVisibility(View.GONE);
-                    startPractise();
+                    startGame(true);
                 }
                 return true;
             }
         });
+
         mPreGameHandler.sendEmptyMessage(0);
     }
 
+    private String getPreGameText(int countDown){
+        return String.format(Locale.TRADITIONAL_CHINESE,"練習結束,<br/><br/><font color='#cc0000'>%1$d</font>秒后進入正式測試",
+                countDown);
+    }
+
     private String getPrePractiseText(int numberOne, int numberTwo, int countDown){
         switch (mGameInfo.gameVersion){
             case GameInfo.VERSION_GAME_ONE:
@@ -118,15 +215,178 @@ public class GameActivity extends BaseActivity {
         }
     }
 
-    private void startPractise(){
+    private void startGame(boolean isPractise){
         hideAllView();
 
-        mVClickArea.setOnClickListener(new View.OnClickListener() {
+        mPractiseNumbers = getPractiseNumbers(isPractise ? mGameInfo.practiseOccurrenceNumber : mGameInfo.errorNumberOccurrenceNumber);
+
+        mRecordList = new ArrayList<>();
+        mResponseList = new ArrayList<>();
+        mPressRecordList = new ArrayList<>();
+
+        mCurrentPractiseIndex = 0;
+        restoreClickInfo(false);
+        mPractiseHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
             @Override
-            public void onClick(View view) {
+            public boolean handleMessage(@NonNull Message message) {
+                switch (message.what){
+                    case 0:
+                        if (mCurrentPractiseIndex == mPractiseNumbers.length){
+                            restoreClickInfo(false);
+                            if (isPractise)
+                                startPregame();
+                            else
+                                startFeedBack();
+                        }else{
+                            restoreClickInfo(true);
+                            mCurrentTime = System.currentTimeMillis();
+                            mTVNumber.setVisibility(View.VISIBLE);
+                            mTVGameHint.setVisibility(View.GONE);
+                            mTVNumber.setText(String.valueOf(mPractiseNumbers[mCurrentPractiseIndex]));
+                            mPractiseHandler.sendEmptyMessageDelayed(1, mGameInfo.displayDuration);
+                        }
+                        break;
+                    case 1:
+                        mTVNumber.setVisibility(View.GONE);
+                        if (isPractise) {
+                            mTVGameHint.setText(Html.fromHtml(getGameHint(), Html.FROM_HTML_MODE_LEGACY));
+                            mTVGameHint.setVisibility(View.VISIBLE);
+                        }else {
+                            int result = getClickResult();
+                            mRecordList.add(result);
+                            mPressRecordList.add(mClickTime == 0 ? 0 : 1);
+                            if (mClickTime != 0 && result == GameInfo.CLICK_RIGHT)
+                                mResponseList.add(mClickTime - mCurrentTime);
+                            else
+                                mResponseList.add(-1L);
+                        }
+                        restoreClickInfo(false);
+                        mCurrentPractiseIndex ++;
+                        mPractiseHandler.sendEmptyMessageDelayed(0, mGameInfo.intervalDuration);
+                        break;
+                }
+                return true;
+            }
+        });
+        mPractiseHandler.sendEmptyMessage(0);
+    }
+
+    private void startPregame(){
+        hideAllView();
 
+        mCurrentPreGameCountDown = mGameInfo.getPractiseDurationSecond();
+        mPreGameHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
+            @Override
+            public boolean handleMessage(@NonNull Message message) {
+                if (mCurrentPreGameCountDown > 0){
+                    mTVPreGameHint.setVisibility(View.VISIBLE);
+                    mTVPreGameHint.setGravity(Gravity.CENTER);
+                    mTVPreGameHint.setText(Html.fromHtml(getPreGameText(mCurrentPreGameCountDown), Html.FROM_HTML_MODE_LEGACY));
+                    mCurrentPreGameCountDown --;
+                    mPreGameHandler.sendEmptyMessageDelayed(0, 1000);
+                }else{
+                    mTVPreGameHint.setVisibility(View.GONE);
+                    startGame(false);
+                }
+                return true;
             }
         });
+
+        mPreGameHandler.sendEmptyMessage(0);
+    }
+
+    private void startFeedBack(){
+        hideAllView();
+        mCLForm.setVisibility(View.VISIBLE);
+        mEndTime = System.currentTimeMillis();
+    }
+
+    private String getGameHint(){
+        String result;
+        int clickResult = getClickResult();
+
+        result = clickResult == GameInfo.CLICK_RIGHT ? "正確" : (clickResult == GameInfo.CLICK_WRONG ? "錯誤" : "錯失");
+
+        if (mClickTime != 0 && clickResult == GameInfo.CLICK_RIGHT)
+            result += String.format(Locale.TRADITIONAL_CHINESE, "<br/>反應時間: <font color='#cc0000'>%1$d</font> 毫秒", mClickTime - mCurrentTime);
+
+        return result;
+    }
+
+    private int getClickResult(){
+        int preNumber = mCurrentPractiseIndex == 0 ? -1 : mPractiseNumbers[mCurrentPractiseIndex - 1];
+        int currentNumber = mPractiseNumbers[mCurrentPractiseIndex];
+        if (mGameInfo.gameVersion == GameInfo.VERSION_GAME_ONE){
+            if (mClickTime == 0){
+                if (mSpecialNumberOne == currentNumber)
+                    return GameInfo.CLICK_MISS;
+                else
+                    return GameInfo.CLICK_RIGHT;
+            }else{
+                if (mSpecialNumberOne == currentNumber)
+                    return GameInfo.CLICK_RIGHT;
+                else
+                    return GameInfo.CLICK_WRONG;
+            }
+        }else if(mGameInfo.gameVersion == GameInfo.VERSION_GAME_TWO){
+            if (preNumber < 0 && mClickTime != 0)
+                return GameInfo.CLICK_WRONG;
+
+            if (mClickTime == 0){
+                if (preNumber == mSpecialNumberOne && currentNumber == mSpecialNumberTwo)
+                    return GameInfo.CLICK_MISS;
+                else
+                    return GameInfo.CLICK_RIGHT;
+            }else{
+                if (preNumber == mSpecialNumberOne && currentNumber == mSpecialNumberTwo)
+                    return GameInfo.CLICK_RIGHT;
+                else
+                    return GameInfo.CLICK_WRONG;
+            }
+        }else if(mGameInfo.gameVersion == GameInfo.VERSION_GAME_THREE){
+            if (mClickTime == 0){
+                if (mSpecialNumberOne == currentNumber)
+                    return GameInfo.CLICK_RIGHT;
+                else
+                    return GameInfo.CLICK_MISS;
+            }else{
+                if (mSpecialNumberOne == currentNumber)
+                    return GameInfo.CLICK_WRONG;
+                else
+                    return GameInfo.CLICK_RIGHT;
+            }
+        }
+
+        return GameInfo.CLICK_MISS;
+    }
+
+    private Integer[] getPractiseNumbers(int otherNumberOccurrence){
+        List<Integer> result = new ArrayList<>();
+        for (int i = 0; i < otherNumberOccurrence; i ++)
+            result.add(getRandomNumber(mSpecialNumberOne));
+
+        for (int i = 0; i < mGameInfo.correctNumberOccurrenceNumber; i ++) {
+            int index = (int) (Math.random() * result.size());
+            if (mGameInfo.gameVersion != GameInfo.VERSION_GAME_TWO)
+                result.add(index, mSpecialNumberOne);
+            else{
+                if (index != 0 && index != result.size() - 1 && result.size() > 2 && ((result.get(index - 1) == mSpecialNumberOne && result.get(index) == mSpecialNumberTwo) || (result.get(index) == mSpecialNumberOne && result.get(index + 1) == mSpecialNumberTwo))){
+                    i--;
+                    continue;
+                }
+
+                result.add(index, mSpecialNumberOne);
+                result.add(index + 1, mSpecialNumberTwo);
+            }
+        }
+
+        return result.toArray(new Integer[0]);
+    }
+
+    private void restoreClickInfo(boolean clickable){
+        mClickable = clickable;
+        mClickTime = 0;
+        mCurrentTime = 0;
     }
 
     private int getRandomNumber(){
@@ -139,7 +399,7 @@ public class GameActivity extends BaseActivity {
     }
 
     private void hideAllView(){
-        ConstraintLayout rootView = ((ConstraintLayout)getWindow().getDecorView().findViewById(R.id.root));
+        ConstraintLayout rootView = getWindow().getDecorView().findViewById(R.id.root);
         for (int i = 0; i < rootView.getChildCount(); i ++){
             if (rootView.getChildAt(i).getId() != R.id.click_area)
                 rootView.getChildAt(i).setVisibility(View.GONE);
@@ -151,4 +411,11 @@ public class GameActivity extends BaseActivity {
         intent.putExtra(PARAM_GAME_INFO, gameInfo);
         context.startActivity(intent);
     }
+
+    public static void LAUNCH_GAME(Activity context, ResGameInfo resGameInfo){
+        Intent intent = new Intent(context, GameActivity.class);
+        intent.putExtra(PARAM_GAME_INFO, resGameInfo.getGameInfo());
+        intent.putExtra(PARAM_RES_GAME_INFO, resGameInfo);
+        context.startActivity(intent);
+    }
 }

+ 24 - 6
app/src/main/java/com/jyc/threegames/activity/NormalUserActivity.java

@@ -1,16 +1,34 @@
 package com.jyc.threegames.activity;
 
-import androidx.appcompat.app.AppCompatActivity;
-
+import android.content.Intent;
 import android.os.Bundle;
 
 import com.jyc.threegames.R;
+import com.jyc.threegames.activity.base.BaseActivity;
+import com.jyc.threegames.bean.result.ResGameInfo;
+
+import butterknife.OnClick;
+
+public class NormalUserActivity extends BaseActivity {
 
-public class NormalUserActivity extends AppCompatActivity {
+    @Override
+    protected String getPageTitle() {
+        return "歡迎使用";
+    }
 
     @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.activity_main);
+    protected void init(Bundle instance) {
+        super.init(instance);
+
+    }
+
+    @Override
+    protected int getRootLayout() {
+        return R.layout.activity_normal_user;
+    }
+
+    @OnClick(R.id.logout)
+    public void clickLogout(){
+        doLogout();
     }
 }

+ 198 - 0
app/src/main/java/com/jyc/threegames/activity/RingActivity.java

@@ -0,0 +1,198 @@
+package com.jyc.threegames.activity;
+
+import android.app.Dialog;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.res.AssetFileDescriptor;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.media.Ringtone;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AlertDialog;
+
+import com.jyc.threegames.App;
+import com.jyc.threegames.R;
+import com.jyc.threegames.activity.base.BaseActivity;
+import com.jyc.threegames.bean.result.ResGameInfo;
+import com.jyc.threegames.controller.GameController;
+import com.jyc.threegames.net.SimpleRequest;
+
+import java.io.IOException;
+
+import butterknife.BindView;
+import butterknife.OnClick;
+import timber.log.Timber;
+
+public class RingActivity extends BaseActivity {
+    private static final String PARAM_GAME_INFO = "info";
+
+    public static final int TYPE_GAME = 0;
+    public static final int TYPE_SCALE = 1;
+
+    @BindView(R.id.label)
+    TextView mTVLabel;
+
+    private ResGameInfo mGameInfo;
+
+    private boolean mGoToGameOrScale = false;
+
+    private int mCurrentVolume = 0;
+
+    private Ringtone mRingTone;
+
+    @Override
+    protected void init(Bundle instance) {
+        super.init(instance);
+
+        App.CAN_PLAY_GAME = false;
+
+        if (instance == null)
+            mGameInfo = getIntent().getParcelableExtra(PARAM_GAME_INFO);
+        else
+            mGameInfo =  instance.getParcelable(PARAM_GAME_INFO);
+
+        if (mGameInfo == null){
+            finish();
+            return;
+        }
+
+        mTVLabel.setText(getLabel());
+
+        maxVolume();
+        playRing();
+    }
+
+    @Override
+    protected void onSaveInstanceState(@NonNull Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putParcelable(PARAM_GAME_INFO, mGameInfo);
+    }
+
+    @Override
+    protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
+        super.onRestoreInstanceState(savedInstanceState);
+        mGameInfo = savedInstanceState.getParcelable(PARAM_GAME_INFO);
+    }
+
+    @Override
+    protected int getRootLayout() {
+        return R.layout.activity_ring;
+    }
+
+    @Override
+    protected void onDestroy() {
+        if (!mGoToGameOrScale)
+            App.CAN_PLAY_GAME = true;
+        stopPlay();
+        restoreVolume();
+        super.onDestroy();
+    }
+
+    @Override
+    public void onBackPressed() { }
+
+    @OnClick(R.id.now)
+    public void clickNow(){
+        mGoToGameOrScale = true;
+        if (mGameInfo.playGameType == ResGameInfo.GAME_TYPE_GAME){
+            GameActivity.LAUNCH_GAME(this, mGameInfo);
+            finish();
+        }
+    }
+
+    @OnClick(R.id.push_back)
+    public void clickPushBack(){
+        String[] items = new String[]{"5分鐘", "10分鐘", "15分鐘", "20分鐘", "25分鐘", "30分鐘"};
+        new AlertDialog.Builder(this,0)
+            .setTitle("請選擇延遲時間")
+            .setItems(items, new DialogInterface.OnClickListener() {
+                @Override
+                public void onClick(DialogInterface dialogInterface, int i) {
+                    if (mGameInfo.playGameType == ResGameInfo.GAME_TYPE_GAME){
+                        dialogInterface.dismiss();
+                        Dialog loading = new ProgressDialog(RingActivity.this);
+                        loading.setTitle("正在延遲");
+                        loading.show();
+                        new SimpleRequest<>().request(RingActivity.this,
+                            GameController.getInstance().delayGame(mGameInfo.playGameId, mGameInfo.gameConfigId, getDelayMin(i)), "延遲失敗!請檢查網絡連接狀態", loading, new SimpleRequest.Executor<Object>() {
+                                @Override
+                                public void execute(Object obj) {
+                                    Toast.makeText(RingActivity.this, "延遲成功", Toast.LENGTH_LONG).show();
+                                    finish();
+                                }
+                            });
+                    }
+
+                }
+            }).create().show();
+    }
+
+    private int getDelayMin(int index){
+        switch (index){
+            case 1:
+                return 10;
+            case 2:
+                return 15;
+            case 3:
+                return 20;
+            case 4:
+                return 25;
+            case 5:
+                return 30;
+            case 0:
+            default:
+                return 5;
+        }
+    }
+
+    private void playRing(){
+        try {
+            Uri notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM);
+            mRingTone = RingtoneManager.getRingtone(this, notification);
+            mRingTone.setLooping(true);
+            mRingTone.play();
+        } catch (Exception e) {
+            e.printStackTrace();
+            Log.e("three_games", "播放鈴聲失敗");
+        }
+    }
+
+    private void maxVolume(){
+        mCurrentVolume = getAudioManager().getStreamVolume(AudioManager.STREAM_ALARM);
+        getAudioManager().setStreamVolume(AudioManager.STREAM_ALARM, getAudioManager().getStreamMaxVolume(AudioManager.STREAM_ALARM), 0);
+    }
+
+    private void restoreVolume(){
+        getAudioManager().setStreamVolume(AudioManager.STREAM_ALARM, mCurrentVolume, 0);
+    }
+
+    private AudioManager getAudioManager(){
+        return (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+    }
+
+    private void stopPlay(){
+        if (mRingTone != null && mRingTone.isPlaying()){
+            mRingTone.stop();
+        }
+    }
+
+    private String getLabel(){
+        return mGameInfo.playGameType == ResGameInfo.GAME_TYPE_GAME ? "您需要做遊戲啦" : "您需要做量表啦";
+    }
+
+    public static void LAUNCH(Context context, ResGameInfo gameInfo){
+        Intent intent = new Intent(context, RingActivity.class);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        intent.putExtra(PARAM_GAME_INFO, gameInfo);
+        context.startActivity(intent);
+    }
+}

+ 17 - 4
app/src/main/java/com/jyc/threegames/activity/ScaleActivity.java

@@ -4,13 +4,26 @@ import androidx.appcompat.app.AppCompatActivity;
 
 import android.os.Bundle;
 
+import com.jyc.threegames.App;
 import com.jyc.threegames.R;
+import com.jyc.threegames.activity.base.BaseActivity;
 
-public class ScaleActivity extends AppCompatActivity {
+public class ScaleActivity extends BaseActivity {
 
     @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.activity_scale);
+    protected void init(Bundle instance) {
+        super.init(instance);
+        App.CAN_PLAY_GAME = false;
+    }
+
+    @Override
+    protected int getRootLayout() {
+        return R.layout.activity_scale;
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        App.CAN_PLAY_GAME = true;
     }
 }

+ 35 - 3
app/src/main/java/com/jyc/threegames/activity/base/BaseActivity.java

@@ -1,5 +1,8 @@
 package com.jyc.threegames.activity.base;
 
+import android.app.Dialog;
+import android.app.ProgressDialog;
+import android.content.Intent;
 import android.graphics.Color;
 import android.os.Build;
 import android.os.Bundle;
@@ -7,6 +10,7 @@ import android.view.View;
 import android.view.Window;
 import android.view.WindowManager;
 import android.widget.TextView;
+import android.widget.Toast;
 
 import androidx.annotation.LayoutRes;
 import androidx.annotation.Nullable;
@@ -14,6 +18,9 @@ import androidx.appcompat.app.AppCompatActivity;
 import androidx.appcompat.widget.Toolbar;
 
 import com.jyc.threegames.R;
+import com.jyc.threegames.activity.LoginActivity;
+import com.jyc.threegames.controller.LoginController;
+import com.jyc.threegames.net.SimpleRequest;
 
 import butterknife.BindView;
 import butterknife.ButterKnife;
@@ -47,6 +54,11 @@ public abstract class BaseActivity extends AppCompatActivity {
         init(savedInstanceState);
     }
 
+    private void initSystemBar() {
+        Window window = getWindow();
+        window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
+    }
+
     protected abstract @LayoutRes int getRootLayout();
 
     protected String getPageTitle(){
@@ -55,8 +67,28 @@ public abstract class BaseActivity extends AppCompatActivity {
 
     protected void init(Bundle instance){}
 
-    public void initSystemBar() {
-        Window window = getWindow();
-        window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
+    protected void doLogout(){
+        if (LoginController.getInstance().isCurrentUserAdmin()){
+            LoginController.getInstance().logout();
+            startActivity(new Intent(BaseActivity.this, LoginActivity.class));
+            finish();
+            return;
+        }
+
+        Dialog loading = new ProgressDialog(this);
+        loading.setTitle("正在退出登录");
+        loading.show();
+        new SimpleRequest<Boolean>()
+            .request(this, LoginController.getInstance().canLogout(), "退出登錄失敗!請檢查網絡狀態", loading, new SimpleRequest.Executor<Boolean>() {
+                @Override
+                public void execute(Boolean obj) {
+                    if (obj != null && obj){
+                        LoginController.getInstance().logout();
+                        startActivity(new Intent(BaseActivity.this, LoginActivity.class));
+                        finish();
+                    }else
+                        Toast.makeText(BaseActivity.this, "禁止退出登錄", Toast.LENGTH_LONG).show();
+                }
+            });
     }
 }

+ 17 - 0
app/src/main/java/com/jyc/threegames/bean/GameAnswer.java

@@ -0,0 +1,17 @@
+package com.jyc.threegames.bean;
+
+public class GameAnswer {
+    public long id;
+    public long gameLogId;
+    public int answer;
+    public int correct;
+    public float reaction;
+    public int showNum;
+
+    public GameAnswer(int answer, int correct, float reaction, int showNum) {
+        this.answer = answer;
+        this.correct = correct;
+        this.reaction = reaction;
+        this.showNum = showNum;
+    }
+}

+ 9 - 1
app/src/main/java/com/jyc/threegames/bean/GameInfo.java

@@ -8,6 +8,10 @@ public class GameInfo implements Parcelable {
     public static final int VERSION_GAME_TWO = 2;
     public static final int VERSION_GAME_THREE = 3;
 
+    public static final int CLICK_RIGHT = 1;
+    public static final int CLICK_WRONG = 2;
+    public static final int CLICK_MISS = 3;
+
     public int gameVersion;
     public long intervalDuration; //數字交換間隔
     public long displayDuration;//顯示時長
@@ -27,7 +31,11 @@ public class GameInfo implements Parcelable {
     }
 
     public static GameInfo EXERCISE(int gameVersion){
-        return new GameInfo(gameVersion, 1000, 1000, 5, 5, 1000, 5);
+        return new GameInfo(gameVersion, 2000, 2000, 5, 5, 5000, 5);
+    }
+
+    public int getPractiseDurationSecond(){
+        return (int) this.practiseDuration / 1000;
     }
 
     @Override

+ 81 - 0
app/src/main/java/com/jyc/threegames/bean/result/ResGameInfo.java

@@ -0,0 +1,81 @@
+package com.jyc.threegames.bean.result;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.jyc.threegames.bean.GameInfo;
+
+public class ResGameInfo implements Parcelable {
+    public static final int GAME_TYPE_SCALE = 1;
+    public static final int GAME_TYPE_GAME = 0;
+
+    public long playGameId = -1;
+    public long gameConfigId = -1;
+    public long userConfigId = -1;
+    public int playGameType = -1; //量表还是游戏 0:游戏 1:量表
+    public boolean needDoGame = false;
+
+    public int gameVersion; //游戏版本
+    public long intervalDuration; //數字交換間隔
+    public long displayDuration;//顯示時長
+    public int correctNumberOccurrenceNumber;//正確數字出現次數
+    public int errorNumberOccurrenceNumber;//錯誤數字出現次數
+    public long practiseDuration; //用戶熱身時長
+    public int practiseOccurrenceNumber;//用戶熱身數字出現次數
+
+    public GameInfo getGameInfo(){
+        return new GameInfo(this.gameVersion, this.intervalDuration, this.displayDuration, this.correctNumberOccurrenceNumber, this.errorNumberOccurrenceNumber, this.practiseDuration, this.practiseOccurrenceNumber);
+    }
+
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeLong(this.playGameId);
+        dest.writeLong(this.gameConfigId);
+        dest.writeLong(this.userConfigId);
+        dest.writeInt(this.playGameType);
+        dest.writeByte(this.needDoGame ? (byte) 1 : (byte) 0);
+        dest.writeInt(this.gameVersion);
+        dest.writeLong(this.intervalDuration);
+        dest.writeLong(this.displayDuration);
+        dest.writeInt(this.correctNumberOccurrenceNumber);
+        dest.writeInt(this.errorNumberOccurrenceNumber);
+        dest.writeLong(this.practiseDuration);
+        dest.writeInt(this.practiseOccurrenceNumber);
+    }
+
+    public ResGameInfo() {
+    }
+
+    protected ResGameInfo(Parcel in) {
+        this.playGameId = in.readLong();
+        this.gameConfigId = in.readLong();
+        this.userConfigId = in.readLong();
+        this.playGameType = in.readInt();
+        this.needDoGame = in.readByte() != 0;
+        this.gameVersion = in.readInt();
+        this.intervalDuration = in.readLong();
+        this.displayDuration = in.readLong();
+        this.correctNumberOccurrenceNumber = in.readInt();
+        this.errorNumberOccurrenceNumber = in.readInt();
+        this.practiseDuration = in.readLong();
+        this.practiseOccurrenceNumber = in.readInt();
+    }
+
+    public static final Creator<ResGameInfo> CREATOR = new Creator<ResGameInfo>() {
+        @Override
+        public ResGameInfo createFromParcel(Parcel source) {
+            return new ResGameInfo(source);
+        }
+
+        @Override
+        public ResGameInfo[] newArray(int size) {
+            return new ResGameInfo[size];
+        }
+    };
+}

+ 21 - 0
app/src/main/java/com/jyc/threegames/bean/result/ResUserConfig.java

@@ -0,0 +1,21 @@
+package com.jyc.threegames.bean.result;
+
+public class ResUserConfig {
+    public static int CAN_LOGOUT = 0;
+    public static int CANT_LOGOUT = 1;
+
+    public long id;
+    public long userId;
+    public String scaleEnd;
+    public String scaleStart;
+    public String scaleTime;
+    public int showTime;
+    public int hideTime;
+    public int correct;
+    public int supporting;
+    public int practice;
+    public int gameCount;
+    public String version;
+    public int exercise;
+    public int loginOut;
+}

+ 118 - 0
app/src/main/java/com/jyc/threegames/controller/GameController.java

@@ -0,0 +1,118 @@
+package com.jyc.threegames.controller;
+
+import com.google.gson.Gson;
+import com.jyc.threegames.App;
+import com.jyc.threegames.bean.ControllerMessage;
+import com.jyc.threegames.bean.GameAnswer;
+import com.jyc.threegames.bean.GameInfo;
+import com.jyc.threegames.bean.result.ResGameInfo;
+import com.jyc.threegames.net.ResData;
+import com.jyc.threegames.net.RetrofitHelper;
+import com.jyc.threegames.net.api.GameService;
+import com.jyc.threegames.net.api.LoginService;
+
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+
+import io.reactivex.Observable;
+
+public class GameController extends BaseController {
+    private static GameController mInstance;
+
+    private GameController(){}
+
+    public static GameController getInstance(){
+        if(mInstance == null){
+            synchronized (GameController.class){
+                if(mInstance == null)
+                    mInstance = new GameController();
+            }
+        }
+        return mInstance;
+    }
+
+    private static GameService mService;
+
+    protected static GameService getGameService(){
+        if (mService == null){
+            synchronized (GameController.class){
+                if (mService == null)
+                    mService = RetrofitHelper.getDefaultRetrofit(App.SERVER_ADDRESS).create(GameService.class);
+            }
+        }
+
+        return mService;
+    }
+
+    public Observable<ControllerMessage<ResGameInfo>> needPlayGame(){
+        return getGameService().questNeedPlayGame()
+            .map(new SimpleDataHandleFunction<ResData<ResGameInfo>, ControllerMessage<ResGameInfo>>());
+    }
+
+    public Observable<ControllerMessage<Object>> delayGame(long gamePlayTimeId, long gameConfigId, int delayMin){
+        return getGameService().delayGame(gamePlayTimeId, gameConfigId, delayMin)
+            .map(new SimpleDataHandleFunction<>());
+    }
+
+    public Observable<ControllerMessage<Object>> addAnswer(
+        long gamePlayId,
+        Integer[] showNumbers,
+        List<Integer> mRecordList,
+        List<Long> mResponseList,
+        List<Integer> mPressRecordList,
+        int mSpecialNumberOne,
+        int mSpecialNumberTwo,
+        int gameVersion,
+        long startTime,
+        long endTime,
+        int right,
+        int grasp
+    ){
+        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
+
+        return getGameService().addGameAnswer(
+            gamePlayId,
+            LoginController.getInstance().getCurrentLoginInfo().info.id,
+            getRealCorrectRate(mRecordList),
+            right,
+            grasp,
+            dateFormat.format(new Date(startTime)),
+            dateFormat.format(new Date(endTime)),
+            gameVersion == GameInfo.VERSION_GAME_ONE ? "A" : gameVersion == GameInfo.VERSION_GAME_TWO ? "B" : "C",
+            gameVersion == GameInfo.VERSION_GAME_TWO ? mSpecialNumberOne + "," + mSpecialNumberTwo : mSpecialNumberOne + "",
+            getGameAnswer(showNumbers, mRecordList, mResponseList, mPressRecordList)
+            ).map(new SimpleDataHandleFunction<>());
+    }
+
+    private String getGameAnswer(
+        Integer[] showNumbers,
+        List<Integer> mRecordList,
+        List<Long> mResponseList,
+        List<Integer> mPressRecordList
+    ){
+        List<GameAnswer> result = new ArrayList<>();
+
+        for (int i = 0; i < showNumbers.length; i ++){
+            result.add(new GameAnswer(
+                mPressRecordList.get(i),
+                mRecordList.get(i),
+                mResponseList.get(i),
+                showNumbers[i]
+            ));
+        }
+
+        return new Gson().toJson(result);
+    }
+
+    private float getRealCorrectRate(List<Integer> mRecordList){
+        int right = 0;
+        for (Integer integer : mRecordList)
+            if (integer == GameInfo.CLICK_RIGHT)
+                right++;
+
+        return right / mRecordList.size();
+    }
+}

+ 33 - 1
app/src/main/java/com/jyc/threegames/controller/LoginController.java

@@ -10,6 +10,7 @@ import com.jyc.threegames.App;
 import com.jyc.threegames.bean.ControllerMessage;
 import com.jyc.threegames.bean.request.ReqLogin;
 import com.jyc.threegames.bean.result.ResLogin;
+import com.jyc.threegames.bean.result.ResUserConfig;
 import com.jyc.threegames.net.ResData;
 import com.jyc.threegames.net.RetrofitHelper;
 import com.jyc.threegames.net.api.LoginService;
@@ -85,6 +86,16 @@ public class LoginController extends BaseController {
         return false;
     }
 
+    public Observable<ControllerMessage<Boolean>> canLogout(){
+        return UserConfigController.getInstance().getUserConfig(mLoginInfo.info.id)
+            .map(new Function<ControllerMessage<ResUserConfig>, ControllerMessage<Boolean>>() {
+                @Override
+                public ControllerMessage<Boolean> apply(ControllerMessage<ResUserConfig> message) throws Exception {
+                    return ControllerMessage.getSimpleMessage(message.isSuccess(), message.getMessage(), message.isSuccess() && message.getObject() != null && message.getObject().loginOut == ResUserConfig.CAN_LOGOUT);
+                }
+            });
+    }
+
     public Observable<ControllerMessage<ResLogin>> login(String userName, String passWord){
         return getLoginService().login(userName, passWord)
             .doOnNext(new Consumer<ResData<ResLogin>>() {
@@ -99,6 +110,23 @@ public class LoginController extends BaseController {
             .map(new SimpleDataHandleFunction<ResData<ResLogin>, ControllerMessage<ResLogin>>());
     }
 
+    public void logout(){
+        this.mLoginInfo = null;
+        deleteLoginInfoOnSDCard();
+    }
+
+    private void deleteLoginInfoOnSDCard(){
+        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+            try {
+                File targetFile = new File(Environment.getExternalStorageDirectory() + PATH + FILE_NAME);
+                if (targetFile.exists())
+                    targetFile.delete();
+            } catch (Exception e){
+                e.printStackTrace();
+            }
+        }
+    }
+
     private void writeLoginInfoToSDCard(){
         if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
             try {
@@ -121,7 +149,7 @@ public class LoginController extends BaseController {
 
     private void continueCreateFile(File sdCardDir) throws Exception{
         File targetFile = new File(sdCardDir.getCanonicalPath() + PATH + FILE_NAME);
-        targetFile.deleteOnExit();
+        targetFile.delete();
         if (targetFile.createNewFile()){
             String content = new Gson().toJson(mLoginInfo);
             FileOutputStream outStream = new FileOutputStream(targetFile);
@@ -131,6 +159,10 @@ public class LoginController extends BaseController {
             Log.e("three_games", "create sdcard file error");
     }
 
+    public ResLogin getCurrentLoginInfo(){
+        return mLoginInfo;
+    }
+
     public boolean isCurrentUserAdmin(){
         if (mLoginInfo != null){
             return mLoginInfo.isAdmin();

+ 44 - 0
app/src/main/java/com/jyc/threegames/controller/UserConfigController.java

@@ -0,0 +1,44 @@
+package com.jyc.threegames.controller;
+
+import com.jyc.threegames.App;
+import com.jyc.threegames.bean.ControllerMessage;
+import com.jyc.threegames.bean.result.ResUserConfig;
+import com.jyc.threegames.net.ResData;
+import com.jyc.threegames.net.RetrofitHelper;
+import com.jyc.threegames.net.api.LoginService;
+import com.jyc.threegames.net.api.UserConfigService;
+
+import io.reactivex.Observable;
+
+public class UserConfigController extends BaseController {
+    private static UserConfigController mInstance;
+
+    private UserConfigController(){}
+
+    public static UserConfigController getInstance(){
+        if(mInstance == null){
+            synchronized (UserConfigController.class){
+                if(mInstance == null)
+                    mInstance = new UserConfigController();
+            }
+        }
+        return mInstance;
+    }
+
+    private static UserConfigService mService;
+
+    protected static UserConfigService getUserConfigService(){
+        if (mService == null){
+            synchronized (UserConfigController.class){
+                if (mService == null)
+                    mService = RetrofitHelper.getDefaultRetrofit(App.SERVER_ADDRESS).create(UserConfigService.class);
+            }
+        }
+        return mService;
+    }
+
+    public Observable<ControllerMessage<ResUserConfig>> getUserConfig(long id){
+        return getUserConfigService().getUserConfig(id)
+            .map(new SimpleDataHandleFunction<ResData<ResUserConfig>, ControllerMessage<ResUserConfig>>());
+    }
+}

+ 6 - 6
app/src/main/java/com/jyc/threegames/net/RetrofitHelper.java

@@ -16,11 +16,11 @@ public class RetrofitHelper {
             mDefaultClient = OkHttpClientHelper.getDefaultOkHttpClient(new TokenInterceptor());
 
         return new Retrofit.Builder()
-                .baseUrl(url)
-                .addConverterFactory(ScalarsConverterFactory.create())
-                .addConverterFactory(GsonConverterFactory.create())
-                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
-                .client(mDefaultClient)
-                .build();
+            .baseUrl(url)
+            .addConverterFactory(ScalarsConverterFactory.create())
+            .addConverterFactory(GsonConverterFactory.create())
+            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
+            .client(mDefaultClient)
+            .build();
     }
 }

+ 1 - 1
app/src/main/java/com/jyc/threegames/net/TokenInterceptor.java

@@ -18,7 +18,7 @@ public class TokenInterceptor implements Interceptor {
             return chain.proceed(chain.request());
         Request request = chain.request()
                 .newBuilder()
-                .addHeader("Authorization", token)
+                .addHeader("Authorization", "Bearer " + token)
                 .build();
         return chain.proceed(request);
     }

+ 33 - 0
app/src/main/java/com/jyc/threegames/net/api/GameService.java

@@ -0,0 +1,33 @@
+package com.jyc.threegames.net.api;
+
+import com.jyc.threegames.bean.result.ResGameInfo;
+import com.jyc.threegames.net.ResData;
+
+import io.reactivex.Observable;
+import retrofit2.http.Field;
+import retrofit2.http.FormUrlEncoded;
+import retrofit2.http.GET;
+import retrofit2.http.POST;
+
+public interface GameService {
+    @GET("push/log/needPlayGame")
+    Observable<ResData<ResGameInfo>> questNeedPlayGame();
+
+    @FormUrlEncoded
+    @POST("/game/answer/add/real")
+    Observable<ResData<Object>> addGameAnswer(
+            @Field("gamePlayId") long gamePlayId,
+            @Field("userId") long userId,
+            @Field("realCorrectRate") float realCorrectRate,
+            @Field("correctRate") float correctRate,
+            @Field("confidence") float confidence,
+            @Field("startTime") String startTime,
+            @Field("endTime") String endTime,
+            @Field("version") String version,
+            @Field("selected") String selected,
+            @Field("gameAnswer") String gameAnswer);
+
+    @FormUrlEncoded
+    @POST("push/log/delay/game")
+    Observable<ResData<Object>> delayGame(@Field("gamePlayTimeId") long gamePlayId, @Field("gameConfigId") long gameConfigId, @Field("delayMin") int delayMin);
+}

+ 1 - 0
app/src/main/java/com/jyc/threegames/net/api/LoginService.java

@@ -2,6 +2,7 @@ package com.jyc.threegames.net.api;
 
 import com.jyc.threegames.bean.request.ReqLogin;
 import com.jyc.threegames.bean.result.ResLogin;
+import com.jyc.threegames.bean.result.ResUserConfig;
 import com.jyc.threegames.net.ResData;
 
 import io.reactivex.Observable;

+ 16 - 0
app/src/main/java/com/jyc/threegames/net/api/UserConfigService.java

@@ -0,0 +1,16 @@
+package com.jyc.threegames.net.api;
+
+import com.jyc.threegames.bean.result.ResUserConfig;
+import com.jyc.threegames.net.ResData;
+
+import io.reactivex.Observable;
+import retrofit2.http.Field;
+import retrofit2.http.FormUrlEncoded;
+import retrofit2.http.POST;
+
+public interface UserConfigService {
+
+    @FormUrlEncoded
+    @POST("user/config/get")
+    Observable<ResData<ResUserConfig>> getUserConfig(@Field("id") long id);
+}

+ 55 - 0
app/src/main/java/com/jyc/threegames/service/GameService.java

@@ -0,0 +1,55 @@
+package com.jyc.threegames.service;
+
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.IBinder;
+
+import androidx.annotation.Nullable;
+
+import com.jyc.threegames.App;
+import com.jyc.threegames.activity.RingActivity;
+import com.jyc.threegames.bean.result.ResGameInfo;
+import com.jyc.threegames.controller.GameController;
+import com.jyc.threegames.controller.LoginController;
+import com.jyc.threegames.net.SimpleRequest;
+
+public class GameService extends Service {
+
+    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (App.CAN_PLAY_GAME && LoginController.getInstance().getCurrentLoginInfo() != null && !LoginController.getInstance().isCurrentUserAdmin())
+                new SimpleRequest<ResGameInfo>().request(context, GameController.getInstance().needPlayGame(), new SimpleRequest.Executor<ResGameInfo>() {
+                    @Override
+                    public void execute(ResGameInfo obj) {
+                        if (App.CAN_PLAY_GAME && obj != null && obj.needDoGame){
+                            RingActivity.LAUNCH(App.app, obj);
+                        }
+                    }
+                });
+        }
+    };
+
+    @Nullable
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(Intent.ACTION_TIME_TICK);
+        registerReceiver(mReceiver, intentFilter);
+    }
+
+    @Override
+    public void onDestroy() {
+        unregisterReceiver(mReceiver);
+        super.onDestroy();
+    }
+}

+ 97 - 1
app/src/main/res/layout/activity_game_one.xml

@@ -36,8 +36,9 @@
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintTop_toTopOf="parent"
-        android:textSize="18sp"
+        android:textSize="25sp"
         android:textStyle="bold"
+        android:gravity="center"
         app:layout_constraintBottom_toBottomOf="parent"/>
 
     <View
@@ -49,4 +50,99 @@
         app:layout_constraintTop_toTopOf="parent"
         app:layout_constraintBottom_toBottomOf="parent"/>
 
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/form"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toTopOf="parent">
+        <TextView
+            android:id="@+id/view_form_title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            android:layout_marginTop="100dp"
+            android:textSize="18sp"
+            android:textColor="#000"
+            android:textStyle="bold"
+            android:text="測試結束,請填寫以下反饋"/>
+
+        <TextView
+            android:id="@+id/view_form_question_one"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:textSize="15sp"
+            android:textColor="#CC000000"
+            android:textStyle="bold"
+            android:text="你覺得你答對了百分之多少?"
+            app:layout_constraintStart_toStartOf="parent"
+            android:layout_marginStart="10dp"
+            app:layout_constraintEnd_toStartOf="@id/right"
+            android:layout_marginEnd="10dp"
+            app:layout_constraintTop_toTopOf="@id/right"
+            app:layout_constraintBottom_toBottomOf="@id/right" />
+
+        <TextView
+            android:id="@+id/view_form_question_two"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:textSize="15sp"
+            android:textColor="#CC000000"
+            android:textStyle="bold"
+            android:text="你對以上的判斷有百分之幾的把握?"
+            app:layout_constraintStart_toStartOf="parent"
+            android:layout_marginStart="10dp"
+            app:layout_constraintEnd_toStartOf="@id/right"
+            android:layout_marginEnd="10dp"
+            app:layout_constraintTop_toTopOf="@id/grasp"
+            app:layout_constraintBottom_toBottomOf="@id/grasp" />
+
+        <it.sephiroth.android.library.numberpicker.NumberPicker
+            style="@style/NumberPicker.EditTextStyle"
+            app:layout_constraintEnd_toEndOf="parent"
+            android:layout_marginEnd="10dp"
+            android:layout_marginTop="30dp"
+            app:layout_constraintTop_toBottomOf="@+id/view_form_title"
+            app:picker_max="100"
+            app:picker_min="0"
+            android:progress="100"
+            app:picker_stepSize="10"
+            app:picker_tracker="exponential"
+            app:picker_orientation="vertical"
+            android:id="@+id/right"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+
+        <it.sephiroth.android.library.numberpicker.NumberPicker
+            style="@style/NumberPicker.EditTextStyle"
+            app:layout_constraintEnd_toEndOf="parent"
+            android:layout_marginEnd="10dp"
+            android:layout_marginTop="20dp"
+            app:layout_constraintTop_toBottomOf="@+id/right"
+            app:picker_max="100"
+            app:picker_min="0"
+            android:progress="100"
+            app:picker_stepSize="10"
+            app:picker_tracker="exponential"
+            app:picker_orientation="vertical"
+            android:id="@+id/grasp"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+
+        <Button
+            android:id="@+id/sure"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="確定"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            android:layout_marginTop="20dp"
+            app:layout_constraintTop_toBottomOf="@+id/view_form_question_two"
+            android:background="@android:color/holo_green_light"/>
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
 </androidx.constraintlayout.widget.ConstraintLayout>

+ 0 - 11
app/src/main/res/layout/activity_main.xml

@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    tools:context=".activity.NormalUserActivity">
-
-
-
-</androidx.constraintlayout.widget.ConstraintLayout>

+ 38 - 0
app/src/main/res/layout/activity_normal_user.xml

@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".activity.NormalUserActivity">
+
+    <androidx.appcompat.widget.Toolbar
+        android:id="@+id/toolbar"
+        android:layout_width="0dp"
+        android:fitsSystemWindows="true"
+        android:layout_height="wrap_content"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        android:background="@color/colorAccent"
+        android:minHeight="?attr/actionBarSize" >
+        <TextView
+            android:id="@+id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:textSize="20sp"
+            android:textStyle="bold"/>
+
+        <TextView
+            android:id="@+id/logout"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="end"
+            android:textSize="14sp"
+            android:text="退出"
+            android:layout_marginEnd="10dp"
+            android:textStyle="bold"/>
+    </androidx.appcompat.widget.Toolbar>
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 41 - 0
app/src/main/res/layout/activity_ring.xml

@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+    <TextView
+        android:id="@+id/label"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="您需要做遊戲啦"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        android:textSize="30sp"
+        app:layout_constraintVertical_bias="0.3"/>
+
+    <Button
+        android:id="@+id/push_back"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:text="推遲"
+        app:layout_constraintWidth_percent="0.8"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/label"
+        android:textSize="30sp"
+        android:layout_marginTop="60dp"/>
+
+    <Button
+        android:id="@+id/now"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:text="現在就做"
+        app:layout_constraintWidth_percent="0.8"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        android:textSize="30sp"
+        app:layout_constraintTop_toBottomOf="@id/push_back"
+        android:layout_marginTop="30dp"/>
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 1 - 0
build.gradle

@@ -17,6 +17,7 @@ allprojects {
     repositories {
         google()
         jcenter()
+        maven { url 'https://jitpack.io' }
     }
 }
 

BIN
key/key.jks


+ 1 - 0
output/release/ThreeGames/loginInfo

@@ -0,0 +1 @@
+{"info":{"accountNonExpired":true,"accountNonLocked":true,"alias":"wanghuiwen312@sina.com","credentialsNonExpired":true,"enabled":true,"id":1,"nickname":"王会文","password":"","roles":["admin"],"type":2,"username":"admin"},"token":"eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhZG1pbiIsImNyZWF0ZWQiOjE1OTM3NzQ0OTQ1NTEsImV4cCI6NDc1NjAxNDQ5NH0.Rhv_7RLzYymkkYijKOjY0q8LYEREmWlFzcWPAcVD_kg6kbXEGbDXed8xjBJKl0sPDecUW8m8-bkYP2JV-vmYLQ"}

BIN
output/release/app-release.aab


BIN
output/release/app-release.apk


+ 20 - 0
output/release/output.json

@@ -0,0 +1,20 @@
+{
+  "version": 1,
+  "artifactType": {
+    "type": "APK",
+    "kind": "Directory"
+  },
+  "applicationId": "com.jyc.threegames",
+  "variantName": "release",
+  "elements": [
+    {
+      "type": "SINGLE",
+      "filters": [],
+      "properties": [],
+      "versionCode": 1,
+      "versionName": "1",
+      "enabled": true,
+      "outputFile": "app-release.apk"
+    }
+  ]
+}

BIN
output/release/release.zip