Skip to content

Commit ed77e1e

Browse files
authored
Merge pull request #215 from amorris13/split-button-main-screen-16930450979114081208
Implement Split Button for Game Resumption on Main Screen
2 parents e5ceff7 + 5cbbb6c commit ed77e1e

40 files changed

Lines changed: 140 additions & 45 deletions

app/src/main/java/com/antsapps/triples/MainActivity.java

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ public class MainActivity extends BaseTriplesActivity {
2727
private MaterialButton mArcadeResumeButton;
2828
private MaterialButton mClassicNewGameButton;
2929
private MaterialButton mArcadeNewGameButton;
30+
private com.google.android.material.button.MaterialButtonToggleGroup mClassicSplitButtonGroup;
31+
private com.google.android.material.button.MaterialButtonToggleGroup mArcadeSplitButtonGroup;
32+
private MaterialButton mClassicDropdownButton;
33+
private MaterialButton mArcadeDropdownButton;
3034
private MaterialButton mDailyPlayButton;
3135
private MaterialButton mDailyStatisticsButton;
3236
private android.widget.TextView mDailyCompletedText;
@@ -48,6 +52,7 @@ protected void onCreate(Bundle savedInstanceState) {
4852
mViewModel = new ViewModelProvider(this).get(MainViewModel.class);
4953
mViewModel.init(mApplication);
5054

55+
mClassicSplitButtonGroup = findViewById(R.id.classic_split_button_group);
5156
mClassicResumeButton = findViewById(R.id.classic_resume_button);
5257
mClassicResumeButton.setOnClickListener(
5358
v ->
@@ -56,12 +61,16 @@ protected void onCreate(Bundle savedInstanceState) {
5661
ClassicGameActivity.class,
5762
ClassicGame.GAME_TYPE_FOR_ANALYTICS));
5863

64+
mClassicDropdownButton = findViewById(R.id.classic_dropdown_button);
65+
mClassicDropdownButton.setOnClickListener(v -> showSplitMenu(v, this::startNewClassicGame));
66+
5967
mClassicNewGameButton = findViewById(R.id.classic_new_game_button);
6068
mClassicNewGameButton.setOnClickListener(v -> startNewClassicGame());
6169

6270
mClassicStatisticsButton = findViewById(R.id.classic_statistics_button);
6371
mClassicStatisticsButton.setOnClickListener(v -> showStatistics("Classic"));
6472

73+
mArcadeSplitButtonGroup = findViewById(R.id.arcade_split_button_group);
6574
mArcadeResumeButton = findViewById(R.id.arcade_resume_button);
6675
mArcadeResumeButton.setOnClickListener(
6776
v ->
@@ -70,6 +79,9 @@ protected void onCreate(Bundle savedInstanceState) {
7079
ArcadeGameActivity.class,
7180
ArcadeGame.GAME_TYPE_FOR_ANALYTICS));
7281

82+
mArcadeDropdownButton = findViewById(R.id.arcade_dropdown_button);
83+
mArcadeDropdownButton.setOnClickListener(v -> showSplitMenu(v, this::startNewArcadeGame));
84+
7385
mArcadeNewGameButton = findViewById(R.id.arcade_new_game_button);
7486
mArcadeNewGameButton.setOnClickListener(v -> startNewArcadeGame());
7587

@@ -124,12 +136,13 @@ protected void onCreate(Bundle savedInstanceState) {
124136
this,
125137
state -> {
126138
if (state.visible) {
127-
mClassicResumeButton.setVisibility(View.VISIBLE);
139+
mClassicSplitButtonGroup.setVisibility(View.VISIBLE);
140+
mClassicNewGameButton.setVisibility(View.GONE);
128141
mClassicResumeButton.setText(
129142
getString(R.string.resume_game_classic_format, state.cardsRemaining));
130-
mClassicNewGameButton.setText(R.string.start_again);
131143
} else {
132-
mClassicResumeButton.setVisibility(View.GONE);
144+
mClassicSplitButtonGroup.setVisibility(View.GONE);
145+
mClassicNewGameButton.setVisibility(View.VISIBLE);
133146
mClassicNewGameButton.setText(R.string.new_game);
134147
}
135148
});
@@ -140,12 +153,13 @@ protected void onCreate(Bundle savedInstanceState) {
140153
this,
141154
state -> {
142155
if (state.visible) {
143-
mArcadeResumeButton.setVisibility(View.VISIBLE);
156+
mArcadeSplitButtonGroup.setVisibility(View.VISIBLE);
157+
mArcadeNewGameButton.setVisibility(View.GONE);
144158
mArcadeResumeButton.setText(
145159
getString(R.string.resume_game_arcade_format, state.triplesFound));
146-
mArcadeNewGameButton.setText(R.string.start_again);
147160
} else {
148-
mArcadeResumeButton.setVisibility(View.GONE);
161+
mArcadeSplitButtonGroup.setVisibility(View.GONE);
162+
mArcadeNewGameButton.setVisibility(View.VISIBLE);
149163
mArcadeNewGameButton.setText(R.string.new_game);
150164
}
151165
});
@@ -219,6 +233,21 @@ private void startNewArcadeGame() {
219233
launchGame(intent, ArcadeGame.GAME_TYPE_FOR_ANALYTICS, AnalyticsConstants.Event.NEW_GAME);
220234
}
221235

236+
private void showSplitMenu(View v, Runnable onStartAgain) {
237+
androidx.appcompat.widget.PopupMenu popup =
238+
new androidx.appcompat.widget.PopupMenu(this, v, android.view.Gravity.END);
239+
popup.getMenuInflater().inflate(R.menu.split_button_dropdown, popup.getMenu());
240+
popup.setOnMenuItemClickListener(
241+
item -> {
242+
if (item.getItemId() == R.id.start_again) {
243+
onStartAgain.run();
244+
return true;
245+
}
246+
return false;
247+
});
248+
popup.show();
249+
}
250+
222251
private void showStatistics(String gameType) {
223252
Intent intent = new Intent(this, StatisticsActivity.class);
224253
intent.putExtra(StatisticsActivity.GAME_TYPE, gameType);

app/src/main/res/layout/activity_main.xml

Lines changed: 76 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -36,33 +36,53 @@
3636
android:text="@string/classic_label"
3737
android:textColor="@color/classic_accent" />
3838

39-
<LinearLayout
39+
<FrameLayout
4040
android:layout_width="match_parent"
4141
android:layout_height="wrap_content"
42-
android:layout_marginTop="16dp"
43-
android:orientation="horizontal">
44-
45-
<com.google.android.material.button.MaterialButton
46-
android:id="@+id/classic_resume_button"
47-
style="@style/Widget.Material3.Button"
48-
android:layout_width="0dp"
49-
android:layout_height="wrap_content"
50-
android:layout_marginEnd="4dp"
51-
android:layout_weight="1"
52-
android:text="@string/resume_game"
53-
android:theme="@style/ClassicButtonTheme"
54-
android:visibility="gone" />
42+
android:layout_marginTop="16dp">
5543

5644
<com.google.android.material.button.MaterialButton
5745
android:id="@+id/classic_new_game_button"
5846
style="@style/Widget.Material3.Button"
59-
android:layout_width="0dp"
47+
android:layout_width="match_parent"
6048
android:layout_height="wrap_content"
61-
android:layout_marginStart="4dp"
62-
android:layout_weight="1"
6349
android:text="@string/new_game"
6450
android:theme="@style/ClassicButtonTheme" />
65-
</LinearLayout>
51+
52+
<com.google.android.material.button.MaterialButtonToggleGroup
53+
android:id="@+id/classic_split_button_group"
54+
android:layout_width="match_parent"
55+
android:layout_height="wrap_content"
56+
android:visibility="gone"
57+
app:singleSelection="true">
58+
59+
<com.google.android.material.button.MaterialButton
60+
android:id="@+id/classic_resume_button"
61+
style="@style/Widget.Material3.Button"
62+
android:layout_width="0dp"
63+
android:layout_height="wrap_content"
64+
android:layout_marginEnd="2dp"
65+
android:layout_weight="1"
66+
android:text="@string/resume_game"
67+
android:theme="@style/ClassicButtonTheme"
68+
app:shapeAppearanceOverlay="@style/SplitButtonLeft" />
69+
70+
<com.google.android.material.button.MaterialButton
71+
android:id="@+id/classic_dropdown_button"
72+
style="@style/Widget.Material3.Button"
73+
android:layout_width="48dp"
74+
android:layout_height="wrap_content"
75+
android:insetLeft="0dp"
76+
android:insetRight="0dp"
77+
android:padding="0dp"
78+
android:theme="@style/ClassicButtonTheme"
79+
app:icon="@drawable/ic_expand_more"
80+
app:iconGravity="textStart"
81+
app:iconPadding="0dp"
82+
app:shapeAppearanceOverlay="@style/SplitButtonRight" />
83+
84+
</com.google.android.material.button.MaterialButtonToggleGroup>
85+
</FrameLayout>
6686

6787
<com.google.android.material.button.MaterialButton
6888
android:id="@+id/classic_statistics_button"
@@ -94,33 +114,53 @@
94114
android:text="@string/arcade_label"
95115
android:textColor="@color/arcade_accent" />
96116

97-
<LinearLayout
117+
<FrameLayout
98118
android:layout_width="match_parent"
99119
android:layout_height="wrap_content"
100-
android:layout_marginTop="16dp"
101-
android:orientation="horizontal">
102-
103-
<com.google.android.material.button.MaterialButton
104-
android:id="@+id/arcade_resume_button"
105-
style="@style/Widget.Material3.Button"
106-
android:layout_width="0dp"
107-
android:layout_height="wrap_content"
108-
android:layout_marginEnd="4dp"
109-
android:layout_weight="1"
110-
android:text="@string/resume_game"
111-
android:theme="@style/ArcadeButtonTheme"
112-
android:visibility="gone" />
120+
android:layout_marginTop="16dp">
113121

114122
<com.google.android.material.button.MaterialButton
115123
android:id="@+id/arcade_new_game_button"
116124
style="@style/Widget.Material3.Button"
117-
android:layout_width="0dp"
125+
android:layout_width="match_parent"
118126
android:layout_height="wrap_content"
119-
android:layout_marginStart="4dp"
120-
android:layout_weight="1"
121127
android:text="@string/new_game"
122128
android:theme="@style/ArcadeButtonTheme" />
123-
</LinearLayout>
129+
130+
<com.google.android.material.button.MaterialButtonToggleGroup
131+
android:id="@+id/arcade_split_button_group"
132+
android:layout_width="match_parent"
133+
android:layout_height="wrap_content"
134+
android:visibility="gone"
135+
app:singleSelection="true">
136+
137+
<com.google.android.material.button.MaterialButton
138+
android:id="@+id/arcade_resume_button"
139+
style="@style/Widget.Material3.Button"
140+
android:layout_width="0dp"
141+
android:layout_height="wrap_content"
142+
android:layout_marginEnd="2dp"
143+
android:layout_weight="1"
144+
android:text="@string/resume_game"
145+
android:theme="@style/ArcadeButtonTheme"
146+
app:shapeAppearanceOverlay="@style/SplitButtonLeft" />
147+
148+
<com.google.android.material.button.MaterialButton
149+
android:id="@+id/arcade_dropdown_button"
150+
style="@style/Widget.Material3.Button"
151+
android:layout_width="48dp"
152+
android:layout_height="wrap_content"
153+
android:insetLeft="0dp"
154+
android:insetRight="0dp"
155+
android:padding="0dp"
156+
android:theme="@style/ArcadeButtonTheme"
157+
app:icon="@drawable/ic_expand_more"
158+
app:iconGravity="textStart"
159+
app:iconPadding="0dp"
160+
app:shapeAppearanceOverlay="@style/SplitButtonRight" />
161+
162+
</com.google.android.material.button.MaterialButtonToggleGroup>
163+
</FrameLayout>
124164

125165
<com.google.android.material.button.MaterialButton
126166
android:id="@+id/arcade_statistics_button"
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<menu xmlns:android="http://schemas.android.com/apk/res/android">
3+
<item
4+
android:id="@+id/start_again"
5+
android:title="@string/start_again" />
6+
</menu>

app/src/main/res/values/styles.xml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,4 +112,19 @@
112112
<item name="android:paddingTop">8dp</item>
113113
<item name="android:paddingBottom">4dp</item>
114114
</style>
115+
116+
<style name="SplitButtonLeft" parent="">
117+
<item name="cornerFamily">rounded</item>
118+
<item name="cornerSize">20dp</item>
119+
<item name="cornerSizeTopRight">0dp</item>
120+
<item name="cornerSizeBottomRight">0dp</item>
121+
</style>
122+
123+
<style name="SplitButtonRight" parent="">
124+
<item name="cornerFamily">rounded</item>
125+
<item name="cornerSize">20dp</item>
126+
<item name="cornerSizeTopLeft">0dp</item>
127+
<item name="cornerSizeBottomLeft">0dp</item>
128+
</style>
129+
115130
</resources>

app/src/test/java/com/antsapps/triples/NavigationTest.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -263,8 +263,11 @@ public void testMainActivityReactiveUpdates() {
263263
try (ActivityScenario<MainActivity> scenario = ActivityScenario.launch(MainActivity.class)) {
264264
scenario.onActivity(
265265
activity -> {
266+
android.view.View splitGroup = activity.findViewById(R.id.classic_split_button_group);
267+
Button newGameButton = activity.findViewById(R.id.classic_new_game_button);
266268
Button resumeButton = activity.findViewById(R.id.classic_resume_button);
267-
assertThat(resumeButton.getVisibility()).isEqualTo(android.view.View.GONE);
269+
assertThat(splitGroup.getVisibility()).isEqualTo(android.view.View.GONE);
270+
assertThat(newGameButton.getVisibility()).isEqualTo(android.view.View.VISIBLE);
268271

269272
// Add a game in progress reactively
270273
ClassicGame game = ClassicGame.createFromSeed(12345L);
@@ -277,7 +280,8 @@ public void testMainActivityReactiveUpdates() {
277280
org.robolectric.shadows.ShadowLooper.idleMainLooper();
278281

279282
// Button should now be visible without activity restart/onResume
280-
assertThat(resumeButton.getVisibility()).isEqualTo(android.view.View.VISIBLE);
283+
assertThat(splitGroup.getVisibility()).isEqualTo(android.view.View.VISIBLE);
284+
assertThat(newGameButton.getVisibility()).isEqualTo(android.view.View.GONE);
281285
assertThat(resumeButton.getText().toString())
282286
.contains(String.valueOf(game.getCardsRemaining()));
283287

@@ -297,7 +301,8 @@ public void testMainActivityReactiveUpdates() {
297301
org.robolectric.shadows.ShadowLooper.idleMainLooper();
298302

299303
// Button should disappear
300-
assertThat(resumeButton.getVisibility()).isEqualTo(android.view.View.GONE);
304+
assertThat(splitGroup.getVisibility()).isEqualTo(android.view.View.GONE);
305+
assertThat(newGameButton.getVisibility()).isEqualTo(android.view.View.VISIBLE);
301306
});
302307
}
303308
}
80 Bytes
Loading
-47 Bytes
Loading
80 Bytes
Loading
-31 Bytes
Loading
2 Bytes
Loading

0 commit comments

Comments
 (0)