From a31882401f825291ca0e2c8b575b2a40e1fa3e2e Mon Sep 17 00:00:00 2001 From: pegasko Date: Thu, 6 Jun 2024 02:13:26 +0300 Subject: [PATCH] initial WIP (may be trashy) --- .aiexclude | 0 .gitignore | 1 + Yeeemp/.aiexclude | 0 Yeeemp/.gitignore | 15 ++ Yeeemp/app/.gitignore | 1 + Yeeemp/app/build.gradle | 73 ++++++ Yeeemp/app/proguard-rules.pro | 21 ++ .../java/art/pegasko/yeeemp/DBImplTest.java | 66 ++++++ .../yeeemp/ExampleInstrumentedTest.java | 26 ++ Yeeemp/app/src/main/AndroidManifest.xml | 27 +++ .../java/art/pegasko/yeeemp/base/Event.java | 28 +++ .../art/pegasko/yeeemp/base/EventMaker.java | 21 ++ .../java/art/pegasko/yeeemp/base/Queue.java | 27 +++ .../art/pegasko/yeeemp/base/QueueMaker.java | 22 ++ .../java/art/pegasko/yeeemp/base/Tag.java | 22 ++ .../art/pegasko/yeeemp/base/TagMaker.java | 22 ++ .../java/art/pegasko/yeeemp/base/TagStat.java | 22 ++ .../java/art/pegasko/yeeemp/base/Wrapper.java | 45 ++++ .../art/pegasko/yeeemp/impl/DBWrapper.java | 140 +++++++++++ .../art/pegasko/yeeemp/impl/EventImpl.java | 222 ++++++++++++++++++ .../pegasko/yeeemp/impl/EventMakerImpl.java | 57 +++++ .../java/art/pegasko/yeeemp/impl/Init.java | 28 +++ .../art/pegasko/yeeemp/impl/QueueImpl.java | 188 +++++++++++++++ .../pegasko/yeeemp/impl/QueueMakerImpl.java | 86 +++++++ .../java/art/pegasko/yeeemp/impl/TagImpl.java | 77 ++++++ .../art/pegasko/yeeemp/impl/TagMakerImpl.java | 95 ++++++++ .../java/art/pegasko/yeeemp/impl/Utils.java | 29 +++ .../yeeemp/ui/activity/FirstFragment.java | 61 +++++ .../yeeemp/ui/activity/MainActivity.java | 103 ++++++++ .../ui/activity/QueueRecyclerViewAdapter.java | 115 +++++++++ .../yeeemp/ui/activity/SecondFragment.java | 61 +++++ .../drawable-v24/ic_launcher_foreground.xml | 30 +++ .../res/drawable/ic_launcher_background.xml | 170 ++++++++++++++ .../drawable/pegasko_listitem_background.xml | 16 ++ .../app/src/main/res/layout/activity_main.xml | 37 +++ .../app/src/main/res/layout/content_main.xml | 20 ++ .../src/main/res/layout/fragment_first.xml | 34 +++ .../src/main/res/layout/fragment_second.xml | 34 +++ .../src/main/res/layout/queue_list_item.xml | 49 ++++ .../res/menu/queue_list_item_action_menu.xml | 10 + .../res/mipmap-anydpi-v26/ic_launcher.xml | 6 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 6 + .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 1404 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 2898 bytes .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 982 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 1772 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 1900 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 3918 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 2884 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 5914 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 3844 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 7778 bytes .../app/src/main/res/navigation/nav_graph.xml | 28 +++ .../src/main/res/navigation/nav_graph2.xml | 28 +++ .../app/src/main/res/values-land/dimens.xml | 3 + .../app/src/main/res/values-night/themes.xml | 9 + Yeeemp/app/src/main/res/values-v23/themes.xml | 7 + .../src/main/res/values-w1240dp/dimens.xml | 3 + .../app/src/main/res/values-w600dp/dimens.xml | 3 + Yeeemp/app/src/main/res/values/colors.xml | 19 ++ Yeeemp/app/src/main/res/values/dimens.xml | 9 + Yeeemp/app/src/main/res/values/strings.xml | 46 ++++ Yeeemp/app/src/main/res/values/themes.xml | 84 +++++++ Yeeemp/app/src/main/res/xml/backup_rules.xml | 13 + .../main/res/xml/data_extraction_rules.xml | 19 ++ .../art/pegasko/yeeemp/ExampleUnitTest.java | 17 ++ Yeeemp/build.gradle | 5 + Yeeemp/gradle.properties | 21 ++ .../gradle/wrapper/gradle-wrapper.properties | 6 + Yeeemp/gradlew | 185 +++++++++++++++ Yeeemp/gradlew.bat | 89 +++++++ Yeeemp/settings.gradle | 17 ++ 72 files changed, 2724 insertions(+) create mode 100644 .aiexclude create mode 100644 Yeeemp/.aiexclude create mode 100644 Yeeemp/.gitignore create mode 100644 Yeeemp/app/.gitignore create mode 100644 Yeeemp/app/build.gradle create mode 100644 Yeeemp/app/proguard-rules.pro create mode 100644 Yeeemp/app/src/androidTest/java/art/pegasko/yeeemp/DBImplTest.java create mode 100644 Yeeemp/app/src/androidTest/java/art/pegasko/yeeemp/ExampleInstrumentedTest.java create mode 100644 Yeeemp/app/src/main/AndroidManifest.xml create mode 100644 Yeeemp/app/src/main/java/art/pegasko/yeeemp/base/Event.java create mode 100644 Yeeemp/app/src/main/java/art/pegasko/yeeemp/base/EventMaker.java create mode 100644 Yeeemp/app/src/main/java/art/pegasko/yeeemp/base/Queue.java create mode 100644 Yeeemp/app/src/main/java/art/pegasko/yeeemp/base/QueueMaker.java create mode 100644 Yeeemp/app/src/main/java/art/pegasko/yeeemp/base/Tag.java create mode 100644 Yeeemp/app/src/main/java/art/pegasko/yeeemp/base/TagMaker.java create mode 100644 Yeeemp/app/src/main/java/art/pegasko/yeeemp/base/TagStat.java create mode 100644 Yeeemp/app/src/main/java/art/pegasko/yeeemp/base/Wrapper.java create mode 100644 Yeeemp/app/src/main/java/art/pegasko/yeeemp/impl/DBWrapper.java create mode 100644 Yeeemp/app/src/main/java/art/pegasko/yeeemp/impl/EventImpl.java create mode 100644 Yeeemp/app/src/main/java/art/pegasko/yeeemp/impl/EventMakerImpl.java create mode 100644 Yeeemp/app/src/main/java/art/pegasko/yeeemp/impl/Init.java create mode 100644 Yeeemp/app/src/main/java/art/pegasko/yeeemp/impl/QueueImpl.java create mode 100644 Yeeemp/app/src/main/java/art/pegasko/yeeemp/impl/QueueMakerImpl.java create mode 100644 Yeeemp/app/src/main/java/art/pegasko/yeeemp/impl/TagImpl.java create mode 100644 Yeeemp/app/src/main/java/art/pegasko/yeeemp/impl/TagMakerImpl.java create mode 100644 Yeeemp/app/src/main/java/art/pegasko/yeeemp/impl/Utils.java create mode 100644 Yeeemp/app/src/main/java/art/pegasko/yeeemp/ui/activity/FirstFragment.java create mode 100644 Yeeemp/app/src/main/java/art/pegasko/yeeemp/ui/activity/MainActivity.java create mode 100644 Yeeemp/app/src/main/java/art/pegasko/yeeemp/ui/activity/QueueRecyclerViewAdapter.java create mode 100644 Yeeemp/app/src/main/java/art/pegasko/yeeemp/ui/activity/SecondFragment.java create mode 100644 Yeeemp/app/src/main/res/drawable-v24/ic_launcher_foreground.xml create mode 100644 Yeeemp/app/src/main/res/drawable/ic_launcher_background.xml create mode 100644 Yeeemp/app/src/main/res/drawable/pegasko_listitem_background.xml create mode 100644 Yeeemp/app/src/main/res/layout/activity_main.xml create mode 100644 Yeeemp/app/src/main/res/layout/content_main.xml create mode 100644 Yeeemp/app/src/main/res/layout/fragment_first.xml create mode 100644 Yeeemp/app/src/main/res/layout/fragment_second.xml create mode 100644 Yeeemp/app/src/main/res/layout/queue_list_item.xml create mode 100644 Yeeemp/app/src/main/res/menu/queue_list_item_action_menu.xml create mode 100644 Yeeemp/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 Yeeemp/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 Yeeemp/app/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 Yeeemp/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 Yeeemp/app/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 Yeeemp/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 Yeeemp/app/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 Yeeemp/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 Yeeemp/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 Yeeemp/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 Yeeemp/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 Yeeemp/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 Yeeemp/app/src/main/res/navigation/nav_graph.xml create mode 100644 Yeeemp/app/src/main/res/navigation/nav_graph2.xml create mode 100644 Yeeemp/app/src/main/res/values-land/dimens.xml create mode 100644 Yeeemp/app/src/main/res/values-night/themes.xml create mode 100644 Yeeemp/app/src/main/res/values-v23/themes.xml create mode 100644 Yeeemp/app/src/main/res/values-w1240dp/dimens.xml create mode 100644 Yeeemp/app/src/main/res/values-w600dp/dimens.xml create mode 100644 Yeeemp/app/src/main/res/values/colors.xml create mode 100644 Yeeemp/app/src/main/res/values/dimens.xml create mode 100644 Yeeemp/app/src/main/res/values/strings.xml create mode 100644 Yeeemp/app/src/main/res/values/themes.xml create mode 100644 Yeeemp/app/src/main/res/xml/backup_rules.xml create mode 100644 Yeeemp/app/src/main/res/xml/data_extraction_rules.xml create mode 100644 Yeeemp/app/src/test/java/art/pegasko/yeeemp/ExampleUnitTest.java create mode 100644 Yeeemp/build.gradle create mode 100644 Yeeemp/gradle.properties create mode 100644 Yeeemp/gradle/wrapper/gradle-wrapper.properties create mode 100755 Yeeemp/gradlew create mode 100644 Yeeemp/gradlew.bat create mode 100644 Yeeemp/settings.gradle diff --git a/.aiexclude b/.aiexclude new file mode 100644 index 0000000..e69de29 diff --git a/.gitignore b/.gitignore index 6c95d4f..fba1cb4 100644 --- a/.gitignore +++ b/.gitignore @@ -46,3 +46,4 @@ captures/ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* +Yeemp/.idea* diff --git a/Yeeemp/.aiexclude b/Yeeemp/.aiexclude new file mode 100644 index 0000000..e69de29 diff --git a/Yeeemp/.gitignore b/Yeeemp/.gitignore new file mode 100644 index 0000000..aa724b7 --- /dev/null +++ b/Yeeemp/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/Yeeemp/app/.gitignore b/Yeeemp/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/Yeeemp/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/Yeeemp/app/build.gradle b/Yeeemp/app/build.gradle new file mode 100644 index 0000000..181e49b --- /dev/null +++ b/Yeeemp/app/build.gradle @@ -0,0 +1,73 @@ +plugins { + id 'com.android.application' + id 'org.jetbrains.kotlin.android' +} + +android { + namespace 'art.pegasko.yeeemp' + compileSdk 34 + + defaultConfig { + applicationId "art.pegasko.yeeemp" + minSdk 21 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + vectorDrawables { + useSupportLibrary true + } + } + + buildTypes { + release { + minifyEnabled true + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + buildFeatures { + viewBinding true + dataBinding = true + compose true + } + kotlinOptions { + jvmTarget = '1.8' + } + composeOptions { + kotlinCompilerExtensionVersion '1.5.1' + } + packaging { + resources { + excludes += '/META-INF/{AL2.0,LGPL2.1}' + } + } +} + +dependencies { + + implementation 'androidx.appcompat:appcompat:1.6.1' + implementation 'com.google.android.material:material:1.11.0' + implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + implementation 'androidx.navigation:navigation-fragment:2.6.0' + implementation 'androidx.navigation:navigation-ui:2.6.0' + implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.1' + implementation 'androidx.activity:activity-compose:1.8.0' + implementation platform('androidx.compose:compose-bom:2023.08.00') + implementation 'androidx.compose.ui:ui' + implementation 'androidx.compose.ui:ui-graphics' + implementation 'androidx.compose.ui:ui-tooling-preview' + implementation 'androidx.compose.material3:material3' + implementation 'androidx.activity:activity:1.8.0' + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.5' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' + androidTestImplementation platform('androidx.compose:compose-bom:2023.08.00') + androidTestImplementation 'androidx.compose.ui:ui-test-junit4' + debugImplementation 'androidx.compose.ui:ui-tooling' + debugImplementation 'androidx.compose.ui:ui-test-manifest' +} \ No newline at end of file diff --git a/Yeeemp/app/proguard-rules.pro b/Yeeemp/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/Yeeemp/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/Yeeemp/app/src/androidTest/java/art/pegasko/yeeemp/DBImplTest.java b/Yeeemp/app/src/androidTest/java/art/pegasko/yeeemp/DBImplTest.java new file mode 100644 index 0000000..b4283cc --- /dev/null +++ b/Yeeemp/app/src/androidTest/java/art/pegasko/yeeemp/DBImplTest.java @@ -0,0 +1,66 @@ +package art.pegasko.yeeemp; + +import android.database.sqlite.SQLiteDatabase; +import android.provider.CalendarContract; + +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import art.pegasko.yeeemp.base.Event; +import art.pegasko.yeeemp.base.Queue; +import art.pegasko.yeeemp.base.Tag; +import art.pegasko.yeeemp.impl.QueueMakerImpl; +import art.pegasko.yeeemp.impl.TagMakerImpl; + +@RunWith(AndroidJUnit4.class) +public class DBImplTest { + + private SQLiteDatabase db; + + @Before + public void setUp() { + db = SQLiteDatabase.create(null); +// db = SQLiteDatabase.openDatabase("test.db", SQLiteDatabase.OpenParams); + } + + @After + public void tearDown() { + db.close(); + } + + @Test + public void testQueueMakerImpl() { + SQLiteDatabase db = SQLiteDatabase.create(null); + QueueMakerImpl queueMaker = new QueueMakerImpl(db); + + Queue q = queueMaker.create(); + + // check initial ID + assert q.getId() == 0; + + // check null name + assert q.getName() == null; + + // check no events + Event[] events = q.getEvents(); + assert events != null; + assert events.length == 0; + + // check no tags + Tag[] tags = q.getGlobalTags(); + assert tags != null; + assert tags.length == 0; + + // Check name set + q.setName("aboba"); + assert q.getName() == "aboba"; + } + + @Test + public void testTagMakerImpl() { + } +} diff --git a/Yeeemp/app/src/androidTest/java/art/pegasko/yeeemp/ExampleInstrumentedTest.java b/Yeeemp/app/src/androidTest/java/art/pegasko/yeeemp/ExampleInstrumentedTest.java new file mode 100644 index 0000000..97cc84c --- /dev/null +++ b/Yeeemp/app/src/androidTest/java/art/pegasko/yeeemp/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package art.pegasko.yeeemp; + +import android.content.Context; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + assertEquals("art.pegasko.yeeemp", appContext.getPackageName()); + } +} \ No newline at end of file diff --git a/Yeeemp/app/src/main/AndroidManifest.xml b/Yeeemp/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..347cd7b --- /dev/null +++ b/Yeeemp/app/src/main/AndroidManifest.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Yeeemp/app/src/main/java/art/pegasko/yeeemp/base/Event.java b/Yeeemp/app/src/main/java/art/pegasko/yeeemp/base/Event.java new file mode 100644 index 0000000..511cd94 --- /dev/null +++ b/Yeeemp/app/src/main/java/art/pegasko/yeeemp/base/Event.java @@ -0,0 +1,28 @@ +/** + * Copyright 2024 pegasko + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package art.pegasko.yeeemp.base; + +public interface Event { + int getId(); + long getTimestamp(); + void setTimestamp(long timestamp); + String getMessage(); + void setMessage(String message); + void addTag(Tag tag); + void removeTag(Tag tag); + Tag[] getTags(); +} diff --git a/Yeeemp/app/src/main/java/art/pegasko/yeeemp/base/EventMaker.java b/Yeeemp/app/src/main/java/art/pegasko/yeeemp/base/EventMaker.java new file mode 100644 index 0000000..17ee337 --- /dev/null +++ b/Yeeemp/app/src/main/java/art/pegasko/yeeemp/base/EventMaker.java @@ -0,0 +1,21 @@ +/** + * Copyright 2024 pegasko + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package art.pegasko.yeeemp.base; + +public interface EventMaker { + Event createInQueue(Queue queue); +} diff --git a/Yeeemp/app/src/main/java/art/pegasko/yeeemp/base/Queue.java b/Yeeemp/app/src/main/java/art/pegasko/yeeemp/base/Queue.java new file mode 100644 index 0000000..898f724 --- /dev/null +++ b/Yeeemp/app/src/main/java/art/pegasko/yeeemp/base/Queue.java @@ -0,0 +1,27 @@ +/** + * Copyright 2024 pegasko + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package art.pegasko.yeeemp.base; + +public interface Queue { + int getId(); + String getName(); + void setName(String name); + Event[] getEvents(); + void addEvent(Event event); + void removeEvent(Event event); + Tag[] getGlobalTags(); +} diff --git a/Yeeemp/app/src/main/java/art/pegasko/yeeemp/base/QueueMaker.java b/Yeeemp/app/src/main/java/art/pegasko/yeeemp/base/QueueMaker.java new file mode 100644 index 0000000..4d43924 --- /dev/null +++ b/Yeeemp/app/src/main/java/art/pegasko/yeeemp/base/QueueMaker.java @@ -0,0 +1,22 @@ +/** + * Copyright 2024 pegasko + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package art.pegasko.yeeemp.base; + +public interface QueueMaker { + Queue create(); + Queue[] list(); +} diff --git a/Yeeemp/app/src/main/java/art/pegasko/yeeemp/base/Tag.java b/Yeeemp/app/src/main/java/art/pegasko/yeeemp/base/Tag.java new file mode 100644 index 0000000..b9239be --- /dev/null +++ b/Yeeemp/app/src/main/java/art/pegasko/yeeemp/base/Tag.java @@ -0,0 +1,22 @@ +/** + * Copyright 2024 pegasko + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package art.pegasko.yeeemp.base; + +public interface Tag { + int getId(); + String getName(); +} diff --git a/Yeeemp/app/src/main/java/art/pegasko/yeeemp/base/TagMaker.java b/Yeeemp/app/src/main/java/art/pegasko/yeeemp/base/TagMaker.java new file mode 100644 index 0000000..d63d3e4 --- /dev/null +++ b/Yeeemp/app/src/main/java/art/pegasko/yeeemp/base/TagMaker.java @@ -0,0 +1,22 @@ +/** + * Copyright 2024 pegasko + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package art.pegasko.yeeemp.base; + +public interface TagMaker { + /** Get or Create distinct name Tag in Queue */ + Tag getOrCreateInQueue(Queue queue, String name); +} diff --git a/Yeeemp/app/src/main/java/art/pegasko/yeeemp/base/TagStat.java b/Yeeemp/app/src/main/java/art/pegasko/yeeemp/base/TagStat.java new file mode 100644 index 0000000..bb7ceb5 --- /dev/null +++ b/Yeeemp/app/src/main/java/art/pegasko/yeeemp/base/TagStat.java @@ -0,0 +1,22 @@ +/** + * Copyright 2024 pegasko + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package art.pegasko.yeeemp.base; + +public class TagStat { + public Tag tag; + public int count; +} diff --git a/Yeeemp/app/src/main/java/art/pegasko/yeeemp/base/Wrapper.java b/Yeeemp/app/src/main/java/art/pegasko/yeeemp/base/Wrapper.java new file mode 100644 index 0000000..b5517fc --- /dev/null +++ b/Yeeemp/app/src/main/java/art/pegasko/yeeemp/base/Wrapper.java @@ -0,0 +1,45 @@ +/** + * Copyright 2024 pegasko + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package art.pegasko.yeeemp.base; + +public abstract class Wrapper { + private static Wrapper instance; + + public static Wrapper instance() { + return Wrapper.instance; + } + + public static void setInstance(Wrapper instance) { + Wrapper.instance = instance; + } + + public static QueueMaker getQueueMaker() { + return Wrapper.instance().queueMaker(); + } + + public static EventMaker getEventMaker() { + return Wrapper.instance().eventMaker(); + } + + public static TagMaker getTagMaker() { + return Wrapper.instance().tagMaker(); + } + + public abstract QueueMaker queueMaker(); + public abstract EventMaker eventMaker(); + public abstract TagMaker tagMaker(); +} diff --git a/Yeeemp/app/src/main/java/art/pegasko/yeeemp/impl/DBWrapper.java b/Yeeemp/app/src/main/java/art/pegasko/yeeemp/impl/DBWrapper.java new file mode 100644 index 0000000..f46c086 --- /dev/null +++ b/Yeeemp/app/src/main/java/art/pegasko/yeeemp/impl/DBWrapper.java @@ -0,0 +1,140 @@ +/** + * Copyright 2024 pegasko + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package art.pegasko.yeeemp.impl; + +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteException; +import android.util.Log; + +import java.io.File; +import java.io.IOException; + +import art.pegasko.yeeemp.base.EventMaker; +import art.pegasko.yeeemp.base.Wrapper; +import art.pegasko.yeeemp.base.QueueMaker; +import art.pegasko.yeeemp.base.TagMaker; + +public class DBWrapper extends Wrapper { + public static final String TAG = DBWrapper.class.getSimpleName(); + public static final boolean DEBUG = true; + + public DBWrapper(Context context) { + this.db = openDB(context); + this.queueMaker = new QueueMakerImpl(this.db); + this.eventMaker = new EventMakerImpl(this.db); + this.tagMaker = new TagMakerImpl(this.db); + } + + // Fields + SQLiteDatabase db; + QueueMaker queueMaker; + EventMaker eventMaker; + TagMaker tagMaker; + + public QueueMaker queueMaker() { + return this.queueMaker; + } + + public EventMaker eventMaker() { + return this.eventMaker; + } + + public TagMaker tagMaker() { + return this.tagMaker; + } + + /** + * SQLite initializer script + */ + // @formatter:off + private static final String[] INIT_SQL = new String[] { + "CREATE TABLE IF NOT EXISTS tag (" + + " id INTEGER PRIMARY KEY AUTOINCREMENT," + + " queue_id INTEGER," + + " name TEXT" + + ");", + + "CREATE INDEX IF NOT EXISTS tag__queue_id ON tag(queue_id);", + + "CREATE INDEX IF NOT EXISTS tag__name ON tag(name);", + + "CREATE TABLE IF NOT EXISTS event (" + + " id INTEGER PRIMARY KEY AUTOINCREMENT," + + " timestamp INTEGER," + + " comment TEXT" + + ");", + + "CREATE TABLE IF NOT EXISTS queue (" + + " id INTEGER PRIMARY KEY AUTOINCREMENT," + + " name TEXT" + + ");", + + "CREATE TABLE IF NOT EXISTS event_tag (" + + " event_id INTEGER," + + " tag_id INTEGER" + + ");", + + "CREATE INDEX IF NOT EXISTS event_tag__event_id_tag_id ON event_tag(event_id, tag_id);", + + "CREATE INDEX IF NOT EXISTS event_tag__event_id ON event_tag(event_id);", + + "CREATE INDEX IF NOT EXISTS event_tag__tag_id ON event_tag(tag_id);", + + "CREATE TABLE IF NOT EXISTS queue_event (" + + " queue_id INTEGER," + + " event_id INTEGER" + + ");" + }; + + private static String DB_PATH = "database.db"; + + /** + * Initialize database object + * + * @param db + */ + private static void initDB(SQLiteDatabase db) { + try { + Log.d(TAG, "Creating database"); + for (String query: INIT_SQL) { + Log.d(TAG, query); + db.execSQL(query); + } + Log.d(TAG, "Database created"); + } catch (SQLiteException e) { + Log.wtf(TAG, e); + } + } + + /** + * @return opened and initialized database + */ + private SQLiteDatabase openDB(Context context) { + if (DBWrapper.DEBUG) { + try { + new File(context.getFilesDir(), DB_PATH).delete(); + } catch (Exception e) { + Log.wtf(DBWrapper.TAG, e); + } + } + + SQLiteDatabase db = SQLiteDatabase.openDatabase(new File(context.getFilesDir(), DB_PATH).getPath(), null, SQLiteDatabase.OPEN_READWRITE | SQLiteDatabase.CREATE_IF_NECESSARY); + initDB(db); + return db; + } +} diff --git a/Yeeemp/app/src/main/java/art/pegasko/yeeemp/impl/EventImpl.java b/Yeeemp/app/src/main/java/art/pegasko/yeeemp/impl/EventImpl.java new file mode 100644 index 0000000..6938e55 --- /dev/null +++ b/Yeeemp/app/src/main/java/art/pegasko/yeeemp/impl/EventImpl.java @@ -0,0 +1,222 @@ +/** + * Copyright 2024 pegasko + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package art.pegasko.yeeemp.impl; + +import android.content.ContentValues; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteException; +import android.util.Log; + +import androidx.annotation.NonNull; + +import art.pegasko.yeeemp.base.Event; +import art.pegasko.yeeemp.base.Queue; +import art.pegasko.yeeemp.base.Tag; + +public class EventImpl implements Event { + public static final String TAG = EventImpl.class.getSimpleName(); + + private final SQLiteDatabase db; + private final Queue queue; + private final int id; + + protected EventImpl(SQLiteDatabase db, Queue queue, int id) { + this.db = db; + this.queue = queue; + this.id = id; + } + + @Override + public int getId() { + return this.id; + } + + @Override + public long getTimestamp() { + synchronized (this.db) { + Cursor cursor = db.query( + "event", + new String[] { "timestamp" }, + "query_id = ? AND id = ?", + new String[] { Integer.toString(queue.getId()), Integer.toString(this.getId()) }, + null, + null, + null + ); + + if (Utils.findResult(cursor)) { + return cursor.getLong(0); + } + + return 0; + } + } + + @Override + public void setTimestamp(long timestamp) { + synchronized (this.db) { + ContentValues cv = new ContentValues(); + cv.put("timestamp", timestamp); + db.update( + "event", + cv, + "id = ?", + new String[] { Integer.toString(this.getId()) } + ); + } + } + + @Override + public String getMessage() { + synchronized (this.db) { + Cursor cursor = db.query( + "event", + new String[] { "message" }, + "id = ?", + new String[] { Integer.toString(this.getId()) }, + null, + null, + null + ); + + if (Utils.findResult(cursor)) { + return cursor.getString(0); + } + + return null; + } + } + + @Override + public void setMessage(String message) { + synchronized (this.db) { + ContentValues cv = new ContentValues(); + cv.put("message", message); + db.update( + "event", + cv, + "id = ?", + new String[] { Integer.toString(this.getId()) } + ); + } + } + + /** !synchronized */ + protected boolean hasTag(Tag tag) { + synchronized (this.db) { + Cursor cursor = db.query( + "event_tag", + new String[] { "1" }, + "event_id = ? AND tag_id = ?", + new String[] { Integer.toString(this.getId()), Integer.toString(tag.getId()) }, + null, + null, + null + ); + + return Utils.findResult(cursor); + } + } + + @Override + public void addTag(Tag tag) { + synchronized (this.db) { + if (tag == null) + return; + + if (this.hasTag(tag)) + return; + + try { + ContentValues cv = new ContentValues(); + cv.put("event_id", this.getId()); + cv.put("tag_id", tag.getId()); + db.insertOrThrow("event_tag", null, cv); + } catch (SQLiteException e) { + Log.w(TAG, e); + } + } + } + + @Override + public void removeTag(Tag tag) { + synchronized (this.db) { + if (tag == null) + return; + + if (!this.hasTag(tag)) + return; + + try { + db.delete( + "event_tag", + "event_id = ? AND tag_id = ?", + new String[] { Integer.toString(this.getId()), Integer.toString(tag.getId()) } + ); + } catch (SQLiteException e) { + Log.w(TAG, e); + } + } + } + + @Override + public Tag[] getTags() { + synchronized (this.db) { + Cursor cursor = db.query( + "event_tag", + new String[] { "tag_id" }, + "event_id = ?", + new String[] { Integer.toString(this.getId()) }, + null, + null, + null + ); + + if (cursor == null) { + return new Tag[0]; + } + + Tag[] tags = new Tag[cursor.getCount()]; + + int index = 0; + while (cursor.moveToNext()) { + tags[index++] = new TagImpl( + this.db, + this.queue, + cursor.getInt(0) + ); + } + + return tags; + } + } + + @NonNull + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("Queue{id="); + sb.append(this.getId()); + sb.append(",timestamp="); + sb.append(this.getTimestamp()); + sb.append(",message="); + sb.append(this.getMessage()); + sb.append("}"); + return sb.toString(); + } +} diff --git a/Yeeemp/app/src/main/java/art/pegasko/yeeemp/impl/EventMakerImpl.java b/Yeeemp/app/src/main/java/art/pegasko/yeeemp/impl/EventMakerImpl.java new file mode 100644 index 0000000..4146606 --- /dev/null +++ b/Yeeemp/app/src/main/java/art/pegasko/yeeemp/impl/EventMakerImpl.java @@ -0,0 +1,57 @@ +/** + * Copyright 2024 pegasko + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package art.pegasko.yeeemp.impl; + +import android.content.ContentValues; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteException; +import android.util.Log; + +import art.pegasko.yeeemp.base.Event; +import art.pegasko.yeeemp.base.EventMaker; +import art.pegasko.yeeemp.base.Queue; + +public class EventMakerImpl implements EventMaker { + public static final String TAG = EventMakerImpl.class.getSimpleName(); + + private SQLiteDatabase db; + + public EventMakerImpl(SQLiteDatabase db) { + this.db = db; + } + + @Override + public Event createInQueue(Queue queue) { + synchronized (this.db) { + try { + ContentValues cv = new ContentValues(); + cv.put("queue_id", queue.getId()); + long rowId = db.insertOrThrow("event", null, cv); + return new EventImpl( + this.db, + queue, + (int) rowId + ); + } catch (SQLiteException e) { + Log.w(TAG, e); + } + + return null; + } + } +} diff --git a/Yeeemp/app/src/main/java/art/pegasko/yeeemp/impl/Init.java b/Yeeemp/app/src/main/java/art/pegasko/yeeemp/impl/Init.java new file mode 100644 index 0000000..87c7ea5 --- /dev/null +++ b/Yeeemp/app/src/main/java/art/pegasko/yeeemp/impl/Init.java @@ -0,0 +1,28 @@ +/** + * Copyright 2024 pegasko + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package art.pegasko.yeeemp.impl; + +import android.content.Context; + +import art.pegasko.yeeemp.base.Wrapper; + +public class Init { + public static void initDB(Context context) { + if (Wrapper.instance() == null) + Wrapper.setInstance(new DBWrapper(context)); + } +} \ No newline at end of file diff --git a/Yeeemp/app/src/main/java/art/pegasko/yeeemp/impl/QueueImpl.java b/Yeeemp/app/src/main/java/art/pegasko/yeeemp/impl/QueueImpl.java new file mode 100644 index 0000000..890c8fb --- /dev/null +++ b/Yeeemp/app/src/main/java/art/pegasko/yeeemp/impl/QueueImpl.java @@ -0,0 +1,188 @@ +/** + * Copyright 2024 pegasko + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package art.pegasko.yeeemp.impl; + +import android.content.ContentValues; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteException; +import android.util.Log; + +import androidx.annotation.NonNull; + +import art.pegasko.yeeemp.base.Event; +import art.pegasko.yeeemp.base.Queue; +import art.pegasko.yeeemp.base.Tag; + +public class QueueImpl implements Queue { + public static final String TAG = QueueImpl.class.getSimpleName(); + + private final SQLiteDatabase db; + private final int id; + + protected QueueImpl(SQLiteDatabase db, int id) { + this.db = db; + this.id = id; + } + + @Override + public int getId() { + return this.id; + } + + @Override + public String getName() { + synchronized (this.db) { + Cursor cursor = db.query( + "queue", + new String[] { "name" }, + "id = ?", + new String[] { Integer.toString(this.getId()) }, + null, + null, + null + ); + + if (Utils.findResult(cursor)) { + return cursor.getString(0); + } + + return null; + } + } + + @Override + public void setName(String name) { + synchronized (this.db) { + ContentValues cv = new ContentValues(); + cv.put("name", name); + db.update( + "queue", + cv, + "id = ?", + new String[] { Integer.toString(this.getId()) } + ); + } + } + + @Override + public Event[] getEvents() { + return new Event[0]; + } + + /** !synchronized */ + protected boolean hasEvent(Event event) { + synchronized (this.db) { + Cursor cursor = db.query( + "query_event", + new String[] { "1" }, + "query_id = ? AND event_id = ?", + new String[] { Integer.toString(this.getId()), Integer.toString(event.getId()) }, + null, + null, + null + ); + + return Utils.findResult(cursor); + } + } + + @Override + public void addEvent(Event event) { + synchronized (this.db) { + if (event == null) + return; + + if (this.hasEvent(event)) + return; + + try { + ContentValues cv = new ContentValues(); + cv.put("queue_id", this.getId()); + cv.put("event_id", event.getId()); + db.insertOrThrow("queue_event", null, cv); + } catch (SQLiteException e) { + Log.w(TAG, e); + } + } + } + + @Override + public void removeEvent(Event event) { + synchronized (this.db) { + if (event == null) + return; + + if (this.hasEvent(event)) + return; + + try { + db.delete( + "queue_event", + "queue_id = ? AND event_id = ?", + new String[] { Integer.toString(this.getId()), Integer.toString(event.getId()) } + ); + } catch (SQLiteException e) { + Log.w(TAG, e); + } + } + } + + @Override + public Tag[] getGlobalTags() { + synchronized (this.db) { + Cursor cursor = db.query( + "tag", + new String[] { "id" }, + "queue_id = ?", + new String[] { Integer.toString(this.getId()) }, + null, + null, + null + ); + + if (cursor == null) { + return new Tag[0]; + } + + Tag[] tags = new Tag[cursor.getCount()]; + + int index = 0; + while (cursor.moveToNext()) { + tags[index++] = new TagImpl( + this.db, + this, + cursor.getInt(0) + ); + } + + return tags; + } + } + + @NonNull + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("Queue{id="); + sb.append(this.getId()); + sb.append(",name="); + sb.append(this.getName()); + sb.append("}"); + return sb.toString(); + } +} diff --git a/Yeeemp/app/src/main/java/art/pegasko/yeeemp/impl/QueueMakerImpl.java b/Yeeemp/app/src/main/java/art/pegasko/yeeemp/impl/QueueMakerImpl.java new file mode 100644 index 0000000..581c89f --- /dev/null +++ b/Yeeemp/app/src/main/java/art/pegasko/yeeemp/impl/QueueMakerImpl.java @@ -0,0 +1,86 @@ +/** + * Copyright 2024 pegasko + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package art.pegasko.yeeemp.impl; + +import android.content.ContentValues; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteException; +import android.util.Log; + +import art.pegasko.yeeemp.base.Queue; +import art.pegasko.yeeemp.base.QueueMaker; + +public class QueueMakerImpl implements QueueMaker { + public static final String TAG = EventMakerImpl.class.getSimpleName(); + + private SQLiteDatabase db; + + public QueueMakerImpl(SQLiteDatabase db) { + this.db = db; + } + + @Override + public Queue create() { + synchronized (this.db) { + try { + ContentValues cv = new ContentValues(); + cv.put("id", (Integer) null); + long rowId = db.insertOrThrow("queue", null, cv); + return new QueueImpl( + this.db, + (int) rowId + ); + } catch (SQLiteException e) { + Log.w(TAG, e); + } + + return null; + } + } + + @Override + public Queue[] list() { + synchronized (this.db) { + Cursor cursor = db.query( + "queue", + new String[] { "id" }, + null, + null, + null, + null, + null + ); + + if (cursor == null) { + return new Queue[0]; + } + + Queue[] queues = new Queue[cursor.getCount()]; + + int index = 0; + while (cursor.moveToNext()) { + queues[index++] = new QueueImpl( + this.db, + cursor.getInt(0) + ); + } + + return queues; + } + } +} diff --git a/Yeeemp/app/src/main/java/art/pegasko/yeeemp/impl/TagImpl.java b/Yeeemp/app/src/main/java/art/pegasko/yeeemp/impl/TagImpl.java new file mode 100644 index 0000000..9607e99 --- /dev/null +++ b/Yeeemp/app/src/main/java/art/pegasko/yeeemp/impl/TagImpl.java @@ -0,0 +1,77 @@ +/** + * Copyright 2024 pegasko + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package art.pegasko.yeeemp.impl; + +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; + +import androidx.annotation.NonNull; + +import art.pegasko.yeeemp.base.Tag; +import art.pegasko.yeeemp.base.Queue; + +public class TagImpl implements Tag { + public static final String TAG = TagImpl.class.getSimpleName(); + + private final SQLiteDatabase db; + private final Queue queue; + private final int id; + + protected TagImpl(SQLiteDatabase db, Queue queue, int id) { + this.db = db; + this.queue = queue; + this.id = id; + } + + @Override + public int getId() { + return this.id; + } + + @Override + public String getName() { + synchronized (this.db) { + Cursor cursor = db.query( + "tag", + new String[] { "name" }, + "query_id = ? AND id = ?", + new String[] { Integer.toString(queue.getId()), Integer.toString(this.getId()) }, + null, + null, + null + ); + + if (Utils.findResult(cursor)) { + return cursor.getString(0); + } + + return null; + } + } + + @NonNull + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("Tag{id="); + sb.append(this.getId()); + sb.append(",name="); + sb.append(this.getName()); + sb.append("}"); + return sb.toString(); + } +} diff --git a/Yeeemp/app/src/main/java/art/pegasko/yeeemp/impl/TagMakerImpl.java b/Yeeemp/app/src/main/java/art/pegasko/yeeemp/impl/TagMakerImpl.java new file mode 100644 index 0000000..859f63e --- /dev/null +++ b/Yeeemp/app/src/main/java/art/pegasko/yeeemp/impl/TagMakerImpl.java @@ -0,0 +1,95 @@ +/** + * Copyright 2024 pegasko + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package art.pegasko.yeeemp.impl; + +import android.content.ContentValues; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteException; +import android.util.Log; + +import art.pegasko.yeeemp.base.Queue; +import art.pegasko.yeeemp.base.Tag; +import art.pegasko.yeeemp.base.TagMaker; + +public class TagMakerImpl implements TagMaker { + public static final String TAG = TagMakerImpl.class.getSimpleName(); + + private SQLiteDatabase db; + + public TagMakerImpl(SQLiteDatabase db) { + this.db = db; + } + + /* !synchronized */ + private Tag getExisting(Queue queue, String name) { + try { + Cursor cursor = db.query( + "tag", + new String[] { "id", "name" }, + "query_id = ? AND name = ?", + new String[] { Integer.toString(queue.getId()), name }, + null, + null, + null + ); + if (Utils.findResult(cursor)) { + return new TagImpl( + db, + queue, + cursor.getInt(0) + ); + } + } catch (SQLiteException e) { + Log.w(TAG, e); + } + + return null; + } + + /* !synchronized */ + private boolean create(Queue queue, String name) { + try { + ContentValues cv = new ContentValues(); + cv.put("queue_id", queue.getId()); + cv.put("name", name); + db.insertOrThrow("tag", null, cv); + } catch (SQLiteException e) { + Log.w(TAG, e); + return false; + } + return true; + } + + @Override + public Tag getOrCreateInQueue(Queue queue, String name) { + synchronized (db) { + name = name.trim().toLowerCase(); + + // Try get existing + Tag existingTag = getExisting(queue, name); + if (existingTag != null) + return existingTag; + + // Create new + create(queue, name); + + // Finally get + return getExisting(queue, name); + } + } +} diff --git a/Yeeemp/app/src/main/java/art/pegasko/yeeemp/impl/Utils.java b/Yeeemp/app/src/main/java/art/pegasko/yeeemp/impl/Utils.java new file mode 100644 index 0000000..ef03098 --- /dev/null +++ b/Yeeemp/app/src/main/java/art/pegasko/yeeemp/impl/Utils.java @@ -0,0 +1,29 @@ +/** + * Copyright 2024 pegasko + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package art.pegasko.yeeemp.impl; + +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; + +public class Utils { + public static boolean findResult(Cursor cursor) { + if (cursor == null) + return false; + cursor.moveToFirst(); + return cursor.getCount() != 0; + } +} diff --git a/Yeeemp/app/src/main/java/art/pegasko/yeeemp/ui/activity/FirstFragment.java b/Yeeemp/app/src/main/java/art/pegasko/yeeemp/ui/activity/FirstFragment.java new file mode 100644 index 0000000..6f50327 --- /dev/null +++ b/Yeeemp/app/src/main/java/art/pegasko/yeeemp/ui/activity/FirstFragment.java @@ -0,0 +1,61 @@ +/** + * Copyright 2024 pegasko + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package art.pegasko.yeeemp.ui.activity; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; +import androidx.navigation.fragment.NavHostFragment; + +import art.pegasko.yeeemp.R; +import art.pegasko.yeeemp.databinding.FragmentFirstBinding; + +public class FirstFragment extends Fragment { + +private FragmentFirstBinding binding; + + @Override + public View onCreateView( + @NonNull LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState + ) { + + binding = FragmentFirstBinding.inflate(inflater, container, false); + return binding.getRoot(); + + } + + public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + binding.buttonFirst.setOnClickListener(v -> + NavHostFragment.findNavController(FirstFragment.this) + .navigate(R.id.action_FirstFragment_to_SecondFragment) + ); + } + +@Override + public void onDestroyView() { + super.onDestroyView(); + binding = null; + } + +} \ No newline at end of file diff --git a/Yeeemp/app/src/main/java/art/pegasko/yeeemp/ui/activity/MainActivity.java b/Yeeemp/app/src/main/java/art/pegasko/yeeemp/ui/activity/MainActivity.java new file mode 100644 index 0000000..198a936 --- /dev/null +++ b/Yeeemp/app/src/main/java/art/pegasko/yeeemp/ui/activity/MainActivity.java @@ -0,0 +1,103 @@ +/** + * Copyright 2024 pegasko + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package art.pegasko.yeeemp.ui.activity; + +import android.os.Bundle; +import com.google.android.material.snackbar.Snackbar; +import androidx.appcompat.app.AppCompatActivity; + +import android.util.Log; +import android.view.View; +import androidx.navigation.NavController; +import androidx.navigation.Navigation; +import androidx.navigation.ui.AppBarConfiguration; +import androidx.navigation.ui.NavigationUI; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import art.pegasko.yeeemp.base.Queue; +import art.pegasko.yeeemp.base.Wrapper; +import art.pegasko.yeeemp.databinding.ActivityMainBinding; + +import art.pegasko.yeeemp.R; +import art.pegasko.yeeemp.impl.EventImpl; +import art.pegasko.yeeemp.impl.Init; + +public class MainActivity extends AppCompatActivity { + public static final String TAG = MainActivity.class.getSimpleName(); + + private AppBarConfiguration appBarConfiguration; + private ActivityMainBinding binding; + private RecyclerView queueList; + private QueueRecyclerViewAdapter queueListAdapter; + + private void updateList() { + runOnUiThread(() -> { + queueListAdapter.reloadItems(); + }); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + Init.initDB(getApplicationContext()); + + binding = ActivityMainBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + + setSupportActionBar(binding.toolbar); + + queueListAdapter = new QueueRecyclerViewAdapter(); + queueList = findViewById(R.id.content_main__queue_list); + queueList.setLayoutManager(new LinearLayoutManager(this)); + queueList.setAdapter(queueListAdapter); + +// NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main); +// appBarConfiguration = new AppBarConfiguration.Builder(navController.getGraph()).build(); +// NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration); + + /* FAB Listeners */ + binding.fab.setOnLongClickListener((View view) -> { + Snackbar.make(view, "Create Queue", Snackbar.LENGTH_LONG) + .setAnchorView(R.id.fab) + .setAction("Action", null).show(); + + return true; + }); + binding.fab.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + Queue q = Wrapper.getQueueMaker().create(); + q.setName("New Queue"); + Log.w(TAG, "Create: " + q.toString()); + + updateList(); + } + }); + + /* Fill lists */ + updateList(); + } + +// @Override +// public boolean onSupportNavigateUp() { +// NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main); +// return NavigationUI.navigateUp(navController, appBarConfiguration) +// || super.onSupportNavigateUp(); +// } +} \ No newline at end of file diff --git a/Yeeemp/app/src/main/java/art/pegasko/yeeemp/ui/activity/QueueRecyclerViewAdapter.java b/Yeeemp/app/src/main/java/art/pegasko/yeeemp/ui/activity/QueueRecyclerViewAdapter.java new file mode 100644 index 0000000..103fba8 --- /dev/null +++ b/Yeeemp/app/src/main/java/art/pegasko/yeeemp/ui/activity/QueueRecyclerViewAdapter.java @@ -0,0 +1,115 @@ +package art.pegasko.yeeemp.ui.activity; + +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.text.InputType; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.EditText; +import android.widget.PopupMenu; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import art.pegasko.yeeemp.R; +import art.pegasko.yeeemp.base.Queue; +import art.pegasko.yeeemp.base.QueueMaker; +import art.pegasko.yeeemp.base.Wrapper; +import art.pegasko.yeeemp.impl.EventImpl; + +public class QueueRecyclerViewAdapter extends RecyclerView.Adapter { + public static final String TAG = QueueRecyclerViewAdapter.class.getSimpleName(); + + private Queue[] queues; + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) { + View view = ( + LayoutInflater + .from(viewGroup.getContext()) + .inflate(R.layout.queue_list_item, viewGroup, false) + ); + + return new ViewHolder(view); + } + + @Override + public void onBindViewHolder(@NonNull ViewHolder viewHolder, int position) { + viewHolder.queueName.setText(queues[position].getName()); + viewHolder.queueItem.setOnLongClickListener((View view) -> { + PopupMenu popupMenu = new PopupMenu(view.getContext(), viewHolder.queueItem); + popupMenu.getMenuInflater().inflate(R.menu.queue_list_item_action_menu, popupMenu.getMenu()); + popupMenu.setOnMenuItemClickListener((MenuItem menuItem) -> { + if (menuItem.getItemId() == R.id.queue_list_item_action_menu_delete) { + new AlertDialog.Builder(view.getContext()) + .setTitle("Delete entry") + .setMessage("Are you sure you want to delete this queue?") + .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { +// Wrapper.getQueueMaker().delete + Log.e(TAG, "Not implemented action"); + } + }) + .setNegativeButton(android.R.string.no, null) + .setIcon(android.R.drawable.ic_dialog_alert) + .show(); + } else if (menuItem.getItemId() == R.id.queue_list_item_action_menu_rename) { + AlertDialog.Builder builder = new AlertDialog.Builder(view.getContext()); + builder.setTitle("Title"); + + final EditText input = new EditText(view.getContext()); + input.setInputType(InputType.TYPE_CLASS_TEXT); + input.setText(queues[position].getName()); + builder.setView(input); + + builder.setPositiveButton("OK", (dialog, which) -> { + String name = input.getText().toString().trim(); + queues[position].setName(name); + + reloadItems(); + }); + builder.setNegativeButton("Cancel", (dialog, which) -> dialog.cancel()); + + builder.show(); + } + return true; + }); + + popupMenu.show(); + + return true; + }); + } + + @Override + public int getItemCount() { + return this.queues.length; + } + + public static class ViewHolder extends RecyclerView.ViewHolder { + private TextView queueName; + private View queueItem; + + public ViewHolder(@NonNull View itemView) { + super(itemView); + + queueItem = itemView.findViewById(R.id.queue_list_item__queue_item); + queueName = (TextView) itemView.findViewById(R.id.queue_list_item__queue_title); + } + + public TextView getQueueName() { + return queueName; + } + } + + public void reloadItems() { + this.queues = Wrapper.getQueueMaker().list(); + this.notifyDataSetChanged(); + } +} diff --git a/Yeeemp/app/src/main/java/art/pegasko/yeeemp/ui/activity/SecondFragment.java b/Yeeemp/app/src/main/java/art/pegasko/yeeemp/ui/activity/SecondFragment.java new file mode 100644 index 0000000..5b65bbe --- /dev/null +++ b/Yeeemp/app/src/main/java/art/pegasko/yeeemp/ui/activity/SecondFragment.java @@ -0,0 +1,61 @@ +/** + * Copyright 2024 pegasko + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package art.pegasko.yeeemp.ui.activity; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; +import androidx.navigation.fragment.NavHostFragment; + +import art.pegasko.yeeemp.R; +import art.pegasko.yeeemp.databinding.FragmentSecondBinding; + +public class SecondFragment extends Fragment { + +private FragmentSecondBinding binding; + + @Override + public View onCreateView( + @NonNull LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState + ) { + + binding = FragmentSecondBinding.inflate(inflater, container, false); + return binding.getRoot(); + + } + + public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + binding.buttonSecond.setOnClickListener(v -> + NavHostFragment.findNavController(SecondFragment.this) + .navigate(R.id.action_SecondFragment_to_FirstFragment) + ); + } + +@Override + public void onDestroyView() { + super.onDestroyView(); + binding = null; + } + +} \ No newline at end of file diff --git a/Yeeemp/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/Yeeemp/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/Yeeemp/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/Yeeemp/app/src/main/res/drawable/ic_launcher_background.xml b/Yeeemp/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/Yeeemp/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Yeeemp/app/src/main/res/drawable/pegasko_listitem_background.xml b/Yeeemp/app/src/main/res/drawable/pegasko_listitem_background.xml new file mode 100644 index 0000000..52e51a3 --- /dev/null +++ b/Yeeemp/app/src/main/res/drawable/pegasko_listitem_background.xml @@ -0,0 +1,16 @@ + + + + + + + + + \ No newline at end of file diff --git a/Yeeemp/app/src/main/res/layout/activity_main.xml b/Yeeemp/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..f1a0546 --- /dev/null +++ b/Yeeemp/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Yeeemp/app/src/main/res/layout/content_main.xml b/Yeeemp/app/src/main/res/layout/content_main.xml new file mode 100644 index 0000000..f1bb748 --- /dev/null +++ b/Yeeemp/app/src/main/res/layout/content_main.xml @@ -0,0 +1,20 @@ + + + + + + \ No newline at end of file diff --git a/Yeeemp/app/src/main/res/layout/fragment_first.xml b/Yeeemp/app/src/main/res/layout/fragment_first.xml new file mode 100644 index 0000000..fcb2229 --- /dev/null +++ b/Yeeemp/app/src/main/res/layout/fragment_first.xml @@ -0,0 +1,34 @@ + + + +