├── .gitignore
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── app
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── chess
│ │ │ └── clock
│ │ │ ├── ClockApplication.java
│ │ │ ├── activities
│ │ │ ├── AppSettingsActivity.java
│ │ │ ├── BaseActivity.java
│ │ │ ├── ClockSoundManager.java
│ │ │ ├── ClockTimersActivity.java
│ │ │ └── TimerSettingsActivity.java
│ │ │ ├── adapters
│ │ │ ├── ThemesAdapter.java
│ │ │ ├── TimeRowMoveCallback.java
│ │ │ └── TimesAdapter.java
│ │ │ ├── dialog
│ │ │ ├── AdjustTimeDialogFragment.java
│ │ │ ├── EditStageDialogFragment.java
│ │ │ ├── EditTimeIncrementDialogFragment.java
│ │ │ └── FullScreenDialogFragment.java
│ │ │ ├── engine
│ │ │ ├── ClockPlayer.java
│ │ │ ├── CountDownTimer.java
│ │ │ ├── Stage.java
│ │ │ ├── StageManager.java
│ │ │ ├── TimeControl.java
│ │ │ ├── TimeControlDefaults.java
│ │ │ ├── TimeControlManager.java
│ │ │ ├── TimeControlParser.java
│ │ │ ├── TimeControlWrapper.java
│ │ │ └── TimeIncrement.java
│ │ │ ├── entities
│ │ │ ├── AppTheme.java
│ │ │ └── ClockTime.java
│ │ │ ├── fragments
│ │ │ ├── BaseFragment.java
│ │ │ ├── TimeControlFragment.java
│ │ │ └── TimeSettingsFragment.java
│ │ │ ├── manager
│ │ │ ├── ChessClockManager.java
│ │ │ └── ChessClockManagerImpl.java
│ │ │ ├── statics
│ │ │ ├── AppConstants.java
│ │ │ ├── AppData.java
│ │ │ └── StaticData.java
│ │ │ ├── util
│ │ │ ├── Args.java
│ │ │ ├── AutoNameFormatter.java
│ │ │ └── ClockUtils.java
│ │ │ └── views
│ │ │ ├── ClockButton.java
│ │ │ ├── ClockMenu.java
│ │ │ ├── StageRowView.java
│ │ │ ├── StyledButton.java
│ │ │ └── ViewUtils.java
│ └── res
│ │ ├── anim
│ │ ├── left_to_right_full.xml
│ │ ├── left_to_right_in.xml
│ │ ├── right_to_left_full.xml
│ │ └── right_to_left_out.xml
│ │ ├── drawable-v21
│ │ └── bg_green_pressed.xml
│ │ ├── drawable
│ │ ├── add_time_button_bg.xml
│ │ ├── bg_btn_clock_running.xml
│ │ ├── bg_green_pressed.xml
│ │ ├── bg_popup.xml
│ │ ├── bg_white_50_rounded.xml
│ │ ├── button_bg_selector_green.xml
│ │ ├── ic_arrow_right_24.xml
│ │ ├── ic_back_arrow_white.xml
│ │ ├── ic_check_box.xml
│ │ ├── ic_check_box_frame.xml
│ │ ├── ic_checkmark.xml
│ │ ├── ic_checkmark_white.xml
│ │ ├── ic_delete.xml
│ │ ├── ic_edit.xml
│ │ ├── ic_edit_pencil.xml
│ │ ├── ic_edit_time.xml
│ │ ├── ic_edit_time_small.xml
│ │ ├── ic_fullscreen.xml
│ │ ├── ic_fullscreen_exit.xml
│ │ ├── ic_launcher_background.xml
│ │ ├── ic_launcher_foreground.xml
│ │ ├── ic_logo.xml
│ │ ├── ic_options.xml
│ │ ├── ic_pause.xml
│ │ ├── ic_play.xml
│ │ ├── ic_plus_green.xml
│ │ ├── ic_radio_frame.xml
│ │ ├── ic_radio_selected.xml
│ │ ├── ic_refresh.xml
│ │ ├── ic_reorder_icon.xml
│ │ ├── ic_settings.xml
│ │ ├── ic_settings_sound_off.xml
│ │ ├── ic_settings_sound_on.xml
│ │ ├── ic_sound.xml
│ │ ├── ic_sound_off.xml
│ │ ├── list_radio_button_selector.xml
│ │ ├── shape_stage_empty.xml
│ │ └── shape_stage_fill.xml
│ │ ├── font
│ │ ├── montserrat_extra_bold.ttf
│ │ └── roboto_medium.ttf
│ │ ├── layout-land
│ │ ├── activity_clock_timers.xml
│ │ ├── activity_clock_timers_reversed.xml
│ │ └── view_clock_menu.xml
│ │ ├── layout
│ │ ├── activity_app_settings.xml
│ │ ├── activity_clock_timers.xml
│ │ ├── activity_timer_settings.xml
│ │ ├── dialog_fragment_adjust_time.xml
│ │ ├── dialog_fragment_edit_stage.xml
│ │ ├── dialog_fragment_edit_time_increment.xml
│ │ ├── dialog_title_text_view.xml
│ │ ├── fragment_settings.xml
│ │ ├── fragment_time_control.xml
│ │ ├── item_theme.xml
│ │ ├── item_view_time.xml
│ │ ├── list_stage_item.xml
│ │ ├── view_clock_button.xml
│ │ ├── view_clock_menu.xml
│ │ └── view_styled_button.xml
│ │ ├── menu
│ │ ├── settings_actions.xml
│ │ └── settings_cab_actions.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── raw
│ │ ├── chess_clock_pause.mp3
│ │ ├── chess_clock_reset.mp3
│ │ ├── chess_clock_switch1.mp3
│ │ ├── chess_clock_switch2.mp3
│ │ └── chess_clock_time_ended.mp3
│ │ ├── transition
│ │ └── fragment_in.xml
│ │ ├── values-ar
│ │ └── strings.xml
│ │ ├── values-fr
│ │ └── strings.xml
│ │ ├── values-sw360dp
│ │ └── dimens.xml
│ │ ├── values-sw600dp
│ │ └── dimens.xml
│ │ ├── values-v19
│ │ └── styles.xml
│ │ ├── values-v35
│ │ └── edgetoedge.xml
│ │ └── values
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── edgetoedge.xml
│ │ ├── plurals.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── chess
│ └── AutoNameFormatterTest.java
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── metadata
└── en-US
│ ├── changelogs
│ ├── 26.txt
│ ├── 27.txt
│ └── 28.txt
│ ├── full_description.txt
│ ├── images
│ ├── featureGraphic.jpg
│ ├── icon.png
│ ├── phoneScreenshots
│ │ ├── 1.png
│ │ ├── 2.png
│ │ ├── 3.png
│ │ ├── 4.png
│ │ ├── 5.png
│ │ └── 6.png
│ ├── sevenInchScreenshots
│ │ ├── 1.png
│ │ └── 2.png
│ └── tenInchScreenshots
│ │ ├── 1.png
│ │ └── 2.png
│ ├── short_description.txt
│ └── title.txt
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | /local.properties
3 | .DS_Store
4 | /.idea
5 | *.iml
6 | **/build
7 |
8 | app/certificate
9 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to contribute
2 |
3 | ## The basics
4 | * Submit a ticket for your issue, if one does not already exist.
5 | * [Fork](https://help.github.com/articles/fork-a-repo/) the repo.
6 | * Do some work, and file a [pull request](https://help.github.com/articles/about-pull-requests/).
7 |
8 | ## Git guidelines
9 | * Branches should be based on *develop*
10 | * To create a branch: `git checkout develop && git pull && git checkout -b issue-abc-comment`
11 | * Pull requests must be rebased onto the latest from *develop* before they will be merged.
12 | * Make your commits logical units. Commits messages should look like: `git commit -m "Issue abc. Comment goes here"`
13 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Chess.com Chess Clock
2 |
3 | Android version of chess clock mobile app.
4 |
5 | [
](https://play.google.com/store/apps/details?id=com.chess.clock)
6 | [
](https://www.f-droid.org/packages/com.chess.clock/)
7 |
8 | ## Prerequisites
9 |
10 | * Install [Android Studio](http://developer.android.com/sdk/installing/studio.html).
11 | * [Fork](https://help.github.com/articles/fork-a-repo/) the repo
12 |
13 | ## Import the project in Android Studio
14 |
15 | * Open Android Studio.
16 | * Choose Import project.
17 | * Select project folder and press ok.
18 |
19 | ## Run the app via Android Studio
20 |
21 | * Run it using the "play" or "debug" buttons in the UI.
22 |
23 | ## Run via Gradle
24 |
25 | * Start an emulator or connect your device
26 | * Run `./gradlew installDebug`
27 | * You should have a new app called Chess Clock in the app drawer.
28 | * Tap it to open the app
29 |
30 | # Questions?
31 | [File an issue!](https://github.com/ChessCom/android-chessclock/issues)
32 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdk 35
5 | namespace 'com.chess.clock'
6 |
7 | defaultConfig {
8 | minSdkVersion 16
9 | targetSdkVersion 35
10 | versionCode 28
11 | versionName "1.2.2"
12 | }
13 |
14 | buildTypes {
15 | release {
16 | minifyEnabled true
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | debug {
20 | minifyEnabled false
21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
22 | }
23 | }
24 |
25 | buildFeatures {
26 | buildConfig = true
27 | resValues = true
28 | }
29 |
30 | lint {
31 | checkReleaseBuilds false
32 | // Or, if you prefer, you can continue to check for errors in release builds,
33 | // but continue the build even when errors are found:
34 | abortOnError false
35 | }
36 | compileOptions {
37 | sourceCompatibility = '1.8'
38 | targetCompatibility = '1.8'
39 | }
40 | }
41 |
42 | dependencies {
43 | implementation 'androidx.appcompat:appcompat:1.6.1'
44 | implementation 'androidx.recyclerview:recyclerview:1.2.1'
45 | implementation 'com.google.android.material:material:1.11.0'
46 |
47 | testImplementation "junit:junit:4.13.2"
48 | }
49 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | -dontobfuscate
2 | -keepattributes SourceFile,LineNumberTable
3 |
4 | -assumenosideeffects class android.util.Log {
5 | public static *** d(...);
6 | public static *** v(...);
7 | public static *** i(...);
8 | public static *** w(...);
9 | public static *** e(...);
10 | }
11 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
28 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chess/clock/ClockApplication.java:
--------------------------------------------------------------------------------
1 | package com.chess.clock;
2 |
3 | import android.app.Application;
4 |
5 | import com.chess.clock.manager.ChessClockManager;
6 | import com.chess.clock.manager.ChessClockManagerImpl;
7 |
8 | public class ClockApplication extends Application {
9 | private static ChessClockManager clockManager;
10 |
11 | public static ChessClockManager getClockManager() {
12 | if (clockManager == null) {
13 | clockManager = new ChessClockManagerImpl();
14 | }
15 | return clockManager;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chess/clock/activities/AppSettingsActivity.java:
--------------------------------------------------------------------------------
1 | package com.chess.clock.activities;
2 |
3 | import android.app.Activity;
4 | import android.app.AlertDialog;
5 | import android.content.res.ColorStateList;
6 | import android.media.MediaPlayer;
7 | import android.os.Bundle;
8 | import android.view.MenuItem;
9 | import android.view.View;
10 | import android.widget.ImageView;
11 | import android.widget.TextView;
12 |
13 | import androidx.appcompat.app.ActionBar;
14 | import androidx.appcompat.widget.SwitchCompat;
15 | import androidx.core.graphics.drawable.DrawableCompat;
16 | import androidx.recyclerview.widget.RecyclerView;
17 |
18 | import com.chess.clock.BuildConfig;
19 | import com.chess.clock.R;
20 | import com.chess.clock.adapters.ThemesAdapter;
21 | import com.chess.clock.entities.AppTheme;
22 |
23 | public class AppSettingsActivity extends BaseActivity {
24 |
25 | private boolean soundsEnabled;
26 | private boolean fullScreenMode;
27 | private ThemesAdapter adapter;
28 |
29 | private ImageView soundImg;
30 | private ImageView fullScreenImg;
31 |
32 | @Override
33 | protected void onCreate(Bundle savedInstanceState) {
34 | super.onCreate(savedInstanceState);
35 | setContentView(R.layout.activity_app_settings);
36 |
37 | soundsEnabled = appData.areSoundsEnabled();
38 | fullScreenMode = appData.getClockFullScreen();
39 |
40 | soundImg = findViewById(R.id.soundImg);
41 | fullScreenImg = findViewById(R.id.fullscreenImg);
42 | RecyclerView recycler = findViewById(R.id.themesRecycler);
43 | SwitchCompat fullScreenSwitch = findViewById(R.id.fullscreenSwitch);
44 | SwitchCompat soundsSwitch = findViewById(R.id.soundSwitch);
45 |
46 | ActionBar actionBar = getSupportActionBar();
47 | if (actionBar != null) {
48 | actionBar.setHomeButtonEnabled(true);
49 | actionBar.setDisplayHomeAsUpEnabled(true);
50 | actionBar.setTitle(R.string.app_settings);
51 | }
52 |
53 | AppTheme selectedTheme = appData.getSelectedTheme();
54 | adapter = new ThemesAdapter(selectedTheme, theme -> {
55 | ColorStateList tintChecked = theme.switchColorStateList(this);
56 | DrawableCompat.setTintList(fullScreenSwitch.getThumbDrawable(), tintChecked);
57 | DrawableCompat.setTintList(soundsSwitch.getThumbDrawable(), tintChecked);
58 | });
59 | recycler.setAdapter(adapter);
60 |
61 | fullScreenSwitch.setChecked(fullScreenMode);
62 | soundsSwitch.setChecked(soundsEnabled);
63 | ColorStateList tintChecked = selectedTheme.switchColorStateList(this);
64 | DrawableCompat.setTintList(fullScreenSwitch.getThumbDrawable(), tintChecked);
65 | DrawableCompat.setTintList(soundsSwitch.getThumbDrawable(), tintChecked);
66 |
67 | fullScreenSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> {
68 | fullScreenMode = isChecked;
69 | updateUiState();
70 | });
71 | MediaPlayer enableSounds = MediaPlayer.create(this, R.raw.chess_clock_pause);
72 | soundsSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> {
73 | soundsEnabled = isChecked;
74 | updateUiState();
75 | if (isChecked) {
76 | enableSounds.start();
77 | }
78 | });
79 |
80 | findViewById(R.id.restoreBtn).setOnClickListener(v -> {
81 | TextView titleView = (TextView) View.inflate(this, R.layout.dialog_title_text_view, null);
82 | titleView.setText(R.string.restore_default_controls);
83 | new AlertDialog.Builder(this, R.style.WhiteButtonsDialogTheme)
84 | .setMessage(R.string.restore_default_controls_confirmation_message)
85 | .setCustomTitle(titleView)
86 | .setPositiveButton(R.string.action_confirm, (dialog, id) -> {
87 | BaseActivity activity = AppSettingsActivity.this;
88 | activity.setResult(Activity.RESULT_OK);
89 | activity.finishWithAnimation();
90 | })
91 | .setNegativeButton(R.string.action_cancel, (dialog, id) -> {
92 | // dismiss
93 | })
94 | .create()
95 | .show();
96 | });
97 |
98 | updateUiState();
99 | setVersionInfo();
100 | }
101 |
102 | private void setVersionInfo() {
103 | TextView versionTv = findViewById(R.id.versionTv);
104 | String version = getString(R.string.version, BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE);
105 | versionTv.setText(version);
106 | }
107 |
108 | private void updateUiState() {
109 | soundImg.setImageResource(soundsEnabled ? R.drawable.ic_settings_sound_on : R.drawable.ic_settings_sound_off);
110 | fullScreenImg.setImageResource(fullScreenMode ? R.drawable.ic_fullscreen : R.drawable.ic_fullscreen_exit);
111 | }
112 |
113 | @Override
114 | protected void onPause() {
115 | super.onPause();
116 | appData.saveAppSetup(
117 | adapter.selectedTheme,
118 | fullScreenMode,
119 | soundsEnabled
120 | );
121 | }
122 |
123 | @Override
124 | public boolean onOptionsItemSelected(MenuItem item) {
125 | if (item.getItemId() == android.R.id.home) {
126 | finishWithAnimation();
127 | return true;
128 | }
129 | return super.onOptionsItemSelected(item);
130 | }
131 |
132 | @Override
133 | public void onBackPressed() {
134 | finishWithAnimation();
135 | }
136 |
137 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/chess/clock/activities/BaseActivity.java:
--------------------------------------------------------------------------------
1 | package com.chess.clock.activities;
2 |
3 | import android.os.Bundle;
4 | import android.view.WindowManager;
5 |
6 | import androidx.annotation.Nullable;
7 | import androidx.appcompat.app.AppCompatActivity;
8 |
9 | import com.chess.clock.ClockApplication;
10 | import com.chess.clock.R;
11 | import com.chess.clock.entities.AppTheme;
12 | import com.chess.clock.manager.ChessClockManager;
13 | import com.chess.clock.statics.AppData;
14 |
15 | public class BaseActivity extends AppCompatActivity {
16 | /**
17 | * Shared preferences wrapper
18 | */
19 | protected AppData appData;
20 |
21 | public AppTheme selectedTheme;
22 |
23 | @Override
24 | protected void onCreate(@Nullable Bundle savedInstanceState) {
25 | getTheme().applyStyle(R.style.OptOutEdgeToEdgeEnforcement, false);
26 | super.onCreate(savedInstanceState);
27 | appData = new AppData(getApplicationContext());
28 | selectedTheme = appData.getSelectedTheme();
29 | }
30 |
31 | @Override
32 | protected void onResume() {
33 | super.onResume();
34 | this.selectedTheme = appData.getSelectedTheme();
35 | }
36 |
37 | public ChessClockManager getClockManager() {
38 | return ClockApplication.getClockManager();
39 | }
40 |
41 | public void hideStatusBar() {
42 | WindowManager.LayoutParams attrs = getWindow().getAttributes();
43 | attrs.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN;
44 | getWindow().setAttributes(attrs);
45 | }
46 |
47 | public void showStatusBar() {
48 | WindowManager.LayoutParams attrs = getWindow().getAttributes();
49 | attrs.flags &= ~WindowManager.LayoutParams.FLAG_FULLSCREEN;
50 | getWindow().setAttributes(attrs);
51 | }
52 |
53 | public void finishWithAnimation() {
54 | finish();
55 | overridePendingTransition(R.anim.left_to_right_in, R.anim.left_to_right_full);
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chess/clock/activities/ClockSoundManager.java:
--------------------------------------------------------------------------------
1 | package com.chess.clock.activities;
2 |
3 | import android.content.Context;
4 | import android.media.AudioManager;
5 | import android.media.SoundPool;
6 |
7 | import com.chess.clock.R;
8 |
9 | import java.util.HashSet;
10 |
11 | enum ClockSound {
12 | PLAYER_ONE_MOVE, PLAYER_TWO_MOVE, GAME_FINISHED, RESET_CLOCK, MENU_ACTION
13 | }
14 |
15 | interface ClockSoundManager {
16 | void init(Context context);
17 |
18 | void releaseSounds();
19 |
20 | void setSoundsEnabled(boolean enabled);
21 |
22 | boolean areSoundsEnabled();
23 |
24 | void playSound(ClockSound sound, AudioManager manager);
25 |
26 | void toggleSound();
27 | }
28 |
29 | class ClockSoundManagerImpl implements ClockSoundManager {
30 | private static final int NO_LOOP = 0;
31 | private static final int NO_SOUND_ID = 0;
32 | private static final float PLAYBACK_RATE = 1f;
33 | private static final int SOUND_PRIORITY = 1;
34 |
35 | public boolean soundsEnabled = true;
36 |
37 | private SoundPool soundPool;
38 | private final HashSet preparedSoundsIds = new HashSet();
39 |
40 | private int menuActionId = NO_SOUND_ID;
41 | private int playerOneMoveId = NO_SOUND_ID;
42 | private int playerTwoMoveId = NO_SOUND_ID;
43 | private int gameFinishedId = NO_SOUND_ID;
44 | private int clockResetId = NO_SOUND_ID;
45 |
46 | @Override
47 | public void init(Context context) {
48 | soundPool = new SoundPool(5, AudioManager.STREAM_MUSIC, 0);
49 | soundPool.setOnLoadCompleteListener((soundPool, sampleId, status) -> preparedSoundsIds.add(sampleId));
50 | menuActionId = soundPool.load(context, R.raw.chess_clock_pause, 1);
51 | playerOneMoveId = soundPool.load(context, R.raw.chess_clock_switch1, 1);
52 | playerTwoMoveId = soundPool.load(context, R.raw.chess_clock_switch2, 1);
53 | gameFinishedId = soundPool.load(context, R.raw.chess_clock_time_ended, 1);
54 | clockResetId = soundPool.load(context, R.raw.chess_clock_reset, 1);
55 | }
56 |
57 | @Override
58 | public void releaseSounds() {
59 | soundPool.release();
60 | }
61 |
62 | @Override
63 | public void setSoundsEnabled(boolean soundsEnabled) {
64 | this.soundsEnabled = soundsEnabled;
65 | }
66 |
67 | @Override
68 | public boolean areSoundsEnabled() {
69 | return soundsEnabled;
70 | }
71 |
72 | @Override
73 | public void playSound(ClockSound sound, AudioManager manager) {
74 | if (!soundsEnabled) return;
75 | int soundId = clockSoundId(sound);
76 |
77 | if (soundId == NO_SOUND_ID) return; // sound failed to load
78 | if (!preparedSoundsIds.contains(soundId)) return; // sound not prepared
79 |
80 | float actualVolume = (float) manager.getStreamVolume(AudioManager.STREAM_MUSIC);
81 | float maxVolume = (float) manager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
82 | float soundVolume = actualVolume / maxVolume;
83 |
84 | soundPool.play(soundId, soundVolume, soundVolume, SOUND_PRIORITY, NO_LOOP, PLAYBACK_RATE);
85 | }
86 |
87 | @Override
88 | public void toggleSound() {
89 | soundsEnabled = !soundsEnabled;
90 | }
91 |
92 | private Integer clockSoundId(ClockSound sound) {
93 | int soundId = NO_SOUND_ID;
94 | switch (sound) {
95 | case PLAYER_ONE_MOVE:
96 | soundId = playerOneMoveId;
97 | break;
98 | case PLAYER_TWO_MOVE:
99 | soundId = playerTwoMoveId;
100 | break;
101 | case GAME_FINISHED:
102 | soundId = gameFinishedId;
103 | break;
104 | case RESET_CLOCK:
105 | soundId = clockResetId;
106 | break;
107 | case MENU_ACTION:
108 | soundId = menuActionId;
109 | break;
110 | }
111 | return soundId;
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chess/clock/adapters/ThemesAdapter.java:
--------------------------------------------------------------------------------
1 | package com.chess.clock.adapters;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.ImageView;
8 |
9 | import androidx.annotation.NonNull;
10 | import androidx.cardview.widget.CardView;
11 | import androidx.core.content.ContextCompat;
12 | import androidx.recyclerview.widget.RecyclerView;
13 |
14 | import com.chess.clock.R;
15 | import com.chess.clock.entities.AppTheme;
16 | import com.chess.clock.views.ViewUtils;
17 |
18 | public class ThemesAdapter extends RecyclerView.Adapter {
19 | public AppTheme selectedTheme;
20 | private final ThemeSelectListener listener;
21 |
22 | public ThemesAdapter(AppTheme initialTheme, ThemeSelectListener listener) {
23 | setHasStableIds(true);
24 | this.selectedTheme = initialTheme;
25 | this.listener = listener;
26 | }
27 |
28 | @NonNull
29 | @Override
30 | public ThemeViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
31 | View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_theme, parent, false);
32 | return new ThemeViewHolder(view);
33 | }
34 |
35 | @SuppressLint("NotifyDataSetChanged")
36 | @Override
37 | public void onBindViewHolder(@NonNull ThemeViewHolder holder, int position) {
38 | AppTheme theme = AppTheme.fromInt(position);
39 | holder.bind(theme, theme == selectedTheme);
40 | holder.themeCard.setOnClickListener(v -> {
41 | selectedTheme = theme;
42 | listener.onSelectTheme(selectedTheme);
43 | notifyDataSetChanged();
44 | });
45 | }
46 |
47 | @Override
48 | public int getItemCount() {
49 | return AppTheme.values().length;
50 | }
51 |
52 | public static class ThemeViewHolder extends RecyclerView.ViewHolder {
53 | private final ImageView checkmarkImg;
54 | private final CardView themeCard;
55 |
56 | public ThemeViewHolder(@NonNull View itemView) {
57 | super(itemView);
58 | checkmarkImg = itemView.findViewById(R.id.checkmarkImg);
59 | themeCard = itemView.findViewById(R.id.themeCard);
60 | }
61 |
62 | public void bind(AppTheme appTheme, boolean selected) {
63 | int color = ContextCompat.getColor(itemView.getContext(), appTheme.primaryColorRes);
64 | themeCard.setCardBackgroundColor(color);
65 | ViewUtils.showView(checkmarkImg, selected);
66 | }
67 | }
68 |
69 | public interface ThemeSelectListener {
70 | void onSelectTheme(AppTheme theme);
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chess/clock/adapters/TimeRowMoveCallback.java:
--------------------------------------------------------------------------------
1 | package com.chess.clock.adapters;
2 |
3 | import androidx.annotation.NonNull;
4 | import androidx.recyclerview.widget.ItemTouchHelper;
5 | import androidx.recyclerview.widget.RecyclerView;
6 |
7 | public class TimeRowMoveCallback extends ItemTouchHelper.Callback {
8 |
9 | TimeItemTouchCallback touchCallback;
10 |
11 | public TimeRowMoveCallback(TimeItemTouchCallback callback) {
12 | this.touchCallback = callback;
13 | }
14 |
15 | @Override
16 | public boolean isLongPressDragEnabled() {
17 | return true;
18 | }
19 |
20 | @Override
21 | public boolean isItemViewSwipeEnabled() {
22 | return false;
23 | }
24 |
25 | @Override
26 | public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
27 | int drag = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
28 | return makeMovementFlags(drag, 0);
29 | }
30 |
31 | @Override
32 | public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
33 | this.touchCallback.onTimeItemMoved(viewHolder.getAbsoluteAdapterPosition(), target.getAbsoluteAdapterPosition());
34 | return true;
35 | }
36 |
37 | @Override
38 | public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
39 |
40 | }
41 |
42 | public interface TimeItemTouchCallback {
43 | void onTimeItemMoved(int from, int to);
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chess/clock/dialog/AdjustTimeDialogFragment.java:
--------------------------------------------------------------------------------
1 | package com.chess.clock.dialog;
2 |
3 | import android.content.res.ColorStateList;
4 | import android.content.res.Configuration;
5 | import android.os.Build;
6 | import android.os.Bundle;
7 | import android.view.View;
8 | import android.widget.EditText;
9 |
10 | import androidx.annotation.NonNull;
11 | import androidx.annotation.Nullable;
12 |
13 | import com.chess.clock.R;
14 | import com.chess.clock.activities.BaseActivity;
15 | import com.chess.clock.entities.AppTheme;
16 | import com.chess.clock.entities.ClockTime;
17 | import com.chess.clock.util.ClockUtils;
18 |
19 | public class AdjustTimeDialogFragment extends FullScreenDialogFragment {
20 |
21 | public static final String TAG = "AdjustTimeDialogFragment";
22 | private static final String ARG_TIME_KEY = "arg_time_key";
23 | private static final String ARG_FIRST_PLAYER_KEY = "arg_first_player_key";
24 |
25 | @Override
26 | int layoutRes() {
27 | return R.layout.dialog_fragment_adjust_time;
28 | }
29 |
30 | @Override
31 | public int bgColorRes() {
32 | return R.color.black_40;
33 | }
34 |
35 | public static AdjustTimeDialogFragment newInstance(Long time, boolean firstPlayer) {
36 |
37 | Bundle args = new Bundle();
38 | args.putBoolean(ARG_FIRST_PLAYER_KEY, firstPlayer);
39 | args.putLong(ARG_TIME_KEY, time);
40 |
41 | AdjustTimeDialogFragment fragment = new AdjustTimeDialogFragment();
42 | fragment.setArguments(args);
43 | return fragment;
44 | }
45 |
46 | @Override
47 | public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
48 | super.onViewCreated(view, savedInstanceState);
49 | boolean firstPlayer = requireArguments().getBoolean(ARG_FIRST_PLAYER_KEY);
50 | if (!firstPlayer && getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
51 | view.setRotation(180);
52 | }
53 | EditText hoursEt = view.findViewById(R.id.hoursEt);
54 | EditText minutesEt = view.findViewById(R.id.minutesEt);
55 | EditText secondsEt = view.findViewById(R.id.secondsEt);
56 |
57 | view.findViewById(R.id.saveBtn).setOnClickListener(v -> {
58 | int hours = ClockUtils.getIntOrZero(hoursEt);
59 | int minutes = ClockUtils.getIntOrZero(minutesEt);
60 | int seconds = ClockUtils.getIntOrZero(secondsEt);
61 | ((TimeAdjustmentsListener) requireActivity()).onTimeAdjustmentsConfirmed(
62 | ClockUtils.durationMillis(hours, minutes, seconds),
63 | firstPlayer
64 | );
65 | dismissAllowingStateLoss();
66 | });
67 | view.findViewById(R.id.cancelBtn).setOnClickListener(v -> dismissAllowingStateLoss());
68 |
69 | if (savedInstanceState == null) {
70 | ClockTime clockTime = ClockTime.calibrated(requireArguments().getLong(ARG_TIME_KEY));
71 | hoursEt.setText(ClockUtils.twoDecimalPlacesFormat(Math.min(clockTime.hours, 99)));
72 | minutesEt.setText(ClockUtils.twoDecimalPlacesFormat(clockTime.minutes));
73 | secondsEt.setText(ClockUtils.twoDecimalPlacesFormat(clockTime.seconds));
74 | }
75 |
76 | ClockUtils.setClockTextWatcher(minutesEt);
77 | ClockUtils.setClockTextWatcher(secondsEt);
78 |
79 | AppTheme theme = ((BaseActivity) requireActivity()).selectedTheme;
80 | if (theme != null) {
81 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
82 | ColorStateList tintList = theme.colorStateListFocused(requireContext());
83 | hoursEt.setBackgroundTintList(tintList);
84 | minutesEt.setBackgroundTintList(tintList);
85 | secondsEt.setBackgroundTintList(tintList);
86 | }
87 | }
88 | }
89 |
90 | public interface TimeAdjustmentsListener {
91 | void onTimeAdjustmentsConfirmed(long timeMs, boolean firstPlayer);
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chess/clock/dialog/FullScreenDialogFragment.java:
--------------------------------------------------------------------------------
1 | package com.chess.clock.dialog;
2 |
3 | import android.app.Dialog;
4 | import android.graphics.drawable.ColorDrawable;
5 | import android.os.Bundle;
6 | import android.view.LayoutInflater;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 | import android.widget.FrameLayout;
10 |
11 | import androidx.annotation.NonNull;
12 | import androidx.annotation.Nullable;
13 | import androidx.core.content.ContextCompat;
14 | import androidx.fragment.app.DialogFragment;
15 |
16 | import com.chess.clock.R;
17 |
18 | abstract class FullScreenDialogFragment extends DialogFragment {
19 | abstract int layoutRes();
20 |
21 | public int bgColorRes() {
22 | return R.color.gray_background;
23 | }
24 |
25 | public int theme() {
26 | return R.style.AppTheme_DialogFullScreen;
27 | }
28 |
29 | @Nullable
30 | @Override
31 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
32 | return inflater.inflate(layoutRes(), container, false);
33 | }
34 |
35 | @NonNull
36 | @Override
37 | public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
38 | Dialog dialog = new Dialog(requireActivity(), theme());
39 | FrameLayout content = new FrameLayout(requireActivity());
40 | content.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
41 | dialog.setContentView(content);
42 | dialog.getWindow().setBackgroundDrawable(
43 | new ColorDrawable(ContextCompat.getColor(requireContext(), bgColorRes()))
44 | );
45 | dialog.getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
46 | return dialog;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chess/clock/engine/ClockPlayer.java:
--------------------------------------------------------------------------------
1 | package com.chess.clock.engine;
2 |
3 | public enum ClockPlayer {
4 | ONE, TWO;
5 |
6 | public static ClockPlayer ofBoolean(boolean playerOne) {
7 | return playerOne ? ONE : TWO;
8 | }
9 |
10 | public boolean isFirstPlayer() {
11 | return this == ONE;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chess/clock/engine/TimeControl.java:
--------------------------------------------------------------------------------
1 | package com.chess.clock.engine;
2 |
3 | import android.os.Parcel;
4 | import android.os.Parcelable;
5 |
6 | import androidx.annotation.NonNull;
7 |
8 | import com.chess.clock.util.Args;
9 |
10 | /**
11 | *
12 | * This class represents the time control of a single player in a chess game. At the most basic
13 | * level, time controls can simply be expressed as the time each player has in order to make all of
14 | * his moves during a game. Commonly, these time controls are called “Game in X”. For instance, you
15 | * might have a time control named G/60 – shorthand for game in 60 minutes.
16 | *
17 | *
18 | * Multiple Time Control Stages
19 | *
20 | *
21 | * Time controls may be broken up into multiple stages. This ensures that while a game might
22 | * be long, players are forced to reach a certain point of the game earlier. One multiple time
23 | * control format seen frequently in major tournaments is 40/120, G/60. This time control requires
24 | * players to make at least 40 moves in their first two hours of playing time, then gives each
25 | * player another hour with which to finish the remainder of the game.
26 | *
27 | *
28 | * Increment and Time Delay
29 | *
30 | *
31 | * With an increment, players have time added to their clock after every move that’s completed,
32 | * thus ensuring that they’ll always have at least the increment time to make a move. Time delay
33 | * works slightly differently. Instead of adding time to the clock, a time delay creates a period
34 | * after your opponent moves during which your clock will not run.
35 | *
36 | */
37 | public class TimeControl implements Parcelable, Cloneable, StageManager.StageManagerListener {
38 |
39 | public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
40 | public TimeControl createFromParcel(Parcel source) {
41 | return new TimeControl(source);
42 | }
43 |
44 | public TimeControl[] newArray(int size) {
45 | return new TimeControl[size];
46 | }
47 | };
48 |
49 | /**
50 | * TimeControl identifier.
51 | */
52 | private String mName;
53 |
54 | /**
55 | * Stage Manager associated with Time Control.
56 | */
57 | private StageManager mStageManager;
58 |
59 | /**
60 | * Listener used to dispatch time control update events.
61 | *
62 | * @see TimeControlListener
63 | */
64 | private TimeControlListener mTimeControlListener;
65 |
66 | /**
67 | * Simple constructor to use when creating a TimeControl.
68 | *
69 | * @param name Name identifier.
70 | * @param stages stages of the TimeControl.
71 | * @throws java.lang.NullPointerException if StageManager or TimeIncrement are not provided.
72 | */
73 | public TimeControl(String name, Stage[] stages) {
74 | Args.checkForNull(stages);
75 |
76 | mName = name;
77 | mStageManager = new StageManager(stages);
78 |
79 | // Set up listener for Stage Manager.
80 | mStageManager.setStageManagerListener(this);
81 | }
82 |
83 | private TimeControl(Parcel parcel) {
84 | this.readFromParcel(parcel);
85 | }
86 |
87 | /**
88 | * Check if TimeControl object is equal to this one.
89 | *
90 | * @param tc TimeControl Object.
91 | * @return True if relevant contents are equal.
92 | */
93 | public boolean isEqual(TimeControl tc) {
94 |
95 | boolean equalNames = (mName == null && tc.getName() == null) ||
96 | mName.equals(tc.getName());
97 |
98 | return (equalNames && mStageManager.isEqual(tc.getStageManager()));
99 | }
100 |
101 | /**
102 | * Register a callback to be invoked when time control updates.
103 | *
104 | * @param listener The callback that will run
105 | */
106 | public void setTimeControlListener(TimeControlListener listener) {
107 | Args.checkForNull(listener);
108 | mTimeControlListener = listener;
109 | }
110 |
111 | /**
112 | * The name identifier of the time control.
113 | *
114 | * @return Time control name.
115 | */
116 | public String getName() {
117 | return mName;
118 | }
119 |
120 | /**
121 | * Set the name identifier of the time control.
122 | *
123 | * @param name Time control name.
124 | */
125 | public void setName(String name) {
126 | mName = name;
127 | }
128 |
129 | /**
130 | * Gets the Stage manager associated with this time control.
131 | *
132 | * @return Stage manager associated with this time control.
133 | */
134 | public StageManager getStageManager() {
135 | return mStageManager;
136 | }
137 |
138 | private void readFromParcel(Parcel parcel) {
139 | mName = parcel.readString();
140 | mStageManager = parcel.readParcelable(StageManager.class.getClassLoader());
141 | mStageManager.setStageManagerListener(this);
142 | }
143 |
144 | @Override
145 | public void writeToParcel(Parcel parcel, int flags) {
146 | parcel.writeString(mName);
147 | parcel.writeParcelable(mStageManager, flags);
148 | }
149 |
150 | @Override
151 | public int describeContents() {
152 | return 0;
153 | }
154 |
155 | /**
156 | * /**
157 | * {@inheritDoc}
158 | */
159 | @Override
160 | public void onNewStageUpdate(Stage stage) {
161 | Args.checkForNull(stage);
162 |
163 | if (mTimeControlListener != null) {
164 | mTimeControlListener.onStageUpdate(stage);
165 | }
166 | }
167 |
168 | /**
169 | * /**
170 | * {@inheritDoc}
171 | */
172 | @Override
173 | public void onMoveCountUpdate(int moveCount) {
174 | if (mTimeControlListener != null) {
175 | mTimeControlListener.onMoveCountUpdate(moveCount);
176 | }
177 | }
178 |
179 | @NonNull
180 | @Override
181 | public Object clone() throws CloneNotSupportedException {
182 | TimeControl clone = (TimeControl) super.clone();
183 |
184 | // Clone StageManager object and set this clone as his listener.
185 | clone.mStageManager = (StageManager) mStageManager.clone();
186 | clone.mStageManager.setStageManagerListener(clone);
187 |
188 | clone.mTimeControlListener = null;
189 | return clone;
190 | }
191 |
192 | /**
193 | * Interface definition for a callback to be invoked when the player clock gets updated.
194 | */
195 | public interface TimeControlListener {
196 |
197 | /**
198 | * Called when new game stage begins.
199 | *
200 | * @param stage The current game stage.
201 | */
202 | void onStageUpdate(Stage stage);
203 |
204 | /**
205 | * Called when the move count is updated.
206 | */
207 | void onMoveCountUpdate(int moves);
208 | }
209 | }
210 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chess/clock/engine/TimeControlDefaults.java:
--------------------------------------------------------------------------------
1 | package com.chess.clock.engine;
2 |
3 | import android.content.Context;
4 |
5 | import com.chess.clock.R;
6 |
7 | import java.util.ArrayList;
8 | import java.util.Arrays;
9 | import java.util.List;
10 |
11 | public class TimeControlDefaults {
12 |
13 | private static final List PRESETS = Arrays.asList(
14 | new TimePreset(1),
15 | new TimePreset(1, 1),
16 | new TimePreset(2, 1),
17 | new TimePreset(3),
18 | new TimePreset(3, 2),
19 | new TimePreset(5),
20 | new TimePreset(5, 5),
21 | new TimePreset(10),
22 | new TimePreset(15, 10),
23 | new TimePreset(20),
24 | new TimePreset(30)
25 | );
26 |
27 | /**
28 | * index of `10 min` preset
29 | */
30 | public static long DEFAULT_TIME_ID = 7L;
31 |
32 | /**
33 | * Creates default TimeControl list, saves it on shared preferences.
34 | *
35 | * @return Default TimeControl list.
36 | */
37 | public static ArrayList buildDefaultTimeControlsList(Context context) {
38 |
39 | ArrayList timeControls = new ArrayList<>();
40 | for (int i = 0, presetsSize = PRESETS.size(); i < presetsSize; i++) {
41 | TimePreset timePreset = PRESETS.get(i);
42 | timeControls.add(timePreset.toTimeControl(context, i));
43 | }
44 |
45 | // Saving default time controls
46 | TimeControlParser.saveTimeControls(context, timeControls);
47 | TimeControlParser.saveSelectedTimeControlId(context, TimeControlDefaults.DEFAULT_TIME_ID);
48 |
49 | return timeControls;
50 | }
51 |
52 | private static class TimePreset {
53 | int minutes;
54 | int incrementSeconds;
55 |
56 | TimePreset(int minutes) {
57 | this.minutes = minutes;
58 | incrementSeconds = 0;
59 | }
60 |
61 | TimePreset(int minutes, int incrementSeconds) {
62 | this.minutes = minutes;
63 | this.incrementSeconds = incrementSeconds;
64 | }
65 |
66 | String getName(Context context) {
67 | if (incrementSeconds == 0) {
68 | return context.getString(R.string.x_min, minutes);
69 | } else {
70 | return context.getString(R.string.x_min_y_sec, minutes, incrementSeconds);
71 | }
72 | }
73 |
74 | TimeControlWrapper toTimeControl(Context context, int defaultIndex) {
75 | TimeIncrement timeIncrement = new TimeIncrement(TimeIncrement.Type.FISCHER, incrementSeconds * 1000L);
76 | Stage stage = new Stage(0, minutes * 60 * 1000L, timeIncrement);
77 | TimeControl timeControlOne = new TimeControl(getName(context), new Stage[]{stage});
78 | TimeControl timeControlTwo = new TimeControl(getName(context), new Stage[]{stage});
79 | return new TimeControlWrapper(defaultIndex, defaultIndex, timeControlOne, timeControlTwo);
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chess/clock/engine/TimeControlWrapper.java:
--------------------------------------------------------------------------------
1 | package com.chess.clock.engine;
2 |
3 | import android.os.Parcel;
4 | import android.os.Parcelable;
5 |
6 | import androidx.annotation.NonNull;
7 |
8 | public class TimeControlWrapper implements Parcelable, Cloneable {
9 | private TimeControl mTimeControlPlayerOne;
10 | private TimeControl mTimeControlPlayerTwo;
11 |
12 | private boolean mSameAsPlayerOne;
13 | private long id;
14 | private int order;
15 |
16 | public TimeControlWrapper(
17 | long id,
18 | int order,
19 | TimeControl playerOne,
20 | TimeControl playerTwo
21 | ) {
22 | this.id = id;
23 | this.order = order;
24 | mTimeControlPlayerOne = playerOne;
25 | mTimeControlPlayerTwo = playerTwo;
26 | mSameAsPlayerOne = true;
27 | }
28 |
29 | private TimeControlWrapper(Parcel in) {
30 | id = in.readLong();
31 | order = in.readInt();
32 | mTimeControlPlayerOne = in.readParcelable(TimeControl.class.getClassLoader());
33 | mTimeControlPlayerTwo = in.readParcelable(TimeControl.class.getClassLoader());
34 | mSameAsPlayerOne = in.readByte() != 0;
35 | }
36 |
37 | @Override
38 | public void writeToParcel(Parcel dest, int flags) {
39 | dest.writeLong(id);
40 | dest.writeInt(order);
41 | dest.writeParcelable(mTimeControlPlayerOne, flags);
42 | dest.writeParcelable(mTimeControlPlayerTwo, flags);
43 | dest.writeByte((byte) (mSameAsPlayerOne ? 1 : 0));
44 | }
45 |
46 |
47 | public static final Creator CREATOR = new Creator() {
48 | @Override
49 | public TimeControlWrapper createFromParcel(Parcel in) {
50 | return new TimeControlWrapper(in);
51 | }
52 |
53 | @Override
54 | public TimeControlWrapper[] newArray(int size) {
55 | return new TimeControlWrapper[size];
56 | }
57 | };
58 |
59 | public TimeControl getTimeControlPlayerOne() {
60 | return mTimeControlPlayerOne;
61 | }
62 |
63 | public void setTimeControlPlayerOne(TimeControl timeControl) {
64 | mTimeControlPlayerOne = timeControl;
65 | }
66 |
67 | public TimeControl getTimeControlPlayerTwo() {
68 | return mTimeControlPlayerTwo;
69 | }
70 |
71 | public void setTimeControlPlayerTwo(TimeControl timeControl) {
72 | mTimeControlPlayerTwo = timeControl;
73 | }
74 |
75 | public boolean isSameAsPlayerOne() {
76 | return mSameAsPlayerOne;
77 | }
78 |
79 | public void setSameAsPlayerOne(boolean sameAsPlayerOne) {
80 | mSameAsPlayerOne = sameAsPlayerOne;
81 | }
82 |
83 | @NonNull
84 | @Override
85 | public Object clone() throws CloneNotSupportedException {
86 | TimeControlWrapper clone = (TimeControlWrapper) super.clone();
87 |
88 | // Clone StageManager object and set this clone as his listener.
89 | clone.mTimeControlPlayerOne = (TimeControl) mTimeControlPlayerOne.clone();
90 | clone.mTimeControlPlayerTwo = (TimeControl) mTimeControlPlayerTwo.clone();
91 | clone.mSameAsPlayerOne = mSameAsPlayerOne;
92 | clone.order = order;
93 | clone.id = id;
94 | return clone;
95 | }
96 |
97 | @Override
98 | public int describeContents() {
99 | return 0;
100 | }
101 |
102 | public boolean isEqual(TimeControlWrapper wrapper) {
103 | return mTimeControlPlayerOne.isEqual(wrapper.getTimeControlPlayerOne()) &&
104 | mTimeControlPlayerTwo.isEqual(wrapper.getTimeControlPlayerTwo()) &&
105 | mSameAsPlayerOne == wrapper.isSameAsPlayerOne() &&
106 | id == wrapper.id &&
107 | order == wrapper.order;
108 | }
109 |
110 | public boolean bothUsersHaveAtLeastOneStage() {
111 | if (mSameAsPlayerOne) {
112 | return mTimeControlPlayerOne.getStageManager().getTotalStages() > 0;
113 | } else {
114 | return mTimeControlPlayerOne.getStageManager().getTotalStages() > 0 && mTimeControlPlayerTwo.getStageManager().getTotalStages() > 0;
115 | }
116 | }
117 |
118 | public long getId() {
119 | return id;
120 | }
121 |
122 | public int getOrder() {
123 | return order;
124 | }
125 |
126 | public void setOrder(int newOrder) {
127 | this.order = newOrder;
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chess/clock/engine/TimeIncrement.java:
--------------------------------------------------------------------------------
1 | package com.chess.clock.engine;
2 |
3 | import android.os.Parcel;
4 | import android.os.Parcelable;
5 | import android.util.Log;
6 |
7 | import androidx.annotation.NonNull;
8 |
9 | import com.chess.clock.entities.ClockTime;
10 |
11 | /**
12 | * A small amount of time that is added for each game addMove.
13 | */
14 | public class TimeIncrement implements Parcelable, Cloneable {
15 |
16 | public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
17 | public TimeIncrement createFromParcel(Parcel source) {
18 | return new TimeIncrement(source);
19 | }
20 |
21 | public TimeIncrement[] newArray(int size) {
22 | return new TimeIncrement[size];
23 | }
24 | };
25 | private static final String TAG = TimeIncrement.class.getName();
26 | /**
27 | * TimeIncrement type used
28 | */
29 | private Type mType;
30 |
31 | /**
32 | * Time Increment amount in milliseconds
33 | */
34 | private long mValue;
35 |
36 | /**
37 | * @param mType Time control TimeIncrement StageType.
38 | * @param value TimeIncrement value in milliseconds
39 | * @see TimeIncrement.Type
40 | */
41 | public TimeIncrement(Type mType, long value) {
42 | this.mType = mType;
43 | this.mValue = validateIncrementValue(value);
44 | }
45 |
46 | private TimeIncrement(Parcel parcel) {
47 | this.readFromParcel(parcel);
48 | }
49 |
50 | public static TimeIncrement defaultIncrement() {
51 | return new TimeIncrement(TimeIncrement.Type.FISCHER, 0);
52 | }
53 |
54 | /**
55 | * @return The increment type.
56 | */
57 | public Type getType() {
58 | return mType;
59 | }
60 |
61 | /**
62 | * Set the type of the time increment.
63 | *
64 | * @param type Type
65 | */
66 | public void setType(Type type) {
67 | mType = type;
68 | }
69 |
70 | /**
71 | * @return Increment time value in milliseconds.
72 | */
73 | public long getValue() {
74 | return mValue;
75 | }
76 |
77 | /**
78 | * @return Int array with {hour,minute,second}
79 | */
80 | public ClockTime getDuration() {
81 | return ClockTime.raw(mValue);
82 | }
83 |
84 | /**
85 | * Check if TimeIncrement object is equal to this one.
86 | *
87 | * @param ti TimeIncrement Object.
88 | * @return True if relevant contents are equal.
89 | */
90 | public boolean isEqual(TimeIncrement ti) {
91 |
92 | if (mValue != ti.getValue()) {
93 | Log.i(TAG, "Value not equal.");
94 | return false;
95 | } else if (mType.getValue() != ti.getType().getValue()) {
96 | Log.i(TAG, "Type not equal.");
97 | return false;
98 | } else {
99 | return true;
100 | }
101 | }
102 |
103 | /**
104 | * Get formatted string ready to UI info display.
105 | *
106 | * @return String representing info content of TimeIncrement.
107 | */
108 | @NonNull
109 | public String toString() {
110 | if (mType == Type.NONE) {
111 | return mType.typeName;
112 | } else {
113 | return mType.typeName + ", " + ClockTime.raw(mValue).toMinutesFormat();
114 | }
115 | }
116 |
117 | /**
118 | * Avoid non valid increment values.
119 | *
120 | * @param value TimeIncrement value in milliseconds
121 | * @return TimeIncrement value or zero if negative.
122 | */
123 | private long validateIncrementValue(long value) {
124 | return Math.max(0, value);
125 | }
126 |
127 | private void readFromParcel(Parcel parcel) {
128 | mType = Type.fromInteger(parcel.readInt());
129 | mValue = parcel.readLong();
130 | }
131 |
132 | @Override
133 | public void writeToParcel(Parcel parcel, int flags) {
134 | parcel.writeInt(mType.getValue());
135 | parcel.writeLong(mValue);
136 | }
137 |
138 | @Override
139 | public int describeContents() {
140 | return 0;
141 | }
142 |
143 | @NonNull
144 | @Override
145 | public Object clone() throws CloneNotSupportedException {
146 | TimeIncrement cloned = (TimeIncrement) super.clone();
147 | cloned.mType = Type.fromInteger(mType.getValue());
148 | return cloned;
149 | }
150 |
151 | /**
152 | * Time Control TimeIncrement StageType.
153 | */
154 | public enum Type {
155 |
156 | /**
157 | * The player's clock starts after the delay period.
158 | */
159 | DELAY(0, "Delay"),
160 |
161 | /**
162 | * Players receive the used portion of the increment at the end of each turn.
163 | */
164 | BRONSTEIN(1, "Bronstein"),
165 |
166 | /**
167 | * Players receive a full increment at the end of each turn.
168 | */
169 | FISCHER(2, "Fischer"),
170 |
171 | /**
172 | * No increment.
173 | */
174 | NONE(3, "None");
175 |
176 | private final int value;
177 | private final String typeName;
178 |
179 | Type(int value, String typeName) {
180 | this.value = value;
181 | this.typeName = typeName;
182 | }
183 |
184 | public static Type fromInteger(int type) {
185 | switch (type) {
186 | case 0:
187 | return DELAY;
188 | case 1:
189 | return BRONSTEIN;
190 | case 2:
191 | return FISCHER;
192 | case 3:
193 | return NONE;
194 | }
195 | return null;
196 | }
197 |
198 | public int getValue() {
199 | return value;
200 | }
201 | }
202 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/chess/clock/entities/AppTheme.java:
--------------------------------------------------------------------------------
1 | package com.chess.clock.entities;
2 |
3 | import android.content.Context;
4 | import android.content.res.ColorStateList;
5 |
6 | import androidx.annotation.ColorRes;
7 | import androidx.core.content.ContextCompat;
8 |
9 | import com.chess.clock.R;
10 |
11 | public enum AppTheme {
12 | GREEN(R.color.green),
13 | BLUE(R.color.blue),
14 | ORANGE(R.color.orange),
15 | TURQUOISE(R.color.turquoise),
16 | GOLD(R.color.gold),
17 | PINK(R.color.pink);
18 |
19 | public final int primaryColorRes;
20 |
21 | AppTheme(@ColorRes int colorRes) {
22 | this.primaryColorRes = colorRes;
23 | }
24 |
25 | public static AppTheme fromInt(int position) {
26 | for (AppTheme theme : AppTheme.values()) {
27 | if (theme.ordinal() == position) {
28 | return theme;
29 | }
30 | }
31 | throw new AssertionError("no app theme for position: " + position);
32 | }
33 |
34 | public ColorStateList switchColorStateList(Context context) {
35 | return checkedStateList(context, R.color.white);
36 | }
37 |
38 | public ColorStateList radioButtonStateList(Context context) {
39 | return checkedStateList(context, R.color.white_20);
40 | }
41 |
42 | private ColorStateList checkedStateList(Context context, @ColorRes int defaultColorRes) {
43 | int[][] states = new int[][]{
44 | new int[]{android.R.attr.state_checked},
45 | new int[]{-android.R.attr.state_checked}
46 | };
47 |
48 | int[] colors = new int[]{
49 | color(context),
50 | ContextCompat.getColor(context, defaultColorRes)
51 | };
52 |
53 | return new ColorStateList(states, colors);
54 | }
55 |
56 | public ColorStateList colorStateListFocused(Context context) {
57 | int[][] states = new int[][]{
58 | new int[]{android.R.attr.state_enabled, android.R.attr.state_focused},
59 | new int[]{}
60 | };
61 |
62 | int[] colors = new int[]{
63 | color(context),
64 | ContextCompat.getColor(context, R.color.gray_controls)
65 | };
66 |
67 | return new ColorStateList(states, colors);
68 | }
69 |
70 | public ColorStateList primaryColorAsStateList(Context context) {
71 | return ColorStateList.valueOf(color(context));
72 | }
73 |
74 | public int color(Context context) {
75 | return ContextCompat.getColor(context, primaryColorRes);
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chess/clock/entities/ClockTime.java:
--------------------------------------------------------------------------------
1 | package com.chess.clock.entities;
2 |
3 | import android.annotation.SuppressLint;
4 |
5 | public final class ClockTime {
6 | public static int HOUR_MILLIS = 3600000;
7 | public static String CLOCK_FORMAT_HOURS = "%d:%02d:%02d";
8 | public static String CLOCK_FORMAT_MINUTES = "%d:%02d";
9 | public final int hours;
10 | public final int minutes;
11 | public final int seconds;
12 | public final long remainingTimeMs;
13 |
14 | private ClockTime(long timeMs) {
15 | remainingTimeMs = timeMs;
16 | seconds = (int) (timeMs / 1000) % 60;
17 | minutes = (int) ((timeMs / (1000 * 60)) % 60);
18 | hours = (int) ((timeMs / (1000 * 60 * 60)));
19 | }
20 |
21 | public static boolean atLeastHourLeft(long timeMs) {
22 | return timeMs >= HOUR_MILLIS;
23 | }
24 |
25 | public static String calibratedReadableFormat(long timeMs) {
26 | long calibratedTime = calibrateTime(timeMs);
27 | if (atLeastHourLeft(calibratedTime)) {
28 | return String.format(
29 | CLOCK_FORMAT_HOURS,
30 | (int) ((calibratedTime / (1000 * 60 * 60))),
31 | (int) ((calibratedTime / (1000 * 60)) % 60),
32 | (int) (calibratedTime / 1000) % 60
33 | );
34 | } else {
35 | return String.format(
36 | CLOCK_FORMAT_MINUTES,
37 | (int) ((calibratedTime / (1000 * 60)) % 60),
38 | (int) (calibratedTime / 1000) % 60
39 | );
40 | }
41 | }
42 |
43 | public static ClockTime calibrated(long timeMs) {
44 | // calibration
45 | long remainingTime = calibrateTime(timeMs);
46 | return new ClockTime(remainingTime);
47 | }
48 |
49 | private static long calibrateTime(long timeMs) {
50 | int rest = (int) (timeMs % 1000);
51 | if (rest > 0 && timeMs > 0) {
52 | return timeMs + 1000;
53 | }
54 | return timeMs;
55 | }
56 |
57 | public static ClockTime raw(long timeMs) {
58 | return new ClockTime(timeMs);
59 | }
60 |
61 | @SuppressLint("DefaultLocale")
62 | public String toReadableFormat() {
63 | if (atLeastOneHourLeft()) {
64 | return String.format(CLOCK_FORMAT_HOURS, hours, minutes, seconds);
65 | } else {
66 | return String.format(CLOCK_FORMAT_MINUTES, minutes, seconds);
67 | }
68 | }
69 |
70 | public int totalMinutes() {
71 | return hours * 60 + minutes;
72 | }
73 |
74 | public String toMinutesFormat() {
75 | return String.format(CLOCK_FORMAT_MINUTES, totalMinutes(), seconds);
76 | }
77 |
78 | public boolean atLeastOneHourLeft() {
79 | return atLeastHourLeft(remainingTimeMs);
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chess/clock/fragments/BaseFragment.java:
--------------------------------------------------------------------------------
1 | package com.chess.clock.fragments;
2 |
3 | import android.os.Bundle;
4 | import android.view.View;
5 |
6 | import androidx.annotation.NonNull;
7 | import androidx.annotation.Nullable;
8 | import androidx.fragment.app.Fragment;
9 |
10 | import com.chess.clock.activities.BaseActivity;
11 | import com.chess.clock.entities.AppTheme;
12 |
13 | /**
14 | * BaseFragment requires BaseActivity as a parent to load theme.
15 | */
16 | public abstract class BaseFragment extends Fragment {
17 | AppTheme loadedTheme;
18 | boolean shouldReloadTheme = false;
19 |
20 | @Override
21 | public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
22 | shouldReloadTheme = true;
23 | super.onViewCreated(view, savedInstanceState);
24 | }
25 |
26 | @Override
27 | public void onResume() {
28 | super.onResume();
29 | AppTheme activityTheme = getBaseActivity().selectedTheme;
30 |
31 | if (activityTheme == null) return;
32 |
33 | if (activityTheme != loadedTheme || shouldReloadTheme) {
34 | loadedTheme = activityTheme;
35 | loadTheme(loadedTheme);
36 | shouldReloadTheme = false;
37 | }
38 | }
39 |
40 | @NonNull
41 | private BaseActivity getBaseActivity() {
42 | return (BaseActivity) requireActivity();
43 | }
44 |
45 | abstract void loadTheme(AppTheme theme);
46 | }
47 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chess/clock/manager/ChessClockManager.java:
--------------------------------------------------------------------------------
1 | package com.chess.clock.manager;
2 |
3 | import com.chess.clock.engine.ClockPlayer;
4 | import com.chess.clock.engine.CountDownTimer;
5 | import com.chess.clock.engine.TimeControlWrapper;
6 |
7 |
8 | public interface ChessClockManager {
9 |
10 | /**
11 | * Add new time controls to Chess Clock and resetTimeControl timer if needed.
12 | *
13 | * @param timeControlWrapper selected time control to set
14 | */
15 | void setupClock(TimeControlWrapper timeControlWrapper);
16 |
17 | /**
18 | * Notifies that ClockPlayer pressed the clock.
19 | */
20 | void pressClock(ClockPlayer player);
21 |
22 | /**
23 | * Pauses the global state of the chess clock.
24 | */
25 | void pauseClock();
26 |
27 | /**
28 | * @return current time for selected player in milliseconds
29 | */
30 | long getTimeForPlayer(ClockPlayer player);
31 |
32 | /**
33 | * @return true if any countdown timer is running
34 | */
35 | boolean isClockStarted();
36 |
37 | /**
38 | * Registers a callbacks to be invoked on players statuses updates.
39 | */
40 | void setListeners(CountDownTimer.Callback playerOneCallback, CountDownTimer.Callback playerTwoCallback);
41 |
42 | /**
43 | * Resets the timer and time control state of both players.
44 | */
45 | void resetClock();
46 |
47 | /**
48 | * Set selected player time regardless of the current settings
49 | */
50 | void setPlayerTime(ClockPlayer ofBoolean, long timeMs);
51 | }
52 |
53 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chess/clock/manager/ChessClockManagerImpl.java:
--------------------------------------------------------------------------------
1 | package com.chess.clock.manager;
2 |
3 | import android.util.Log;
4 |
5 | import com.chess.clock.engine.ClockPlayer;
6 | import com.chess.clock.engine.CountDownTimer;
7 | import com.chess.clock.engine.TimeControlWrapper;
8 | import com.chess.clock.entities.ClockTime;
9 | import com.chess.clock.util.Args;
10 |
11 | public class ChessClockManagerImpl implements ChessClockManager {
12 |
13 | /**
14 | * Logs stuff
15 | */
16 | private static final String TAG = ChessClockManager.class.getName();
17 |
18 | /**
19 | * True if the game is on-going. Note: pause state still counts as game running.
20 | */
21 | private boolean chessGameRunning;
22 |
23 | /**
24 | * Count down timers for both players.
25 | */
26 | private final CountDownTimer mPlayerOneTimer;
27 | private final CountDownTimer mPlayerTwoTimer;
28 |
29 |
30 | public ChessClockManagerImpl() {
31 | chessGameRunning = false;
32 |
33 | long DEFAULT_COUNT_DOWN_INTERVAL_MS = 100;
34 | mPlayerOneTimer = new CountDownTimer(DEFAULT_COUNT_DOWN_INTERVAL_MS);
35 | mPlayerTwoTimer = new CountDownTimer(DEFAULT_COUNT_DOWN_INTERVAL_MS);
36 |
37 | CountDownTimer.FinishCallback mFinishListener = () -> {
38 |
39 | chessGameRunning = false;
40 | mPlayerOneTimer.stop();
41 | mPlayerTwoTimer.stop();
42 |
43 | log("Game finished.");
44 | };
45 | mPlayerOneTimer.setFinishListener(mFinishListener);
46 | mPlayerTwoTimer.setFinishListener(mFinishListener);
47 | }
48 |
49 | @Override
50 | public void setupClock(TimeControlWrapper timeControlWrapper) {
51 | Args.checkForNull(timeControlWrapper);
52 |
53 | // Finish running game
54 | if (chessGameRunning) {
55 | log("Finishing current timers.");
56 | // Reset the clock to new time controls
57 | resetClock();
58 | chessGameRunning = false;
59 | }
60 |
61 | mPlayerOneTimer.setTimeControl(timeControlWrapper.getTimeControlPlayerOne());
62 | mPlayerTwoTimer.setTimeControl(timeControlWrapper.getTimeControlPlayerTwo());
63 |
64 | log("Time Control set.");
65 | }
66 |
67 |
68 | @Override
69 | public void pressClock(ClockPlayer player) {
70 | switch (player) {
71 | case ONE:
72 | log("(1) pressed the clock.");
73 | pressPlayerClock(mPlayerOneTimer, mPlayerTwoTimer);
74 | break;
75 | case TWO:
76 | log("(2) pressed the clock.");
77 | pressPlayerClock(mPlayerTwoTimer, mPlayerOneTimer);
78 | break;
79 | }
80 | }
81 |
82 | @Override
83 | public void pauseClock() {
84 | // The pause instruction only affects the time control which has
85 | // the count down timer in the state CountDownTimer.TimerState.RUNNING.
86 | if (mPlayerOneTimer != null && mPlayerTwoTimer != null) {
87 |
88 | mPlayerOneTimer.pause();
89 | mPlayerTwoTimer.pause();
90 |
91 | log("Paused the clock timers.");
92 | }
93 | }
94 |
95 | @Override
96 | public void resetClock() {
97 | mPlayerOneTimer.resetTimeControl();
98 | mPlayerTwoTimer.resetTimeControl();
99 | chessGameRunning = false;
100 | }
101 |
102 | @Override
103 | public long getTimeForPlayer(ClockPlayer player) {
104 | long time = 0L;
105 | switch (player) {
106 | case ONE:
107 | time = mPlayerOneTimer.getTime();
108 | break;
109 | case TWO:
110 | time = mPlayerTwoTimer.getTime();
111 | break;
112 | }
113 | return time;
114 | }
115 |
116 | @Override
117 | public void setPlayerTime(ClockPlayer player, long timeMs) {
118 | switch (player) {
119 | case ONE:
120 | mPlayerOneTimer.setTime(timeMs);
121 | break;
122 | case TWO:
123 | mPlayerTwoTimer.setTime(timeMs);
124 | break;
125 | }
126 | }
127 |
128 |
129 | /**
130 | * Notifies the Chess Clock Service that a player made a move.
131 | *
132 | * @param playerTimer The player timer which made a move.
133 | * @param opponentTimer The opponent timer.
134 | */
135 | private void pressPlayerClock(CountDownTimer playerTimer, CountDownTimer opponentTimer) {
136 |
137 | // Ignore clock press if timers are not initiated or opponent timer already finished.
138 | if (opponentTimer.isNotFinished() && playerTimer.isNotFinished()) {
139 |
140 | // Game already running? stop player timer and start the opponent timer.
141 | if (chessGameRunning) {
142 | playerTimer.stop();
143 | opponentTimer.start();
144 |
145 | log("Move number: " + playerTimer.getTotalMoveCount() + ", time left: " + ClockTime.raw(playerTimer.getTime()).toReadableFormat());
146 | }
147 | // First move: do not stop player clock to avoid invalid initial time increment.
148 | else {
149 | opponentTimer.start();
150 | chessGameRunning = true;
151 |
152 | log("Game started.");
153 | }
154 | } else {
155 | log("Discarded clock press due to Time Controls not available or game finished already.");
156 | }
157 | }
158 |
159 | @Override
160 | public boolean isClockStarted() {
161 | return mPlayerOneTimer.isStarted() || mPlayerTwoTimer.isStarted();
162 | }
163 |
164 | @Override
165 | public void setListeners(CountDownTimer.Callback playerOneCallback, CountDownTimer.Callback playerTwoCallback) {
166 | if (playerOneCallback != null) {
167 | log("(1)registered listener: #" + playerOneCallback.hashCode());
168 | }
169 | mPlayerOneTimer.setClockTimerListener(playerOneCallback);
170 |
171 | if (playerTwoCallback != null) {
172 | log("(2) registered listener: #" + playerTwoCallback.hashCode());
173 | }
174 | mPlayerTwoTimer.setClockTimerListener(playerTwoCallback);
175 | }
176 |
177 | private void log(String message) {
178 | Log.d(TAG, message);
179 | }
180 | }
181 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chess/clock/statics/AppConstants.java:
--------------------------------------------------------------------------------
1 | package com.chess.clock.statics;
2 |
3 | /**
4 | * Created with Android Studio.
5 | * User: pedroteixeira pmcteixeira@gmail.com
6 | * Date: 17/09/14
7 | * Time: 14:19
8 | */
9 | public class AppConstants {
10 | public static final String PREF_CLOCK_FULL_SCREEN = "clock_full_screen";
11 | public static final String PREF_CLOCK_SOUNDS_ON = "clock_sounds_on";
12 | public static final String PREF_THEME = "theme";
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chess/clock/statics/AppData.java:
--------------------------------------------------------------------------------
1 | package com.chess.clock.statics;
2 |
3 | import static com.chess.clock.statics.AppConstants.PREF_THEME;
4 |
5 | import android.content.Context;
6 | import android.content.SharedPreferences;
7 |
8 | import com.chess.clock.entities.AppTheme;
9 |
10 | /**
11 | * Created with Android Studio.
12 | * User: pedroteixeira pmcteixeira@gmail.com
13 | * Date: 17/09/14
14 | * Time: 14:15
15 | */
16 | public class AppData {
17 |
18 | private final SharedPreferences preferences;
19 |
20 | public AppData(Context context) {
21 | this.preferences = context.getSharedPreferences(StaticData.SHARED_DATA_NAME, Context.MODE_PRIVATE);
22 | }
23 |
24 | public boolean getClockFullScreen() {
25 | return preferences.getBoolean(AppConstants.PREF_CLOCK_FULL_SCREEN, true);
26 | }
27 |
28 | public boolean areSoundsEnabled() {
29 | return preferences.getBoolean(AppConstants.PREF_CLOCK_SOUNDS_ON, true);
30 | }
31 |
32 | public void setSoundsEnabled(Boolean enabled) {
33 | preferences.edit().putBoolean(AppConstants.PREF_CLOCK_SOUNDS_ON, enabled).apply();
34 | }
35 |
36 | public AppTheme getSelectedTheme() {
37 | int themeOrdinal = preferences.getInt(PREF_THEME, AppTheme.GREEN.ordinal());
38 | return AppTheme.fromInt(themeOrdinal);
39 | }
40 |
41 | public void saveAppSetup(AppTheme theme, boolean fullScreen, boolean soundsEnabled) {
42 | preferences.edit()
43 | .putInt(PREF_THEME, theme.ordinal())
44 | .putBoolean(AppConstants.PREF_CLOCK_SOUNDS_ON, soundsEnabled)
45 | .putBoolean(AppConstants.PREF_CLOCK_FULL_SCREEN, fullScreen)
46 | .apply();
47 | }
48 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/chess/clock/statics/StaticData.java:
--------------------------------------------------------------------------------
1 | package com.chess.clock.statics;
2 |
3 | /**
4 | * Created with Android Studio.
5 | * User: pedroteixeira pmcteixeira@gmail.com
6 | * Date: 17/09/14
7 | * Time: 14:16
8 | */
9 | public class StaticData {
10 |
11 | public static final String SHARED_DATA_NAME = "sharedData";
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chess/clock/util/Args.java:
--------------------------------------------------------------------------------
1 | package com.chess.clock.util;
2 |
3 | /**
4 | *
5 | * This class has utility methods for common argument validations. Replaces if statements
6 | * at the forceStart of a method with more compact method calls.
7 | *
8 | *
9 | * Example use case. Instead of:
10 | *
11 | * public void doThis(String aText) {
12 | * if (!Util.textHasContent(aText)) {
13 | * throw new IllegalArgumentException();
14 | * }
15 | * //..main body elided
16 | * }
17 | *
18 | *
19 | *
20 | *
21 | * One may instead write:
22 | *
23 | *
24 | * public void doThis(String aText) {
25 | * Args.checkForContent(aText);
26 | * //..main body elided
27 | * }
28 | *
29 | *
30 | *
31 | */
32 | public final class Args {
33 |
34 | /**
35 | * If aText
does not satisfy {@link Args#textHasContent}, then throw an
36 | * IllegalArgumentException
. Most text used in an application is meaningful only
37 | * if it has visible content.
38 | */
39 | public static void checkForContent(String aText) {
40 | if (!textHasContent(aText)) {
41 | throw new IllegalArgumentException("Text has no visible content");
42 | }
43 | }
44 |
45 | /**
46 | * If aNumber is less than 1, then throw an IllegalArgumentException.
47 | */
48 | public static void checkForPositive(long aNumber) {
49 | if (aNumber < 1) {
50 | throw new IllegalArgumentException(aNumber + " is less than 1");
51 | }
52 | }
53 |
54 | /**
55 | * If aNumber is less than 0, then throw an IllegalArgumentException.
56 | */
57 | public static void checkForZeroOrNegative(long aNumber) {
58 | if (aNumber < 0) {
59 | throw new IllegalArgumentException(aNumber + " is less than 0");
60 | }
61 | }
62 |
63 | /**
64 | * Returns true if aText is non-null and has visible content. This is a test which is often
65 | * performed, and should probably be placed in a general utility class.
66 | */
67 | public static boolean textHasContent(String aText) {
68 | String EMPTY_STRING = "";
69 | return (aText != null) && (!aText.trim().equals(EMPTY_STRING));
70 | }
71 |
72 | /**
73 | * If aObject
is null, then throw a NullPointerException
.
74 | */
75 | public static void checkForNull(Object aObject) {
76 | if (aObject == null) {
77 | throw new NullPointerException();
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chess/clock/util/AutoNameFormatter.java:
--------------------------------------------------------------------------------
1 | package com.chess.clock.util;
2 |
3 | import androidx.annotation.Nullable;
4 |
5 | /**
6 | * SCHEME: “GAME_MIN(ifExist) GAME_SEC(ifExist) | INCREMENT_MIN(ifExist) INCREMENT_SEC(ifExist)”
7 | *
8 | * If there is no game time(only increment), the result should be null.
9 | * You need to provide a formatter for minutes and seconds depending on your needs.
10 | */
11 | public class AutoNameFormatter {
12 |
13 | private final NameParametersFormat paramsFormat;
14 |
15 | public AutoNameFormatter(NameParametersFormat format) {
16 | this.paramsFormat = format;
17 | }
18 |
19 | @Nullable
20 | public String prepareAutoName(
21 | int minutes,
22 | int seconds,
23 | int incrementMinutes,
24 | int incrementSeconds) {
25 | if (minutes == 0 && seconds == 0) return null;
26 |
27 | StringBuilder builder = new StringBuilder();
28 | if (minutes > 0) {
29 | builder.append(paramsFormat.getMinutesFormatted(minutes));
30 | }
31 | if (seconds > 0) {
32 | builder.append(" ");
33 | builder.append(paramsFormat.getSecondsFormatted(seconds));
34 | }
35 | if (incrementMinutes > 0 || incrementSeconds > 0) {
36 | builder.append(" |");
37 | }
38 | if (incrementMinutes > 0) {
39 | builder.append(" ");
40 | builder.append(paramsFormat.getMinutesFormatted(incrementMinutes));
41 | }
42 | if (incrementSeconds > 0) {
43 | builder.append(" ");
44 | builder.append(paramsFormat.getSecondsFormatted(incrementSeconds));
45 | }
46 |
47 | return builder.toString().trim();
48 | }
49 |
50 | public interface NameParametersFormat {
51 | String getMinutesFormatted(int minutes);
52 |
53 | String getSecondsFormatted(int seconds);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chess/clock/util/ClockUtils.java:
--------------------------------------------------------------------------------
1 | package com.chess.clock.util;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.text.Editable;
5 | import android.text.TextWatcher;
6 | import android.util.Log;
7 | import android.view.inputmethod.EditorInfo;
8 | import android.widget.EditText;
9 |
10 | public class ClockUtils {
11 | @SuppressLint("DefaultLocale")
12 | public static String twoDecimalPlacesFormat(int value) {
13 | return String.format("%02d", value);
14 | }
15 |
16 | public static int getIntOrZero(EditText et) {
17 | int intValue = 0;
18 | String textValue = et.getText().toString();
19 | try {
20 | intValue = textValue.isEmpty() ? 0 : Integer.parseInt(textValue);
21 | } catch (NumberFormatException exception) {
22 | Log.i("ClockUtils", "Cannot parse value:" + textValue + " to Integer.");
23 | }
24 | return intValue;
25 | }
26 |
27 | public static void setClockTextWatcher(EditText editText) {
28 | setClockTextWatcherWithCallback(editText, () -> {
29 | // no-op
30 | });
31 | }
32 |
33 | public static void setClockTextWatcherWithCallback(EditText editText, OnTimeEditCallback callback) {
34 | TextWatcher minutesTextWatcher = new TextWatcher() {
35 | final int MAX = 59;
36 |
37 | @Override
38 | public void beforeTextChanged(CharSequence s, int start, int count, int after) {
39 | }
40 |
41 | @Override
42 | public void onTextChanged(CharSequence s, int start, int before, int count) {
43 | }
44 |
45 | @Override
46 | public void afterTextChanged(Editable s) {
47 | editText.removeTextChangedListener(this);
48 | String minutesAsString = s.toString();
49 | int minutes = minutesAsString.isEmpty() ? 0 : Integer.parseInt(minutesAsString);
50 | if (minutes > MAX) {
51 | s.clear();
52 | s.append(twoDecimalPlacesFormat(MAX));
53 | }
54 | editText.addTextChangedListener(this);
55 | callback.onTimeChange();
56 | }
57 | };
58 | editText.addTextChangedListener(minutesTextWatcher);
59 | editText.setOnEditorActionListener((v, actionId, event) -> {
60 | if (actionId == EditorInfo.IME_ACTION_NEXT || actionId == EditorInfo.IME_ACTION_DONE) {
61 | String minutesAsString = v.getText().toString();
62 | int minutes = minutesAsString.isEmpty() ? 0 : Integer.parseInt(minutesAsString);
63 | v.setText(twoDecimalPlacesFormat(minutes));
64 | v.clearFocus();
65 | }
66 | return false;
67 | });
68 | }
69 |
70 | public static void clearFocusOnActionDone(EditText editText) {
71 | editText.setOnEditorActionListener((v, actionId, event) -> {
72 | if (actionId == EditorInfo.IME_ACTION_DONE) {
73 | v.clearFocus();
74 | }
75 | return false;
76 | });
77 | }
78 |
79 | public static void onTimeChangedAction(EditText editText, OnTimeEditCallback callback) {
80 | TextWatcher textWatcher = new TextWatcher() {
81 |
82 | @Override
83 | public void beforeTextChanged(CharSequence s, int start, int count, int after) {
84 | }
85 |
86 | @Override
87 | public void onTextChanged(CharSequence s, int start, int before, int count) {
88 | }
89 |
90 | @Override
91 | public void afterTextChanged(Editable s) {
92 | callback.onTimeChange();
93 | }
94 | };
95 | editText.addTextChangedListener(textWatcher);
96 | }
97 |
98 |
99 | public static long durationMillis(int hours, int minutes, int seconds) {
100 | return hours * 60 * 60 * 1000L + minutes * 60 * 1000L + seconds * 1000L;
101 | }
102 |
103 | public interface OnTimeEditCallback {
104 | void onTimeChange();
105 | }
106 |
107 | }
108 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chess/clock/views/ClockButton.java:
--------------------------------------------------------------------------------
1 | package com.chess.clock.views;
2 |
3 | import static com.chess.clock.views.ViewUtils.showView;
4 |
5 | import android.annotation.SuppressLint;
6 | import android.content.Context;
7 | import android.util.AttributeSet;
8 | import android.util.TypedValue;
9 | import android.view.LayoutInflater;
10 | import android.view.View;
11 | import android.widget.FrameLayout;
12 | import android.widget.TextView;
13 |
14 | import androidx.annotation.Keep;
15 | import androidx.core.content.ContextCompat;
16 |
17 | import com.chess.clock.R;
18 | import com.chess.clock.entities.AppTheme;
19 | import com.chess.clock.entities.ClockTime;
20 |
21 | @Keep
22 | public class ClockButton extends FrameLayout {
23 |
24 | private final int idleTextColor;
25 | private final int runningTextColor;
26 | private final TextView timeTv;
27 | private final TextView movesTv;
28 | private final TextView controlNameTv;
29 | private final View timeOptions;
30 | private final View stageOne;
31 | private final View stageTwo;
32 | private final View stageThree;
33 | int idleBgColor = ContextCompat.getColor(getContext(), R.color.gray_light);
34 |
35 | public ClockButton(Context context, AttributeSet attrs) {
36 | super(context, attrs);
37 | LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
38 | View view = inflater.inflate(R.layout.view_clock_button, this, true);
39 | timeTv = view.findViewById(R.id.clockTimeTv);
40 | movesTv = view.findViewById(R.id.movesTv);
41 | stageOne = view.findViewById(R.id.stageOne);
42 | stageTwo = view.findViewById(R.id.stageTwo);
43 | stageThree = view.findViewById(R.id.stageThree);
44 | controlNameTv = view.findViewById(R.id.stageNameTv);
45 | timeOptions = view.findViewById(R.id.adjustTimeImg);
46 |
47 | setForeground(ViewUtils.getSelectableItemBgDrawable(getContext()));
48 |
49 | idleTextColor = getResources().getColor(R.color.black_70);
50 | runningTextColor = getResources().getColor(R.color.white);
51 | }
52 |
53 |
54 | public void setTime(long timeMillis) {
55 | String readableTime = ClockTime.calibratedReadableFormat(timeMillis);
56 |
57 | if (timeTv.getText().equals(readableTime)) return;
58 |
59 | timeTv.setText(readableTime);
60 | if (ClockTime.atLeastHourLeft(timeMillis)) {
61 | timeTv.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimension(R.dimen.clock_timer_textSize_small));
62 | } else {
63 | timeTv.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimension(R.dimen.clock_timer_textSize_normal));
64 | }
65 | }
66 |
67 | @SuppressLint("DefaultLocale")
68 | public void setMoves(int moves) {
69 | movesTv.setText(getContext().getString(R.string.moves_x, moves));
70 | }
71 |
72 | public void setClockButtonClickListener(ClockClickListener listener) {
73 | setOnClickListener(v -> listener.onClickClock());
74 | timeOptions.setOnClickListener(v -> listener.onClickOptions());
75 | }
76 |
77 | public void updateUi(
78 | AppTheme theme,
79 | State state
80 | ) {
81 | switch (state) {
82 | case IDLE:
83 | case LOCKED:
84 | setBackgroundColor(idleBgColor);
85 | timeTv.setTextColor(idleTextColor);
86 | break;
87 | case RUNNING:
88 | setBackgroundColor(ContextCompat.getColor(getContext(), theme.primaryColorRes));
89 | timeTv.setTextColor(runningTextColor);
90 | break;
91 | case FINISHED:
92 | setBackgroundColor(ContextCompat.getColor(getContext(), R.color.red));
93 | timeTv.setTextColor(idleTextColor);
94 | break;
95 | }
96 | setClickable(state != State.LOCKED);
97 | boolean hideStageControls = state != State.IDLE;
98 | ViewUtils.isInvisible(timeOptions, hideStageControls);
99 | ViewUtils.isInvisible(controlNameTv, hideStageControls);
100 | }
101 |
102 | private void setStageBg(View stage, Boolean active) {
103 | if (active) {
104 | stage.setBackgroundResource(R.drawable.shape_stage_fill);
105 | } else {
106 | stage.setBackgroundResource(R.drawable.shape_stage_empty);
107 | }
108 | }
109 |
110 | public void updateStage(int stageId, String timeControlName) {
111 | //stage one is always filled if visible
112 | setStageBg(stageTwo, stageId > 0);
113 | setStageBg(stageThree, stageId > 1);
114 | controlNameTv.setText(timeControlName);
115 | }
116 |
117 | public void setStages(int stagesNumber) {
118 | // no stages indicators visible for 1 stage game
119 | showView(stageOne, stagesNumber > 1);
120 | showView(stageTwo, stagesNumber > 1);
121 | showView(stageThree, stagesNumber > 2);
122 | }
123 |
124 | public enum State {
125 | IDLE, LOCKED, RUNNING, FINISHED
126 | }
127 |
128 | @Keep
129 | public interface ClockClickListener {
130 | void onClickClock();
131 |
132 | void onClickOptions();
133 | }
134 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/chess/clock/views/ClockMenu.java:
--------------------------------------------------------------------------------
1 | package com.chess.clock.views;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.widget.ImageView;
8 |
9 | import androidx.constraintlayout.widget.ConstraintLayout;
10 |
11 | import com.chess.clock.R;
12 |
13 | public class ClockMenu extends ConstraintLayout {
14 |
15 | private final ImageView settingsButton;
16 | private final ImageView playPauseButton;
17 | private final ImageView resetButton;
18 | private final ImageView soundButton;
19 |
20 | public ClockMenu(Context context, AttributeSet attrs) {
21 | super(context, attrs);
22 | LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
23 | View view = inflater.inflate(R.layout.view_clock_menu, this, true);
24 | view.setBackgroundResource(R.color.gray_controls);
25 |
26 | settingsButton = view.findViewById(R.id.settingsBtn);
27 | playPauseButton = view.findViewById(R.id.playPauseBtn);
28 | resetButton = view.findViewById(R.id.resetBtn);
29 | soundButton = view.findViewById(R.id.soundBtn);
30 | }
31 |
32 | public void setListener(MenuClickListener listener) {
33 | settingsButton.setOnClickListener(v -> listener.timeSettingsClicked());
34 | playPauseButton.setOnClickListener(v -> listener.playPauseClicked());
35 | resetButton.setOnClickListener(v -> listener.resetClicked());
36 | soundButton.setOnClickListener(v -> listener.soundClicked());
37 | }
38 |
39 | public void showPause() {
40 | playPauseButton.setImageResource(R.drawable.ic_pause);
41 | playPauseButton.setVisibility(View.VISIBLE);
42 | }
43 |
44 | public void showPlay() {
45 | playPauseButton.setImageResource(R.drawable.ic_play);
46 | playPauseButton.setVisibility(View.VISIBLE);
47 | }
48 |
49 | public void hidePlayPauseBtn() {
50 | playPauseButton.setVisibility(View.INVISIBLE);
51 | }
52 |
53 | public void updateSoundIcon(boolean soundsEnabled) {
54 | if (soundsEnabled) {
55 | soundButton.setImageResource(R.drawable.ic_sound);
56 | } else {
57 | soundButton.setImageResource(R.drawable.ic_sound_off);
58 | }
59 | }
60 |
61 | public interface MenuClickListener {
62 | void timeSettingsClicked();
63 |
64 | void playPauseClicked();
65 |
66 | void resetClicked();
67 |
68 | void soundClicked();
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chess/clock/views/StageRowView.java:
--------------------------------------------------------------------------------
1 | package com.chess.clock.views;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.widget.TextView;
8 |
9 | import androidx.annotation.NonNull;
10 | import androidx.annotation.Nullable;
11 | import androidx.constraintlayout.widget.ConstraintLayout;
12 |
13 | import com.chess.clock.R;
14 | import com.chess.clock.engine.Stage;
15 |
16 | public class StageRowView extends ConstraintLayout {
17 |
18 | TextView stageDetails;
19 | TextView timeIncrementDetails;
20 | TextView positionLabel;
21 |
22 | public StageRowView(@NonNull Context context, @Nullable AttributeSet attrs) {
23 | super(context, attrs);
24 |
25 | LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
26 | View view = inflater.inflate(R.layout.list_stage_item, this, true);
27 |
28 | positionLabel = view.findViewById(R.id.positionTv);
29 | stageDetails = view.findViewById(R.id.stageDetailsTv);
30 | timeIncrementDetails = view.findViewById(R.id.incrementDetailsTv);
31 | }
32 |
33 | public void updateData(int position, Stage stage) {
34 | positionLabel.setText(String.valueOf(position));
35 | String details = "";
36 | String timeFormatted = stage.durationTimeFormatted();
37 | if (stage.getStageType() == Stage.StageType.GAME) {
38 | details = getContext().getString(R.string.game_in_x, timeFormatted);
39 | } else {
40 | details = getResources().getQuantityString(R.plurals.x_moves_in, stage.getTotalMoves(), stage.getTotalMoves(), timeFormatted);
41 | }
42 | stageDetails.setText(details);
43 | timeIncrementDetails.setText(stage.getTimeIncrement().toString());
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chess/clock/views/StyledButton.java:
--------------------------------------------------------------------------------
1 | package com.chess.clock.views;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.util.AttributeSet;
6 | import android.view.LayoutInflater;
7 | import android.view.View;
8 | import android.widget.FrameLayout;
9 | import android.widget.TextView;
10 |
11 | import androidx.cardview.widget.CardView;
12 |
13 | import com.chess.clock.R;
14 |
15 | public class StyledButton extends FrameLayout {
16 |
17 | private final CardView topLayer;
18 | private final CardView bottomLayer;
19 |
20 | public StyledButton(Context context, AttributeSet attrs) {
21 | super(context, attrs);
22 |
23 | LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
24 | View view = inflater.inflate(R.layout.view_styled_button, this, true);
25 | TextView text = view.findViewById(R.id.text);
26 |
27 | topLayer = view.findViewById(R.id.topCard);
28 | bottomLayer = view.findViewById(R.id.bottomCard);
29 |
30 | if (attrs != null) {
31 | TypedArray t = context.obtainStyledAttributes(attrs, R.styleable.StyledButton, 0, 0);
32 | String buttonText = t.getString(R.styleable.StyledButton_android_text);
33 | t.recycle();
34 | text.setText(buttonText);
35 | }
36 | }
37 |
38 | public void setButtonBackground(int color) {
39 | topLayer.setCardBackgroundColor(color);
40 | bottomLayer.setCardBackgroundColor(color);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chess/clock/views/ViewUtils.java:
--------------------------------------------------------------------------------
1 | package com.chess.clock.views;
2 |
3 | import android.app.Dialog;
4 | import android.content.Context;
5 | import android.content.res.Resources;
6 | import android.content.res.TypedArray;
7 | import android.graphics.drawable.Drawable;
8 | import android.util.TypedValue;
9 | import android.view.View;
10 | import android.widget.TextView;
11 |
12 | import androidx.core.content.ContextCompat;
13 |
14 | import com.chess.clock.R;
15 |
16 | public class ViewUtils {
17 | public static void showView(View v, Boolean show) {
18 | if (show) {
19 | v.setVisibility(View.VISIBLE);
20 | } else {
21 | v.setVisibility(View.GONE);
22 | }
23 | }
24 |
25 | public static void isInvisible(View v, boolean invisible) {
26 | if (invisible) {
27 | v.setVisibility(View.INVISIBLE);
28 | } else {
29 | v.setVisibility(View.VISIBLE);
30 | }
31 | }
32 |
33 | /**
34 | * call before dialog `show()`
35 | */
36 | public static void setLargePopupMessageTextSize(Dialog dialog, Resources resources) {
37 | dialog.setOnShowListener(dialogInterface -> {
38 | TextView messageTv = dialog.findViewById(android.R.id.message);
39 | if (messageTv != null) {
40 | messageTv.setTextSize(TypedValue.COMPLEX_UNIT_PX, resources.getDimension(R.dimen.text_size_dialog_title));
41 | }
42 | });
43 | }
44 |
45 | public static Drawable getSelectableItemBgDrawable(Context context) {
46 | TypedArray a = context.getTheme().obtainStyledAttributes(R.style.AppTheme, new int[]{android.R.attr.selectableItemBackground});
47 | int attributeResourceId = a.getResourceId(0, 0);
48 | return ContextCompat.getDrawable(context, attributeResourceId);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/left_to_right_full.xml:
--------------------------------------------------------------------------------
1 |
3 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/left_to_right_in.xml:
--------------------------------------------------------------------------------
1 |
3 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/right_to_left_full.xml:
--------------------------------------------------------------------------------
1 |
3 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/right_to_left_out.xml:
--------------------------------------------------------------------------------
1 |
3 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v21/bg_green_pressed.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | -
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/add_time_button_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_btn_clock_running.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_green_pressed.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_popup.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_white_50_rounded.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/button_bg_selector_green.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_arrow_right_24.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_back_arrow_white.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_check_box.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_check_box_frame.xml:
--------------------------------------------------------------------------------
1 |
6 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_checkmark.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_checkmark_white.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_delete.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_edit.xml:
--------------------------------------------------------------------------------
1 |
6 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_edit_pencil.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_edit_time.xml:
--------------------------------------------------------------------------------
1 |
7 |
11 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_edit_time_small.xml:
--------------------------------------------------------------------------------
1 |
7 |
11 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_fullscreen.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_fullscreen_exit.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
6 |
10 |
13 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
6 |
10 |
13 |
16 |
19 |
22 |
25 |
28 |
31 |
34 |
37 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_logo.xml:
--------------------------------------------------------------------------------
1 |
6 |
10 |
14 |
18 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_options.xml:
--------------------------------------------------------------------------------
1 |
6 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_pause.xml:
--------------------------------------------------------------------------------
1 |
7 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_play.xml:
--------------------------------------------------------------------------------
1 |
7 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_plus_green.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_radio_frame.xml:
--------------------------------------------------------------------------------
1 |
6 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_radio_selected.xml:
--------------------------------------------------------------------------------
1 |
6 |
11 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_refresh.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_reorder_icon.xml:
--------------------------------------------------------------------------------
1 |
6 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_settings.xml:
--------------------------------------------------------------------------------
1 |
6 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_settings_sound_off.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_settings_sound_on.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_sound.xml:
--------------------------------------------------------------------------------
1 |
7 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_sound_off.xml:
--------------------------------------------------------------------------------
1 |
7 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/list_radio_button_selector.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/shape_stage_empty.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/shape_stage_fill.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/font/montserrat_extra_bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChessCom/android-chessclock/9e5862c9ce2caa28f42ad53009a3cb8b7a93decf/app/src/main/res/font/montserrat_extra_bold.ttf
--------------------------------------------------------------------------------
/app/src/main/res/font/roboto_medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChessCom/android-chessclock/9e5862c9ce2caa28f42ad53009a3cb8b7a93decf/app/src/main/res/font/roboto_medium.ttf
--------------------------------------------------------------------------------
/app/src/main/res/layout-land/activity_clock_timers.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
12 |
13 |
21 |
22 |
31 |
32 |
40 |
41 |
--------------------------------------------------------------------------------
/app/src/main/res/layout-land/activity_clock_timers_reversed.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
12 |
13 |
21 |
22 |
31 |
32 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/app/src/main/res/layout-land/view_clock_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
20 |
21 |
33 |
34 |
35 |
47 |
48 |
60 |
61 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_clock_timers.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
14 |
18 |
19 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_timer_settings.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_fragment_edit_time_increment.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
23 |
24 |
35 |
36 |
37 |
45 |
46 |
59 |
60 |
73 |
74 |
87 |
88 |
101 |
102 |
111 |
112 |
113 |
121 |
122 |
130 |
131 |
141 |
142 |
146 |
147 |
154 |
155 |
156 |
157 |
158 |
162 |
163 |
169 |
170 |
171 |
172 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_title_text_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
16 |
23 |
24 |
32 |
33 |
42 |
43 |
44 |
45 |
46 |
55 |
56 |
64 |
65 |
72 |
73 |
81 |
82 |
83 |
84 |
85 |
92 |
93 |
94 |
95 |
102 |
103 |
104 |
105 |
117 |
118 |
119 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_theme.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
18 |
19 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_view_time.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
21 |
22 |
40 |
41 |
54 |
55 |
69 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/list_stage_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
23 |
24 |
36 |
37 |
49 |
50 |
59 |
60 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/view_clock_button.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
11 |
12 |
21 |
22 |
28 |
29 |
37 |
38 |
46 |
47 |
48 |
49 |
60 |
61 |
74 |
75 |
90 |
91 |
108 |
109 |
110 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/view_clock_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
21 |
22 |
35 |
36 |
42 |
43 |
56 |
57 |
70 |
71 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/view_styled_button.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
17 |
18 |
25 |
26 |
42 |
43 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/settings_actions.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/settings_cab_actions.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChessCom/android-chessclock/9e5862c9ce2caa28f42ad53009a3cb8b7a93decf/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChessCom/android-chessclock/9e5862c9ce2caa28f42ad53009a3cb8b7a93decf/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChessCom/android-chessclock/9e5862c9ce2caa28f42ad53009a3cb8b7a93decf/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChessCom/android-chessclock/9e5862c9ce2caa28f42ad53009a3cb8b7a93decf/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChessCom/android-chessclock/9e5862c9ce2caa28f42ad53009a3cb8b7a93decf/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChessCom/android-chessclock/9e5862c9ce2caa28f42ad53009a3cb8b7a93decf/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChessCom/android-chessclock/9e5862c9ce2caa28f42ad53009a3cb8b7a93decf/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChessCom/android-chessclock/9e5862c9ce2caa28f42ad53009a3cb8b7a93decf/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChessCom/android-chessclock/9e5862c9ce2caa28f42ad53009a3cb8b7a93decf/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChessCom/android-chessclock/9e5862c9ce2caa28f42ad53009a3cb8b7a93decf/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/raw/chess_clock_pause.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChessCom/android-chessclock/9e5862c9ce2caa28f42ad53009a3cb8b7a93decf/app/src/main/res/raw/chess_clock_pause.mp3
--------------------------------------------------------------------------------
/app/src/main/res/raw/chess_clock_reset.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChessCom/android-chessclock/9e5862c9ce2caa28f42ad53009a3cb8b7a93decf/app/src/main/res/raw/chess_clock_reset.mp3
--------------------------------------------------------------------------------
/app/src/main/res/raw/chess_clock_switch1.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChessCom/android-chessclock/9e5862c9ce2caa28f42ad53009a3cb8b7a93decf/app/src/main/res/raw/chess_clock_switch1.mp3
--------------------------------------------------------------------------------
/app/src/main/res/raw/chess_clock_switch2.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChessCom/android-chessclock/9e5862c9ce2caa28f42ad53009a3cb8b7a93decf/app/src/main/res/raw/chess_clock_switch2.mp3
--------------------------------------------------------------------------------
/app/src/main/res/raw/chess_clock_time_ended.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChessCom/android-chessclock/9e5862c9ce2caa28f42ad53009a3cb8b7a93decf/app/src/main/res/raw/chess_clock_time_ended.mp3
--------------------------------------------------------------------------------
/app/src/main/res/transition/fragment_in.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/values-ar/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/app/src/main/res/values-fr/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Horloge d\'échecs
6 | Remettre l\'horloge à zéro ?
7 | Non
8 | Oui
9 |
10 |
11 | Paramètres
12 | Modifier
13 | Supprimer
14 | Plein écran
15 |
16 |
17 | Ajouter une étape
18 | Enregistrer
19 | Veuillez ajouter un nom pour enregistrer.
20 |
21 |
22 | Liste vide. Liste par défaut chargée.
23 | Identique que pour Joueur 1
24 |
25 |
26 | Supprimer une étape ?
27 |
28 |
29 | Modifier l\'étape
30 | Annuler
31 |
32 |
33 | Délai
34 | Bronstein
35 | Fischer
36 | Aucun
37 |
38 |
39 | L\'horloge du joueur démarre après la période de retard.
40 | Les joueurs reçoivent la partie utilisée de l\'incrément à la fin de chaque tour.
41 | Les joueurs reçoivent l\'incrément complet à la fin de chaque tour.
42 |
43 |
44 |
45 | Joueur 1
46 | Joueur 2
47 |
48 |
49 | Démarrer
50 | Paramètres du temps
51 | Préréglages
52 | Nouveau minuteur personnalisé
53 | Paramètres de l\'application
54 | Couleur du thème
55 | Son
56 | Temps personnalisé
57 | %d min
58 | %d sec
59 | %d min | %2d sec
60 | Nom
61 | Temps
62 | Incrément
63 | Mode avancé
64 | Veuillez ajouter un temps de sauvegarde.
65 | Stages
66 | Etape 1
67 | Etape 2
68 | Etape 3
69 | Mouvement
70 | Valeur
71 | Type
72 | Veuillez ajouter au moins une étape pour chaque utilisateur.
73 | Delete custom time?
74 | Garder
75 | Jeu dans %s
76 | Heure
77 | Minute
78 | Seconde
79 | Sauvegarder le temps
80 | Ajuster le temps
81 | Mouvements: %d
82 | Etes-vous sûr de vouloir abandonner le nouveau minuteur personnalisé ?
83 | Restaurer les paramètres de minuteur par défaut
84 | Etes vous sûr ? Cela supprimera tous les minuteurs personnalisés que vous avez.
85 | Confirmer
86 | OK
87 | Attention
88 | L\'horloge nécessite au moins un minuteur actif. Veuillez modifier le minuteur existant ou en créer un nouveau.
89 |
--------------------------------------------------------------------------------
/app/src/main/res/values-sw360dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 40sp
3 | 84sp
4 | 116sp
5 | 70dp
6 | 32dp
7 | 68dp
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/values-sw600dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 100sp
4 | 144sp
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values-v19/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
15 |
16 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/values-v35/edgetoedge.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | #000
5 | #33000000
6 | #66000000
7 | #A6000000
8 | #B3000000
9 | #0381b8
10 | #7fa650
11 | #A67FA650
12 | #c1a45a
13 | #e58f2a
14 | #f38272
15 | #ed3239
16 | #00000000
17 | #1bada6
18 | #fff
19 | #33FFFFFF
20 | #80ffffff
21 | #A6ffffff
22 | #D9FFFFFF
23 | #4b4847
24 | #666564
25 | #8b8987
26 | #e7e6e5
27 |
28 |
29 | #312e2b
30 | #262421
31 |
32 |
33 | #FF312e2b
34 | #6f6862
35 | #ff59534e
36 | #aa312e2b
37 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 4dp
3 | 8dp
4 | 10dp
5 | 12dp
6 | 16dp
7 | 24dp
8 | 32dp
9 |
10 | 4dp
11 | 10dp
12 | 4dp
13 | 480dp
14 | 56dp
15 | 48dp
16 |
17 |
18 | 12sp
19 | 14sp
20 | 16sp
21 | 17sp
22 | 22sp
23 | 20sp
24 |
25 |
26 | 32sp
27 | 64sp
28 | 76sp
29 | 12dp
30 | 56dp
31 | 16dp
32 | 56dp
33 | 40dp
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/values/edgetoedge.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/values/plurals.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | - %d move in %s
5 | - %d moves in %s
6 |
7 |
8 |
9 | - %d selected
10 | - %d selected
11 | - %d selected
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | :
5 | 00
6 |
7 |
8 | v%s (%d)
9 |
10 |
11 | Chess Clock
12 | Reset the clock?
13 | No
14 | Yes
15 |
16 |
17 | Settings
18 | Edit
19 | Delete
20 | Full Screen
21 |
22 |
23 | Add Stage
24 | Save
25 | Please add a name to save.
26 |
27 |
28 | List empty. Default list loaded.
29 | Same as Player One
30 |
31 |
32 | Delete stage?
33 |
34 |
35 | Edit Stage
36 | Cancel
37 |
38 |
39 | Delay
40 | Bronstein
41 | Fischer
42 | None
43 |
44 |
45 | The player\'s clock starts after the delay period.
46 | Players receive the used portion of the increment at the end of each
47 | turn.
48 |
49 | Players receive the full increment at the end of each turn.
50 |
51 |
52 | Player One
53 | Player Two
54 |
55 |
56 | Start
57 | Time Controls
58 | Presets
59 | New Custom Time
60 | App Settings
61 | Theme color
62 | Sound
63 | Custom Time
64 | %d min
65 | %d sec
66 | %d min | %2d sec
67 | Name
68 | Time
69 | Increment
70 | Advanced mode
71 | Please add a time to save.
72 | Stages
73 | Stage One
74 | Stage Two
75 | Stage Three
76 | Moves
77 | Value
78 | Type
79 | Please add at least one stage for each user.
80 | Delete custom time?
81 | Keep
82 | Game in %s
83 | Hour
84 | Minute
85 | Second
86 | Save time
87 | Adjust Time
88 | Moves: %d
89 | Are you sure you want to discard new custom time?
90 | Restore Default Time Controls
91 | Are you sure? This will delete any custom time controls you have.
92 | Confirm
93 | Ok
94 | Attention
95 | The clock requires at least one active time control. Please edit the existing time control or create a new one.
96 |
97 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
16 |
17 |
18 |
23 |
24 |
29 |
30 |
38 |
39 |
42 |
43 |
56 |
57 |
70 |
71 |
82 |
83 |
89 |
90 |
91 |
92 |
95 |
96 |
100 |
101 |
110 |
111 |
112 |
113 |
114 |
--------------------------------------------------------------------------------
/app/src/test/java/com/chess/AutoNameFormatterTest.java:
--------------------------------------------------------------------------------
1 | package com.chess;
2 |
3 | import static org.junit.Assert.assertEquals;
4 | import static org.junit.Assert.assertNull;
5 |
6 | import com.chess.clock.util.AutoNameFormatter;
7 |
8 | import org.junit.Test;
9 |
10 | public class AutoNameFormatterTest {
11 |
12 | private final AutoNameFormatter.NameParametersFormat sampleParametersFormat = new AutoNameFormatter.NameParametersFormat() {
13 | @Override
14 | public String getMinutesFormatted(int minutes) {
15 | return minutes + " min";
16 | }
17 |
18 | @Override
19 | public String getSecondsFormatted(int seconds) {
20 | return seconds + " sec";
21 | }
22 | };
23 | private final AutoNameFormatter formatter = new AutoNameFormatter(sampleParametersFormat);
24 |
25 | @Test
26 | public void noGameTime_withOrWithoutIncrement_returnsNull() {
27 | // when
28 | String noParamsName = formatter.prepareAutoName(0, 0, 0, 0);
29 | String incrementOnlyName = formatter.prepareAutoName(0, 0, 1, 1);
30 |
31 | // then
32 | assertNull(noParamsName);
33 | assertNull(incrementOnlyName);
34 | }
35 |
36 | @Test
37 | public void minutesOnly_returnsMinutesString() {
38 | // given
39 | int minutes = 10;
40 | String expectedName = "10 min";
41 |
42 | // when
43 | String name = formatter.prepareAutoName(minutes, 0, 0, 0);
44 |
45 | // then
46 | assertEquals(name, expectedName);
47 | }
48 |
49 | @Test
50 | public void secondsOnly_returnsSecondsString() {
51 | // given
52 | int seconds = 10;
53 | String expectedName = "10 sec";
54 |
55 | // when
56 | String name = formatter.prepareAutoName(0, seconds, 0, 0);
57 |
58 | // then
59 | assertEquals(name, expectedName);
60 | }
61 |
62 | @Test
63 | public void minutesGameWithSecondsIncrement_returnsProperFormat() {
64 | // given
65 | int gameTimeMinutes = 5;
66 | int incrementSeconds = 2;
67 | String expectedName = "5 min | 2 sec";
68 |
69 | // when
70 | String name = formatter.prepareAutoName(gameTimeMinutes, 0, 0, incrementSeconds);
71 |
72 | // then
73 | assertEquals(name, expectedName);
74 | }
75 |
76 | @Test
77 | public void secondsGameWithMinutesIncrement_returnsProperFormat() {
78 | // given
79 | int gameTimeSeconds = 55;
80 | int incrementMinutes = 1;
81 | String expectedName = "55 sec | 1 min";
82 |
83 | // when
84 | String name = formatter.prepareAutoName(0, gameTimeSeconds, incrementMinutes, 0);
85 |
86 | // then
87 | assertEquals(name, expectedName);
88 | }
89 |
90 | @Test
91 | public void gameParamsOnly_returnsFormatWithoutIncrement() {
92 | // given
93 | int gameTimeMinutes = 2;
94 | int gameTimeSeconds = 30;
95 | String expectedName = "2 min 30 sec";
96 |
97 | // when
98 | String name = formatter.prepareAutoName(gameTimeMinutes, gameTimeSeconds, 0, 0);
99 |
100 | // then
101 | assertEquals(name, expectedName);
102 | }
103 |
104 | @Test
105 | public void fullGameParamsWithFullIncrement_returnsFullFormat() {
106 | // given
107 | int gameTimeMinutes = 2;
108 | int gameTimeSeconds = 30;
109 | int incrementMinutes = 1;
110 | int incrementSeconds = 30;
111 | String expectedName = "2 min 30 sec | 1 min 30 sec";
112 |
113 | // when
114 | String name = formatter.prepareAutoName(gameTimeMinutes, gameTimeSeconds, incrementMinutes, incrementSeconds);
115 |
116 | // then
117 | assertEquals(name, expectedName);
118 | }
119 |
120 | }
121 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | google()
4 | mavenCentral()
5 | }
6 | dependencies {
7 | classpath 'com.android.tools.build:gradle:8.7.3'
8 | }
9 | }
10 |
11 | allprojects {
12 | repositories {
13 | google()
14 | mavenCentral()
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | android.useAndroidX=true
2 | android.enableJetifier=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChessCom/android-chessclock/9e5862c9ce2caa28f42ad53009a3cb8b7a93decf/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 | @rem SPDX-License-Identifier: Apache-2.0
17 | @rem
18 |
19 | @if "%DEBUG%"=="" @echo off
20 | @rem ##########################################################################
21 | @rem
22 | @rem Gradle startup script for Windows
23 | @rem
24 | @rem ##########################################################################
25 |
26 | @rem Set local scope for the variables with windows NT shell
27 | if "%OS%"=="Windows_NT" setlocal
28 |
29 | set DIRNAME=%~dp0
30 | if "%DIRNAME%"=="" set DIRNAME=.
31 | @rem This is normally unused
32 | set APP_BASE_NAME=%~n0
33 | set APP_HOME=%DIRNAME%
34 |
35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
37 |
38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
40 |
41 | @rem Find java.exe
42 | if defined JAVA_HOME goto findJavaFromJavaHome
43 |
44 | set JAVA_EXE=java.exe
45 | %JAVA_EXE% -version >NUL 2>&1
46 | if %ERRORLEVEL% equ 0 goto execute
47 |
48 | echo. 1>&2
49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
50 | echo. 1>&2
51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
52 | echo location of your Java installation. 1>&2
53 |
54 | goto fail
55 |
56 | :findJavaFromJavaHome
57 | set JAVA_HOME=%JAVA_HOME:"=%
58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
59 |
60 | if exist "%JAVA_EXE%" goto execute
61 |
62 | echo. 1>&2
63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
64 | echo. 1>&2
65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
66 | echo location of your Java installation. 1>&2
67 |
68 | goto fail
69 |
70 | :execute
71 | @rem Setup the command line
72 |
73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
74 |
75 |
76 | @rem Execute Gradle
77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
78 |
79 | :end
80 | @rem End local scope for the variables with windows NT shell
81 | if %ERRORLEVEL% equ 0 goto mainEnd
82 |
83 | :fail
84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
85 | rem the _cmd.exe /c_ return code!
86 | set EXIT_CODE=%ERRORLEVEL%
87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
89 | exit /b %EXIT_CODE%
90 |
91 | :mainEnd
92 | if "%OS%"=="Windows_NT" endlocal
93 |
94 | :omega
95 |
--------------------------------------------------------------------------------
/metadata/en-US/changelogs/26.txt:
--------------------------------------------------------------------------------
1 | Hey, chess-lovers! The best clock app for your over-the-board games just got even better, with more fun features and a polished new look!
2 |
3 | - Brand new, more usable interface for clock and settings!
4 | - Toggle clock sounds as you play
5 | - Customize your button colors!
6 | - Try something new? Pick from a variety of new time-control presets!
7 |
8 | We hope you love this update. If so, please consider giving it a fresh rating or review. It really helps! Thank you - and enjoy your chess.
--------------------------------------------------------------------------------
/metadata/en-US/changelogs/27.txt:
--------------------------------------------------------------------------------
1 | Minor sounds update, bugfixes and maintenance.
--------------------------------------------------------------------------------
/metadata/en-US/changelogs/28.txt:
--------------------------------------------------------------------------------
1 | Fix edge to edge overlap issue.
--------------------------------------------------------------------------------
/metadata/en-US/full_description.txt:
--------------------------------------------------------------------------------
1 | Replace your chess clock with this free game timer! It's easy to use, yet fully featured to handle any time control. 100% free: no in-app purchases, no ads!
2 |
3 | Choose your time control and you're ready to play. The 2nd player presses her button to start the 1st player's clock - and the game is on!
4 |
5 | FEATURES
6 | - Large, easy-to-read buttons
7 | - Works in landscape and portrait on all devices
8 | - Quickly customize the app for one-tap access to all your favorite time controls
9 | - Time controls include base minutes per player and optional per-move delays or bonus time. The app supports both Fischer and Bronstein increments, as well as simple delays. The duration is up to you!
10 | - Supports multiple-stage time controls commonly seen in tournaments, such as "40 moves in 2 hours + game in 60 minutes." A glance at the clock shows your current stage!
11 | - Clock pauses automatically if the app is interrupted; manually pause the clock at any time
12 | - Pleasant sounds for buttons and "time's up" alert
13 |
14 | At Chess.com, we really do love chess, and sometimes we even play it offline! For times like that, we created this clock - and we hope you love it as much as we do.
--------------------------------------------------------------------------------
/metadata/en-US/images/featureGraphic.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChessCom/android-chessclock/9e5862c9ce2caa28f42ad53009a3cb8b7a93decf/metadata/en-US/images/featureGraphic.jpg
--------------------------------------------------------------------------------
/metadata/en-US/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChessCom/android-chessclock/9e5862c9ce2caa28f42ad53009a3cb8b7a93decf/metadata/en-US/images/icon.png
--------------------------------------------------------------------------------
/metadata/en-US/images/phoneScreenshots/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChessCom/android-chessclock/9e5862c9ce2caa28f42ad53009a3cb8b7a93decf/metadata/en-US/images/phoneScreenshots/1.png
--------------------------------------------------------------------------------
/metadata/en-US/images/phoneScreenshots/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChessCom/android-chessclock/9e5862c9ce2caa28f42ad53009a3cb8b7a93decf/metadata/en-US/images/phoneScreenshots/2.png
--------------------------------------------------------------------------------
/metadata/en-US/images/phoneScreenshots/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChessCom/android-chessclock/9e5862c9ce2caa28f42ad53009a3cb8b7a93decf/metadata/en-US/images/phoneScreenshots/3.png
--------------------------------------------------------------------------------
/metadata/en-US/images/phoneScreenshots/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChessCom/android-chessclock/9e5862c9ce2caa28f42ad53009a3cb8b7a93decf/metadata/en-US/images/phoneScreenshots/4.png
--------------------------------------------------------------------------------
/metadata/en-US/images/phoneScreenshots/5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChessCom/android-chessclock/9e5862c9ce2caa28f42ad53009a3cb8b7a93decf/metadata/en-US/images/phoneScreenshots/5.png
--------------------------------------------------------------------------------
/metadata/en-US/images/phoneScreenshots/6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChessCom/android-chessclock/9e5862c9ce2caa28f42ad53009a3cb8b7a93decf/metadata/en-US/images/phoneScreenshots/6.png
--------------------------------------------------------------------------------
/metadata/en-US/images/sevenInchScreenshots/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChessCom/android-chessclock/9e5862c9ce2caa28f42ad53009a3cb8b7a93decf/metadata/en-US/images/sevenInchScreenshots/1.png
--------------------------------------------------------------------------------
/metadata/en-US/images/sevenInchScreenshots/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChessCom/android-chessclock/9e5862c9ce2caa28f42ad53009a3cb8b7a93decf/metadata/en-US/images/sevenInchScreenshots/2.png
--------------------------------------------------------------------------------
/metadata/en-US/images/tenInchScreenshots/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChessCom/android-chessclock/9e5862c9ce2caa28f42ad53009a3cb8b7a93decf/metadata/en-US/images/tenInchScreenshots/1.png
--------------------------------------------------------------------------------
/metadata/en-US/images/tenInchScreenshots/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChessCom/android-chessclock/9e5862c9ce2caa28f42ad53009a3cb8b7a93decf/metadata/en-US/images/tenInchScreenshots/2.png
--------------------------------------------------------------------------------
/metadata/en-US/short_description.txt:
--------------------------------------------------------------------------------
1 | Adjust your favorite timer to play chess online with friends!
--------------------------------------------------------------------------------
/metadata/en-US/title.txt:
--------------------------------------------------------------------------------
1 | Chess Clock
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------