media, FilesReceiver receiver) {
39 | if (media.isEmpty()) activity.toast(R.string.toast_media_not_selected);
40 | else receiver.provide(media);
41 | activity.chime(RESUME);
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/event/EventScheduler.java:
--------------------------------------------------------------------------------
1 | package net.emilla.event;
2 |
3 | import android.app.AlarmManager;
4 | import android.app.PendingIntent;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.os.Build;
8 |
9 | import net.emilla.util.Services;
10 |
11 | public abstract class EventScheduler {
12 |
13 | protected final Context context;
14 | private final AlarmManager mAlarmManager;
15 |
16 | public EventScheduler(Context ctx) {
17 | context = ctx;
18 | mAlarmManager = Services.alarm(ctx);
19 | }
20 |
21 | public final void plan(P plan) {
22 | var pendingIntent = pendingIntentFor(plan);
23 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S || mAlarmManager.canScheduleExactAlarms()) {
24 | mAlarmManager.setExact(AlarmManager.RTC_WAKEUP, plan.time, pendingIntent);
25 | } else mAlarmManager.set(AlarmManager.RTC_WAKEUP, plan.time, pendingIntent);
26 | // Todo: communicate which will happen in the settings.
27 | }
28 |
29 | public final void cancel(P plan) {
30 | mAlarmManager.cancel(pendingIntentFor(plan));
31 | }
32 |
33 | private PendingIntent pendingIntentFor(P plan) {
34 | int flags = PendingIntent.FLAG_UPDATE_CURRENT
35 | | PendingIntent.FLAG_IMMUTABLE;
36 | return PendingIntent.getBroadcast(context, plan.slot, intentFor(plan), flags);
37 | }
38 |
39 | protected abstract Intent intentFor(P plan);
40 | }
41 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/event/PingPlan.java:
--------------------------------------------------------------------------------
1 | package net.emilla.event;
2 |
3 | import android.app.Notification;
4 |
5 | public final class PingPlan extends Plan {
6 |
7 | public final Notification ping;
8 | public final String channel;
9 |
10 | public PingPlan(int slot, long time, Notification ping, String channel) {
11 | super(slot, time);
12 |
13 | this.ping = ping;
14 | this.channel = channel;
15 | }
16 |
17 | public static PingPlan afterSeconds(int slot, int seconds, Notification ping, String channel) {
18 | return new PingPlan(slot, System.currentTimeMillis() + seconds * 1000L, ping, channel);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/event/PingReceiver.java:
--------------------------------------------------------------------------------
1 | package net.emilla.event;
2 |
3 | import static net.emilla.BuildConfig.DEBUG;
4 |
5 | import android.annotation.SuppressLint;
6 | import android.content.BroadcastReceiver;
7 | import android.content.Context;
8 | import android.content.Intent;
9 | import android.util.Log;
10 |
11 | import net.emilla.ping.PingIntent;
12 | import net.emilla.ping.Pinger;
13 | import net.emilla.util.Permissions;
14 |
15 | public final class PingReceiver extends BroadcastReceiver {
16 |
17 | private static final String TAG = PingReceiver.class.getSimpleName();
18 |
19 | @Override @SuppressLint("MissingPermission")
20 | public void onReceive(Context ctx, Intent intent) {
21 | if (Permissions.pings(ctx)) Pinger.of(ctx, new PingIntent(intent)).ping();
22 | else if (DEBUG) Log.e(TAG, "Unable to ping due to lack of permission.");
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/event/PingScheduler.java:
--------------------------------------------------------------------------------
1 | package net.emilla.event;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 |
6 | import net.emilla.ping.PingIntent;
7 |
8 | public final class PingScheduler extends EventScheduler {
9 |
10 | public PingScheduler(Context ctx) {
11 | super(ctx);
12 | }
13 |
14 | @Override
15 | protected Intent intentFor(PingPlan plan) {
16 | return new PingIntent(context, plan.ping, plan.channel);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/event/Plan.java:
--------------------------------------------------------------------------------
1 | package net.emilla.event;
2 |
3 | public /*open*/ class Plan {
4 |
5 | public static final int
6 | POMODORO_WARNING = 1,
7 | POMODORO_ENDED = 2;
8 |
9 | public final int slot;
10 | public final long time;
11 |
12 | public Plan(int slot, long time) {
13 | this.slot = slot;
14 | this.time = time;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/exception/EmillaException.java:
--------------------------------------------------------------------------------
1 | package net.emilla.exception;
2 |
3 | import androidx.annotation.StringRes;
4 |
5 | public final class EmillaException extends RuntimeException {
6 |
7 | @StringRes
8 | public final int title, message;
9 |
10 | public EmillaException(@StringRes int title, @StringRes int message) {
11 | super();
12 |
13 | this.title = title;
14 | this.message = message;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/lang/date/Duration.java:
--------------------------------------------------------------------------------
1 | package net.emilla.lang.date;
2 |
3 | import androidx.annotation.StringRes;
4 |
5 | import net.emilla.R;
6 | import net.emilla.exception.EmillaException;
7 |
8 | public final class Duration {
9 |
10 | public final int seconds;
11 |
12 | public Duration(int seconds, @StringRes int errorTitle) {
13 | if (seconds <= 0) throw new EmillaException(errorTitle, R.string.error_bad_minutes);
14 | this.seconds = seconds;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/lang/date/HourMin.java:
--------------------------------------------------------------------------------
1 | package net.emilla.lang.date;
2 |
3 | public interface HourMin {
4 |
5 | int hour24();
6 | int minute();
7 | }
8 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/lang/date/Weekdays.java:
--------------------------------------------------------------------------------
1 | package net.emilla.lang.date;
2 |
3 | import java.util.ArrayList;
4 |
5 | public interface Weekdays {
6 |
7 | boolean empty();
8 | ArrayList days();
9 | }
10 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/lang/date/impl/DurationEN_US.java:
--------------------------------------------------------------------------------
1 | package net.emilla.lang.date.impl;
2 |
3 | import static java.lang.Double.parseDouble;
4 |
5 | import androidx.annotation.StringRes;
6 |
7 | import net.emilla.R;
8 | import net.emilla.exception.EmillaException;
9 | import net.emilla.lang.date.Duration;
10 |
11 | public final class DurationEN_US {
12 |
13 | public static Duration instance(String minutes, @StringRes int errorTitle) {
14 | try {
15 | int seconds = (int) (parseDouble(minutes) * 60.0);
16 | // Todo: other time units, clock notation.
17 | return new Duration(seconds, errorTitle);
18 | } catch (NumberFormatException e) {
19 | throw new EmillaException(errorTitle, R.string.error_bad_minutes);
20 | }
21 | }
22 |
23 | private DurationEN_US() {}
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/lang/grammar/ListPhrase.java:
--------------------------------------------------------------------------------
1 | package net.emilla.lang.grammar;
2 |
3 | @FunctionalInterface
4 | public interface ListPhrase {
5 |
6 | String[] items();
7 | }
8 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/lang/grammar/impl/ListPhraseEN_US.java:
--------------------------------------------------------------------------------
1 | package net.emilla.lang.grammar.impl;
2 |
3 | import net.emilla.lang.grammar.ListPhrase;
4 |
5 | import java.util.regex.Pattern;
6 |
7 | public final class ListPhraseEN_US implements ListPhrase {
8 |
9 | private static final String
10 | CONJUNCTION = "(?i)(?<=[^\\s,])\\s+(and|&)\\s+(?=[^\\s,])",
11 | COORDINATION = "(?<=[^\\s,]),[\\s,]*(?=[^\\s,])",
12 | SERIAL = "(?i)(?<=\\S),[\\s,]*(and|&)\\s+(?=[^\\s,])";
13 |
14 | private final String[] mItems;
15 |
16 | public ListPhraseEN_US(String phrase) {
17 | // todo: this may incorrectly destroy tokens like "red, and blue" -> "red" "blue" instead of
18 | // "red" "and blue". the approach doesn't permit various list interpretations such as
19 | // "items with commas", but it will suffice for now.
20 | // don't put commas in your contact names u jackal :P
21 |
22 | var coordination = Pattern.compile(COORDINATION);
23 | if (coordination.matcher(phrase).find()) {
24 | mItems = phrase.split(SERIAL + "|" + COORDINATION);
25 | return;
26 | }
27 |
28 | var conjunction = Pattern.compile(CONJUNCTION);
29 | if (conjunction.matcher(phrase).find()) {
30 | mItems = conjunction.split(phrase);
31 | return;
32 | }
33 |
34 | mItems = new String[]{phrase};
35 | }
36 |
37 | @Override
38 | public String[] items() {
39 | return mItems;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/lang/measure/CelsiusConversion.kt:
--------------------------------------------------------------------------------
1 | package net.emilla.lang.measure
2 |
3 | data class CelsiusConversion(@JvmField val degrees: Double, @JvmField val fromKelvin: Boolean) {
4 |
5 | fun convert() = if (fromKelvin) degrees - 273.15 else (degrees - 32.0) * 5.0 / 9.0
6 | }
7 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/lang/measure/FahrenheitConversion.kt:
--------------------------------------------------------------------------------
1 | package net.emilla.lang.measure
2 |
3 | data class FahrenheitConversion(@JvmField val degrees: Double, @JvmField val fromKelvin: Boolean) {
4 |
5 | fun convert(): Double = if (fromKelvin) degrees - 459.67 else degrees * 9.0 / 5.0 + 32.0
6 | }
7 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/lang/measure/impl/CelsiusConversionEN_US.kt:
--------------------------------------------------------------------------------
1 | package net.emilla.lang.measure.impl
2 |
3 | import androidx.annotation.StringRes
4 | import net.emilla.R
5 | import net.emilla.exception.EmillaException
6 | import net.emilla.lang.LatinToken.Letter
7 | import net.emilla.lang.LatinToken.Word
8 | import net.emilla.lang.LatinTokens
9 | import net.emilla.lang.measure.CelsiusConversion
10 |
11 | object CelsiusConversionEN_US {
12 |
13 | @JvmStatic
14 | fun instance(s: String, @StringRes errorTitle: Int): CelsiusConversion { try {
15 | val tokens = LatinTokens(s)
16 |
17 | val degrees = tokens.nextNumber()
18 | if (tokens.finished()) return CelsiusConversion(degrees, false)
19 |
20 | tokens.skipFirst(arrayOf(
21 | Letter(false, '°', false),
22 | // todo: degree sign technically isn't applicable to Kelvin
23 | Word(true, "degrees", true),
24 | ))
25 | if (tokens.finished()) return CelsiusConversion(degrees, false)
26 |
27 | val token: String = tokens.nextOf(arrayOf(
28 | Word(true, "fahrenheit", true),
29 | Letter(false, 'f', true),
30 | Word(true, "kelvin", true),
31 | Letter(false, 'k', true),
32 | ))
33 |
34 | tokens.requireFinished()
35 |
36 | val isKelvin = when (token.lowercase()) {
37 | "f", "fahrenheit" -> false
38 | "k", "kelvin" -> true
39 | else -> throw IllegalArgumentException()
40 | }
41 |
42 | return CelsiusConversion(degrees, isKelvin)
43 | } catch (_: IllegalStateException) {
44 | throw EmillaException(errorTitle, R.string.error_bad_temperature)
45 | }}
46 | }
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/lang/measure/impl/FahrenheitConversionEN_US.kt:
--------------------------------------------------------------------------------
1 | package net.emilla.lang.measure.impl
2 |
3 | import androidx.annotation.StringRes
4 | import net.emilla.R
5 | import net.emilla.exception.EmillaException
6 | import net.emilla.lang.LatinToken.Letter
7 | import net.emilla.lang.LatinToken.Word
8 | import net.emilla.lang.LatinTokens
9 | import net.emilla.lang.measure.FahrenheitConversion
10 |
11 | object FahrenheitConversionEN_US {
12 |
13 | @JvmStatic
14 | fun instance(s: String, @StringRes errorTitle: Int): FahrenheitConversion { try {
15 | val tokens = LatinTokens(s)
16 |
17 | val degrees = tokens.nextNumber()
18 | if (tokens.finished()) return FahrenheitConversion(degrees, false)
19 |
20 | tokens.skipFirst(arrayOf(
21 | Letter(false, '°', false),
22 | // todo: degree sign technically isn't applicable to Kelvin
23 | Word(true, "degrees", true),
24 | ))
25 | if (tokens.finished()) return FahrenheitConversion(degrees, false)
26 |
27 | val token: String = tokens.nextOf(arrayOf(
28 | Word(true, "celsius", true),
29 | Letter(false, 'c', true),
30 | Word(true, "kelvin", true),
31 | Letter(false, 'k', true),
32 | ))
33 |
34 | tokens.requireFinished()
35 |
36 | val isKelvin = when (token.lowercase()) {
37 | "c", "celsius" -> false
38 | "k", "kelvin" -> true
39 | else -> throw IllegalArgumentException()
40 | }
41 |
42 | return FahrenheitConversion(degrees, isKelvin)
43 | } catch (_: IllegalStateException) {
44 | throw EmillaException(errorTitle, R.string.error_bad_temperature)
45 | }}
46 | }
47 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/lang/phrase/Dice.java:
--------------------------------------------------------------------------------
1 | package net.emilla.lang.phrase;
2 |
3 | import java.util.Objects;
4 | import java.util.Random;
5 |
6 | public final class Dice implements Comparable {
7 |
8 | private int mCount;
9 | public final int faces;
10 |
11 | public Dice(int count, int faces) {
12 | mCount = count;
13 | this.faces = faces;
14 | }
15 |
16 | public void add(int count) {
17 | mCount += count;
18 | }
19 |
20 | public int count() {
21 | return mCount;
22 | }
23 |
24 | public int roll(Random rand) {
25 | if (faces == 1) return mCount;
26 |
27 | int result = 0;
28 | if (mCount >= 0) {
29 | for (int i = 0; i < mCount; ++i) {
30 | result += rand.nextInt(faces) + 1;
31 | }
32 | } else {
33 | for (int i = 0; i > mCount; --i) {
34 | result -= rand.nextInt(faces) + 1;
35 | }
36 | }
37 |
38 | return result;
39 | }
40 |
41 | @Override
42 | public int compareTo(Dice that) {
43 | return faces - that.faces;
44 | }
45 |
46 | @Override
47 | public boolean equals(Object that) {
48 | return this == that
49 | || that instanceof Dice dice
50 | && faces == dice.faces;
51 | }
52 |
53 | @Override
54 | public int hashCode() {
55 | return Objects.hash(faces);
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/lang/phrase/Dices.java:
--------------------------------------------------------------------------------
1 | package net.emilla.lang.phrase;
2 |
3 | import java.util.Random;
4 |
5 | public final class Dices {
6 |
7 | private final Iterable mDices;
8 |
9 | public Dices(Iterable dices) {
10 | mDices = dices;
11 | }
12 |
13 | public int roll(Random rand) {
14 | int result = 0;
15 | for (Dice dice : mDices) result += dice.roll(rand);
16 | return result;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/lang/phrase/RandRange.java:
--------------------------------------------------------------------------------
1 | package net.emilla.lang.phrase;
2 |
3 | import static java.lang.Math.min;
4 |
5 | import androidx.annotation.StringRes;
6 |
7 | import net.emilla.R;
8 | import net.emilla.exception.EmillaException;
9 |
10 | public final class RandRange {
11 |
12 | public final int inclusStart;
13 | public final int exclusEnd;
14 |
15 | public RandRange(int inclusEnd, @StringRes int errorTitle) {
16 | this(min(inclusEnd, 1), inclusEnd <= 0 ? 0 : inclusEnd + 1, errorTitle);
17 | }
18 |
19 | public RandRange(int inclusStart, int exclusEnd, @StringRes int errorTitle) {
20 | if (inclusStart >= exclusEnd) {
21 | throw new EmillaException(errorTitle, R.string.error_invalid_number_range);
22 | }
23 |
24 | this.inclusStart = inclusStart;
25 | this.exclusEnd = exclusEnd;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/math/BinaryOperator.kt:
--------------------------------------------------------------------------------
1 | package net.emilla.math
2 |
3 | import net.emilla.math.CalcToken.InfixToken
4 | import kotlin.math.pow
5 |
6 | internal enum class BinaryOperator(
7 | @JvmField val precedence: Int,
8 | @JvmField val rightAssociative: Boolean
9 | ) : InfixToken {
10 | PLUS(1, false) {
11 | override fun Double.apply(n: Double) = this + n
12 | },
13 | MINUS(1, false) {
14 | override fun Double.apply(n: Double) = this - n
15 | },
16 | TIMES(2, false) {
17 | override fun Double.apply(n: Double) = this * n
18 | },
19 | DIV(2, false) {
20 | override fun Double.apply(n: Double) = this / n
21 | },
22 | POW(3, true) {
23 | override fun Double.apply(n: Double) = pow(n)
24 | };
25 |
26 | abstract fun Double.apply(n: Double): Double
27 |
28 | companion object {
29 | @JvmField
30 | val LPAREN: BinaryOperator? = null
31 |
32 | @JvmStatic
33 | fun of(token: Char) = when (token) {
34 | // todo: natural language like "add", "to the power of", ..
35 | '+' -> PLUS
36 | '-' -> MINUS
37 | '*' -> TIMES
38 | '/' -> DIV
39 | '^' -> POW
40 | else -> throw IllegalArgumentException()
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/math/BitwiseSign.kt:
--------------------------------------------------------------------------------
1 | package net.emilla.math
2 |
3 | import net.emilla.math.CalcToken.BitwiseToken
4 |
5 | internal enum class BitwiseSign(@JvmField val postfix: Boolean) : BitwiseToken {
6 | POSITIVE(false) {
7 | override fun Long.apply() = this
8 | },
9 | NEGATIVE(false) {
10 | override fun Long.apply() = -this
11 | },
12 | NOT(false) {
13 | override fun Long.apply() = inv()
14 | },
15 | FACTORIAL(true) {
16 | override fun Long.apply() = factorial()
17 | };
18 |
19 | abstract fun Long.apply(): Long
20 |
21 | companion object {
22 | @JvmField
23 | val LPAREN: BitwiseSign? = null
24 |
25 | @JvmStatic
26 | fun of(token: Char) = when (token) {
27 | // todo: natural language like "positive", "factorial", ..
28 | '+' -> POSITIVE
29 | '-' -> NEGATIVE
30 | '~' -> NOT
31 | '!' -> FACTORIAL
32 | else -> throw IllegalArgumentException()
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/math/CalcToken.kt:
--------------------------------------------------------------------------------
1 | package net.emilla.math
2 |
3 | internal sealed interface CalcToken {
4 | sealed interface InfixToken : CalcToken
5 | sealed interface BitwiseToken : CalcToken
6 |
7 | object LParen : InfixToken, BitwiseToken
8 | object RParen : InfixToken, BitwiseToken
9 | }
10 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/math/FloatingPointNumber.kt:
--------------------------------------------------------------------------------
1 | package net.emilla.math
2 |
3 | import androidx.annotation.StringRes
4 | import net.emilla.math.CalcToken.InfixToken
5 |
6 | internal class FloatingPointNumber(num: String, @StringRes errorTitle: Int) : InfixToken {
7 | @JvmField
8 | val value: Double = tryParseDouble(num, errorTitle)
9 | }
10 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/math/IntegerNumber.kt:
--------------------------------------------------------------------------------
1 | package net.emilla.math
2 |
3 | import androidx.annotation.StringRes
4 | import net.emilla.math.CalcToken.BitwiseToken
5 |
6 | internal class IntegerNumber(num: String, @StringRes errorTitle: Int) : BitwiseToken {
7 | @JvmField
8 | val value: Long = tryParseLong(num, errorTitle)
9 | }
10 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/math/Maths.kt:
--------------------------------------------------------------------------------
1 | @file:JvmName("Maths")
2 |
3 | package net.emilla.math
4 |
5 | import androidx.annotation.StringRes
6 | import net.emilla.R
7 | import net.emilla.exception.EmillaException
8 | import java.text.DecimalFormat
9 |
10 | // todo: configurable sig digs.
11 | fun prettyNumber(n: Double): String = DecimalFormat("#.######").format(n)
12 |
13 | fun tryParseLong(num: String, @StringRes errorTitle: Int) = try {
14 | num.toLong()
15 | } catch (_: NumberFormatException) {
16 | throw malformedExpression(errorTitle)
17 | }
18 |
19 | fun tryParseDouble(num: String, @StringRes errorTitle: Int) = try {
20 | num.toDouble()
21 | } catch (_: NumberFormatException) {
22 | throw malformedExpression(errorTitle)
23 | }
24 |
25 | fun malformedExpression(@StringRes errorTitle: Int): EmillaException {
26 | return EmillaException(errorTitle, R.string.error_calc_malformed_expression)
27 | }
28 |
29 | fun undefined(@StringRes errorTitle: Int): EmillaException {
30 | return EmillaException(errorTitle, R.string.error_calc_undefined)
31 | }
32 |
33 | fun Double.factorial(): Double {
34 | return toLong().factorial().toDouble()
35 | // TODO: this discards the fractional part. Find the proper way to compute factorial of
36 | // fractional numbers
37 | }
38 |
39 | fun Long.factorial(): Long {
40 | if (this < 0L) throw ArithmeticException()
41 | if (this < 2L) return 1L
42 |
43 | var fact = 2L
44 | for (i in 3L..this) fact *= i
45 |
46 | return fact
47 | }
48 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/math/UnaryOperator.kt:
--------------------------------------------------------------------------------
1 | package net.emilla.math
2 |
3 | import net.emilla.math.CalcToken.InfixToken
4 |
5 | internal enum class UnaryOperator(@JvmField val postfix: Boolean) : InfixToken {
6 | POSITIVE(false) {
7 | override fun Double.apply() = this
8 | },
9 | NEGATIVE(false) {
10 | override fun Double.apply() = -this
11 | },
12 | PERCENT(true) {
13 | override fun Double.apply() = this / 100.0
14 | },
15 | FACTORIAL(true) {
16 | override fun Double.apply() = factorial()
17 | };
18 |
19 | abstract fun Double.apply(): Double
20 |
21 | companion object {
22 | @JvmField
23 | val LPAREN: UnaryOperator? = null
24 |
25 | @JvmStatic
26 | fun of(token: Char) = when (token) {
27 | // todo: natural language like "positive", "factorial", ..
28 | '+' -> POSITIVE
29 | '-' -> NEGATIVE
30 | '%' -> PERCENT
31 | '!' -> FACTORIAL
32 | else -> throw IllegalArgumentException()
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/ping/ClassicPinger.java:
--------------------------------------------------------------------------------
1 | package net.emilla.ping;
2 |
3 | import android.Manifest;
4 | import android.app.Notification;
5 | import android.app.NotificationManager;
6 | import android.content.Context;
7 |
8 | import androidx.annotation.RequiresPermission;
9 |
10 | import net.emilla.util.Services;
11 |
12 | /*internal open*/ class ClassicPinger implements Pinger {
13 |
14 | protected final NotificationManager pingManager;
15 | private final Notification mPing;
16 | private final int mSlot;
17 |
18 | /**
19 | * Singleton that decrements for each notification posted using
20 | * {@link PingChannel#SLOT_UNLIMITED}
21 | */
22 | private static int sSlot = 0;
23 |
24 | public ClassicPinger(Context ctx, Notification ping, PingChannel channel) {
25 | pingManager = Services.notification(ctx);
26 | mPing = ping;
27 | mSlot = channel.slot;
28 | }
29 |
30 | @Override @RequiresPermission(Manifest.permission.POST_NOTIFICATIONS)
31 | public /*open*/ void ping() {
32 | int id = mSlot == PingChannel.SLOT_UNLIMITED ? --sSlot : mSlot;
33 | // this can be used to edit or remove the notification later.
34 | pingManager.notify(id, mPing);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/ping/ModernPinger.java:
--------------------------------------------------------------------------------
1 | package net.emilla.ping;
2 |
3 | import android.Manifest;
4 | import android.app.Notification;
5 | import android.content.Context;
6 | import android.content.res.Resources;
7 | import android.os.Build;
8 |
9 | import androidx.annotation.RequiresApi;
10 | import androidx.annotation.RequiresPermission;
11 |
12 | @RequiresApi(Build.VERSION_CODES.O)
13 | /*internal*/ final class ModernPinger extends ClassicPinger {
14 |
15 | private final PingChannel mChannel;
16 | private final Resources mRes;
17 |
18 | public ModernPinger(Context ctx, Notification ping, PingChannel channel) {
19 | super(ctx, ping, channel);
20 |
21 | mChannel = channel;
22 | mRes = ctx.getResources();
23 | }
24 |
25 | @Override @RequiresPermission(Manifest.permission.POST_NOTIFICATIONS)
26 | public void ping() {
27 | if (pingManager.getNotificationChannel(mChannel.id) == null) {
28 | pingManager.createNotificationChannel(mChannel.make(mRes));
29 | // TODO LANG: you need to update the channel name & description when language changes.
30 | // todo: more centralized channel ensurement so there's consistent order in the app
31 | // notifications settings.
32 | }
33 | super.ping();
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/ping/PingIntent.java:
--------------------------------------------------------------------------------
1 | package net.emilla.ping;
2 |
3 | import android.app.Notification;
4 | import android.content.Context;
5 | import android.content.Intent;
6 |
7 | import net.emilla.event.PingReceiver;
8 |
9 | public final class PingIntent extends Intent {
10 |
11 | private static final String
12 | EXTRA_PING = "ping",
13 | EXTRA_CHANNEL = "channel";
14 |
15 | public PingIntent(Intent intent) {
16 | super(intent);
17 | }
18 |
19 | public PingIntent(Context ctx, Notification ping, String channel) {
20 | super(ctx, PingReceiver.class);
21 |
22 | putExtra(EXTRA_PING, ping);
23 | putExtra(EXTRA_CHANNEL, channel);
24 | }
25 |
26 | public Notification ping() {
27 | return getParcelableExtra(EXTRA_PING);
28 | }
29 |
30 | public PingChannel channel() {
31 | return PingChannel.of(getStringExtra(EXTRA_CHANNEL));
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/ping/Pinger.java:
--------------------------------------------------------------------------------
1 | package net.emilla.ping;
2 |
3 | import android.Manifest;
4 | import android.app.Notification;
5 | import android.content.Context;
6 | import android.os.Build;
7 |
8 | import androidx.annotation.RequiresPermission;
9 |
10 | @FunctionalInterface
11 | public interface Pinger {
12 |
13 | @RequiresPermission(Manifest.permission.POST_NOTIFICATIONS)
14 | void ping();
15 |
16 | static Pinger of(Context ctx, PingIntent intent) {
17 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
18 | return new ModernPinger(ctx, intent.ping(), intent.channel());
19 | } return new ClassicPinger(ctx, intent.ping(), intent.channel());
20 | }
21 |
22 | static Pinger of(Context ctx, Notification ping, PingChannel channel) {
23 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
24 | return new ModernPinger(ctx, ping, channel);
25 | } return new ClassicPinger(ctx, ping, channel);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/run/AppGift.kt:
--------------------------------------------------------------------------------
1 | package net.emilla.run
2 |
3 | import android.app.Activity
4 | import android.content.Intent
5 | import net.emilla.R
6 | import net.emilla.activity.DummyActivity
7 | import net.emilla.app.Apps
8 | import net.emilla.exception.EmillaException
9 |
10 | class AppGift(private val activity: Activity, private val intent: Intent) : Gift {
11 |
12 | override fun run() {
13 | if (intent.resolveActivity(activity.packageManager) != null) {
14 | activity.finishAndRemoveTask()
15 | val dummy: Intent = Apps.meTask(activity, DummyActivity::class.java)
16 | .putExtra(Intent.EXTRA_INTENT, intent)
17 | .addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
18 | activity.startActivity(dummy)
19 | } else throw EmillaException(R.string.error, R.string.error_no_app)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/run/AppSuccess.java:
--------------------------------------------------------------------------------
1 | package net.emilla.run;
2 |
3 | import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
4 |
5 | import android.app.Activity;
6 | import android.content.ActivityNotFoundException;
7 | import android.content.Intent;
8 |
9 | import net.emilla.R;
10 | import net.emilla.exception.EmillaException;
11 |
12 | public final class AppSuccess implements Success {
13 |
14 | private final Activity mActivity;
15 | private final Intent mIntent;
16 |
17 | public AppSuccess(Activity act, Intent intent) {
18 | mActivity = act;
19 | mIntent = intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
20 | }
21 |
22 | @Override
23 | public void run() {
24 | var pm = mActivity.getPackageManager();
25 | if (mIntent.resolveActivity(pm) != null) {
26 | mActivity.finishAndRemoveTask();
27 | mActivity.startActivity(mIntent);
28 | } else try {
29 | mActivity.startActivity(mIntent);
30 | mActivity.finishAndRemoveTask();
31 | } catch (ActivityNotFoundException e) {
32 | throw new EmillaException(R.string.error, R.string.error_no_app);
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/run/BroadcastGift.java:
--------------------------------------------------------------------------------
1 | package net.emilla.run;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 |
6 | public final class BroadcastGift implements Gift {
7 |
8 | private final Context mContext;
9 | private final Intent mIntent;
10 |
11 | public BroadcastGift(Context ctx, Intent intent) {
12 | mContext = ctx;
13 | mIntent = intent;
14 | }
15 |
16 | @Override
17 | public void run() {
18 | mContext.sendBroadcast(mIntent);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/run/CommandRun.java:
--------------------------------------------------------------------------------
1 | package net.emilla.run;
2 |
3 | @FunctionalInterface
4 | public interface CommandRun extends Runnable {}
5 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/run/CopyGift.java:
--------------------------------------------------------------------------------
1 | package net.emilla.run;
2 |
3 | import android.content.ClipData;
4 | import android.content.ClipboardManager;
5 |
6 | import net.emilla.activity.AssistActivity;
7 | import net.emilla.util.Services;
8 |
9 | public final class CopyGift implements Gift {
10 |
11 | private final AssistActivity mActivity;
12 | private final String mText;
13 |
14 | public CopyGift(AssistActivity act, String text) {
15 | mActivity = act;
16 | mText = text;
17 | }
18 |
19 | @Override
20 | public void run() {
21 | ClipboardManager clipMgr = Services.clipboard(mActivity);
22 | clipMgr.setPrimaryClip(ClipData.newPlainText(null, mText));
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/run/DialogRun.java:
--------------------------------------------------------------------------------
1 | package net.emilla.run;
2 |
3 | import androidx.appcompat.app.AlertDialog;
4 |
5 | import net.emilla.activity.AssistActivity;
6 |
7 | public /*open*/ class DialogRun implements Gift, Offering, Failure {
8 |
9 | private final AssistActivity mActivity;
10 | private final AlertDialog mDialog;
11 |
12 | public DialogRun(AssistActivity act, AlertDialog.Builder builder) {
13 | this(act, builder.create());
14 | }
15 |
16 | public DialogRun(AssistActivity act, AlertDialog dialog) {
17 | mActivity = act;
18 | dialog.setOnCancelListener(dlg -> {
19 | mActivity.onCloseDialog();
20 | mActivity.resume();
21 | });
22 | // Todo: don't require this
23 | mDialog = dialog;
24 | }
25 |
26 | @Override
27 | public final void run() {
28 | mActivity.prepareForDialog();
29 | mDialog.show();
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/run/Failure.java:
--------------------------------------------------------------------------------
1 | package net.emilla.run;
2 |
3 | @FunctionalInterface
4 | public interface Failure extends CommandRun {}
5 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/run/Gift.java:
--------------------------------------------------------------------------------
1 | package net.emilla.run;
2 |
3 | @FunctionalInterface
4 | public interface Gift extends CommandRun {}
5 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/run/MessageFailure.java:
--------------------------------------------------------------------------------
1 | package net.emilla.run;
2 |
3 | import androidx.annotation.StringRes;
4 |
5 | import net.emilla.R;
6 | import net.emilla.activity.AssistActivity;
7 | import net.emilla.exception.EmillaException;
8 | import net.emilla.util.Dialogs;
9 |
10 | public final class MessageFailure extends DialogRun {
11 |
12 | public MessageFailure(AssistActivity act, EmillaException e) {
13 | this(act, e.title, e.message);
14 | }
15 |
16 | public MessageFailure(AssistActivity act, @StringRes int title, @StringRes int msg) {
17 | super(act, Dialogs.message(act, title, msg)
18 | .setNeutralButton(R.string.leave, (dlg, which) -> act.cancel()));
19 | }
20 |
21 | public MessageFailure(AssistActivity act, CharSequence title, @StringRes int msg) {
22 | super(act, Dialogs.message(act, title, msg)
23 | .setNeutralButton(R.string.leave, (dlg, which) -> act.cancel()));
24 | }
25 |
26 | public MessageFailure(AssistActivity act, CharSequence title, CharSequence msg) {
27 | super(act, Dialogs.message(act, title, msg)
28 | .setNeutralButton(R.string.leave, (dlg, which) -> act.cancel()));
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/run/MessageGift.java:
--------------------------------------------------------------------------------
1 | package net.emilla.run;
2 |
3 | import androidx.annotation.StringRes;
4 |
5 | import net.emilla.activity.AssistActivity;
6 | import net.emilla.util.Dialogs;
7 |
8 | public final class MessageGift extends DialogRun {
9 |
10 | public MessageGift(AssistActivity act, @StringRes int title, @StringRes int msg) {
11 | super(act, Dialogs.message(act, title, msg));
12 | }
13 |
14 | public MessageGift(AssistActivity act, @StringRes int title, CharSequence msg) {
15 | super(act, Dialogs.message(act, title, msg));
16 | }
17 |
18 | public MessageGift(AssistActivity act, CharSequence title, @StringRes int msg) {
19 | super(act, Dialogs.message(act, title, msg));
20 | }
21 |
22 | public MessageGift(AssistActivity act, CharSequence title, CharSequence msg) {
23 | super(act, Dialogs.message(act, title, msg));
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/run/Offering.java:
--------------------------------------------------------------------------------
1 | package net.emilla.run;
2 |
3 | @FunctionalInterface
4 | public interface Offering extends CommandRun {}
5 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/run/PermissionFailure.java:
--------------------------------------------------------------------------------
1 | package net.emilla.run;
2 |
3 | import android.content.Intent;
4 | import android.content.pm.PackageManager;
5 |
6 | import androidx.annotation.StringRes;
7 | import androidx.appcompat.app.AlertDialog;
8 |
9 | import net.emilla.R;
10 | import net.emilla.activity.AssistActivity;
11 | import net.emilla.app.Apps;
12 | import net.emilla.util.Dialogs;
13 |
14 | public final class PermissionFailure extends DialogRun {
15 |
16 | private static AlertDialog.Builder dialog(AssistActivity act, @StringRes int permissionName) {
17 | // todo: this often results in an activity restart, which messes with the resume chime and
18 | // probably other elements of state. handle accordingly.
19 | Intent appInfo = Apps.infoTask();
20 | PackageManager pm = act.getPackageManager();
21 | if (appInfo.resolveActivity(pm) != null) {
22 | return Dialogs.dual(act, permissionName, R.string.dlg_msg_perm_denial, R.string.app_info,
23 | (dlg, which) -> act.startActivity(appInfo));
24 | }
25 |
26 | return Dialogs.message(act, permissionName, R.string.dlg_msg_perm_denial);
27 | // this should pretty much never happen.
28 | }
29 |
30 | public PermissionFailure(AssistActivity act, @StringRes int permissionName) {
31 | super(act, dialog(act, permissionName));
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/run/PermissionOffering.java:
--------------------------------------------------------------------------------
1 | package net.emilla.run;
2 |
3 | import android.os.Build;
4 |
5 | import androidx.annotation.Nullable;
6 | import androidx.annotation.RequiresApi;
7 |
8 | import net.emilla.activity.AssistActivity;
9 |
10 | /**
11 | *
12 | * Presents the user with a system permission request.
13 | *
14 | * This can only be used when
15 | * {@link android.app.Activity#shouldShowRequestPermissionRationale(String)} is true for the
16 | * permission(s) being requested.
17 | */
18 | @RequiresApi(api = Build.VERSION_CODES.M)
19 | public final class PermissionOffering implements Offering {
20 |
21 | private final AssistActivity mActivity;
22 | private final String[] mPermissions;
23 | @Nullable
24 | private final Runnable mOnGrant;
25 |
26 | public PermissionOffering(AssistActivity act, String permission, @Nullable Runnable onGrant) {
27 | this(act, new String[]{permission}, onGrant);
28 | }
29 |
30 | public PermissionOffering(AssistActivity act, String[] permissions, @Nullable Runnable onGrant) {
31 | mActivity = act;
32 | mPermissions = permissions;
33 | mOnGrant = onGrant;
34 | }
35 |
36 | @Override
37 | public void run() {
38 | mActivity.offerPermissions(mPermissions, mOnGrant);
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/run/PingGift.java:
--------------------------------------------------------------------------------
1 | package net.emilla.run;
2 |
3 | import android.Manifest;
4 | import android.app.Notification;
5 | import android.content.Context;
6 |
7 | import androidx.annotation.RequiresPermission;
8 |
9 | import net.emilla.ping.PingChannel;
10 | import net.emilla.ping.Pinger;
11 |
12 | public final class PingGift implements Gift {
13 |
14 | private final Context mContext;
15 | private final Notification mPing;
16 | private final PingChannel mChannel;
17 |
18 | @RequiresPermission(Manifest.permission.POST_NOTIFICATIONS)
19 | public PingGift(Context ctx, Notification ping, PingChannel channel) {
20 | mContext = ctx;
21 | mPing = ping;
22 | mChannel = channel;
23 | }
24 |
25 | @Override @RequiresPermission(Manifest.permission.POST_NOTIFICATIONS)
26 | public void run() {
27 | Pinger.of(mContext, mPing, mChannel).ping();
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/run/Success.java:
--------------------------------------------------------------------------------
1 | package net.emilla.run;
2 |
3 | @FunctionalInterface
4 | public interface Success extends CommandRun {}
5 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/run/TimePickerOffering.java:
--------------------------------------------------------------------------------
1 | package net.emilla.run;
2 |
3 | import android.app.TimePickerDialog;
4 | import android.app.TimePickerDialog.OnTimeSetListener;
5 | import android.text.format.DateFormat;
6 |
7 | import net.emilla.activity.AssistActivity;
8 |
9 | public final class TimePickerOffering implements Offering {
10 |
11 | private final AssistActivity mActivity;
12 | private final TimePickerDialog mDialog;
13 |
14 | public TimePickerOffering(AssistActivity act, OnTimeSetListener timeSet) {
15 | mActivity = act;
16 | mDialog = new TimePickerDialog(act, 0, timeSet, 12, 0, DateFormat.is24HourFormat(act));
17 | // TODO: this isn't respecting the LineageOS system 24-hour setting.
18 | // should there be an option for default time to be noon vs. the current time? noon seems
19 | // much more reasonable in all cases tbh. infinitely more predictable—who the heck wants to
20 | // set a timer for right now?!
21 | mDialog.setOnCancelListener(dlg -> {
22 | mActivity.onCloseDialog();
23 | mActivity.resume();
24 | });
25 | // Todo: don't require this.
26 | }
27 |
28 | @Override
29 | public void run() {
30 | mActivity.prepareForDialog();
31 | mDialog.show();
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/run/ToastGift.java:
--------------------------------------------------------------------------------
1 | package net.emilla.run;
2 |
3 | import net.emilla.activity.AssistActivity;
4 |
5 | public final class ToastGift implements Gift {
6 |
7 | private final AssistActivity mActivity;
8 | private final CharSequence mMessage;
9 | private final boolean mLong;
10 |
11 | public ToastGift(AssistActivity activity, CharSequence msg, boolean longToast) {
12 | mActivity = activity;
13 | mMessage = msg;
14 | mLong = longToast;
15 | }
16 |
17 | @Override
18 | public void run() {
19 | mActivity.toast(mMessage, mLong);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/system/BackgroundServiceUtil.java:
--------------------------------------------------------------------------------
1 | package net.emilla.system;
2 |
3 | public final class BackgroundServiceUtil {
4 |
5 | // private enum State {
6 | // A11Y_WAKE_WORD, A11Y, WAKE_WORD, FAST_LOAD, OFF;
7 | //
8 | // public static State of() {
9 | // ;
10 | // }
11 | // }
12 | //
13 | // static {
14 | // var state = State.of();
15 | // switch (state) {
16 | // case A11Y_WAKE_WORD -> {
17 | // ensureA11yService();
18 | // ensureWakeWordService();
19 | // }
20 | // case A11Y -> {
21 | // ensureA11yService();
22 | // }
23 | // case WAKE_WORD -> {
24 | // ensureWakeWordService();
25 | // }
26 | // case FAST_LOAD -> {
27 | // ensureFastLoadService();
28 | // }}
29 | // }
30 |
31 | private BackgroundServiceUtil() {}
32 | }
33 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/system/EmillaA11yService.java:
--------------------------------------------------------------------------------
1 | package net.emilla.system;
2 |
3 | import static android.content.Intent.ACTION_VOICE_COMMAND;
4 | import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
5 |
6 | import android.accessibilityservice.AccessibilityButtonController;
7 | import android.accessibilityservice.AccessibilityButtonController.AccessibilityButtonCallback;
8 | import android.accessibilityservice.AccessibilityService;
9 | import android.content.Intent;
10 | import android.os.Build;
11 | import android.view.accessibility.AccessibilityEvent;
12 |
13 | import androidx.annotation.RequiresApi;
14 |
15 | public final class EmillaA11yService extends AccessibilityService {
16 |
17 | // TODO: google assistant (maybe?) changes the accessibility menu icon for "assistant," so we
18 | // should also do this. I wonder if you can add items to that menu..
19 |
20 | @Override
21 | public void onAccessibilityEvent(AccessibilityEvent event) {}
22 |
23 | @Override
24 | public void onInterrupt() {}
25 |
26 | @Override @RequiresApi(api = Build.VERSION_CODES.O)
27 | public void onCreate() {
28 | var controller = getAccessibilityButtonController();
29 | var callback = new AssistButtonCallback();
30 | controller.registerAccessibilityButtonCallback(callback);
31 | }
32 |
33 | @RequiresApi(api = Build.VERSION_CODES.O)
34 | private /*inner*/ class AssistButtonCallback extends AccessibilityButtonCallback {
35 |
36 | @Override
37 | public void onClicked(AccessibilityButtonController controller) {
38 | startActivity(new Intent(ACTION_VOICE_COMMAND).addFlags(FLAG_ACTIVITY_NEW_TASK));
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/system/EmillaFileProvider.java:
--------------------------------------------------------------------------------
1 | package net.emilla.system;
2 |
3 | import androidx.core.content.FileProvider;
4 |
5 | import net.emilla.R;
6 |
7 | public final class EmillaFileProvider extends FileProvider {
8 |
9 | public EmillaFileProvider() {
10 | super(R.xml.paths);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/util/CalendarDetails.java:
--------------------------------------------------------------------------------
1 | package net.emilla.util;
2 |
3 | import androidx.annotation.StringRes;
4 |
5 | import net.emilla.R;
6 | import net.emilla.exception.EmillaException;
7 |
8 | public final class CalendarDetails {
9 |
10 | public static int parseAvailability(String s, @StringRes int errorTitle) {
11 | throw new EmillaException(errorTitle, R.string.error_unfinished_feature);
12 | }
13 |
14 | public static int parseVisibility(String s, @StringRes int errorTitle) {
15 | throw new EmillaException(errorTitle, R.string.error_unfinished_feature);
16 | }
17 |
18 | private CalendarDetails() {}
19 | }
20 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/util/Chars.kt:
--------------------------------------------------------------------------------
1 | @file:JvmName("Chars")
2 |
3 | package net.emilla.util
4 |
5 | fun Char.notSpace() = !isWhitespace()
6 |
7 | fun Char.isNonLineSpace() = when (this) {
8 | '\n', '\r' -> false
9 | else -> isWhitespace()
10 | }
11 |
12 | @JvmName("differentLetters")
13 | fun Char.differentLetter(c: Char): Boolean {
14 | return compareToIgnoreCase(c) != 0
15 | }
16 |
17 | @JvmName("compareIgnoreCase")
18 | fun Char.compareToIgnoreCase(c: Char): Int {
19 | if (this != c && uppercaseChar() != c.uppercaseChar()) {
20 | val a = lowercaseChar()
21 | val b = c.lowercaseChar()
22 | if (a != b) return a - b
23 | }
24 |
25 | return 0
26 | }
27 |
28 | fun Char.isSignOrDigit() = isDigit() || isSign()
29 | fun Char.isSignOrNumberChar() = isNumberChar() || isSign()
30 |
31 | fun Char.isDigit() = this in '0'..'9'
32 |
33 | fun Char.isNumberChar() = this == '.' || this in '0'..'9'
34 |
35 | fun Char.isSign() = when (this) {
36 | '+', '-' -> true
37 | else -> false
38 | }
39 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/util/Features.kt:
--------------------------------------------------------------------------------
1 | @file:JvmName("Features")
2 |
3 | package net.emilla.util
4 |
5 | import android.content.pm.PackageManager
6 | import android.os.Build
7 |
8 | @JvmName("camera")
9 | fun PackageManager.hasCameraFeature(): Boolean {
10 | return hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY)
11 | }
12 |
13 | @JvmName("phone")
14 | fun PackageManager.hasPhoneFeature(): Boolean {
15 | return hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
16 | }
17 |
18 | @JvmName("sms")
19 | fun PackageManager.hasSmsFeature(): Boolean {
20 | return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
21 | hasSystemFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
22 | } else {
23 | hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
24 | }
25 | }
26 |
27 | @JvmName("torch")
28 | fun hasTorchFeature(pm: PackageManager): Boolean {
29 | // todo: it'd be a good idea to test this on a minSdk device.
30 | return pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH)
31 | }
32 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/util/IndexWindow.java:
--------------------------------------------------------------------------------
1 | package net.emilla.util;
2 |
3 | public final class IndexWindow {
4 |
5 | public final int start;
6 | public final int last;
7 | public final int end;
8 |
9 | public IndexWindow(int start, int last) {
10 | this.start = start;
11 | this.last = last;
12 | this.end = last + 1;
13 | }
14 |
15 | public int size() {
16 | return end - start;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/util/MediaControl.kt:
--------------------------------------------------------------------------------
1 | @file:JvmName("MediaControl")
2 |
3 | package net.emilla.util
4 |
5 | import android.media.AudioManager
6 | import android.view.KeyEvent
7 |
8 | fun AudioManager.sendPlayEvent() = sendButtonEvent(KeyEvent.KEYCODE_MEDIA_PLAY)
9 | fun AudioManager.sendPauseEvent() = sendButtonEvent(KeyEvent.KEYCODE_MEDIA_PAUSE)
10 | fun AudioManager.sendPlayPauseEvent() = sendButtonEvent(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE)
11 |
12 | private fun AudioManager.sendButtonEvent(keyCode: Int) {
13 | dispatchMediaKeyEvent(KeyEvent(KeyEvent.ACTION_DOWN, keyCode))
14 | dispatchMediaKeyEvent(KeyEvent(KeyEvent.ACTION_UP, keyCode))
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/util/Services.kt:
--------------------------------------------------------------------------------
1 | @file:JvmName("Services")
2 |
3 | package net.emilla.util
4 |
5 | import android.app.AlarmManager
6 | import android.app.NotificationManager
7 | import android.content.ClipboardManager
8 | import android.content.Context
9 | import android.hardware.camera2.CameraManager
10 | import android.media.AudioManager
11 | import android.view.accessibility.AccessibilityManager
12 |
13 | @JvmName("accessibility")
14 | fun Context.accessibilityService(): AccessibilityManager {
15 | return getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
16 | }
17 |
18 | @JvmName("alarm")
19 | fun Context.alarmService(): AlarmManager {
20 | return getSystemService(Context.ALARM_SERVICE) as AlarmManager
21 | }
22 |
23 | @JvmName("audio")
24 | fun Context.audioService(): AudioManager {
25 | return getSystemService(Context.AUDIO_SERVICE) as AudioManager
26 | }
27 |
28 | @JvmName("camera")
29 | fun Context.cameraService(): CameraManager {
30 | return getSystemService(Context.CAMERA_SERVICE) as CameraManager
31 | }
32 |
33 | @JvmName("clipboard")
34 | fun Context.clipboardService(): ClipboardManager {
35 | return getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
36 | }
37 |
38 | @JvmName("notification")
39 | fun Context.notificationService(): NotificationManager {
40 | return getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
41 | }
42 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/util/Strings.kt:
--------------------------------------------------------------------------------
1 | @file:JvmName("Strings")
2 |
3 | package net.emilla.util
4 |
5 | fun String.trimLeading(): String {
6 | val index = indexOfNonSpace()
7 | return if (index > 0) substring(index) else this
8 | }
9 |
10 | fun String.indexOfNonSpace(): Int {
11 | if (isEmpty() || !this[0].isWhitespace()) return 0
12 |
13 | var index = 0
14 | do if (++index == length) return length
15 | while (this[index].isWhitespace())
16 |
17 | return index
18 | }
19 |
20 | fun CharArray.indexOfNonSpace(): Int {
21 | if (isEmpty() || !this[0].isWhitespace()) return 0
22 |
23 | var index = 0
24 | do if (++index == size) return size
25 | while (this[index].isWhitespace())
26 |
27 | return index
28 | }
29 |
30 | fun String.containsIgnoreCase(other: String): Boolean {
31 | if (other.isEmpty()) return true
32 | if (isEmpty()) return false
33 |
34 | val first = other[0]
35 | val max = length - other.length
36 | val trgLast = other.length - 1
37 |
38 | var i = 0
39 | while (i <= max) {
40 | // look for first char.
41 | if (this[i].differentLetter(first)) {
42 | do ++i
43 | while (i <= max && this[i].differentLetter(first))
44 |
45 | if (i > max) return false
46 | }
47 |
48 | // found first character, now look at the rest of target.
49 | ++i
50 | if (regionMatches(i, other, 1, trgLast)) return true
51 | }
52 |
53 | return false
54 | }
55 |
56 | fun Char.repeat(count: Int): String = String(CharArray(count) { this })
57 | fun CharArray.substring(start: Int = 0) = substring(start, size)
58 | fun CharArray.substring(start: Int = 0, end: Int) = String(copyOfRange(start, end))
59 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/util/Timeit.java:
--------------------------------------------------------------------------------
1 | package net.emilla.util;
2 |
3 | import android.util.Log;
4 |
5 | public final class Timeit {
6 |
7 | private static final String TAG = Timeit.class.getSimpleName();
8 |
9 | private static long sPrevTime = 0;
10 |
11 | public static long nanos(String label) {
12 | if (sPrevTime == 0) sPrevTime = System.nanoTime();
13 |
14 | var s = String.valueOf(System.nanoTime() - sPrevTime);
15 | var sb = new StringBuilder(label).append(": ");
16 | int start = sb.length();
17 | sb.append(s);
18 |
19 | for (int i = sb.length() - 3; i > start; i -= 3) {
20 | sb.insert(i, ',');
21 | }
22 | sb.append(" nanoseconds");
23 |
24 | Log.d(TAG, sb.toString());
25 |
26 | return sPrevTime = System.nanoTime();
27 | }
28 |
29 | private Timeit() {}
30 | }
31 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/util/trie/HashTrieMap.java:
--------------------------------------------------------------------------------
1 | package net.emilla.util.trie;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 |
6 | public final class HashTrieMap> extends TrieMap {
7 |
8 | private static final class HashTrieNode> extends TrieNode {
9 |
10 | @Override
11 | Map> newMap() {
12 | return new HashMap<>();
13 | }
14 | }
15 |
16 | @Override
17 | TrieNode newNode() {
18 | return new HashTrieNode<>();
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/src/main/java/net/emilla/util/trie/SortedTrieMap.java:
--------------------------------------------------------------------------------
1 | package net.emilla.util.trie;
2 |
3 | import androidx.annotation.Nullable;
4 |
5 | import java.util.List;
6 | import java.util.Map;
7 | import java.util.TreeMap;
8 |
9 | public final class SortedTrieMap, V extends TrieMap.Value>
10 | extends TrieMap {
11 |
12 | private static final class SortedTrieNode, V extends Value>
13 | extends TrieNode {
14 |
15 | @Override
16 | Map> newMap() {
17 | return new TreeMap<>();
18 | }
19 | }
20 |
21 | @Override
22 | TrieNode newNode() {
23 | return new SortedTrieNode<>();
24 | }
25 |
26 | /**
27 | * Sorted list of elements that start with a given prefix.
28 | *
29 | * @param prefix phrase to get prefixed values of.
30 | * @return list of elements that start with {@code prefix} in sorted depth-first order, or
31 | * {@code null} if no such values exist.
32 | */
33 | @Nullable
34 | public List elementsWithPrefix(Phrase prefix) {
35 | TrieNode current = root;
36 | for (K item : prefix) {
37 | TrieNode get = current.children().get(item);
38 | if (get == null) return null;
39 | current = get;
40 | }
41 | return current.values();
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/app/src/main/res/color/bg_activatable_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/color/bg_assistant.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/color/bg_text_button.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_action_button.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_edit_box.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_edit_box_focused.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
7 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_edit_box_unfocused.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
7 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_selectable_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/btn_star.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/btn_star_checked.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/btn_star_unchecked.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_alarm.xml:
--------------------------------------------------------------------------------
1 |
7 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_app.xml:
--------------------------------------------------------------------------------
1 |
7 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_assistant.xml:
--------------------------------------------------------------------------------
1 |
7 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_attach.xml:
--------------------------------------------------------------------------------
1 |
7 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_bookmark.xml:
--------------------------------------------------------------------------------
1 |
7 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_calculate.xml:
--------------------------------------------------------------------------------
1 |
7 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_calendar.xml:
--------------------------------------------------------------------------------
1 |
8 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_call.xml:
--------------------------------------------------------------------------------
1 |
8 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_clock.xml:
--------------------------------------------------------------------------------
1 |
7 |
9 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_command.xml:
--------------------------------------------------------------------------------
1 |
7 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_contact.xml:
--------------------------------------------------------------------------------
1 |
7 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_copy.xml:
--------------------------------------------------------------------------------
1 |
7 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_cursor_start.xml:
--------------------------------------------------------------------------------
1 |
8 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_dial.xml:
--------------------------------------------------------------------------------
1 |
7 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_email.xml:
--------------------------------------------------------------------------------
1 |
7 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_find.xml:
--------------------------------------------------------------------------------
1 |
7 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_help.xml:
--------------------------------------------------------------------------------
1 |
7 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_hide_data.xml:
--------------------------------------------------------------------------------
1 |
7 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_info.xml:
--------------------------------------------------------------------------------
1 |
7 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launch.xml:
--------------------------------------------------------------------------------
1 |
8 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_assistant_foreground.xml:
--------------------------------------------------------------------------------
1 |
6 |
11 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
6 |
11 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_location.xml:
--------------------------------------------------------------------------------
1 |
7 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_media.xml:
--------------------------------------------------------------------------------
1 |
7 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_message.xml:
--------------------------------------------------------------------------------
1 |
8 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_navigate.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_note.xml:
--------------------------------------------------------------------------------
1 |
8 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_notify.xml:
--------------------------------------------------------------------------------
1 |
7 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_pause.xml:
--------------------------------------------------------------------------------
1 |
7 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_person.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_play.xml:
--------------------------------------------------------------------------------
1 |
7 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_pomodoro.xml:
--------------------------------------------------------------------------------
1 |
7 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_random_number.xml:
--------------------------------------------------------------------------------
1 |
8 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_roll.xml:
--------------------------------------------------------------------------------
1 |
7 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_search.xml:
--------------------------------------------------------------------------------
1 |
7 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_select_all.xml:
--------------------------------------------------------------------------------
1 |
7 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_settings.xml:
--------------------------------------------------------------------------------
1 |
7 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_share.xml:
--------------------------------------------------------------------------------
1 |
7 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_show_data.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_sms.xml:
--------------------------------------------------------------------------------
1 |
8 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_snippets.xml:
--------------------------------------------------------------------------------
1 |
8 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_subject.xml:
--------------------------------------------------------------------------------
1 |
8 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_temperature.xml:
--------------------------------------------------------------------------------
1 |
8 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_timer.xml:
--------------------------------------------------------------------------------
1 |
7 |
9 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_toast.xml:
--------------------------------------------------------------------------------
1 |
7 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_todo.xml:
--------------------------------------------------------------------------------
1 |
7 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_torch.xml:
--------------------------------------------------------------------------------
1 |
7 |
9 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_uninstall.xml:
--------------------------------------------------------------------------------
1 |
7 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_weather.xml:
--------------------------------------------------------------------------------
1 |
7 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_web.xml:
--------------------------------------------------------------------------------
1 |
7 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/list_divider_emilla.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_config.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
19 |
20 |
31 |
32 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/btn_action.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/field_extra.xml:
--------------------------------------------------------------------------------
1 |
2 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_assistant.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
18 |
19 |
27 |
28 |
36 |
37 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_commands.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/prefrence_widget_switch.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/snippet_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/snippet_item_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/bottom_nav_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_assistant.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devycarol/Emilla/36f052a4b0b38e1d79504e7c938860423d09ff24/app/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_assistant.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devycarol/Emilla/36f052a4b0b38e1d79504e7c938860423d09ff24/app/src/main/res/mipmap-hdpi/ic_launcher_assistant.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_assistant_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devycarol/Emilla/36f052a4b0b38e1d79504e7c938860423d09ff24/app/src/main/res/mipmap-hdpi/ic_launcher_assistant_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devycarol/Emilla/36f052a4b0b38e1d79504e7c938860423d09ff24/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devycarol/Emilla/36f052a4b0b38e1d79504e7c938860423d09ff24/app/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_assistant.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devycarol/Emilla/36f052a4b0b38e1d79504e7c938860423d09ff24/app/src/main/res/mipmap-mdpi/ic_launcher_assistant.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_assistant_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devycarol/Emilla/36f052a4b0b38e1d79504e7c938860423d09ff24/app/src/main/res/mipmap-mdpi/ic_launcher_assistant_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devycarol/Emilla/36f052a4b0b38e1d79504e7c938860423d09ff24/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devycarol/Emilla/36f052a4b0b38e1d79504e7c938860423d09ff24/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_assistant.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devycarol/Emilla/36f052a4b0b38e1d79504e7c938860423d09ff24/app/src/main/res/mipmap-xhdpi/ic_launcher_assistant.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_assistant_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devycarol/Emilla/36f052a4b0b38e1d79504e7c938860423d09ff24/app/src/main/res/mipmap-xhdpi/ic_launcher_assistant_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devycarol/Emilla/36f052a4b0b38e1d79504e7c938860423d09ff24/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devycarol/Emilla/36f052a4b0b38e1d79504e7c938860423d09ff24/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_assistant.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devycarol/Emilla/36f052a4b0b38e1d79504e7c938860423d09ff24/app/src/main/res/mipmap-xxhdpi/ic_launcher_assistant.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_assistant_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devycarol/Emilla/36f052a4b0b38e1d79504e7c938860423d09ff24/app/src/main/res/mipmap-xxhdpi/ic_launcher_assistant_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devycarol/Emilla/36f052a4b0b38e1d79504e7c938860423d09ff24/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devycarol/Emilla/36f052a4b0b38e1d79504e7c938860423d09ff24/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_assistant.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devycarol/Emilla/36f052a4b0b38e1d79504e7c938860423d09ff24/app/src/main/res/mipmap-xxxhdpi/ic_launcher_assistant.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_assistant_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devycarol/Emilla/36f052a4b0b38e1d79504e7c938860423d09ff24/app/src/main/res/mipmap-xxxhdpi/ic_launcher_assistant_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devycarol/Emilla/36f052a4b0b38e1d79504e7c938860423d09ff24/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/navigation/navigation_config.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
19 |
20 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/raw/nebula_act.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devycarol/Emilla/36f052a4b0b38e1d79504e7c938860423d09ff24/app/src/main/res/raw/nebula_act.ogg
--------------------------------------------------------------------------------
/app/src/main/res/raw/nebula_exit.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devycarol/Emilla/36f052a4b0b38e1d79504e7c938860423d09ff24/app/src/main/res/raw/nebula_exit.ogg
--------------------------------------------------------------------------------
/app/src/main/res/raw/nebula_fail.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devycarol/Emilla/36f052a4b0b38e1d79504e7c938860423d09ff24/app/src/main/res/raw/nebula_fail.ogg
--------------------------------------------------------------------------------
/app/src/main/res/raw/nebula_pend.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devycarol/Emilla/36f052a4b0b38e1d79504e7c938860423d09ff24/app/src/main/res/raw/nebula_pend.ogg
--------------------------------------------------------------------------------
/app/src/main/res/raw/nebula_resume.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devycarol/Emilla/36f052a4b0b38e1d79504e7c938860423d09ff24/app/src/main/res/raw/nebula_resume.ogg
--------------------------------------------------------------------------------
/app/src/main/res/raw/nebula_start.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devycarol/Emilla/36f052a4b0b38e1d79504e7c938860423d09ff24/app/src/main/res/raw/nebula_start.ogg
--------------------------------------------------------------------------------
/app/src/main/res/raw/nebula_succeed.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devycarol/Emilla/36f052a4b0b38e1d79504e7c938860423d09ff24/app/src/main/res/raw/nebula_succeed.ogg
--------------------------------------------------------------------------------
/app/src/main/res/values-notnight/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
17 |
18 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/values-sw600dp/config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | always
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values-v31/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | @android:color/system_accent1_200
4 | @android:color/system_accent1_400
5 | @android:color/system_accent1_500
6 | @android:color/system_accent1_700
7 | @android:color/system_accent2_200
8 | @android:color/system_accent2_500
9 | @android:color/system_accent2_700
10 | @android:color/system_neutral1_900
11 | @android:color/system_neutral1_800
12 | @android:color/system_neutral1_50
13 | @android:color/system_neutral1_100
14 |
--------------------------------------------------------------------------------
/app/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FC8BFF
4 | #BD50FF
5 | #9D30FF
6 | #5A00DF
7 | #03DAC5
8 | #008B79
9 | #018786
10 | #1A1B20
11 | #2F3036
12 | #F1F0F7
13 | #E2E2E9
14 |
--------------------------------------------------------------------------------
/app/src/main/res/values/config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | portrait
4 |
5 | true
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 16dp
3 | 8dp
4 | 52dp
5 |
--------------------------------------------------------------------------------
/app/src/main/res/values/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #BA575F
4 | #796AC0
5 |
--------------------------------------------------------------------------------
/app/src/main/res/values/ids.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 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/accessibility_service_config.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/backup_rules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/data_extraction_rules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/paths.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/shortcuts.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
10 |
11 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/test/java/net/emilla/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package net.emilla;
2 |
3 | import static org.junit.Assert.assertEquals;
4 |
5 | import org.junit.Test;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public final class ExampleUnitTest {
13 |
14 | @Test
15 | public void addition_isCorrect() {
16 | assertEquals(4, 2 + 2);
17 | }
18 | }
--------------------------------------------------------------------------------
/build.gradle.kts:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | plugins {
3 | id("com.android.application") version "8.9.1" apply false
4 | id("org.jetbrains.kotlin.android") version "2.1.10" apply false
5 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Enables namespacing of each library's R class so that its R class includes only the
19 | # resources declared in the library itself and none from the library's dependencies,
20 | # thereby reducing the size of the R class for that library
21 | android.nonTransitiveRClass=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devycarol/Emilla/36f052a4b0b38e1d79504e7c938860423d09ff24/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sun Oct 08 12:08:35 MDT 2023
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | google()
4 | mavenCentral()
5 | gradlePluginPortal()
6 | }
7 | }
8 | dependencyResolutionManagement {
9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
10 | repositories {
11 | google()
12 | mavenCentral()
13 | }
14 | }
15 |
16 | rootProject.name = "Emilla"
17 | include(":app")
18 |
--------------------------------------------------------------------------------