to kotlit
This commit is contained in:
parent
0b29993073
commit
8051c2c4ed
|
|
@ -1,7 +1,12 @@
|
||||||
*.iml
|
*.iml
|
||||||
.gradle
|
.gradle
|
||||||
/local.properties
|
/local.properties
|
||||||
/.idea
|
/.idea/caches
|
||||||
|
/.idea/libraries
|
||||||
|
/.idea/modules.xml
|
||||||
|
/.idea/workspace.xml
|
||||||
|
/.idea/navEditor.xml
|
||||||
|
/.idea/assetWizardSettings.xml
|
||||||
.DS_Store
|
.DS_Store
|
||||||
/build
|
/build
|
||||||
/captures
|
/captures
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
dermy
|
dermy_app
|
||||||
|
|
@ -0,0 +1,123 @@
|
||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<code_scheme name="Project" version="173">
|
||||||
|
<JetCodeStyleSettings>
|
||||||
|
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||||
|
</JetCodeStyleSettings>
|
||||||
|
<codeStyleSettings language="XML">
|
||||||
|
<option name="FORCE_REARRANGE_MODE" value="1" />
|
||||||
|
<indentOptions>
|
||||||
|
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||||
|
</indentOptions>
|
||||||
|
<arrangement>
|
||||||
|
<rules>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>xmlns:android</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>xmlns:.*</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
<order>BY_NAME</order>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*:id</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*:name</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>name</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>style</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
<order>BY_NAME</order>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
<order>ANDROID_ATTRIBUTE_ORDER</order>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>.*</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
<order>BY_NAME</order>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
</rules>
|
||||||
|
</arrangement>
|
||||||
|
</codeStyleSettings>
|
||||||
|
<codeStyleSettings language="kotlin">
|
||||||
|
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||||
|
</codeStyleSettings>
|
||||||
|
</code_scheme>
|
||||||
|
</component>
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<state>
|
||||||
|
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||||
|
</state>
|
||||||
|
</component>
|
||||||
|
|
@ -4,14 +4,6 @@
|
||||||
<selectionStates>
|
<selectionStates>
|
||||||
<SelectionState runConfigName="app">
|
<SelectionState runConfigName="app">
|
||||||
<option name="selectionMode" value="DROPDOWN" />
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
<DropdownSelection timestamp="2024-06-04T18:01:16.272682016Z">
|
|
||||||
<Target type="DEFAULT_BOOT">
|
|
||||||
<handle>
|
|
||||||
<DeviceId pluginId="LocalEmulator" identifier="path=/home/r0r5chach/.config/.android/avd/Pixel_8_Pro_API_30.avd" />
|
|
||||||
</handle>
|
|
||||||
</Target>
|
|
||||||
</DropdownSelection>
|
|
||||||
<DialogSelection />
|
|
||||||
</SelectionState>
|
</SelectionState>
|
||||||
</selectionStates>
|
</selectionStates>
|
||||||
</component>
|
</component>
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,9 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
|
||||||
<component name="GradleSettings">
|
<component name="GradleSettings">
|
||||||
<option name="linkedExternalProjectsSettings">
|
<option name="linkedExternalProjectsSettings">
|
||||||
<GradleProjectSettings>
|
<GradleProjectSettings>
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
<option name="gradleHome" value="/usr/share/java/gradle" />
|
|
||||||
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
|
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
|
||||||
<option name="modules">
|
<option name="modules">
|
||||||
<set>
|
<set>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="KotlinJpsPluginSettings">
|
||||||
|
<option name="version" value="1.9.0" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
|
|
@ -1,14 +1,15 @@
|
||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.android.application)
|
alias(libs.plugins.android.application)
|
||||||
|
alias(libs.plugins.jetbrains.kotlin.android)
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "xyz.r0r5chach.dermy"
|
namespace = "xyz.r0r5chach.dermy_app"
|
||||||
compileSdk = 34
|
compileSdk = 34
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "xyz.r0r5chach.dermy"
|
applicationId = "xyz.r0r5chach.dermy_app"
|
||||||
minSdk = 22
|
minSdk = 24
|
||||||
targetSdk = 34
|
targetSdk = 34
|
||||||
versionCode = 1
|
versionCode = 1
|
||||||
versionName = "1.0"
|
versionName = "1.0"
|
||||||
|
|
@ -23,27 +24,33 @@ android {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
sourceCompatibility = JavaVersion.VERSION_16
|
||||||
targetCompatibility = JavaVersion.VERSION_1_8
|
targetCompatibility = JavaVersion.VERSION_16
|
||||||
|
}
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = "1.8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
implementation(libs.appcompat)
|
implementation(libs.androidx.core.ktx)
|
||||||
|
implementation(libs.androidx.appcompat)
|
||||||
implementation(libs.material)
|
implementation(libs.material)
|
||||||
|
implementation(libs.androidx.preference)
|
||||||
|
implementation(libs.androidx.room.common)
|
||||||
testImplementation(libs.junit)
|
testImplementation(libs.junit)
|
||||||
androidTestImplementation(libs.ext.junit)
|
androidTestImplementation(libs.androidx.junit)
|
||||||
androidTestImplementation(libs.espresso.core)
|
androidTestImplementation(libs.androidx.espresso.core)
|
||||||
|
|
||||||
// CameraX core library
|
//FragmentX
|
||||||
implementation(libs.camera.core)
|
implementation(libs.androidx.fragment.ktx)
|
||||||
implementation(libs.camera.camera2)
|
|
||||||
implementation(libs.camera.lifecycle)
|
|
||||||
implementation(libs.camera.view)
|
|
||||||
implementation(libs.camera.extensions)
|
|
||||||
|
|
||||||
//TFLite
|
//CameraX
|
||||||
implementation(libs.tensorflow.lite)
|
implementation(libs.androidx.camera.view)
|
||||||
|
implementation(libs.androidx.camera.core)
|
||||||
|
implementation(libs.androidx.camera.lifecycle)
|
||||||
|
|
||||||
|
//MongoDB
|
||||||
|
implementation(libs.mongodb.driver.kotlin.sync)
|
||||||
}
|
}
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
package xyz.r0r5chach.dermy;
|
|
||||||
|
|
||||||
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 <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
|
||||||
*/
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
|
||||||
public class ExampleInstrumentedTest {
|
|
||||||
@Test
|
|
||||||
public void useAppContext() {
|
|
||||||
// Context of the app under test.
|
|
||||||
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
|
||||||
assertEquals("xyz.r0r5chach.dermy", appContext.getPackageName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
package xyz.r0r5chach.dermy_app
|
||||||
|
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
|
import org.junit.Assert.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instrumented test, which will execute on an Android device.
|
||||||
|
*
|
||||||
|
* See [testing documentation](http://d.android.com/tools/testing).
|
||||||
|
*/
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class ExampleInstrumentedTest {
|
||||||
|
@Test
|
||||||
|
fun useAppContext() {
|
||||||
|
// Context of the app under test.
|
||||||
|
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||||
|
assertEquals("xyz.r0r5chach.dermy_app", appContext.packageName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,9 +2,6 @@
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.CAMERA" />
|
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!--TODO: Update to more modern permissions-->
|
|
||||||
<uses-feature android:name="android.hardware.camera" />
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||||
|
|
@ -13,17 +10,14 @@
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/Theme.Dermy"
|
android:theme="@style/Theme.Dermy_app"
|
||||||
tools:targetApi="31" >
|
tools:targetApi="31">
|
||||||
|
|
||||||
<activity android:name=".MainActivity"
|
<activity android:name=".MainActivity"
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
package xyz.r0r5chach.dermy;
|
|
||||||
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
|
||||||
|
|
||||||
import xyz.r0r5chach.dermy.fragments.CameraFragment;
|
|
||||||
|
|
||||||
public class MainActivity extends AppCompatActivity {
|
|
||||||
|
|
||||||
public static final int REQUEST_CAMERA_PERMISSION = 200;
|
|
||||||
|
|
||||||
public MainActivity() {
|
|
||||||
super(R.layout.activity_main);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
|
|
||||||
if (savedInstanceState == null) {
|
|
||||||
startCamera();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void startCamera() {
|
|
||||||
getSupportFragmentManager().beginTransaction()
|
|
||||||
.setReorderingAllowed(true)
|
|
||||||
.add(R.id.fragment_container, CameraFragment.class, null)
|
|
||||||
.commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { //FIXME: deprecated listener fir request permissions
|
|
||||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
|
||||||
if (requestCode == REQUEST_CAMERA_PERMISSION) {
|
|
||||||
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
|
||||||
startCamera();
|
|
||||||
} else {
|
|
||||||
Toast.makeText(this, "Camera permission is required", Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
package xyz.r0r5chach.dermy.entities;
|
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
|
|
||||||
import xyz.r0r5chach.dermy.entities.locations.Location;
|
|
||||||
import xyz.r0r5chach.dermy.entities.logs.LogBook;
|
|
||||||
|
|
||||||
public class Mole {
|
|
||||||
private Location location;
|
|
||||||
private LocalDateTime dateCreated;
|
|
||||||
private LogBook logEntries;
|
|
||||||
|
|
||||||
public Location getLocation() {
|
|
||||||
return location;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LogBook getLogBook() {
|
|
||||||
return logEntries;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
package xyz.r0r5chach.dermy.entities.locations;
|
|
||||||
|
|
||||||
public interface Location {
|
|
||||||
}
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
package xyz.r0r5chach.dermy.entities.locations;
|
|
||||||
|
|
||||||
public enum RightSideLocation implements Location {
|
|
||||||
Head,
|
|
||||||
Ear,
|
|
||||||
Cheek,
|
|
||||||
Neck,
|
|
||||||
Shoulder,
|
|
||||||
UpperArm,
|
|
||||||
Elbow,
|
|
||||||
LowerArm,
|
|
||||||
Wrist,
|
|
||||||
Hand,
|
|
||||||
UpperTorso,
|
|
||||||
LowerTorso,
|
|
||||||
Hip,
|
|
||||||
UpperLeg,
|
|
||||||
Knee,
|
|
||||||
LowerLeg,
|
|
||||||
Ankle,
|
|
||||||
Foot
|
|
||||||
}
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
package xyz.r0r5chach.dermy.entities.logs;
|
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class LogBook {
|
|
||||||
private final List<LogEntry> logEntries;
|
|
||||||
|
|
||||||
public LogBook() {
|
|
||||||
logEntries = new ArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addEntry(LogEntry entry) {
|
|
||||||
logEntries.add(entry);
|
|
||||||
} //TODO: Add to db
|
|
||||||
|
|
||||||
//TODO: Remove Entry
|
|
||||||
|
|
||||||
//TODO: Update Entry
|
|
||||||
|
|
||||||
private LogBook getEntriesBy(Object obj) {
|
|
||||||
LogBook results = new LogBook();
|
|
||||||
|
|
||||||
for (LogEntry entry: logEntries) {
|
|
||||||
if (entry.getDateCreated().equals(obj) || entry.getNotes().contains((CharSequence) obj)) {
|
|
||||||
results.addEntry(entry);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LogBook getEntriesByDate(LocalDateTime dateCreated) {
|
|
||||||
return getEntriesBy(dateCreated);
|
|
||||||
}
|
|
||||||
|
|
||||||
public LogBook getEntriesByNote(String noteFragment) {
|
|
||||||
return getEntriesBy(noteFragment);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
package xyz.r0r5chach.dermy.entities.logs;
|
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
|
|
||||||
public class LogEntry {
|
|
||||||
private LocalDateTime dateCreated;
|
|
||||||
private String imagePath, notes;
|
|
||||||
|
|
||||||
public LocalDateTime getDateCreated() {
|
|
||||||
return dateCreated;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getNotes() {
|
|
||||||
return notes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,120 +0,0 @@
|
||||||
package xyz.r0r5chach.dermy.fragments;
|
|
||||||
|
|
||||||
import static xyz.r0r5chach.dermy.MainActivity.REQUEST_CAMERA_PERMISSION;
|
|
||||||
|
|
||||||
import android.Manifest;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.graphics.BitmapFactory;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.Button;
|
|
||||||
import android.widget.Toast;
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.camera.core.CameraSelector;
|
|
||||||
import androidx.camera.core.ImageCapture;
|
|
||||||
import androidx.camera.core.ImageCaptureException;
|
|
||||||
import androidx.camera.core.Preview;
|
|
||||||
import androidx.camera.lifecycle.ProcessCameraProvider;
|
|
||||||
import androidx.camera.view.PreviewView;
|
|
||||||
import androidx.core.app.ActivityCompat;
|
|
||||||
import androidx.core.content.ContextCompat;
|
|
||||||
import androidx.fragment.app.Fragment;
|
|
||||||
|
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
|
|
||||||
import xyz.r0r5chach.dermy.R;
|
|
||||||
import xyz.r0r5chach.dermy.models.Model;
|
|
||||||
import xyz.r0r5chach.dermy.models.binary_classifiers.BinaryMobileNetV2;
|
|
||||||
|
|
||||||
public class CameraFragment extends Fragment {
|
|
||||||
private PreviewView previewView;
|
|
||||||
private ImageCapture imageCapture;
|
|
||||||
private ExecutorService cameraExecutor;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
|
||||||
View view = inflater.inflate(R.layout.fragment_camera, container, false);
|
|
||||||
previewView = view.findViewById(R.id.camera_preview);
|
|
||||||
|
|
||||||
if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
|
|
||||||
startCamera();
|
|
||||||
} else {
|
|
||||||
ActivityCompat.requestPermissions(requireActivity(), new String[]{Manifest.permission.CAMERA}, REQUEST_CAMERA_PERMISSION);
|
|
||||||
}
|
|
||||||
|
|
||||||
previewView.setOnClickListener(v -> takePhoto());
|
|
||||||
|
|
||||||
cameraExecutor = Executors.newSingleThreadExecutor();
|
|
||||||
|
|
||||||
return view;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void startCamera() {
|
|
||||||
ListenableFuture<ProcessCameraProvider> cameraProviderFuture = ProcessCameraProvider.getInstance(requireContext());
|
|
||||||
|
|
||||||
cameraProviderFuture.addListener(() -> {
|
|
||||||
try {
|
|
||||||
ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
|
|
||||||
bindPreview(cameraProvider);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace(); //TODO: Replace with better logging
|
|
||||||
}
|
|
||||||
}, ContextCompat.getMainExecutor(requireContext()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void bindPreview(@NonNull ProcessCameraProvider cameraProvider) {
|
|
||||||
Preview preview = new Preview.Builder().build();
|
|
||||||
preview.setSurfaceProvider(previewView.getSurfaceProvider());
|
|
||||||
|
|
||||||
imageCapture = new ImageCapture.Builder().build();
|
|
||||||
|
|
||||||
CameraSelector cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA;
|
|
||||||
|
|
||||||
cameraProvider.unbindAll();
|
|
||||||
cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void takePhoto() {
|
|
||||||
File photoFile = new File(requireContext().getExternalFilesDir(null), System.currentTimeMillis() + ".jpg");
|
|
||||||
ImageCapture.OutputFileOptions outputFileOptions = new ImageCapture.OutputFileOptions.Builder(photoFile).build();
|
|
||||||
imageCapture.takePicture(outputFileOptions, ContextCompat.getMainExecutor(requireContext()), new ImageCapture.OnImageSavedCallback() {
|
|
||||||
@Override
|
|
||||||
public void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) {
|
|
||||||
Bitmap photo = BitmapFactory.decodeFile(photoFile.getAbsolutePath());
|
|
||||||
try {
|
|
||||||
//TODO: Change Model based on preference
|
|
||||||
Model model = new BinaryMobileNetV2(getResources());
|
|
||||||
|
|
||||||
float[] results = model.runInference(photo, 2);
|
|
||||||
Toast.makeText(requireContext(), "Results = " + Arrays.toString(results), Toast.LENGTH_LONG).show();
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(@NonNull ImageCaptureException exception) {
|
|
||||||
exception.printStackTrace(); //TODO: Replace with better logging
|
|
||||||
Toast.makeText(requireContext(), "Error saving photo: " + exception.getMessage(), Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroy() {
|
|
||||||
super.onDestroy();
|
|
||||||
cameraExecutor.shutdown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
package xyz.r0r5chach.dermy.fragments;
|
|
||||||
|
|
||||||
public class MapFragment {
|
|
||||||
}
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
package xyz.r0r5chach.dermy.fragments;
|
|
||||||
|
|
||||||
public class NearbyFragment {
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
package xyz.r0r5chach.dermy.fragments;
|
|
||||||
|
|
||||||
public class PreferencesFragment {
|
|
||||||
//TODO: Disclaimers
|
|
||||||
//TODO: Useful Links
|
|
||||||
//TODO: Export File
|
|
||||||
//TODO: Backup data
|
|
||||||
//TODO: Recover Account
|
|
||||||
}
|
|
||||||
|
|
@ -1,67 +0,0 @@
|
||||||
package xyz.r0r5chach.dermy.models;
|
|
||||||
|
|
||||||
import android.content.res.AssetFileDescriptor;
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.graphics.Bitmap;
|
|
||||||
|
|
||||||
import org.tensorflow.lite.Interpreter;
|
|
||||||
|
|
||||||
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.ByteOrder;
|
|
||||||
import java.nio.MappedByteBuffer;
|
|
||||||
import java.nio.channels.FileChannel;
|
|
||||||
import java.nio.channels.FileChannel.MapMode;
|
|
||||||
|
|
||||||
import xyz.r0r5chach.dermy.R;
|
|
||||||
|
|
||||||
public class Model {
|
|
||||||
protected final Interpreter modelInterpreter;
|
|
||||||
|
|
||||||
public Model(Resources resources, int modelId) throws IOException {
|
|
||||||
modelInterpreter = new Interpreter(loadModel(resources, modelId));
|
|
||||||
}
|
|
||||||
|
|
||||||
public float[] runInference(Bitmap image, int classes) {
|
|
||||||
ByteBuffer inputBuffer = preprocessImage(image);
|
|
||||||
float[][] output = new float[1][classes];
|
|
||||||
modelInterpreter.run(inputBuffer, output);
|
|
||||||
|
|
||||||
return output[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
public MappedByteBuffer loadModel(Resources resources, int modelId) throws IOException {
|
|
||||||
AssetFileDescriptor fileDescriptor = resources.openRawResourceFd(modelId);
|
|
||||||
FileInputStream inputStream = new FileInputStream(fileDescriptor.getFileDescriptor()); //FIXME: add try-with-resources
|
|
||||||
|
|
||||||
FileChannel fileChannel = inputStream.getChannel();
|
|
||||||
long startOffset = fileDescriptor.getStartOffset();
|
|
||||||
long declaredLength = fileDescriptor.getDeclaredLength();
|
|
||||||
|
|
||||||
return fileChannel.map(MapMode.READ_ONLY, startOffset, declaredLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ByteBuffer preprocessImage(Bitmap image) {
|
|
||||||
ByteBuffer buffer = ByteBuffer.allocateDirect(4 * 280 * 280 * 3);
|
|
||||||
buffer.order(ByteOrder.nativeOrder());
|
|
||||||
|
|
||||||
int[] intValues = new int[280 * 280];
|
|
||||||
|
|
||||||
image.getPixels(intValues, 0, image.getWidth(), 0, 0, image.getWidth(), image.getHeight());
|
|
||||||
|
|
||||||
int pixel = 0;
|
|
||||||
for (int i = 0; i < 280; ++i) {
|
|
||||||
for (int j = 0; j < 280; ++j) {
|
|
||||||
int val = intValues[pixel++];
|
|
||||||
buffer.putFloat((((val >> 16) & 0xFF) - 127) / 128.0f);
|
|
||||||
buffer.putFloat((((val >> 8) & 0xFF) - 127) / 128.0f);
|
|
||||||
buffer.putFloat(((val & 0xFF) - 127) / 128.0f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
package xyz.r0r5chach.dermy.models.binary_classifiers;
|
|
||||||
|
|
||||||
import android.content.res.AssetManager;
|
|
||||||
import android.content.res.Resources;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import xyz.r0r5chach.dermy.R;
|
|
||||||
import xyz.r0r5chach.dermy.models.Model;
|
|
||||||
|
|
||||||
public class BinaryEfficientNetLite3 extends Model {
|
|
||||||
|
|
||||||
public BinaryEfficientNetLite3(Resources resources) throws IOException {
|
|
||||||
super(resources, R.raw.binary_efficientnet_lite3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
package xyz.r0r5chach.dermy.models.binary_classifiers;
|
|
||||||
|
|
||||||
import android.content.res.AssetManager;
|
|
||||||
import android.content.res.Resources;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import xyz.r0r5chach.dermy.R;
|
|
||||||
import xyz.r0r5chach.dermy.models.Model;
|
|
||||||
|
|
||||||
public class BinaryEfficientNetLite4 extends Model {
|
|
||||||
|
|
||||||
public BinaryEfficientNetLite4(Resources resources) throws IOException {
|
|
||||||
super(resources, R.raw.binary_efficientnet_lite4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
package xyz.r0r5chach.dermy.models.binary_classifiers;
|
|
||||||
|
|
||||||
import android.content.res.AssetManager;
|
|
||||||
import android.content.res.Resources;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import xyz.r0r5chach.dermy.R;
|
|
||||||
import xyz.r0r5chach.dermy.models.Model;
|
|
||||||
|
|
||||||
public class BinaryMobileNetV2 extends Model {
|
|
||||||
|
|
||||||
public BinaryMobileNetV2(Resources resources) throws IOException {
|
|
||||||
super(resources, R.raw.binary_mobilenet_v2);
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: Add runInference method that runs super but transforms output into usable format
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
package xyz.r0r5chach.dermy_app
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.fragment.app.add
|
||||||
|
import androidx.fragment.app.commit
|
||||||
|
import xyz.r0r5chach.dermy_app.fragments.HomeFragment
|
||||||
|
|
||||||
|
class MainActivity: AppCompatActivity(R.layout.activity_main) {
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
if (savedInstanceState == null) {
|
||||||
|
supportFragmentManager.commit {
|
||||||
|
setReorderingAllowed(true)
|
||||||
|
add<HomeFragment>(R.id.fragment_container, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
package xyz.r0r5chach.dermy_app.db
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.room.Delete
|
||||||
|
import androidx.room.Insert
|
||||||
|
import androidx.room.Query
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
interface LogDao {
|
||||||
|
@Query("SELECT * FROM logs")
|
||||||
|
fun getAll(): List<LogEntry>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM logs WHERE moleId = :moleId")
|
||||||
|
fun findMoleLogs(moleId: String?): List<LogEntry>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM logs WHERE dateCreated = :date")
|
||||||
|
fun findByDate(date: LocalDateTime): List<LogEntry>
|
||||||
|
|
||||||
|
@Insert
|
||||||
|
fun insertOne(log: Log?)
|
||||||
|
|
||||||
|
@Delete
|
||||||
|
fun deleteOne(log: Log?)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
package xyz.r0r5chach.dermy_app.db
|
||||||
|
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
import org.bson.BsonType
|
||||||
|
import org.bson.codecs.pojo.annotations.BsonId
|
||||||
|
import org.bson.codecs.pojo.annotations.BsonProperty
|
||||||
|
import org.bson.codecs.pojo.annotations.BsonRepresentation
|
||||||
|
import java.time.LocalDate
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
@Entity(tableName = "logs")
|
||||||
|
data class LogEntry(
|
||||||
|
@PrimaryKey
|
||||||
|
@BsonId
|
||||||
|
@BsonRepresentation(BsonType.OBJECT_ID)
|
||||||
|
val id: String,
|
||||||
|
@BsonRepresentation(BsonType.OBJECT_ID)
|
||||||
|
@BsonProperty("_mole_id")
|
||||||
|
val moleId: String,
|
||||||
|
@BsonRepresentation(BsonType.DATE_TIME)
|
||||||
|
@BsonProperty("_date_created")
|
||||||
|
val dateCreated: LocalDateTime,
|
||||||
|
val contents: String
|
||||||
|
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
package xyz.r0r5chach.dermy_app.db
|
||||||
|
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
import org.bson.BsonType
|
||||||
|
import org.bson.codecs.pojo.annotations.BsonId
|
||||||
|
import org.bson.codecs.pojo.annotations.BsonProperty
|
||||||
|
import org.bson.codecs.pojo.annotations.BsonRepresentation
|
||||||
|
import xyz.r0r5chach.dermy_app.db.locations.Location
|
||||||
|
|
||||||
|
@Entity(tableName = "moles")
|
||||||
|
data class Mole(
|
||||||
|
@PrimaryKey
|
||||||
|
@BsonId
|
||||||
|
@BsonRepresentation(BsonType.OBJECT_ID)
|
||||||
|
val id: String,
|
||||||
|
@BsonRepresentation(BsonType.OBJECT_ID)
|
||||||
|
@BsonProperty("_user_id")
|
||||||
|
val userId: String,
|
||||||
|
@BsonRepresentation(BsonType.STRING)
|
||||||
|
val location: Location
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
package xyz.r0r5chach.dermy_app.db
|
||||||
|
|
||||||
|
import androidx.room.Delete
|
||||||
|
import androidx.room.Insert
|
||||||
|
import androidx.room.Query
|
||||||
|
import xyz.r0r5chach.dermy_app.db.locations.Location
|
||||||
|
|
||||||
|
interface MoleDao {
|
||||||
|
@Query("SELECT * FROM moles")
|
||||||
|
fun getAll(): List<Mole>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM moles WHERE location = :location")
|
||||||
|
fun findByLocation(location: Location): List<Mole>
|
||||||
|
|
||||||
|
@Insert
|
||||||
|
fun insertOne(mole: Mole)
|
||||||
|
|
||||||
|
@Delete
|
||||||
|
fun deleteOne(mole: Mole)
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
package xyz.r0r5chach.dermy.entities.locations;
|
package xyz.r0r5chach.dermy_app.db.locations
|
||||||
|
|
||||||
public enum BackLocation implements Location {
|
enum class BackLocation : Location {
|
||||||
Head,
|
Head,
|
||||||
Neck,
|
Neck,
|
||||||
LeftShoulder,
|
LeftShoulder,
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
package xyz.r0r5chach.dermy.entities.locations;
|
package xyz.r0r5chach.dermy_app.db.locations
|
||||||
|
|
||||||
public enum FrontLocation implements Location {
|
enum class FrontLocation : Location {
|
||||||
Forehead,
|
Forehead,
|
||||||
Nose,
|
Nose,
|
||||||
LeftEye,
|
LeftEye,
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
package xyz.r0r5chach.dermy_app.db.locations
|
||||||
|
|
||||||
|
interface Location
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
package xyz.r0r5chach.dermy.entities.locations;
|
package xyz.r0r5chach.dermy_app.db.locations
|
||||||
|
|
||||||
public enum LeftSideLocation implements Location {
|
enum class SideLocation : Location {
|
||||||
Head,
|
Head,
|
||||||
Ear,
|
Ear,
|
||||||
Cheek,
|
Cheek,
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
package xyz.r0r5chach.dermy_app.fragments;
|
||||||
|
|
||||||
|
public class AccountFragment {
|
||||||
|
//TODO: Login if no API Key
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,109 @@
|
||||||
|
package xyz.r0r5chach.dermy_app.fragments
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import androidx.camera.core.CameraSelector
|
||||||
|
import androidx.camera.core.ImageCapture
|
||||||
|
import androidx.camera.core.ImageCapture.OutputFileOptions
|
||||||
|
import androidx.camera.core.ImageCaptureException
|
||||||
|
import androidx.camera.core.Preview
|
||||||
|
import androidx.camera.lifecycle.ProcessCameraProvider
|
||||||
|
import androidx.camera.view.PreviewView
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import com.google.common.util.concurrent.ListenableFuture
|
||||||
|
import xyz.r0r5chach.dermy_app.R
|
||||||
|
import java.io.File
|
||||||
|
import java.util.concurrent.ExecutorService
|
||||||
|
import java.util.concurrent.Executors
|
||||||
|
|
||||||
|
class CameraFragment : Fragment(R.layout.fragment_camera) {
|
||||||
|
private lateinit var previewView: PreviewView
|
||||||
|
private lateinit var imageCapture: ImageCapture
|
||||||
|
private lateinit var cameraExecutor: ExecutorService
|
||||||
|
|
||||||
|
private val requestPermissionLauncher = registerForActivityResult(
|
||||||
|
ActivityResultContracts.RequestPermission()
|
||||||
|
) {isGranted: Boolean ->
|
||||||
|
if (isGranted) {
|
||||||
|
startCamera()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Toast.makeText(context, "Camera permission is required", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
previewView = view?.findViewById(R.id.camera_preview)!!
|
||||||
|
|
||||||
|
if (context?.checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
requestPermissionLauncher.launch(Manifest.permission.CAMERA)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
startCamera()
|
||||||
|
}
|
||||||
|
|
||||||
|
previewView.setOnClickListener { takePhoto() }
|
||||||
|
cameraExecutor = Executors.newSingleThreadExecutor()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun startCamera() {
|
||||||
|
val cameraProviderFuture: ListenableFuture<ProcessCameraProvider> = ProcessCameraProvider.getInstance(requireContext())
|
||||||
|
|
||||||
|
cameraProviderFuture.addListener({
|
||||||
|
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
|
||||||
|
|
||||||
|
val preview = Preview.Builder().build().also {
|
||||||
|
it.setSurfaceProvider(previewView.surfaceProvider)
|
||||||
|
}
|
||||||
|
|
||||||
|
imageCapture = ImageCapture.Builder().build()
|
||||||
|
|
||||||
|
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
|
||||||
|
|
||||||
|
try {
|
||||||
|
cameraProvider.unbindAll()
|
||||||
|
cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture)
|
||||||
|
} catch (exc: Exception) {
|
||||||
|
exc.printStackTrace()
|
||||||
|
}
|
||||||
|
|
||||||
|
}, ContextCompat.getMainExecutor(requireContext()))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun takePhoto() {
|
||||||
|
val photoFile = File(context?.getExternalFilesDir(null), "${System.currentTimeMillis()}.jpg") //TODO: Save image as MoleId
|
||||||
|
val outputFileOptions: OutputFileOptions = OutputFileOptions.Builder(photoFile).build()
|
||||||
|
imageCapture.takePicture(
|
||||||
|
outputFileOptions,
|
||||||
|
ContextCompat.getMainExecutor(requireContext()),
|
||||||
|
object : ImageCapture.OnImageSavedCallback {
|
||||||
|
override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
|
||||||
|
val photo: Bitmap = BitmapFactory.decodeFile(photoFile.absolutePath)
|
||||||
|
//TODO: redirect to next fragment
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onError(exception: ImageCaptureException) {
|
||||||
|
exception.printStackTrace() //TODO: Replace with better logging
|
||||||
|
Toast.makeText(
|
||||||
|
requireContext(),
|
||||||
|
"Error saving photo: ${exception.message}",
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
cameraExecutor.shutdown()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
package xyz.r0r5chach.dermy_app.fragments
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.Button
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.fragment.app.commit
|
||||||
|
import androidx.fragment.app.replace
|
||||||
|
import xyz.r0r5chach.dermy_app.R
|
||||||
|
|
||||||
|
class HomeFragment : Fragment(R.layout.fragment_home) {
|
||||||
|
private var buttons: IntArray = intArrayOf(R.id.add_mole_button, R.id.map_button, R.id.links_button, R.id.settings_button)
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
for (button in buttons) {
|
||||||
|
view.findViewById<Button>(button).setOnClickListener {
|
||||||
|
initButton(button)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initButton(button: Int) {
|
||||||
|
val manager = requireActivity().supportFragmentManager
|
||||||
|
|
||||||
|
when (button) {
|
||||||
|
R.id.add_mole_button -> {
|
||||||
|
manager.commit {
|
||||||
|
setReorderingAllowed(true)
|
||||||
|
replace<MoleFragment>(R.id.fragment_container, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
R.id.map_button -> {
|
||||||
|
manager.commit {
|
||||||
|
setReorderingAllowed(true)
|
||||||
|
replace<MapFragment>(R.id.fragment_container, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
R.id.links_button -> {
|
||||||
|
manager.commit {
|
||||||
|
setReorderingAllowed(true)
|
||||||
|
replace<LinksFragment>(R.id.fragment_container, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
R.id.settings_button -> {
|
||||||
|
manager.commit {
|
||||||
|
setReorderingAllowed(true)
|
||||||
|
replace<SettingsFragment>(R.id.fragment_container, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
package xyz.r0r5chach.dermy_app.fragments;
|
||||||
|
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
|
public class LinksFragment extends Fragment {
|
||||||
|
//TODO: NHS links etc
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
package xyz.r0r5chach.dermy_app.fragments;
|
||||||
|
|
||||||
|
public class LocationFragment {
|
||||||
|
//TODO: Recycler View of Moles at location from last fragment
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
package xyz.r0r5chach.dermy_app.fragments;
|
||||||
|
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
|
public class MapFragment extends Fragment {
|
||||||
|
//TODO: Map of all locations
|
||||||
|
//When pressed route to LocationFragment with specified location
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
package xyz.r0r5chach.dermy_app.fragments;
|
||||||
|
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
|
public class MoleFragment extends Fragment {
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
package xyz.r0r5chach.dermy_app.fragments
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
|
import xyz.r0r5chach.dermy_app.R
|
||||||
|
|
||||||
|
|
||||||
|
class SettingsFragment : PreferenceFragmentCompat() {
|
||||||
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
|
setPreferencesFromResource(R.xml.preferences, rootKey)
|
||||||
|
} //TODO: Disclaimers
|
||||||
|
//TODO: Useful Links
|
||||||
|
//TODO: Export File
|
||||||
|
//TODO: Backup data
|
||||||
|
//TODO: Recover Account
|
||||||
|
// Login button
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/add_mole_button"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:text="@string/string_add_mole"
|
||||||
|
app:layout_constrainedWidth="true"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/links_button"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/map_button"
|
||||||
|
app:layout_constraintHeight_percent="0.1"
|
||||||
|
app:layout_constraintHorizontal_bias="0.5"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintWidth_max="320dp"
|
||||||
|
app:layout_constraintWidth_percent="0.28" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/map_button"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:text="@string/string_body_map"
|
||||||
|
app:layout_constrainedWidth="true"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/settings_button"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHeight_percent="0.1"
|
||||||
|
app:layout_constraintHorizontal_bias="0.5"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/add_mole_button"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintWidth_max="320dp"
|
||||||
|
app:layout_constraintWidth_percent="0.28" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/links_button"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:text="@string/string_links"
|
||||||
|
app:layout_constrainedWidth="true"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/settings_button"
|
||||||
|
app:layout_constraintHeight_percent="0.1"
|
||||||
|
app:layout_constraintHorizontal_bias="0.5"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/add_mole_button"
|
||||||
|
app:layout_constraintWidth_max="320dp"
|
||||||
|
app:layout_constraintWidth_percent="0.28" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/settings_button"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:text="@string/string_settings"
|
||||||
|
app:layout_constrainedWidth="true"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHeight_percent="0.1"
|
||||||
|
app:layout_constraintHorizontal_bias="0.5"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/links_button"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/map_button"
|
||||||
|
app:layout_constraintWidth_max="320dp"
|
||||||
|
app:layout_constraintWidth_percent="0.28" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1,6 +1,6 @@
|
||||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
<!-- Base application theme. -->
|
<!-- Base application theme. -->
|
||||||
<style name="Theme.Dermy" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
|
<style name="Theme.Dermy_app" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
|
||||||
<!-- Primary brand color. -->
|
<!-- Primary brand color. -->
|
||||||
<item name="colorPrimary">@color/purple_200</item>
|
<item name="colorPrimary">@color/purple_200</item>
|
||||||
<item name="colorPrimaryVariant">@color/purple_700</item>
|
<item name="colorPrimaryVariant">@color/purple_700</item>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name" translatable="false">dermy</string>
|
<string name="app_name">dermy_app</string>
|
||||||
<string name="image_capture_button_text">Capture</string>
|
<!-- TODO: Remove or change this placeholder text -->
|
||||||
|
<string name="hello_blank_fragment">Hello blank fragment</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
<!-- Base application theme. -->
|
<!-- Base application theme. -->
|
||||||
<style name="Theme.Dermy" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
|
<style name="Theme.Dermy_app" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
|
||||||
<!-- Primary brand color. -->
|
<!-- Primary brand color. -->
|
||||||
<item name="colorPrimary">@color/purple_500</item>
|
<item name="colorPrimary">@color/purple_500</item>
|
||||||
<item name="colorPrimaryVariant">@color/purple_700</item>
|
<item name="colorPrimaryVariant">@color/purple_700</item>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
</androidx.preference.PreferenceScreen>
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
package xyz.r0r5chach.dermy;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Example local unit test, which will execute on the development machine (host).
|
|
||||||
*
|
|
||||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
|
||||||
*/
|
|
||||||
public class ExampleUnitTest {
|
|
||||||
@Test
|
|
||||||
public void addition_isCorrect() {
|
|
||||||
assertEquals(4, 2 + 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
package xyz.r0r5chach.dermy_app
|
||||||
|
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
import org.junit.Assert.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example local unit test, which will execute on the development machine (host).
|
||||||
|
*
|
||||||
|
* See [testing documentation](http://d.android.com/tools/testing).
|
||||||
|
*/
|
||||||
|
class ExampleUnitTest {
|
||||||
|
@Test
|
||||||
|
fun addition_isCorrect() {
|
||||||
|
assertEquals(4, 2 + 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.android.application) apply false
|
alias(libs.plugins.android.application) apply false
|
||||||
|
alias(libs.plugins.jetbrains.kotlin.android) apply false
|
||||||
}
|
}
|
||||||
|
|
@ -15,6 +15,8 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
||||||
# Android operating system, and which are packaged with your app's APK
|
# Android operating system, and which are packaged with your app's APK
|
||||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
|
# Kotlin code style for this project: "official" or "obsolete":
|
||||||
|
kotlin.code.style=official
|
||||||
# Enables namespacing of each library's R class so that its R class includes only the
|
# Enables namespacing of each library's R class so that its R class includes only the
|
||||||
# resources declared in the library itself and none from the library's dependencies,
|
# resources declared in the library itself and none from the library's dependencies,
|
||||||
# thereby reducing the size of the R class for that library
|
# thereby reducing the size of the R class for that library
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,36 @@
|
||||||
[versions]
|
[versions]
|
||||||
agp = "8.4.1"
|
agp = "8.4.1"
|
||||||
cameraCore = "1.3.3"
|
fragmentKtx = "1.7.1"
|
||||||
|
kotlin = "1.9.0"
|
||||||
|
coreKtx = "1.13.1"
|
||||||
junit = "4.13.2"
|
junit = "4.13.2"
|
||||||
junitVersion = "1.1.5"
|
junitVersion = "1.1.5"
|
||||||
espressoCore = "3.5.1"
|
espressoCore = "3.5.1"
|
||||||
appcompat = "1.7.0"
|
appcompat = "1.7.0"
|
||||||
material = "1.12.0"
|
material = "1.12.0"
|
||||||
tensorflowLite = "2.6.0"
|
cameraView = "1.3.3"
|
||||||
|
cameraCore = "1.3.3"
|
||||||
|
cameraLifecycle = "1.3.3"
|
||||||
|
mongodbDriverKotlinSync = "5.1.1"
|
||||||
|
preference = "1.2.1"
|
||||||
|
roomCommon = "2.6.1"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
camera-camera2 = { module = "androidx.camera:camera-camera2", version.ref = "cameraCore" }
|
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
||||||
camera-core = { module = "androidx.camera:camera-core", version.ref = "cameraCore" }
|
androidx-fragment-ktx = { module = "androidx.fragment:fragment-ktx", version.ref = "fragmentKtx" }
|
||||||
camera-extensions = { module = "androidx.camera:camera-extensions", version.ref = "cameraCore" }
|
|
||||||
camera-lifecycle = { module = "androidx.camera:camera-lifecycle", version.ref = "cameraCore" }
|
|
||||||
camera-view = { module = "androidx.camera:camera-view", version.ref = "cameraCore" }
|
|
||||||
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
||||||
ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
|
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
|
||||||
espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
|
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
|
||||||
appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
|
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
|
||||||
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
|
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
|
||||||
tensorflow-lite = { module = "org.tensorflow:tensorflow-lite", version.ref = "tensorflowLite" }
|
androidx-camera-view = { group = "androidx.camera", name = "camera-view", version.ref = "cameraView" }
|
||||||
|
androidx-camera-core = { group = "androidx.camera", name = "camera-core", version.ref = "cameraCore" }
|
||||||
|
androidx-camera-lifecycle = { group = "androidx.camera", name = "camera-lifecycle", version.ref = "cameraLifecycle" }
|
||||||
|
androidx-preference = { group = "androidx.preference", name = "preference", version.ref = "preference" }
|
||||||
|
androidx-room-common = { group = "androidx.room", name = "room-common", version.ref = "roomCommon" }
|
||||||
|
mongodb-driver-kotlin-sync = { module = "org.mongodb:mongodb-driver-kotlin-sync", version.ref = "mongodbDriverKotlinSync" }
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||||
|
jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
#Mon Jun 03 19:20:03 BST 2024
|
#Wed Jun 12 12:41:18 BST 2024
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,6 @@ dependencyResolutionManagement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rootProject.name = "dermy"
|
rootProject.name = "dermy_app"
|
||||||
include(":app")
|
include(":app")
|
||||||
|
|
||||||
Loading…
Reference in New Issue