...
 
Commits (4)
......@@ -11,11 +11,14 @@ import android.os.Build;
import android.util.Log;
import android.view.View;
import android.widget.Checkable;
import android.widget.NumberPicker;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.test.espresso.UiController;
import androidx.test.espresso.ViewAction;
import androidx.test.espresso.ViewInteraction;
import androidx.test.espresso.action.ViewActions;
import androidx.test.espresso.matcher.ViewMatchers;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SdkSuppress;
import androidx.test.rule.ActivityTestRule;
......@@ -26,6 +29,7 @@ import androidx.test.uiautomator.Until;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
......@@ -34,7 +38,6 @@ import org.junit.runner.RunWith;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static androidx.test.espresso.Espresso.onData;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu;
import static androidx.test.espresso.action.ViewActions.click;
......@@ -42,20 +45,17 @@ import static androidx.test.espresso.action.ViewActions.scrollTo;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isChecked;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withSpinnerText;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static androidx.work.testing.WorkManagerTestInitHelper.initializeTestWorkManager;
import static com.smilla.greentooth.GreenApplication.APP_KEY;
import static com.smilla.greentooth.GreenApplication.DEFAULT_DELAY;
import static com.smilla.greentooth.GreenApplication.DELAY_KEY;
import static com.smilla.greentooth.GreenApplication.ENABLED_KEY;
import static com.smilla.greentooth.GreenApplication.POST_DISABLE_NOTIFICATIONS_KEY;
import static com.smilla.greentooth.GreenApplication.PRE_DISABLE_NOTIFICATIONS_KEY;
import static com.smilla.greentooth.Util.sendNotification;
import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.isA;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals;
......@@ -120,6 +120,27 @@ public class InstrumentedTests {
};
}
public static ViewAction setNumberPickerValue(final int num) {
return new ViewAction() {
@Override
public void perform(UiController uiController, View view) {
NumberPicker np = (NumberPicker) view;
np.setValue(num);
}
@Override
public String getDescription() {
return "Set the passed number into the NumberPicker";
}
@Override
public Matcher<View> getConstraints() {
return ViewMatchers.isAssignableFrom(NumberPicker.class);
}
};
}
@Test
public void useAppContext() {
// Context of the app under test.
......@@ -201,27 +222,28 @@ public class InstrumentedTests {
assertFalse(notifEnabled);
}
@Test
public void userCanSelectTimeSpinnerOptions() {
onView(withId(R.id.timeSpinner)).perform(ViewActions.scrollTo());
for (String selectionText: targetContext.getResources().
getStringArray(R.array.wait_entries)) {
onView(withId(R.id.timeSpinner)).perform(click());
onData(allOf(is(instanceOf(String.class)), is(selectionText))).perform(click());
onView(withId(R.id.timeSpinner)).check(matches(withSpinnerText(containsString(selectionText))));
}
public void userCanSetDelays() {
final int targetSeconds = 33;
final int targetMinutes = 44;
setAndCheckDelay(0, 0);
setAndCheckDelay(targetSeconds, 0);
setAndCheckDelay(0, targetMinutes);
setAndCheckDelay(targetSeconds, targetMinutes);
}
@Test
public void settingsClickerWorksAsSpinner() {
onView(withId(R.id.timeSpinner)).perform(ViewActions.scrollTo());
for (String selectionText : targetContext.getResources().
getStringArray(R.array.wait_entries)) {
onView((withId(R.id.timeClicker))).perform(ViewActions.scrollTo()).perform(click());
onData(allOf(is(instanceOf(String.class)), is(selectionText))).perform(click());
onView((withId(R.id.timeSpinner))).perform(ViewActions.scrollTo())
.check(matches(withSpinnerText(containsString(selectionText))));
}
private void setAndCheckDelay(int targetSeconds, int targetMinutes) {
final int targetDelay = targetMinutes * 60 + targetSeconds;
String expectedString = Util.getDelayString(targetContext, targetDelay);
onView((withId(R.id.timeClicker))).perform(ViewActions.scrollTo()).perform(click());
ViewInteraction secondsInteraction = onView(withId(R.id.secondsPicker));
ViewInteraction minutesInteraction = onView(withId(R.id.minutesPicker));
secondsInteraction.perform(setNumberPickerValue(targetSeconds));
minutesInteraction.perform(setNumberPickerValue(targetMinutes));
onView(withText(R.string.okay)).perform(click());
assertEquals(targetDelay, sharedPreferences.getInt(DELAY_KEY, DEFAULT_DELAY));
onView(withId(R.id.delayValue)).check(matches(withText(expectedString)));
}
@Test
......@@ -231,7 +253,7 @@ public class InstrumentedTests {
onView(withText(R.string.about_menu_title)).perform(click());
onView(withId(android.R.id.message))
.check(matches(withText(containsString(targetContext.getString(R.string.about_string)))));
onView(withText(R.string.about_button_positive)).perform(click());
onView(withText(R.string.okay)).perform(click());
}
public void openThemesMenu() {
......@@ -306,11 +328,11 @@ public class InstrumentedTests {
mDevice.openNotification();
mDevice.wait(Until.hasObject(By.text(testTitle)), TIMEOUT);
//Expand notification if it starts collapsed
UiObject2 abortButton = mDevice.findObject(By.desc(targetContext.getString(R.string.abort_button)));
UiObject2 abortButton = mDevice.findObject(By.desc(targetContext.getString(R.string.abort_job_button)));
if (abortButton == null) {
UiObject2 expandButton = mDevice.findObject(By.res(EXPAND_BUTTON_RES_ID));
expandButton.click();
abortButton = mDevice.findObject(By.desc(targetContext.getString(R.string.abort_button)));
abortButton = mDevice.findObject(By.desc(targetContext.getString(R.string.abort_job_button)));
}
abortButton.click();
}
......
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<integer-array name="wait_values" tools:ignore="InconsistentArrays">
<item>0</item>
<item>20</item>
<item>30</item>
<item>60</item>
<item>90</item>
<item>120</item>
</integer-array>
</resources>
\ No newline at end of file
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="time_none">None</string>
<string name="time_20">20 seconds</string>
<string name="time_30">30 seconds</string>
<string name="time_60">1 minute</string>
<string name="time_90">1 minute 30 seconds</string>
<string name="time_120">2 minutes</string>
<string-array name="wait_entries" tools:ignore="InconsistentArrays">
<item>@string/time_none</item>
<item>@string/time_20</item>
<item>@string/time_30</item>
<item>@string/time_60</item>
<item>@string/time_90</item>
<item>@string/time_120</item>
</string-array>
</resources>
......@@ -33,7 +33,7 @@ public class AboutFragment extends DialogFragment {
.setCancelable(false)
.setTitle("About")
.setIcon(R.drawable.ic_launcher)
.setPositiveButton(R.string.about_button_positive, (dialogInterface, id) -> dialogInterface.dismiss());
.setPositiveButton(R.string.okay, (dialogInterface, id) -> dialogInterface.dismiss());
return builder.create();
}
......
......@@ -15,6 +15,7 @@ import androidx.work.WorkManager;
import java.util.concurrent.TimeUnit;
import static com.smilla.greentooth.GreenApplication.APP_KEY;
import static com.smilla.greentooth.GreenApplication.DEFAULT_DELAY;
import static com.smilla.greentooth.GreenApplication.DELAY_KEY;
import static com.smilla.greentooth.GreenApplication.ENABLED_KEY;
import static com.smilla.greentooth.GreenApplication.NOTIFICATION_TAG;
......@@ -34,7 +35,7 @@ public class BluetoothReceiver extends BroadcastReceiver {
boolean greentoothEnabled = sharedPreferences.getBoolean(ENABLED_KEY, false);
if (isBluetoothEnabled(bluetoothAdapter) && !isBluetoothConnected(bluetoothAdapter)
&& !bluetoothAdapter.isDiscovering() && greentoothEnabled) {
long waitTime = sharedPreferences.getInt(DELAY_KEY, 20);
long waitTime = sharedPreferences.getInt(DELAY_KEY, DEFAULT_DELAY);
OneTimeWorkRequest bluetoothWorkRequest = new OneTimeWorkRequest.Builder(
BluetoothWorker.class).setInitialDelay(waitTime, TimeUnit.SECONDS).
setBackoffCriteria(BackoffPolicy.LINEAR, OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
......
package com.smilla.greentooth;
import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;
import android.widget.NumberPicker;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.DialogFragment;
import java.util.Locale;
import static com.smilla.greentooth.GreenApplication.APP_KEY;
import static com.smilla.greentooth.GreenApplication.DELAY_KEY;
public class DelayFragment extends DialogFragment {
interface DelayInterface {
void updateDelayText(int delay);
void updateDelayText();
}
DelayInterface delayInterface;
@Override
@NonNull
public Dialog onCreateDialog(Bundle savedInstanceState) {
Activity activity = getActivity();
LayoutInflater layoutInflater = LayoutInflater.from(activity);
View view = layoutInflater.inflate(R.layout.delay_dialog, null);
NumberPicker minutesPicker = view.findViewById(R.id.minutesPicker);
NumberPicker secondsPicker = view.findViewById(R.id.secondsPicker);
SharedPreferences sharedPreferences = getActivity().getSharedPreferences(APP_KEY, 0);
minutesPicker.setMaxValue(GreenApplication.MAX_MINUTE_DELAY);
minutesPicker.setMinValue(GreenApplication.MIN_MINUTE_DELAY);
secondsPicker.setMaxValue(GreenApplication.MAX_SECOND_DELAY);
secondsPicker.setMinValue(GreenApplication.MIN_SECOND_DELAY);
//Show 01 instead of 1, etc...
NumberPicker.Formatter formatter = value -> String.format(Locale.getDefault(), "%02d", value);
minutesPicker.setFormatter(formatter);
secondsPicker.setFormatter(formatter);
int currentDelay = Util.getSaneDelay(sharedPreferences);
if (currentDelay > 0) {
int minutes = currentDelay / 60;
int seconds = currentDelay % 60;
minutesPicker.setValue(minutes);
secondsPicker.setValue(seconds);
}
/* Getting the EditText children and extracting the text from them allows the user to not have to
press "done" on the software keyboard to save their changes. */
int textId = Resources.getSystem().getIdentifier("numberpicker_input", "id", "android");
EditText minutesText = minutesPicker.findViewById(textId);
EditText secondsText = secondsPicker.findViewById(textId);
AlertDialog alertDialog = new AlertDialog.Builder(activity)
.setPositiveButton(R.string.okay, (dialog, which) -> {
minutesPicker.setValue(Integer.parseInt(minutesText.getText().toString()));
secondsPicker.setValue(Integer.parseInt(secondsText.getText().toString()));
int newDelay = minutesPicker.getValue() * 60 + secondsPicker.getValue();
sharedPreferences.edit().putInt(DELAY_KEY, newDelay).apply();
delayInterface.updateDelayText();
dialog.dismiss();
})
.setNegativeButton(R.string.cancel, null)
.create();
alertDialog.setView(view);
return alertDialog;
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
try {
delayInterface = (DelayInterface) context;
} catch (ClassCastException e) {
// The activity doesn't implement the interface, throw exception
throw new ClassCastException(getActivity().toString() + " must implement DelayInterface");
}
}
}
......@@ -19,7 +19,6 @@ public class GreenApplication extends Application {
public static final String DELAY_KEY = "waitTime";
public static final String PRE_DISABLE_NOTIFICATIONS_KEY = "preDisableNotificationsEnabled";
public static final String POST_DISABLE_NOTIFICATIONS_KEY = "postDisableNotificationsEnabled";
public static final String TIME_SPINNER_POSITION_KEY = "timeSpinnerPosition";
public static final String THEME_KEY = "theme";
public static final String PRE_DISABLE_CHANNEL_ID = "preDisableGreentoothChannel";
public static final String POST_DISABLE_CHANNEL_ID = "postDisableGreentoothChannel";
......@@ -31,6 +30,11 @@ public class GreenApplication extends Application {
public static final int NOTIFICATION_TYPE_PRE_DISABLE = 0;
public static final int NOTIFICATION_TYPE_POST_DISABLE = 1;
public static final int PRE_DISABLE_NOTIFICATION_ID = 999;
public static final int DEFAULT_DELAY = 20;
public static final int MAX_MINUTE_DELAY = 59;
public static final int MIN_MINUTE_DELAY = 0;
public static final int MAX_SECOND_DELAY = 59;
public static final int MIN_SECOND_DELAY = 0;
@Override
public void onCreate() {
......
......@@ -13,9 +13,7 @@ import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.Spinner;
import android.widget.TextView;
import androidx.annotation.NonNull;
......@@ -30,18 +28,17 @@ import com.google.android.material.bottomsheet.BottomSheetDialog;
import com.google.android.material.card.MaterialCardView;
import static com.smilla.greentooth.GreenApplication.APP_KEY;
import static com.smilla.greentooth.GreenApplication.DELAY_KEY;
import static com.smilla.greentooth.GreenApplication.ENABLED_KEY;
import static com.smilla.greentooth.GreenApplication.POST_DISABLE_NOTIFICATIONS_KEY;
import static com.smilla.greentooth.GreenApplication.PRE_DISABLE_NOTIFICATIONS_KEY;
import static com.smilla.greentooth.GreenApplication.THEME_KEY;
import static com.smilla.greentooth.GreenApplication.TIME_SPINNER_POSITION_KEY;
public class MainActivity extends AppCompatActivity {
public class MainActivity extends AppCompatActivity implements DelayFragment.DelayInterface {
private int shortAnimationDuration;
private SharedPreferences sharedPreferences;
private SwitchCompat onSwitch;
private Spinner timeSpinner;
private View timeClicker;
private TextView delayValue;
private SwitchCompat preDisableNotificationsSwitch;
private SwitchCompat postDisableNotificationsSwitch;
private BroadcastReceiver broadcastReceiver;
......@@ -58,7 +55,8 @@ public class MainActivity extends AppCompatActivity {
shortAnimationDuration = getResources().getInteger(android.R.integer.config_shortAnimTime);
sharedPreferences = this.getSharedPreferences(APP_KEY, 0);
onSwitch = findViewById(R.id.onSwitch);
timeSpinner = findViewById(R.id.timeSpinner);
timeClicker = findViewById(R.id.timeClicker);
delayValue = findViewById(R.id.delayValue);
preDisableNotificationsSwitch = findViewById(R.id.preDisableNotificationSwitch);
postDisableNotificationsSwitch = findViewById(R.id.postDisableNotificationSwitch);
......@@ -81,19 +79,10 @@ public class MainActivity extends AppCompatActivity {
MaterialCardView switchCard = findViewById(R.id.switchCard);
switchCard.setOnClickListener(v -> onSwitch.setChecked(!onSwitch.isChecked()));
timeSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
int[] values = getApplicationContext().getResources().getIntArray(R.array.wait_values);
sharedPreferences.edit().putInt(DELAY_KEY, values[position]).apply();
sharedPreferences.edit().putInt(TIME_SPINNER_POSITION_KEY, position).apply();
}
@Override
public void onNothingSelected(AdapterView<?> parent) {}
timeClicker.setOnClickListener(v -> {
DelayFragment delayFragment = new DelayFragment();
delayFragment.show(getSupportFragmentManager(), "CustomDurationFragment");
});
View timeClicker = findViewById(R.id.timeClicker);
timeClicker.setOnClickListener(v -> timeSpinner.performClick());
preDisableNotificationsSwitch.setOnCheckedChangeListener((buttonView, isChecked) ->
sharedPreferences.edit().putBoolean(PRE_DISABLE_NOTIFICATIONS_KEY, isChecked).apply());
......@@ -119,9 +108,10 @@ public class MainActivity extends AppCompatActivity {
@Override
protected void onResume() {
super.onResume();
timeSpinner.setSelection(sharedPreferences.getInt(TIME_SPINNER_POSITION_KEY, 0));
updateDelayText();
onSwitch.setChecked(sharedPreferences.getBoolean(ENABLED_KEY, false));
preDisableNotificationsSwitch.setChecked(sharedPreferences.getBoolean(PRE_DISABLE_NOTIFICATIONS_KEY, false));
postDisableNotificationsSwitch.setChecked(sharedPreferences.getBoolean(POST_DISABLE_NOTIFICATIONS_KEY, false));
}
@Override
......@@ -201,4 +191,12 @@ public class MainActivity extends AppCompatActivity {
public void disableOnSwitch() {
onSwitch.setChecked(false);
}
public void updateDelayText() {
updateDelayText(Util.getSaneDelay(sharedPreferences));
}
public void updateDelayText(int delay) {
delayValue.setText(Util.getDelayString(this, delay));
}
}
//TODO: CREATE WRAPPER FUNCTION FOR GETTING DELAY INT FROM SHAREDPREFERENCES
package com.smilla.greentooth;
import android.annotation.SuppressLint;
......@@ -7,13 +8,18 @@ import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.os.Build;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import static com.smilla.greentooth.GreenApplication.APP_KEY;
import static com.smilla.greentooth.GreenApplication.DEFAULT_DELAY;
import static com.smilla.greentooth.GreenApplication.DELAY_KEY;
import static com.smilla.greentooth.GreenApplication.LAST_NOTIFICATION_ID_KEY;
import static com.smilla.greentooth.GreenApplication.MAX_MINUTE_DELAY;
import static com.smilla.greentooth.GreenApplication.MAX_SECOND_DELAY;
import static com.smilla.greentooth.GreenApplication.NOTIFICATION_TYPE_PRE_DISABLE;
import static com.smilla.greentooth.GreenApplication.POST_DISABLE_CHANNEL_ID;
import static com.smilla.greentooth.GreenApplication.PRE_DISABLE_CHANNEL_ID;
......@@ -111,7 +117,7 @@ class Util {
Intent tempDisableIntent = new Intent(context, BluetoothReceiver.class);
tempDisableIntent.setAction(GreenApplication.ACTION_TEMP_DISABLE);
PendingIntent tempDisablePendingEvent = PendingIntent.getBroadcast(context, 0, tempDisableIntent, 0);
builder.addAction(0, context.getString(R.string.abort_button), tempDisablePendingEvent);
builder.addAction(0, context.getString(R.string.abort_job_button), tempDisablePendingEvent);
Intent disableIntent = new Intent(context, BluetoothReceiver.class);
disableIntent.setAction(GreenApplication.ACTION_DISABLE);
......@@ -135,4 +141,35 @@ class Util {
return id;
}
public static String getDelayString(Context context, int delay) {
Resources res = context.getResources();
if (delay == 0) {
return res.getString(R.string.none);
}
int minutes = delay / 60;
int seconds = delay % 60;
String minutesString = res.getQuantityString(R.plurals.minutes_delay, minutes, minutes);
String secondsString = res.getQuantityString(R.plurals.seconds_delay, seconds, seconds);
String resultString;
if ((minutes > 0) && (seconds > 0)) {
resultString = String.format("%s %s", minutesString, secondsString);
} else if (minutes > 0) {
resultString = String.format("%s", minutesString);
} else {
resultString = String.format("%s", secondsString);
}
return resultString;
}
public static int getSaneDelay(SharedPreferences sharedPreferences) {
int delay = sharedPreferences.getInt(DELAY_KEY, DEFAULT_DELAY);
int maxDelay = MAX_MINUTE_DELAY * 60 + MAX_SECOND_DELAY;
if (delay > maxDelay) {
delay = maxDelay;
} else if (delay < 0) {
delay = 0;
}
return delay;
}
}
......@@ -105,9 +105,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/waitTitle"
android:id="@+id/delayTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
......@@ -121,7 +120,7 @@
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/waitText"
android:id="@+id/delayDesc"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
......@@ -129,28 +128,10 @@
android:paddingStart="8dp"
android:paddingLeft="8dp"
android:text="@string/wait_desc"
app:layout_constraintEnd_toStartOf="@+id/timeSpinner"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/waitTitle"
app:layout_constraintTop_toBottomOf="@+id/delayTitle"
tools:ignore="RtlSymmetry" />
<Spinner
android:id="@+id/timeSpinner"
style="@style/Widget.AppCompat.Spinner"
android:layout_width="@dimen/_130sdp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginBottom="8dp"
android:contentDescription="@string/minimum_delay"
android:duplicateParentState="false"
android:entries="@array/wait_entries"
android:spinnerMode="dialog"
app:layout_constraintBottom_toTopOf="@id/divider1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/waitTitle"
app:layout_constraintVertical_bias="0.75" />
<View
android:id="@+id/timeClicker"
android:layout_width="0dp"
......@@ -171,14 +152,12 @@
android:layout_marginLeft="16dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:alpha="0.2"
android:background="@color/colorOnSurface"
android:contentDescription="@null"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/waitText"
android:contentDescription="@null"/>
app:layout_constraintTop_toBottomOf="@+id/delayValue" />
<TextView
android:id="@+id/preDisableNotificationTitle"
......@@ -310,6 +289,20 @@
android:orientation="vertical"
app:layout_constraintGuide_percent="0.74" />
<TextView
android:id="@+id/delayValue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:padding="8dp"
android:text="20 seconds"
android:textAllCaps="false"
android:textColor="@color/colorSecondary"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/delayDesc" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
......
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/minutesText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/minutes_uppercase"
app:layout_constraintBottom_toTopOf="@+id/minutesPicker"
app:layout_constraintEnd_toEndOf="@+id/minutesPicker"
app:layout_constraintStart_toStartOf="@+id/minutesPicker"
app:layout_constraintTop_toBottomOf="@+id/title" />
<TextView
android:id="@+id/secondsText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/seconds_uppercase"
app:layout_constraintBottom_toTopOf="@+id/secondsPicker"
app:layout_constraintEnd_toEndOf="@+id/secondsPicker"
app:layout_constraintStart_toStartOf="@+id/secondsPicker"
app:layout_constraintTop_toBottomOf="@+id/title" />
<TextView
android:id="@+id/separator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/duration_separator"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="@+id/secondsPicker"
app:layout_constraintEnd_toStartOf="@+id/secondsPicker"
app:layout_constraintStart_toEndOf="@+id/minutesPicker"
app:layout_constraintTop_toBottomOf="@+id/secondsText" />
<NumberPicker
android:id="@+id/minutesPicker"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
app:layout_constraintEnd_toStartOf="@+id/guideline2"
app:layout_constraintTop_toBottomOf="@+id/minutesText" />
<NumberPicker
android:id="@+id/secondsPicker"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
app:layout_constraintStart_toEndOf="@+id/guideline2"
app:layout_constraintTop_toBottomOf="@+id/secondsText" />
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="0dp"
android:background="@color/primaryColor"
android:paddingLeft="16dp"
android:paddingTop="8dp"
android:paddingRight="16dp"
android:paddingBottom="8dp"
android:text="@string/wait_title"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
android:textColor="@color/onPrimaryColor"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/editTextNumber"
android:layout_width="@dimen/_80sdp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:ems="10"
android:hint="@string/minutes_uppercase"
android:inputType="number"
android:visibility="gone"
app:layout_constraintEnd_toStartOf="@+id/guideline2"
app:layout_constraintTop_toBottomOf="@id/title" />
<EditText
android:id="@+id/editTextNumber2"
android:layout_width="@dimen/_80sdp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:ems="10"
android:hint="@string/seconds_uppercase"
android:inputType="number"
android:visibility="gone"
app:layout_constraintStart_toStartOf="@+id/guideline2"
app:layout_constraintTop_toBottomOf="@id/title" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
......@@ -57,7 +57,7 @@
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginRight="16dp"
android:text="@string/help_button_text"
android:text="@string/okay"
android:layout_marginBottom="8dp"
android:layout_marginEnd="16dp" />
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<integer-array name="wait_values">
<item>20</item>
<item>30</item>
<item>60</item>
<item>90</item>
<item>120</item>
</integer-array>
</resources>
\ No newline at end of file
<resources>
<string name="app_name">Greentooth</string>
<string name="time_20">20 seconds</string>
<string name="time_30">30 seconds</string>
<string name="time_60">1 minute</string>
<string name="time_90">1 minute 30 seconds</string>
<string name="time_120">2 minutes</string>
<string-array name="wait_entries">
<item>@string/time_20</item>
<item>@string/time_30</item>
<item>@string/time_60</item>
<item>@string/time_90</item>
<item>@string/time_120</item>
</string-array>
<string name="help_1">Greentooth can help you save battery and mitigate the security risks of Bluetooth by
automatically turning Bluetooth off when you\'re no longer using it.</string>
<string name="help_2">When the last Bluetooth device has disconnected Greentooth will wait for an adjustable amount
......@@ -24,7 +12,6 @@
<string name="pre_disable_channel_description">Notifications sent before Bluetooth is disabled</string>
<string name="about_string">\n\nCopyright 2020 Nicklas Bergman\nSPDX-License-Identifier: Apache-2.0\n\n
https://gitlab.com/nbergman/greentooth/</string>
<string name="about_button_positive">OK</string>
<string name="about_menu_title">About</string>
<string name="themes_menu">Theme</string>
<string name="light_theme">Light</string>
......@@ -32,22 +19,39 @@
<string name="default_theme">System default</string>
<string name="switch_description">Enable Greentooth to automatically turn Bluetooth off when the last device has disconnected.</string>
<string name="wait_title">Delay</string>
<string name="wait_desc">Minimum delay before Bluetooth is turned off. </string>
<string name="wait_desc">Minimum delay before Bluetooth is turned off: </string>
<string name="pre_disabling_notifications">Notify before disabling</string>
<string name="pre_disabling_notifications_desc">Send a low-priority notification before Bluetooth is turned off, with options to keep Bluetooth on temporarily or disable Greentooth entirely.</string>
<string name="pre_disabling_notifications_desc">Send a low-priority notification before Bluetooth is turned off, with options to keep
Bluetooth on temporarily or disable Greentooth entirely.</string>
<string name="post_disabling_notifications">Notify after disabling</string>
<string name="post_disabling_notifications_desc">Send a notification after Bluetooth has been turned off.</string>
<string name="enabled">Enabled</string>
<string name="disabled">Disabled</string>
<string name="enabled_desc">Greentooth is enabled and will turn off Bluetooth when it is no longer in use.</string>
<string name="disabled_desc">Enable Greentooth to automatically turn Bluetooth off when the last device has disconnected and a small delay has passed.</string>
<string name="disabled_desc">Enable Greentooth to automatically turn Bluetooth off when the last device has disconnected and a small
delay has passed.</string>
<string name="help">Help</string>
<string name="help_button_text">OK</string>
<string name="okay">OK</string>
<string name="notification_title">Bluetooth disabled</string>
<string name="notification_body">No devices connected.</string>
<string name="enable_greentooth">Enable Greentooth</string>
<string name="minimum_delay">Minimum delay</string>
<string name="enable_notifications">Enable notifications</string>
<string name="abort_button">Keep Bluetooth on</string>
<string name="abort_job_button">Keep Bluetooth on</string>
<string name="disable_button">Disable Greentooth</string>
<string name="seconds">seconds</string>
<string name="minutes">minutes</string>
<string name="seconds_uppercase">Seconds</string>
<string name="minutes_uppercase">Minutes</string>
<string name="none">None</string>
<string name="duration_separator">:</string>
<string name="cancel">Cancel</string>
<plurals name="minutes_delay">
<item quantity="one">%d minute</item>
<item quantity="other">%d minutes</item>
</plurals>
<plurals name="seconds_delay">
<item quantity="one">%d second</item>
<item quantity="other">%d seconds</item>
</plurals>
</resources>
......@@ -9,6 +9,7 @@
<item name="colorPrimaryDark">@color/primaryDarkColor</item>
<item name="colorOnPrimarySurface">@color/onPrimarySurfaceColor</item>
<item name="colorSurface">@color/colorSurface</item>
<item name="alertDialogTheme">@style/AlertDialogTheme</item>
</style>
<style name="ThemeOverlay_Toolbar" parent="ThemeOverlay.MaterialComponents.Toolbar.Primary">
......@@ -26,4 +27,17 @@
<style name="CustomSwitch" parent="AppTheme">
<item name="colorControlActivated">@color/switchThumbDisabledColor</item>
</style>
<style name="AlertDialogTheme" parent="Theme.MaterialComponents.DayNight.Dialog.Alert">
<item name="buttonBarPositiveButtonStyle">@style/Alert.Button.Positive</item>
<item name="buttonBarNegativeButtonStyle">@style/Alert.Button.Negative</item>
</style>
<style name="Alert.Button.Positive" parent="Widget.MaterialComponents.Button.TextButton">
<item name="android:textColor">@color/colorSecondary</item>
</style>
<style name="Alert.Button.Negative" parent="Widget.MaterialComponents.Button.TextButton">
<item name="android:textColor">@color/colorSecondary</item>
</style>
</resources>