cs-3443: bring LakeWatch in-tree
11
Summer-2024/CS-3443/LakeWatch/.envrc
Normal file
@ -0,0 +1,11 @@
|
||||
export APP_API_HOST="localhost"
|
||||
export APP_API_PORT="8000"
|
||||
|
||||
export APP_DATABASE_HOST="localhost"
|
||||
export APP_DATABASE_PORT="5432"
|
||||
export APP_DATABASE_USERNAME="postgres"
|
||||
export APP_DATABASE_PASSWORD="password"
|
||||
export APP_DATABASE_NAME="lakewatch"
|
||||
export APP_DATABASE_REQUIRE_SSL="false"
|
||||
|
||||
export DATABASE_URL="postgres://${APP_DATABASE_USERNAME}:${APP_DATABASE_PASSWORD}@${APP_DATABASE_HOST}:${APP_DATABASE_PORT}/${APP_DATABASE_NAME}"
|
1
Summer-2024/CS-3443/LakeWatch/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
.idea/
|
10
Summer-2024/CS-3443/LakeWatch/LakeWatch/.gitignore
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
local.properties
|
3
Summer-2024/CS-3443/LakeWatch/LakeWatch/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# `LakeWatch`
|
||||
|
||||
This repository contains the actual Android application.
|
1
Summer-2024/CS-3443/LakeWatch/LakeWatch/app/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/build
|
43
Summer-2024/CS-3443/LakeWatch/LakeWatch/app/build.gradle.kts
Normal file
@ -0,0 +1,43 @@
|
||||
plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "edu.utsa.cs3443.lakewatch"
|
||||
compileSdk = 34
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "edu.utsa.cs3443.lakewatch"
|
||||
minSdk = 26
|
||||
targetSdk = 34
|
||||
versionCode = 1
|
||||
versionName = "1.0"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation(libs.appcompat)
|
||||
implementation(libs.material)
|
||||
implementation(libs.activity)
|
||||
implementation(libs.constraintlayout)
|
||||
testImplementation(libs.junit)
|
||||
androidTestImplementation(libs.ext.junit)
|
||||
androidTestImplementation(libs.espresso.core)
|
||||
}
|
21
Summer-2024/CS-3443/LakeWatch/LakeWatch/app/proguard-rules.pro
vendored
Normal file
@ -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
|
@ -0,0 +1,26 @@
|
||||
package edu.utsa.cs3443.lakewatch;
|
||||
|
||||
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("edu.utsa.cs3443.lakewatch", appContext.getPackageName());
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<application
|
||||
android:name=".model.userSettings"
|
||||
android:allowBackup="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.LakeWatch"
|
||||
tools:targetApi="31">
|
||||
<activity
|
||||
android:name=".MainRampMenuActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".BoatRampActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".WaterLevelActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".WeatherActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".SettingsActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
@ -0,0 +1,111 @@
|
||||
package edu.utsa.cs3443.lakewatch;
|
||||
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.activity.EdgeToEdge;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
import androidx.cardview.widget.CardView;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.graphics.Insets;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
import edu.utsa.cs3443.lakewatch.model.BoatRampData;
|
||||
import edu.utsa.cs3443.lakewatch.model.BoatRampStatus;
|
||||
|
||||
public class BoatRampActivity extends AppCompatActivity {
|
||||
|
||||
/**
|
||||
* Drive the view for a single boat ramp page
|
||||
*/
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
EdgeToEdge.enable(this);
|
||||
setContentView(R.layout.activity_boat_ramp);
|
||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
|
||||
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||
return insets;
|
||||
});
|
||||
|
||||
BoatRampData rampData = (BoatRampData) this.getIntent().getExtras().getSerializable("rampData");
|
||||
((TextView)this.findViewById(R.id.rampName)).setText(rampData.getName());
|
||||
|
||||
ImageView rampImage = this.findViewById(R.id.rampImage);
|
||||
for (Field field : R.drawable.class.getFields()) {
|
||||
String fieldName = field.getName();
|
||||
if (fieldName.equals("ramp" + rampData.getRampNumber())) {
|
||||
rampImage.setImageResource(this.getResources().getIdentifier(field.getName(), "drawable", this.getPackageName()));
|
||||
}
|
||||
}
|
||||
|
||||
ImageView statusIcon = this.findViewById(R.id.statusIcon);
|
||||
GradientDrawable statusShape = new GradientDrawable();
|
||||
statusShape.setStroke(3, Color.parseColor("#000000"));
|
||||
statusShape.setCornerRadius(8000);
|
||||
if (rampData.getStatus() == BoatRampStatus.OPEN) {
|
||||
statusIcon.setImageResource(R.drawable.circle_check);
|
||||
statusShape.setColor(ContextCompat.getColor(this, R.color.rampStatusOpen));
|
||||
} else if (rampData.getStatus() == BoatRampStatus.CLOSED) {
|
||||
statusIcon.setImageResource(R.drawable.circle_x);
|
||||
statusShape.setColor(ContextCompat.getColor(this, R.color.rampStatusClosed));
|
||||
} else {
|
||||
statusIcon.setImageResource(R.drawable.circle_guess);
|
||||
statusShape.setColor(ContextCompat.getColor(this, R.color.rampStatusUnknown));
|
||||
}
|
||||
CardView statusCard = this.findViewById(R.id.statusCard);
|
||||
statusCard.setBackground(statusShape);
|
||||
((TextView)this.findViewById(R.id.statusText)).setText(rampData.getStatus().toString());
|
||||
|
||||
GradientDrawable cardShape = new GradientDrawable();
|
||||
cardShape.setColor(ContextCompat.getColor(this, R.color.rampInfoBackground));
|
||||
cardShape.setStroke(3, Color.parseColor("#000000"));
|
||||
cardShape.setCornerRadius(8000);
|
||||
|
||||
CardView openTimesCard = this.findViewById(R.id.openTimes);
|
||||
((TextView)openTimesCard.findViewById(R.id.openTimesText)).setText(rampData.getOpenTimes());
|
||||
openTimesCard.setBackground(cardShape);
|
||||
|
||||
CardView addressCard = this.findViewById(R.id.address);
|
||||
((TextView)addressCard.findViewById(R.id.addressText)).setText(rampData.getAddress());
|
||||
addressCard.setBackground(cardShape);
|
||||
addressCard.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
ClipData clip = ClipData.newPlainText("", rampData.getAddress());
|
||||
clipboard.setPrimaryClip(clip);
|
||||
Toast toast = Toast.makeText(view.getContext(), "Address copied to clipboard!", Toast.LENGTH_SHORT);
|
||||
View toastView = toast.getView();
|
||||
toastView.findViewById(android.R.id.message).setBackgroundColor(Color.TRANSPARENT);
|
||||
toastView.getBackground().setTint(ContextCompat.getColor(toastView.getContext(), R.color.buttonBackgroundColor));
|
||||
toast.show();
|
||||
}
|
||||
});
|
||||
|
||||
CardView operatorCard = this.findViewById(R.id.operator);
|
||||
((TextView)operatorCard.findViewById(R.id.operatorText)).setText(rampData.getOperator());
|
||||
operatorCard.setBackground(cardShape);
|
||||
|
||||
CardView lastUpdatedCard = this.findViewById(R.id.lastUpdated);
|
||||
((TextView)lastUpdatedCard.findViewById(R.id.lastUpdatedText)).setText(DateTimeFormatter.ofPattern("MM/dd/yyyy").format(rampData.getLastUpdated()));
|
||||
lastUpdatedCard.setBackground(cardShape);
|
||||
}
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
package edu.utsa.cs3443.lakewatch;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.activity.EdgeToEdge;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.graphics.Insets;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
EdgeToEdge.enable(this);
|
||||
setContentView(R.layout.activity_main);
|
||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
|
||||
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||
return insets;
|
||||
});
|
||||
|
||||
//Button Initialization(s)
|
||||
Button weatherButton = findViewById(R.id.button); //Weather
|
||||
Button waterStatusButton = findViewById(R.id.button2); //WaterStatus
|
||||
Button rampsButton = findViewById(R.id.button3); //Ramps
|
||||
Button settingsButton = findViewById(R.id.settingsButton); // Settings
|
||||
|
||||
//Weather View OnClickListener
|
||||
weatherButton.setOnClickListener(new View.OnClickListener(){
|
||||
@Override
|
||||
public void onClick(View v) { openWeatherActivity(); }
|
||||
});
|
||||
|
||||
//Water Status View OnClickListener
|
||||
waterStatusButton.setOnClickListener(new View.OnClickListener(){
|
||||
@Override
|
||||
public void onClick(View v) { openWaterLevelActivity();}
|
||||
});
|
||||
|
||||
//Boat Ramps View OnClickListener
|
||||
rampsButton.setOnClickListener(new View.OnClickListener(){
|
||||
@Override
|
||||
public void onClick(View v)
|
||||
{
|
||||
openMainRampMenuActivity();
|
||||
}
|
||||
});
|
||||
|
||||
//Settings View OnClickListener
|
||||
settingsButton.setOnClickListener(new View.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(View v)
|
||||
{
|
||||
openSettingsActivity();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
//This function is used to navigate to the settings menu via the intent object
|
||||
private void openSettingsActivity()
|
||||
{
|
||||
Intent intent = new Intent(this, SettingsActivity.class);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
private void openWeatherActivity()
|
||||
{
|
||||
Intent intent = new Intent(this, WeatherActivity.class);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
private void openWaterLevelActivity()
|
||||
{
|
||||
Intent intent = new Intent(this, WaterLevelActivity.class);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
private void openMainRampMenuActivity()
|
||||
{
|
||||
Intent intent = new Intent(this, MainRampMenuActivity.class);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,166 @@
|
||||
package edu.utsa.cs3443.lakewatch;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import androidx.activity.EdgeToEdge;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.cardview.widget.CardView;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.graphics.Insets;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
import edu.utsa.cs3443.lakewatch.model.BoatRampData;
|
||||
import edu.utsa.cs3443.lakewatch.model.BoatRampStatus;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
public class MainRampMenuActivity extends AppCompatActivity {
|
||||
|
||||
private ExecutorService executorService;
|
||||
|
||||
/**
|
||||
* The main driver behind the full listing of the boat ramps
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
EdgeToEdge.enable(this);
|
||||
setContentView(R.layout.activity_main_ramp_menu);
|
||||
ViewCompat.setOnApplyWindowInsetsListener(
|
||||
findViewById(R.id.main),
|
||||
(v, insets) -> {
|
||||
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||
return insets;
|
||||
});
|
||||
|
||||
executorService = Executors.newSingleThreadExecutor();
|
||||
|
||||
Callable<ArrayList<BoatRampData>> fetchRampDataTask =
|
||||
() -> {
|
||||
ArrayList<BoatRampData> fetchedData = BoatRampData.fetchData();
|
||||
BoatRampData.saveData(this.findViewById(R.id.main).getContext(), fetchedData);
|
||||
return fetchedData;
|
||||
};
|
||||
|
||||
Future<ArrayList<BoatRampData>> future = executorService.submit(fetchRampDataTask);
|
||||
new Handler(Looper.getMainLooper())
|
||||
.post(
|
||||
() -> {
|
||||
ArrayList<BoatRampData> allRampData = new ArrayList<>();
|
||||
try {
|
||||
allRampData = future.get();
|
||||
} catch (Exception e) {
|
||||
Toast toast =
|
||||
Toast.makeText(
|
||||
this.findViewById(R.id.main).getContext(),
|
||||
"API Fetch Failed, failling back to cache.",
|
||||
Toast.LENGTH_SHORT);
|
||||
View toastView = toast.getView();
|
||||
toastView.findViewById(android.R.id.message).setBackgroundColor(Color.TRANSPARENT);
|
||||
toastView
|
||||
.getBackground()
|
||||
.setTint(
|
||||
ContextCompat.getColor(
|
||||
toastView.getContext(), R.color.buttonBackgroundColor));
|
||||
toast.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM, 0, 50);
|
||||
toast.show();
|
||||
|
||||
Toast cacheToast =
|
||||
Toast.makeText(
|
||||
this.findViewById(R.id.main).getContext(),
|
||||
"Failed to load boat ramp data!",
|
||||
Toast.LENGTH_SHORT);
|
||||
View cacheToastView = cacheToast.getView();
|
||||
cacheToastView
|
||||
.findViewById(android.R.id.message)
|
||||
.setBackgroundColor(Color.TRANSPARENT);
|
||||
cacheToastView
|
||||
.getBackground()
|
||||
.setTint(
|
||||
ContextCompat.getColor(
|
||||
cacheToastView.getContext(), R.color.buttonBackgroundColor));
|
||||
cacheToast.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM, 0, 50);
|
||||
|
||||
try {
|
||||
allRampData =
|
||||
BoatRampData.loadCachedData(this.findViewById(R.id.main).getContext());
|
||||
} catch (Exception ex) {
|
||||
cacheToast.show();
|
||||
}
|
||||
}
|
||||
|
||||
LinearLayout rampListing = this.findViewById(R.id.rampListing);
|
||||
int ramps_open = 0;
|
||||
LayoutInflater inflater =
|
||||
(LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
for (BoatRampData rampData : allRampData) {
|
||||
if (rampData.getStatus() == BoatRampStatus.OPEN) {
|
||||
ramps_open++;
|
||||
}
|
||||
|
||||
View rampView = inflater.inflate(R.layout.ramp_menu_card, rampListing, false);
|
||||
CardView rampMenuCard = rampView.findViewById(R.id.rampMenuCard);
|
||||
((TextView) rampMenuCard.findViewById(R.id.rampName)).setText(rampData.getName());
|
||||
((TextView) rampMenuCard.findViewById(R.id.rampStatus))
|
||||
.setText(rampData.getStatus().toString());
|
||||
ImageView statusImage = rampView.findViewById(R.id.statusIcon);
|
||||
GradientDrawable shape = new GradientDrawable();
|
||||
|
||||
if (rampData.getStatus() == BoatRampStatus.OPEN) {
|
||||
statusImage.setImageResource(R.drawable.circle_check);
|
||||
shape.setColor(ContextCompat.getColor(this, R.color.rampStatusOpen));
|
||||
} else if (rampData.getStatus() == BoatRampStatus.CLOSED) {
|
||||
statusImage.setImageResource(R.drawable.circle_x);
|
||||
shape.setColor(ContextCompat.getColor(this, R.color.rampStatusClosed));
|
||||
} else {
|
||||
statusImage.setImageResource(R.drawable.circle_guess);
|
||||
shape.setColor(ContextCompat.getColor(this, R.color.rampStatusUnknown));
|
||||
}
|
||||
|
||||
shape.setStroke(3, Color.parseColor("#000000"));
|
||||
shape.setCornerRadius(80);
|
||||
rampMenuCard.setBackground(shape);
|
||||
|
||||
rampMenuCard.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
launchBoatRampActivity(rampData);
|
||||
}
|
||||
});
|
||||
rampListing.addView(rampView);
|
||||
}
|
||||
((TextView) this.findViewById(R.id.rampsOpen))
|
||||
.setText(String.format("%d/%d Ramps Open", ramps_open, allRampData.size()));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch a specific page of a boat ramp
|
||||
*
|
||||
* @param rampData The ramp data to use for the page
|
||||
*/
|
||||
private void launchBoatRampActivity(BoatRampData rampData) {
|
||||
Intent intent = new Intent(this, BoatRampActivity.class);
|
||||
intent.putExtra("rampData", (Serializable) rampData);
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
@ -0,0 +1,167 @@
|
||||
package edu.utsa.cs3443.lakewatch;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import androidx.activity.EdgeToEdge;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.graphics.Insets;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import edu.utsa.cs3443.lakewatch.model.BoatRampData;
|
||||
import edu.utsa.cs3443.lakewatch.model.WaterLevelData;
|
||||
import edu.utsa.cs3443.lakewatch.model.WeatherData;
|
||||
|
||||
public class SettingsActivity extends AppCompatActivity {
|
||||
|
||||
private ExecutorService executorService;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
EdgeToEdge.enable(this);
|
||||
setContentView(R.layout.activity_settings);
|
||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
|
||||
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||
return insets;
|
||||
});
|
||||
|
||||
//Button Initializations
|
||||
Button btnToggleDark = findViewById(R.id.button5);
|
||||
Button dataFetchButton = findViewById(R.id.button6);
|
||||
Button clearCacheButton = findViewById(R.id.button7);
|
||||
|
||||
//Saving state of our app
|
||||
//using SharedPreferences
|
||||
//necessary for Dark Mode
|
||||
SharedPreferences sharedPreferences = getSharedPreferences("sharedPrefs",MODE_PRIVATE);
|
||||
final SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
final boolean isDarkModeOn = sharedPreferences.getBoolean("isDarkModeOn",false);
|
||||
|
||||
// When user reopens the app
|
||||
// after applying a respective
|
||||
// color mode
|
||||
if(isDarkModeOn)
|
||||
{
|
||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
|
||||
btnToggleDark.setText("DARK MODE ON");
|
||||
}
|
||||
else {
|
||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
|
||||
btnToggleDark.setText("DARK MODE OFF");
|
||||
}
|
||||
|
||||
//Dark Mode OnClickListener
|
||||
//contains the logic to toggle
|
||||
//Dark mode
|
||||
btnToggleDark.setOnClickListener(new View.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(View view)
|
||||
{
|
||||
// When user taps the enable/disable
|
||||
// dark mode button
|
||||
if (isDarkModeOn) {
|
||||
// if dark mode is on
|
||||
// it will turn off dark mode
|
||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
|
||||
// sets isDarkModeOn
|
||||
// boolean to false
|
||||
editor.putBoolean("isDarkModeOn", false);
|
||||
editor.apply();
|
||||
}
|
||||
else {
|
||||
// if dark mode is off
|
||||
// it will turn on dark mode
|
||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
|
||||
// sets isDarkModeOn
|
||||
// boolean to true
|
||||
editor.putBoolean("isDarkModeOn", true);
|
||||
editor.apply();
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
dataFetchButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
Toast toast = Toast.makeText(view.getContext(), "Data Fetched.", Toast.LENGTH_SHORT);
|
||||
View toastView = toast.getView();
|
||||
toastView.findViewById(android.R.id.message).setBackgroundColor(Color.TRANSPARENT);
|
||||
toastView.getBackground().setTint(ContextCompat.getColor(toastView.getContext(), R.color.buttonBackgroundColor));
|
||||
toast.setGravity(Gravity.CENTER_HORIZONTAL|Gravity.BOTTOM,0,50);
|
||||
|
||||
|
||||
executorService = Executors.newSingleThreadExecutor();
|
||||
|
||||
Callable<ArrayList<BoatRampData>> fetchRampDataTask =
|
||||
() -> {
|
||||
ArrayList<BoatRampData> fetchedData = BoatRampData.fetchData();
|
||||
BoatRampData.saveData(view.getContext(), fetchedData);
|
||||
return fetchedData;
|
||||
};
|
||||
|
||||
Future<ArrayList<BoatRampData>> future = executorService.submit(fetchRampDataTask);
|
||||
|
||||
new Handler(Looper.getMainLooper()).post(() -> {
|
||||
try {
|
||||
BoatRampData.saveData(view.getContext(), future.get());
|
||||
} catch (Exception e) {
|
||||
toast.setText("Failed to force a data fetch!");
|
||||
}
|
||||
});
|
||||
// Fetch Water Level Data
|
||||
toast.show();
|
||||
|
||||
WaterLevelData.forceDataFetch(getApplicationContext());
|
||||
}
|
||||
});
|
||||
|
||||
clearCacheButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
Toast toast = Toast.makeText(view.getContext(), "Cache Cleared.", Toast.LENGTH_SHORT);
|
||||
View toastView = toast.getView();
|
||||
toastView.findViewById(android.R.id.message).setBackgroundColor(Color.TRANSPARENT);
|
||||
toastView.getBackground().setTint(ContextCompat.getColor(toastView.getContext(), R.color.buttonBackgroundColor));
|
||||
toast.setGravity(Gravity.CENTER_HORIZONTAL|Gravity.BOTTOM,0,50);
|
||||
try {
|
||||
BoatRampData.wipeCache(view.getContext());
|
||||
} catch (IOException e) {
|
||||
toast.setText("Failed to wipe ramp data cache!");
|
||||
}
|
||||
|
||||
toast.show();
|
||||
|
||||
try {
|
||||
WeatherData.wipeCache(view.getContext());
|
||||
} catch (IOException e) {
|
||||
toast.setText("Failed to wipe weather data cache!");
|
||||
}
|
||||
toast.show();
|
||||
|
||||
WaterLevelData.clearCache(getApplicationContext());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,117 @@
|
||||
package edu.utsa.cs3443.lakewatch;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import androidx.activity.EdgeToEdge;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.graphics.Insets;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
import android.widget.TableLayout;
|
||||
import android.widget.TableRow;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.ProtocolException;
|
||||
import java.net.URL;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import edu.utsa.cs3443.lakewatch.model.WaterLevelData;
|
||||
|
||||
public class WaterLevelActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
EdgeToEdge.enable(this);
|
||||
setContentView(R.layout.activity_water);
|
||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
|
||||
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||
return insets;
|
||||
});
|
||||
|
||||
new FetchWaterLevelDataTask().execute();
|
||||
}
|
||||
private class FetchWaterLevelDataTask extends AsyncTask<Void, Void, WaterLevelData> {
|
||||
|
||||
@Override
|
||||
protected WaterLevelData doInBackground(Void... voids) {
|
||||
WaterLevelData data = null;
|
||||
try {
|
||||
String jsonResponse = WaterLevelData.getJsonResponseFromApi();
|
||||
data = WaterLevelData._loadWaterLevelData(jsonResponse);
|
||||
if (data != null) {
|
||||
data.saveWaterLevelData(WaterLevelActivity.this);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if (data == null) {
|
||||
data = WaterLevelData.loadSavedWaterLevelData(WaterLevelActivity.this);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(WaterLevelData currentData) {
|
||||
updateUI(currentData);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateUI(WaterLevelData currentData) {
|
||||
TableLayout tableLayout = findViewById(R.id.tableLayout);
|
||||
|
||||
TableRow row1 = (TableRow) tableLayout.getChildAt(0);
|
||||
TextView row1Column2 = (TextView) row1.getChildAt(1);
|
||||
if (currentData != null) {
|
||||
row1Column2.setText(String.format("%.2f%%", currentData.getPercentFull()));
|
||||
|
||||
TableRow row2 = (TableRow) tableLayout.getChildAt(1);
|
||||
TextView row2Column2 = (TextView) row2.getChildAt(1);
|
||||
row2Column2.setText(String.format("%.2f", currentData.getWaterLevel()));
|
||||
|
||||
TableRow row3 = (TableRow) tableLayout.getChildAt(2);
|
||||
TextView row3Column2 = (TextView) row3.getChildAt(1);
|
||||
row3Column2.setText(String.format("%.2f", currentData.getSurfaceArea()));
|
||||
|
||||
TextView waterLevelValue = findViewById(R.id.waterLevelValue);
|
||||
TextView dateTime = findViewById(R.id.dateTime);
|
||||
TextView levelStatus = findViewById(R.id.levelStatus);
|
||||
|
||||
waterLevelValue.setText(String.format("%.2f", currentData.getWaterLevel()));
|
||||
dateTime.setText(currentData.getDate());
|
||||
levelStatus.setText(String.format("Level is %.2f feet below full pool of 909.00 ft", 909.00 - currentData.getWaterLevel()));
|
||||
} else {
|
||||
TextView waterLevelValue = findViewById(R.id.waterLevelValue);
|
||||
TextView dateTime = findViewById(R.id.dateTime);
|
||||
TextView levelStatus = findViewById(R.id.levelStatus);
|
||||
|
||||
waterLevelValue.setText("0.0");
|
||||
dateTime.setText("N/A");
|
||||
levelStatus.setText("No data available");
|
||||
|
||||
row1 = (TableRow) tableLayout.getChildAt(0);
|
||||
row1Column2 = (TextView) row1.getChildAt(1);
|
||||
row1Column2.setText("0.0%");
|
||||
|
||||
TableRow row2 = (TableRow) tableLayout.getChildAt(1);
|
||||
TextView row2Column2 = (TextView) row2.getChildAt(1);
|
||||
row2Column2.setText("0.0");
|
||||
|
||||
TableRow row3 = (TableRow) tableLayout.getChildAt(2);
|
||||
TextView row3Column2 = (TextView) row3.getChildAt(1);
|
||||
row3Column2.setText("0.0");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,131 @@
|
||||
package edu.utsa.cs3443.lakewatch;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.activity.EdgeToEdge;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.graphics.Insets;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
|
||||
import edu.utsa.cs3443.lakewatch.model.WeatherData;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
public class WeatherActivity extends AppCompatActivity {
|
||||
|
||||
private TextView weatherDescription;
|
||||
private TextView temperature;
|
||||
private TextView windDirection;
|
||||
private TextView windSpeed;
|
||||
private TextView windGust;
|
||||
private TextView chancePrecipitation;
|
||||
private TextView amountPrecipitation;
|
||||
|
||||
private ExecutorService executorService;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
EdgeToEdge.enable(this);
|
||||
setContentView(R.layout.activity_weather);
|
||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
|
||||
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||
return insets;
|
||||
});
|
||||
|
||||
weatherDescription = findViewById(R.id.weather_description);
|
||||
temperature = findViewById(R.id.temperature);
|
||||
windDirection = findViewById(R.id.wind_direction);
|
||||
windSpeed = findViewById(R.id.wind_speed);
|
||||
windGust = findViewById(R.id.wind_gust);
|
||||
chancePrecipitation = findViewById(R.id.chance_precipitation);
|
||||
amountPrecipitation = findViewById(R.id.amount_precipitation);
|
||||
|
||||
// Initialize the ExecutorService
|
||||
executorService = Executors.newSingleThreadExecutor();
|
||||
// Fetch weather data using ExecutorService
|
||||
fetchWeatherData();
|
||||
|
||||
Button weatherButton = findViewById(R.id.weather_button);
|
||||
weatherButton.setOnClickListener(v -> {
|
||||
String url = "https://forecast.weather.gov/MapClick.php?lat=29.699301&lon=-98.115109";
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse(url));
|
||||
startActivity(intent);
|
||||
});
|
||||
}
|
||||
|
||||
// Fetch weather data and update UI
|
||||
private void fetchWeatherData() {
|
||||
Callable<WeatherData> fetchWeatherTask = () -> {
|
||||
try {
|
||||
WeatherData weatherData = new WeatherData();
|
||||
WeatherData result = weatherData.loadWeatherData();
|
||||
Log.d("WeatherActivity", "WeatherData loaded: " + result);
|
||||
return result;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
Future<WeatherData> future = executorService.submit(fetchWeatherTask);
|
||||
|
||||
// Update the UI on the main thread
|
||||
new Handler(Looper.getMainLooper()).post(() -> {
|
||||
try {
|
||||
WeatherData weatherData = future.get();
|
||||
if (weatherData != null) {
|
||||
weatherData.saveWeatherData(WeatherActivity.this);
|
||||
updateUIWithWeatherData(weatherData);
|
||||
} else {
|
||||
weatherData = WeatherData.loadSavedWeatherData(WeatherActivity.this);
|
||||
if (weatherData != null) {
|
||||
updateUIWithWeatherData(weatherData);
|
||||
} else {
|
||||
weatherDescription.setText("No saved weather data available");
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
weatherDescription.setText("Error fetching weather data");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateUIWithWeatherData(WeatherData weatherData) {
|
||||
weatherDescription.setText(weatherData.getWeatherDescription());
|
||||
temperature.setText(String.format("H:%d°F L:%d°F", weatherData.getTempHigh(), weatherData.getTempLow()));
|
||||
String windDirectionValue = weatherData.getWindDirection();
|
||||
if (windDirectionValue.isEmpty()) {
|
||||
windDirectionValue = "N/A";
|
||||
}
|
||||
windDirection.setText(String.format("%s", windDirectionValue));
|
||||
windSpeed.setText(weatherData.getWindSpeed());
|
||||
windGust.setText(String.format("%.0f mph", weatherData.getWindGust()));
|
||||
chancePrecipitation.setText(String.format("%d%% Chance Precipitation", weatherData.getChancePrecipitation()));
|
||||
amountPrecipitation.setText(String.format("%.1f\u2033 Precipitation Next 24 HR", weatherData.getAmountPrecipitation()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
// Shut down the ExecutorService when the activity is destroyed
|
||||
if (executorService != null && !executorService.isShutdown()) {
|
||||
executorService.shutdown();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,318 @@
|
||||
package edu.utsa.cs3443.lakewatch.model;
|
||||
|
||||
import android.content.Context;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Serializable;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* Model class containing logic and data for all boat ramps
|
||||
*
|
||||
*/
|
||||
public class BoatRampData implements Serializable {
|
||||
private Integer rampNumber;
|
||||
private String name;
|
||||
private BoatRampStatus status;
|
||||
private String openTimes;
|
||||
private String address;
|
||||
private String operator;
|
||||
private LocalDate lastUpdated;
|
||||
|
||||
private static final String CACHE_FILE_NAME = "BoatRampData.json";
|
||||
|
||||
/**
|
||||
* Create a single boat ramp instance
|
||||
*
|
||||
* @param rampNumber The official ramp number
|
||||
* @param name The more commonly known name of the ramp
|
||||
* @param status The open/closed/unknown status of the ramp
|
||||
* @param openTimes The times the ramp can be used if its status is "open"
|
||||
* @param address The approximate address of the ramp
|
||||
* @param operator The owner of the ramp, like Comal County
|
||||
* @param lastUpdated The last time the given data was updated
|
||||
*/
|
||||
public BoatRampData(
|
||||
Integer rampNumber,
|
||||
String name,
|
||||
BoatRampStatus status,
|
||||
String openTimes,
|
||||
String address,
|
||||
String operator,
|
||||
LocalDate lastUpdated) {
|
||||
this.setRampNumber(rampNumber);
|
||||
this.setName(name);
|
||||
this.setStatus(status);
|
||||
this.setOpenTimes(openTimes);
|
||||
this.setAddress(address);
|
||||
this.setOperator(operator);
|
||||
this.setLastUpdated(lastUpdated);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the common name of the ramp
|
||||
*
|
||||
* @return name of the ramp
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the common name of the ramp
|
||||
*
|
||||
* @param name common name of the ramp
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the open/closed/unknown status of the ramp
|
||||
*
|
||||
* @return status of the ramp
|
||||
*/
|
||||
public BoatRampStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the open/closed/unknown status of the ramp
|
||||
*
|
||||
* @param status new status for the ramp
|
||||
*/
|
||||
public void setStatus(BoatRampStatus status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the operating times of the ramp
|
||||
*
|
||||
* @return operating times of the ramp
|
||||
*/
|
||||
public String getOpenTimes() {
|
||||
return openTimes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the operating times of the ramp
|
||||
*
|
||||
* @param openTimes new operating times for the ramp
|
||||
*/
|
||||
public void setOpenTimes(String openTimes) {
|
||||
this.openTimes = openTimes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the approximate address of the ramp
|
||||
*
|
||||
* @return address of the ramp
|
||||
*/
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the address of the ramp
|
||||
*
|
||||
* @param address new address for the ramp
|
||||
*/
|
||||
public void setAddress(String address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the operator of the ramp
|
||||
*
|
||||
* @return operator of the ramp
|
||||
*/
|
||||
public String getOperator() {
|
||||
return operator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the operator of the ramp
|
||||
*
|
||||
* @param operator new operator for the ramp
|
||||
*/
|
||||
public void setOperator(String operator) {
|
||||
this.operator = operator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last time the given data set was updated
|
||||
*
|
||||
* @return last updated time for the ramp data
|
||||
*/
|
||||
public LocalDate getLastUpdated() {
|
||||
return lastUpdated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the last updated time for the ramp
|
||||
*
|
||||
* @param lastUpdated new update time for the ramp's data
|
||||
*/
|
||||
public void setLastUpdated(LocalDate lastUpdated) {
|
||||
this.lastUpdated = lastUpdated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a full listing of all the ramp's data from a JSON Array
|
||||
*
|
||||
* @param in The json stream to parse
|
||||
* @return A full listing of boat ramp data
|
||||
* @throws IOException when readers cannot be created
|
||||
* @throws JSONException when the input stream contains invalid json
|
||||
*/
|
||||
private static ArrayList<BoatRampData> fromJsonStream(InputStream in)
|
||||
throws IOException, JSONException {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
|
||||
StringBuilder out = new StringBuilder();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
out.append(line);
|
||||
}
|
||||
reader.close();
|
||||
ArrayList<BoatRampData> rampData = new ArrayList<>();
|
||||
JSONArray json = new JSONArray(out.toString());
|
||||
for (int i = 0; i < json.length(); i++) {
|
||||
JSONObject rampJson = json.getJSONObject(i);
|
||||
rampData.add(BoatRampData.fromJson(rampJson));
|
||||
}
|
||||
|
||||
rampData.sort(Comparator.comparing(BoatRampData::getStatus));
|
||||
return rampData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the latest ramp data from the API
|
||||
*
|
||||
* @return An updated full listing of all boat ramp data
|
||||
* @throws Exception When the request fails in any shape way or form
|
||||
*/
|
||||
public static ArrayList<BoatRampData> fetchData() throws Exception {
|
||||
HttpURLConnection conn =
|
||||
(HttpURLConnection)
|
||||
new URL("https://lakewatch.orion-technologies.io/v1/ramps").openConnection();
|
||||
conn.setRequestMethod("GET");
|
||||
conn.setRequestProperty("Accept", "application/json");
|
||||
return BoatRampData.fromJsonStream(conn.getInputStream());
|
||||
}
|
||||
|
||||
/**
|
||||
* Load's the data from a cached file on disk if it exists
|
||||
*
|
||||
* @param context a given android context, used to lookup common paths
|
||||
* @return The last cached full listing of all boat ramp data
|
||||
* @throws IOException If the cache file cannot be read or doesn't exist
|
||||
* @throws JSONException If the data contained within the file is invalid
|
||||
*/
|
||||
public static ArrayList<BoatRampData> loadCachedData(Context context)
|
||||
throws IOException, JSONException {
|
||||
File file = new File(context.getCacheDir(), BoatRampData.CACHE_FILE_NAME);
|
||||
return BoatRampData.fromJsonStream(Files.newInputStream(file.toPath()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Wipe the cache file on disk
|
||||
*
|
||||
* @param context the android context used to look up the cache directory
|
||||
* @throws IOException If the file cannot be deleted and it exists
|
||||
*/
|
||||
public static void wipeCache(Context context) throws IOException {
|
||||
File file = new File(context.getCacheDir(), BoatRampData.CACHE_FILE_NAME);
|
||||
if (file.exists()) {
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize a ramp's data into JSON for export
|
||||
*
|
||||
* @return The json representation of the ramp
|
||||
* @throws JSONException If it's not possible to parse the ramp data into JSON
|
||||
*/
|
||||
public JSONObject toJson() throws JSONException {
|
||||
JSONObject json = new JSONObject();
|
||||
json.put("number", this.getRampNumber());
|
||||
json.put("name", this.getName());
|
||||
json.put("address", this.getAddress());
|
||||
json.put("operator", this.getOperator());
|
||||
json.put("status", this.getStatus().toString());
|
||||
json.put(
|
||||
"last_updated", this.getLastUpdated().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
|
||||
json.put("operating_times", this.getOpenTimes());
|
||||
return json;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize a given ramp's data from JSON
|
||||
*
|
||||
* @param json The json object to deserialize
|
||||
* @return A single boat ramp's data
|
||||
* @throws JSONException If the data contains invalid json
|
||||
*/
|
||||
public static BoatRampData fromJson(JSONObject json) throws JSONException {
|
||||
Integer rampNumber = json.getInt("number");
|
||||
String name = json.getString("name");
|
||||
String address = json.getString("address");
|
||||
String operator = json.getString("operator");
|
||||
BoatRampStatus status = BoatRampStatus.fromString(json.getString("status"));
|
||||
LocalDate lastUpdated = LocalDate.parse(json.getString("last_updated"));
|
||||
String openTimes = json.getString("operating_times");
|
||||
return new BoatRampData(rampNumber, name, status, openTimes, address, operator, lastUpdated);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a full listing of ramp data to a cache file on disk
|
||||
*
|
||||
* @param context Used to get android paths on the file system
|
||||
* @param rampData The full listing of ramp data to save
|
||||
* @throws IOException If the cache file cannot be written to
|
||||
* @throws JSONException If the ramp data cannot be serialized into JSON
|
||||
*/
|
||||
public static void saveData(Context context, ArrayList<BoatRampData> rampData)
|
||||
throws IOException, JSONException {
|
||||
File file = new File(context.getCacheDir(), BoatRampData.CACHE_FILE_NAME);
|
||||
BufferedWriter bw = new BufferedWriter(new FileWriter(file.getAbsoluteFile()));
|
||||
JSONArray jsonData = new JSONArray();
|
||||
for (BoatRampData ramp : rampData) {
|
||||
jsonData.put(ramp.toJson());
|
||||
}
|
||||
bw.write(jsonData.toString());
|
||||
bw.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of the ramp
|
||||
*
|
||||
* @return number of the ramp
|
||||
*/
|
||||
public Integer getRampNumber() {
|
||||
return rampNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a new number for the ramp
|
||||
*
|
||||
* @param rampNumber new number for the ramp
|
||||
*/
|
||||
public void setRampNumber(Integer rampNumber) {
|
||||
this.rampNumber = rampNumber;
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package edu.utsa.cs3443.lakewatch.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public enum BoatRampStatus implements Serializable {
|
||||
OPEN,
|
||||
CLOSED,
|
||||
UNKNOWN;
|
||||
|
||||
/**
|
||||
* Get the string representation of the current Enum
|
||||
*/
|
||||
public String toString() {
|
||||
switch (this) {
|
||||
case OPEN:
|
||||
return "Open";
|
||||
case CLOSED:
|
||||
return "Closed";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a boat ramp status from a String
|
||||
*
|
||||
* @param status The string to convert to a status enum
|
||||
*/
|
||||
public static BoatRampStatus fromString(String status) {
|
||||
switch (status.toLowerCase()) {
|
||||
case "open":
|
||||
return BoatRampStatus.OPEN;
|
||||
case "closed":
|
||||
return BoatRampStatus.CLOSED;
|
||||
default:
|
||||
return BoatRampStatus.UNKNOWN;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,324 @@
|
||||
package edu.utsa.cs3443.lakewatch.model;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
public class WaterLevelData {
|
||||
|
||||
private Date date;
|
||||
private double waterLevel;
|
||||
private double surfaceArea;
|
||||
private int reservoirStorage;
|
||||
private int conservationStorage;
|
||||
private double percentFull;
|
||||
private int conservationCapacity;
|
||||
private int deadPoolCapacity;
|
||||
private boolean isCurrent;
|
||||
|
||||
|
||||
public WaterLevelData(Date date, double waterLevel, boolean isCurrent) {
|
||||
this.date = date;
|
||||
this.waterLevel = waterLevel;
|
||||
this.isCurrent = isCurrent;
|
||||
this.surfaceArea = 0.0;
|
||||
this.reservoirStorage = 0;
|
||||
this.conservationStorage = 0;
|
||||
this.percentFull = 0.0;
|
||||
this.conservationCapacity = 0;
|
||||
this.deadPoolCapacity = 0;
|
||||
}
|
||||
|
||||
// Getters and Setters
|
||||
public String getDate() {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
|
||||
return sdf.format(date);
|
||||
}
|
||||
|
||||
public void setDate(Date date) {
|
||||
this.date = date;
|
||||
}
|
||||
|
||||
public double getWaterLevel() {
|
||||
return waterLevel;
|
||||
}
|
||||
|
||||
public void setWaterLevel(double waterLevel) {
|
||||
this.waterLevel = waterLevel;
|
||||
}
|
||||
|
||||
public double getSurfaceArea() {
|
||||
return surfaceArea;
|
||||
}
|
||||
|
||||
public void setSurfaceArea(double surfaceArea) {
|
||||
this.surfaceArea = surfaceArea;
|
||||
}
|
||||
|
||||
public int getReservoirStorage() {
|
||||
return reservoirStorage;
|
||||
}
|
||||
|
||||
public void setReservoirStorage(int reservoirStorage) {
|
||||
this.reservoirStorage = reservoirStorage;
|
||||
}
|
||||
|
||||
public int getConservationStorage() {
|
||||
return conservationStorage;
|
||||
}
|
||||
|
||||
public void setConservationStorage(int conservationStorage) {
|
||||
this.conservationStorage = conservationStorage;
|
||||
}
|
||||
|
||||
public double getPercentFull() {
|
||||
return percentFull;
|
||||
}
|
||||
|
||||
public void setPercentFull(double percentFull) {
|
||||
this.percentFull = percentFull;
|
||||
}
|
||||
|
||||
public int getConservationCapacity() {
|
||||
return conservationCapacity;
|
||||
}
|
||||
|
||||
public void setConservationCapacity(int conservationCapacity) {
|
||||
this.conservationCapacity = conservationCapacity;
|
||||
}
|
||||
|
||||
public int getDeadPoolCapacity() {
|
||||
return deadPoolCapacity;
|
||||
}
|
||||
|
||||
public void setDeadPoolCapacity(int deadPoolCapacity) {
|
||||
this.deadPoolCapacity = deadPoolCapacity;
|
||||
}
|
||||
|
||||
public boolean isCurrent() {
|
||||
return isCurrent;
|
||||
}
|
||||
|
||||
public void setCurrent(boolean isCurrent) {
|
||||
this.isCurrent = isCurrent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "WaterLevelData{" +
|
||||
"date=" + date +
|
||||
", waterLevel=" + waterLevel +
|
||||
", surfaceArea=" + surfaceArea +
|
||||
", reservoirStorage=" + reservoirStorage +
|
||||
", conservationStorage=" + conservationStorage +
|
||||
", percentFull=" + percentFull +
|
||||
", conservationCapacity=" + conservationCapacity +
|
||||
", deadPoolCapacity=" + deadPoolCapacity +
|
||||
", isCurrent=" + isCurrent +
|
||||
'}';
|
||||
}
|
||||
|
||||
public static String getJsonResponseFromApi() {
|
||||
String urlString = "https://lakewatch.orion-technologies.io/v1/waterdata?start=1900-01-01&end=3000-01-01"; //API URL
|
||||
HttpURLConnection urlConnection = null;
|
||||
BufferedReader reader = null;
|
||||
String jsonResponse = null;
|
||||
|
||||
try {
|
||||
URL url = new URL(urlString);
|
||||
urlConnection = (HttpURLConnection) url.openConnection();
|
||||
urlConnection.setRequestMethod("GET");
|
||||
urlConnection.connect();
|
||||
|
||||
InputStream inputStream = urlConnection.getInputStream();
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
if (inputStream == null) {
|
||||
return null;
|
||||
}
|
||||
reader = new BufferedReader(new InputStreamReader(inputStream));
|
||||
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
buffer.append(line).append("\n");
|
||||
}
|
||||
|
||||
if (buffer.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
jsonResponse = buffer.toString();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (urlConnection != null) {
|
||||
urlConnection.disconnect();
|
||||
}
|
||||
if (reader != null) {
|
||||
try {
|
||||
reader.close();
|
||||
} catch (final IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return jsonResponse;
|
||||
}
|
||||
|
||||
|
||||
public static WaterLevelData _loadWaterLevelData(String jsonResponse) {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
|
||||
|
||||
try {
|
||||
JSONArray jsonArray = new JSONArray(jsonResponse);
|
||||
if (jsonArray.length() > 0) {
|
||||
JSONObject jsonObject = jsonArray.getJSONObject(jsonArray.length() - 1); // Get the last entry
|
||||
|
||||
Date date;
|
||||
try {
|
||||
date = sdf.parse(jsonObject.getString("date"));
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
date = new Date(); // Default to current date if parsing fails
|
||||
}
|
||||
|
||||
double waterLevel = jsonObject.optDouble("water_level", 0.0);
|
||||
|
||||
WaterLevelData data = new WaterLevelData(date, waterLevel, true);
|
||||
|
||||
data.setSurfaceArea(jsonObject.optDouble("surface_area", 0.0));
|
||||
data.setReservoirStorage(jsonObject.optInt("reservoir_storage", 0));
|
||||
data.setConservationStorage(jsonObject.optInt("conservation_storage", 0));
|
||||
data.setPercentFull(jsonObject.optDouble("percent_full", 0.0));
|
||||
data.setConservationCapacity(jsonObject.optInt("conservation_capacity", 0));
|
||||
data.setDeadPoolCapacity(jsonObject.optInt("dead_pool_capacity", 0));
|
||||
|
||||
return data;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null; // Return null if no data is available
|
||||
}
|
||||
|
||||
|
||||
// Save the data to a JSON file
|
||||
public void saveWaterLevelData(Context context) {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
|
||||
try {
|
||||
jsonObject.put("date", sdf.format(date));
|
||||
jsonObject.put("water_level", waterLevel);
|
||||
jsonObject.put("surface_area", surfaceArea);
|
||||
jsonObject.put("reservoir_storage", reservoirStorage);
|
||||
jsonObject.put("conservation_storage", conservationStorage);
|
||||
jsonObject.put("percent_full", percentFull);
|
||||
jsonObject.put("conservation_capacity", conservationCapacity);
|
||||
jsonObject.put("dead_pool_capacity", deadPoolCapacity);
|
||||
|
||||
File file = new File(context.getFilesDir(), "water_level_data.json");
|
||||
FileWriter fileWriter = new FileWriter(file);
|
||||
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
|
||||
bufferedWriter.write(jsonObject.toString());
|
||||
bufferedWriter.close();
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e("WaterLevelData", "Failed to save data", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Load the data from a JSON file
|
||||
public static WaterLevelData loadSavedWaterLevelData(Context context) {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
|
||||
File file = new File(context.getFilesDir(), "water_level_data.json");
|
||||
|
||||
if (!file.exists()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
FileReader fileReader = new FileReader(file);
|
||||
BufferedReader bufferedReader = new BufferedReader(fileReader);
|
||||
StringBuilder jsonBuilder = new StringBuilder();
|
||||
String line;
|
||||
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
jsonBuilder.append(line);
|
||||
}
|
||||
|
||||
bufferedReader.close();
|
||||
JSONObject jsonObject = new JSONObject(jsonBuilder.toString());
|
||||
|
||||
Date date;
|
||||
try {
|
||||
date = sdf.parse(jsonObject.getString("date"));
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
date = new Date(); // Default to current date if parsing fails
|
||||
}
|
||||
|
||||
double waterLevel = jsonObject.optDouble("water_level", 0.0);
|
||||
|
||||
WaterLevelData data = new WaterLevelData(date, waterLevel, true);
|
||||
|
||||
data.setSurfaceArea(jsonObject.optDouble("surface_area", 0.0));
|
||||
data.setReservoirStorage(jsonObject.optInt("reservoir_storage", 0));
|
||||
data.setConservationStorage(jsonObject.optInt("conservation_storage", 0));
|
||||
data.setPercentFull(jsonObject.optDouble("percent_full", 0.0));
|
||||
data.setConservationCapacity(jsonObject.optInt("conservation_capacity", 0));
|
||||
data.setDeadPoolCapacity(jsonObject.optInt("dead_pool_capacity", 0));
|
||||
|
||||
return data;
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e("WaterLevelData", "Failed to load data", e);
|
||||
}
|
||||
|
||||
return null; // Return null if loading fails
|
||||
}
|
||||
|
||||
// Clear cached data
|
||||
public static void clearCache(Context context) {
|
||||
File file = new File(context.getFilesDir(), "water_level_data.json");
|
||||
if (file.exists()) {
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
|
||||
// Force data fetch
|
||||
public static void forceDataFetch(Context context) {
|
||||
new Thread(() -> {
|
||||
String jsonResponse = getJsonResponseFromApi();
|
||||
if (jsonResponse != null) {
|
||||
WaterLevelData data = _loadWaterLevelData(jsonResponse);
|
||||
if (data != null) {
|
||||
data.saveWaterLevelData(context);
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,382 @@
|
||||
package edu.utsa.cs3443.lakewatch.model;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.ProtocolException;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* Represents the weather data for a specified location. This class holds various weather attributes
|
||||
* such as temperature, wind speed, wind direction, and precipitation amounts.
|
||||
* It provides methods to load weather data from a remote API and populate its fields accordingly.
|
||||
*
|
||||
* The weather data includes:
|
||||
* - Weather description
|
||||
* - High and low temperatures
|
||||
* - Wind speed and gust
|
||||
* - Wind direction
|
||||
* - Chance of precipitation
|
||||
* - Amount of precipitation
|
||||
*
|
||||
* @author Ethan Grams - bib016
|
||||
*/
|
||||
public class WeatherData {
|
||||
|
||||
private String weatherDescription;
|
||||
private int tempHigh;
|
||||
private int tempLow;
|
||||
private String windSpeed;
|
||||
private double windGust;
|
||||
private String windDirection;
|
||||
private int chancePrecipitation;
|
||||
private double amountPrecipitation;
|
||||
|
||||
private static final String CACHE_FILE_NAME = "weather_data.json";
|
||||
|
||||
// Constructor with all fields
|
||||
public WeatherData(String weatherDescription, int tempHigh, int tempLow, String windSpeed, double windGust, String windDirection, int chancePrecipitation, double amountPrecipitation) {
|
||||
this.weatherDescription = weatherDescription;
|
||||
this.tempHigh = tempHigh;
|
||||
this.tempLow = tempLow;
|
||||
this.windSpeed = windSpeed;
|
||||
this.windGust = windGust;
|
||||
this.windDirection = windDirection;
|
||||
this.chancePrecipitation = chancePrecipitation;
|
||||
this.amountPrecipitation = amountPrecipitation;
|
||||
}
|
||||
|
||||
// Default Constructor
|
||||
public WeatherData() {}
|
||||
|
||||
/**
|
||||
* Loads weather data from the weather.gov API and creates a WeatherData object.
|
||||
* This method fetches both hourly and general weather data for a specified weather station,
|
||||
* populating the fields of the WeatherData object with the retrieved information.
|
||||
*
|
||||
* @return a WeatherData object populated with the fetched weather data
|
||||
* @throws IOException if there is an error during the network connection or data retrieval
|
||||
*/
|
||||
public WeatherData loadWeatherData() throws IOException {
|
||||
WeatherData weatherData = new WeatherData();
|
||||
|
||||
String hourlyUrl = "https://api.weather.gov/gridpoints/EWX/136,74/forecast/hourly";
|
||||
String generalUrl = "https://api.weather.gov/gridpoints/EWX/136,74";
|
||||
|
||||
// Fetch hourly weather data
|
||||
fetchHourlyWeatherData(hourlyUrl, weatherData);
|
||||
|
||||
// Fetch general weather data
|
||||
fetchGeneralWeatherData(generalUrl, weatherData);
|
||||
|
||||
return weatherData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches and processes hourly weather data for a specified weather station.
|
||||
* Establishes a connection to the given URL, retrieves the weather data in JSON format,
|
||||
* and extracts the necessary values to populate the provided WeatherData object.
|
||||
* Handles weather description, wind speed and direction, and chance of precipitation.
|
||||
*
|
||||
* @param url the URL to fetch weather data from
|
||||
* @param weatherData the WeatherData object to populate with the fetched data
|
||||
* @throws IOException if there is an error during the network connection or data retrieval
|
||||
*/
|
||||
private void fetchHourlyWeatherData(String url, WeatherData weatherData) throws IOException {
|
||||
HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
|
||||
|
||||
try {
|
||||
setupConnection(urlConnection);
|
||||
|
||||
int responseCode = urlConnection.getResponseCode();
|
||||
if (responseCode == HttpURLConnection.HTTP_OK) {
|
||||
String response = readStream(urlConnection.getInputStream());
|
||||
JSONObject jsonResponse = new JSONObject(response);
|
||||
JSONArray periods = jsonResponse.getJSONObject("properties").getJSONArray("periods");
|
||||
JSONObject firstPeriod = periods.getJSONObject(0);
|
||||
|
||||
weatherData.setWeatherDescription(firstPeriod.getString("shortForecast"));
|
||||
weatherData.setWindSpeed(firstPeriod.getString("windSpeed"));
|
||||
weatherData.setWindDirection(firstPeriod.getString("windDirection"));
|
||||
weatherData.setChancePrecipitation(firstPeriod.getJSONObject("probabilityOfPrecipitation").optInt("value", 0));
|
||||
} else {
|
||||
throw new IOException("HTTP error code: " + responseCode);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
urlConnection.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches and processes general weather data for a specified weather station.
|
||||
* Establishes a connection to the given URL, retrieves the weather data in JSON format,
|
||||
* and extracts the necessary values to populate the provided WeatherData object.
|
||||
* Handles high and low temperatures, wind gusts, and precipitation amounts,
|
||||
* converting units as necessary.
|
||||
*
|
||||
* @param url the URL to fetch weather data from
|
||||
* @param weatherData the WeatherData object to populate with the fetched data
|
||||
* @throws IOException if there is an error during the network connection or data retrieval
|
||||
*/
|
||||
private void fetchGeneralWeatherData(String url, WeatherData weatherData) throws IOException {
|
||||
HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
|
||||
|
||||
try {
|
||||
setupConnection(urlConnection);
|
||||
|
||||
int responseCode = urlConnection.getResponseCode();
|
||||
if (responseCode == HttpURLConnection.HTTP_OK) {
|
||||
String response = readStream(urlConnection.getInputStream());
|
||||
JSONObject jsonResponse = new JSONObject(response);
|
||||
JSONObject properties = jsonResponse.getJSONObject("properties");
|
||||
|
||||
weatherData.setTempHigh(convertToFahrenheit(getMaxTemp(properties.getJSONObject("maxTemperature").getJSONArray("values"))));
|
||||
weatherData.setTempLow(convertToFahrenheit(getMinTemp(properties.getJSONObject("minTemperature").getJSONArray("values"))));
|
||||
weatherData.setWindGust(convertKmhToMph(properties.getJSONObject("windGust").getJSONArray("values").getJSONObject(0).optInt("value")));
|
||||
weatherData.setAmountPrecipitation(convertMmToInches(sumPrecipitation(properties.getJSONObject("quantitativePrecipitation").getJSONArray("values"), 4)));
|
||||
} else {
|
||||
throw new IOException("HTTP error code: " + responseCode);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
urlConnection.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the given HttpURLConnection with the necessary request properties.
|
||||
* Sets the request method to GET, and specifies that the response should be in JSON format.
|
||||
* Also sets the User-Agent to mimic a modern web browser.
|
||||
*
|
||||
* @param connection the HttpURLConnection to be configured
|
||||
* @throws ProtocolException if there is an error in the underlying protocol
|
||||
*/
|
||||
private void setupConnection(HttpURLConnection connection) throws ProtocolException {
|
||||
connection.setRequestMethod("GET");
|
||||
connection.setRequestProperty("Accept", "application/json");
|
||||
connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36");
|
||||
}
|
||||
|
||||
// Reads in data and returns it in one String
|
||||
private String readStream(InputStream in) throws IOException {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
|
||||
StringBuilder out = new StringBuilder();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
out.append(line);
|
||||
}
|
||||
reader.close();
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
// Takes two high temperatures within a 24 hour period and returns the maximum
|
||||
private int getMaxTemp(JSONArray values) throws JSONException {
|
||||
return Math.max(values.getJSONObject(0).optInt("value", 0), values.getJSONObject(1).optInt("value", 0));
|
||||
}
|
||||
|
||||
// Returns the current low temperature
|
||||
private int getMinTemp(JSONArray values) throws JSONException {
|
||||
return values.getJSONObject(0).optInt("value", 0);
|
||||
}
|
||||
|
||||
// Sums precipitation over a 24 hour period
|
||||
private double sumPrecipitation(JSONArray values, int count) throws JSONException {
|
||||
double sum = 0.0;
|
||||
for (int i = 0; i < count; i++) {
|
||||
sum += values.getJSONObject(i).optInt("value", 0);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
// Converts Celsius to Fahrenheit
|
||||
private int convertToFahrenheit(int celsius) {
|
||||
return (int) (celsius * (9.0 / 5.0)) + 32;
|
||||
}
|
||||
|
||||
// Converts Kilometers Per Hour to Miles Per Hour
|
||||
private double convertKmhToMph(double kmh) {
|
||||
return kmh * 0.621371;
|
||||
}
|
||||
|
||||
// Converts millimeters to inches
|
||||
private double convertMmToInches(double mm) {
|
||||
return mm * 0.0393701;
|
||||
}
|
||||
|
||||
// Save the data to a JSON file
|
||||
public void saveWeatherData(Context context) {
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
try {
|
||||
jsonObject.put("weather_description", weatherDescription);
|
||||
jsonObject.put("temp_high", tempHigh);
|
||||
jsonObject.put("temp_low", tempLow);
|
||||
jsonObject.put("wind_speed", windSpeed);
|
||||
jsonObject.put("wind_gust", windGust);
|
||||
jsonObject.put("wind_direction", windDirection);
|
||||
jsonObject.put("chance_precipitation", chancePrecipitation);
|
||||
jsonObject.put("amount_precipitation", amountPrecipitation);
|
||||
File file = new File(context.getFilesDir(), "weather_data.json");
|
||||
FileWriter fileWriter = new FileWriter(file);
|
||||
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
|
||||
bufferedWriter.write(jsonObject.toString());
|
||||
bufferedWriter.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e("WaterLevelData", "Failed to save data", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Load the data from a JSON file
|
||||
public static WeatherData loadSavedWeatherData(Context context) {
|
||||
File file = new File(context.getFilesDir(), "weather_data.json");
|
||||
|
||||
if (!file.exists()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
FileReader fileReader = new FileReader(file);
|
||||
BufferedReader bufferedReader = new BufferedReader(fileReader);
|
||||
StringBuilder jsonBuilder = new StringBuilder();
|
||||
String line;
|
||||
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
jsonBuilder.append(line);
|
||||
}
|
||||
|
||||
bufferedReader.close();
|
||||
JSONObject jsonObject = new JSONObject(jsonBuilder.toString());
|
||||
|
||||
|
||||
double waterLevel = jsonObject.optDouble("water_level", 0.0);
|
||||
|
||||
WeatherData weatherData = new WeatherData();
|
||||
|
||||
weatherData.setWeatherDescription(jsonObject.optString("weather_description", ""));
|
||||
weatherData.setTempHigh(jsonObject.optInt("temp_high", 0));
|
||||
weatherData.setTempLow(jsonObject.optInt("temp_low", 0));
|
||||
weatherData.setWindSpeed(jsonObject.optString("wind_speed", ""));
|
||||
weatherData.setWindGust(jsonObject.optDouble("wind_gust", 0.0));
|
||||
weatherData.setWindDirection(jsonObject.optString("wind_direction", ""));
|
||||
weatherData.setChancePrecipitation(jsonObject.optInt("chance_precipitation", 0));
|
||||
weatherData.setAmountPrecipitation(jsonObject.optDouble("amount_precipitation", 0.0));
|
||||
|
||||
return weatherData;
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e("WeatherData", "Failed to load data", e);
|
||||
}
|
||||
|
||||
return null; // Return null if loading fails
|
||||
}
|
||||
|
||||
/**
|
||||
* Wipe the cache file on disk
|
||||
*
|
||||
* @param context the android context used to look up the cache directory
|
||||
* @throws IOException If the file cannot be deleted and it exists
|
||||
*/
|
||||
public static void wipeCache(Context context) throws IOException {
|
||||
File file = new File(context.getCacheDir(), WeatherData.CACHE_FILE_NAME);
|
||||
if (file.exists()) {
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "WeatherData{" +
|
||||
"weatherDescription='" + weatherDescription + '\'' +
|
||||
", tempHigh=" + tempHigh +
|
||||
", tempLow=" + tempLow +
|
||||
", windSpeed='" + windSpeed + '\'' +
|
||||
", windGust=" + windGust +
|
||||
", windDirection='" + windDirection + '\'' +
|
||||
", chancePrecipitation=" + chancePrecipitation +
|
||||
", amountPrecipitation=" + amountPrecipitation +
|
||||
'}';
|
||||
}
|
||||
|
||||
// Getters and Setters
|
||||
public String getWeatherDescription() {
|
||||
return weatherDescription;
|
||||
}
|
||||
|
||||
public void setWeatherDescription(String weatherDescription) {
|
||||
this.weatherDescription = weatherDescription;
|
||||
}
|
||||
|
||||
public int getTempHigh() {
|
||||
return tempHigh;
|
||||
}
|
||||
|
||||
public void setTempHigh(int tempHigh) {
|
||||
this.tempHigh = tempHigh;
|
||||
}
|
||||
|
||||
public int getTempLow() {
|
||||
return tempLow;
|
||||
}
|
||||
|
||||
public void setTempLow(int tempLow) {
|
||||
this.tempLow = tempLow;
|
||||
}
|
||||
|
||||
public String getWindSpeed() {
|
||||
return windSpeed;
|
||||
}
|
||||
|
||||
public void setWindSpeed(String windSpeed) {
|
||||
this.windSpeed = windSpeed;
|
||||
}
|
||||
|
||||
public double getWindGust() {
|
||||
return windGust;
|
||||
}
|
||||
|
||||
public void setWindGust(double windGust) {
|
||||
this.windGust = windGust;
|
||||
}
|
||||
|
||||
public String getWindDirection() {
|
||||
return windDirection;
|
||||
}
|
||||
|
||||
public void setWindDirection(String windDirection) {
|
||||
this.windDirection = windDirection;
|
||||
}
|
||||
|
||||
public int getChancePrecipitation() {
|
||||
return chancePrecipitation;
|
||||
}
|
||||
|
||||
public void setChancePrecipitation(int chancePrecipitation) {
|
||||
this.chancePrecipitation = chancePrecipitation;
|
||||
}
|
||||
|
||||
public double getAmountPrecipitation() {
|
||||
return amountPrecipitation;
|
||||
}
|
||||
|
||||
public void setAmountPrecipitation(double amountPrecipitation) {
|
||||
this.amountPrecipitation = amountPrecipitation;
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package edu.utsa.cs3443.lakewatch.model;
|
||||
import android.app.Application;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Color;
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
|
||||
/**
|
||||
* This class is referenced on line 6
|
||||
* of the AndroidManifest.XML to implement
|
||||
* dark mode universally within the app's views
|
||||
* @author Gavin Diab
|
||||
*/
|
||||
public class userSettings extends Application {
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
// Load the theme setting from SharedPreferences
|
||||
SharedPreferences sharedPreferences = getSharedPreferences("sharedPrefs", MODE_PRIVATE);
|
||||
boolean isDarkModeOn = sharedPreferences.getBoolean("isDarkModeOn", false);
|
||||
|
||||
// Apply the theme setting
|
||||
if (isDarkModeOn) {
|
||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
|
||||
} else {
|
||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="800dp"
|
||||
android:height="800dp"
|
||||
android:viewportWidth="50"
|
||||
android:viewportHeight="50">
|
||||
<path
|
||||
android:pathData="M8.5,7.092l9.565,2.639 5.309,-3.731 1.847,0.455 -5.259,3.742 28.985,8.053 -2.121,7.882 -29.093,-8.031c-11.271,-3.399 -9.216,-11.009 -9.216,-11.009"
|
||||
android:fillColor="?attr/colorControlNormal"/>
|
||||
<path
|
||||
android:pathData="M33.957,27.258c-0.035,-0.658 -0.16,-1.285 -0.375,-1.877l13.281,3.697 -0.426,1.639 -12.48,-3.459zM21.891,23.926c0.358,-0.521 0.782,-0.991 1.264,-1.398l-22.155,-6.12v1.763l20.891,5.755zM27.377,29.762c1.191,0 2.158,-0.969 2.158,-2.16 0,-1.195 -0.967,-2.162 -2.158,-2.162 -1.195,0 -2.161,0.967 -2.161,2.162 0,1.191 0.966,2.16 2.161,2.16zM49,43c-1.051,0 -2.051,-0.238 -2.943,-0.648 -0.928,-0.42 -1.963,-0.672 -3.047,-0.672 -1.08,0 -2.121,0.252 -3.035,0.672 -0.905,0.41 -1.903,0.648 -2.955,0.648 -1.051,0 -2.053,-0.238 -2.955,-0.648 -0.92,-0.42 -1.953,-0.672 -3.035,-0.672 -1.086,0 -2.119,0.252 -3.045,0.672 -0.893,0.41 -1.905,0.648 -2.951,0.648 -1.045,0 -2.051,-0.238 -2.949,-0.648 -0.926,-0.42 -1.967,-0.672 -3.046,-0.672 -1.08,0 -2.12,0.252 -3.035,0.672 -0.898,0.41 -1.909,0.648 -2.956,0.648 -1.045,0 -2.051,-0.238 -2.955,-0.648 -0.916,-0.42 -1.956,-0.672 -3.036,-0.672 -1.079,0 -2.119,0.252 -3.04,0.672 -0.897,0.41 -1.909,0.648 -2.949,0.648l-0.068,-17.605 24.227,6.686c-1.67,-0.807 -2.83,-2.5 -2.83,-4.479 0,-2.754 2.227,-4.983 4.979,-4.983 2.744,0 4.977,2.229 4.977,4.983 0,2.752 -2.232,4.98 -4.977,4.98l-0.49,-0.047 22.078,6.088 0.036,4.377z"
|
||||
android:fillColor="?attr/colorControlNormal"/>
|
||||
</vector>
|
@ -0,0 +1,81 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="512.35dp"
|
||||
android:height="512.35dp"
|
||||
android:viewportWidth="512.35"
|
||||
android:viewportHeight="512.35">
|
||||
<path
|
||||
android:pathData="M256.17,256.17m-255.17,0a255.17,255.17 0,1 1,510.35 0a255.17,255.17 0,1 1,-510.35 0"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#f2fbb7"
|
||||
android:strokeColor="#000000"/>
|
||||
<path
|
||||
android:pathData="m92.23,143.09h327.89a10,10 45,0 1,10 10v227.89a10,10 135,0 1,-10 10L92.23,390.98a10,10 45,0 1,-10 -10L82.23,153.09a10,10 135,0 1,10 -10z"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2.10845"
|
||||
android:fillColor="#ffffff"
|
||||
android:strokeColor="#000000"
|
||||
android:strokeLineCap="butt"/>
|
||||
<path
|
||||
android:pathData="M87.39,135.48L424.96,135.48a10,10 45,0 1,10 10v17.57a10,10 135,0 1,-10 10L87.39,173.05a10,10 45,0 1,-10 -10v-17.57a10,10 135,0 1,10 -10z"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2.42674"
|
||||
android:fillColor="#000000"
|
||||
android:strokeColor="#000000"
|
||||
android:strokeLineCap="butt"/>
|
||||
<path
|
||||
android:pathData="M107.23,142.67a16.44,26.44 0,1 0,32.89 0a16.44,26.44 0,1 0,-32.89 0z"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2.11426"
|
||||
android:fillColor="#000000"
|
||||
android:strokeColor="#000000"
|
||||
android:strokeLineCap="butt"/>
|
||||
<path
|
||||
android:pathData="M372.23,142.67a16.44,26.44 0,1 0,32.89 0a16.44,26.44 0,1 0,-32.89 0z"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2.11426"
|
||||
android:fillColor="#000000"
|
||||
android:strokeColor="#000000"
|
||||
android:strokeLineCap="butt"/>
|
||||
<path
|
||||
android:pathData="m139.19,200h61.32a4,4 45,0 1,4 4v61.32a4,4 135,0 1,-4 4h-61.32a4,4 45,0 1,-4 -4v-61.32a4,4 135,0 1,4 -4z"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#000000"
|
||||
android:strokeColor="#000000"
|
||||
android:strokeLineCap="butt"/>
|
||||
<path
|
||||
android:pathData="m311.83,200h61.32a4,4 45,0 1,4 4v61.32a4,4 135,0 1,-4 4h-61.32a4,4 45,0 1,-4 -4v-61.32a4,4 135,0 1,4 -4z"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#000000"
|
||||
android:strokeColor="#000000"
|
||||
android:strokeLineCap="butt"/>
|
||||
<path
|
||||
android:pathData="m225.51,286.32h61.32a4,4 45,0 1,4 4v61.32a4,4 135,0 1,-4 4h-61.32a4,4 45,0 1,-4 -4v-61.32a4,4 135,0 1,4 -4z"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#000000"
|
||||
android:strokeColor="#000000"
|
||||
android:strokeLineCap="butt"/>
|
||||
<path
|
||||
android:pathData="m225.51,200h61.32a4,4 45,0 1,4 4v61.32a4,4 135,0 1,-4 4h-61.32a4,4 45,0 1,-4 -4v-61.32a4,4 135,0 1,4 -4z"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#000000"
|
||||
android:strokeColor="#000000"
|
||||
android:strokeLineCap="butt"/>
|
||||
<path
|
||||
android:pathData="m139.19,286.32h61.32a4,4 45,0 1,4 4v61.32a4,4 135,0 1,-4 4h-61.32a4,4 45,0 1,-4 -4v-61.32a4,4 135,0 1,4 -4z"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#000000"
|
||||
android:strokeColor="#000000"
|
||||
android:strokeLineCap="butt"/>
|
||||
<path
|
||||
android:pathData="m311.83,286.32h61.32a4,4 45,0 1,4 4v61.32a4,4 135,0 1,-4 4h-61.32a4,4 45,0 1,-4 -4v-61.32a4,4 135,0 1,4 -4z"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#000000"
|
||||
android:strokeColor="#000000"
|
||||
android:strokeLineCap="butt"/>
|
||||
</vector>
|
@ -0,0 +1,16 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="512.35dp"
|
||||
android:height="512.35dp"
|
||||
android:viewportWidth="512.35"
|
||||
android:viewportHeight="512.35">
|
||||
<path
|
||||
android:pathData="M256.17,256.17m-255.17,0a255.17,255.17 0,1 1,510.35 0a255.17,255.17 0,1 1,-510.35 0"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#72F18A"
|
||||
android:strokeColor="#000000"/>
|
||||
<path
|
||||
android:pathData="m345.65,142.86 l-136.43,136.43 -42.83,-42.83 -44.94,44.94 88.09,88.09 0.35,-0.35 44.59,-44.59h0L390.9,188.11Z"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#ffffff"
|
||||
android:strokeColor="#000000"/>
|
||||
</vector>
|
@ -0,0 +1,25 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="512.35dp"
|
||||
android:height="512.35dp"
|
||||
android:viewportWidth="512.35"
|
||||
android:viewportHeight="512.35">
|
||||
<path
|
||||
android:pathData="M256.17,256.17m-255.17,0a255.17,255.17 0,1 1,510.35 0a255.17,255.17 0,1 1,-510.35 0"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#b7f8fb"
|
||||
android:strokeColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M147.58,131.93l232.62,0l0,232.62l-232.62,0z"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="7.96"
|
||||
android:fillColor="#ffffff"
|
||||
android:strokeColor="#000000"
|
||||
android:strokeLineCap="butt"/>
|
||||
<path
|
||||
android:pathData="M132.15,147.8l232.62,0l0,232.62l-232.62,0z"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="7.96"
|
||||
android:fillColor="#ffffff"
|
||||
android:strokeColor="#000000"
|
||||
android:strokeLineCap="butt"/>
|
||||
</vector>
|
@ -0,0 +1,16 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="512.35dp"
|
||||
android:height="512.35dp"
|
||||
android:viewportWidth="512.35"
|
||||
android:viewportHeight="512.35">
|
||||
<path
|
||||
android:pathData="M256.17,256.17m-255.17,0a255.17,255.17 0,1 1,510.35 0a255.17,255.17 0,1 1,-510.35 0"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#cacaca"
|
||||
android:strokeColor="#000000"/>
|
||||
<path
|
||||
android:pathData="m258.67,119.57q23.63,0 40.54,8.45 17.1,8.45 26.13,22.09 9.03,13.45 9.03,28.63 0,18.44 -6.92,30.16 -6.92,11.53 -17.29,19.6 -10.18,7.88 -20.56,15.56 -10.37,7.49 -17.48,17.68 -6.92,10.18 -6.92,26.13v13.26L232.73,301.13L232.73,285.95q0,-18.64 6.72,-30.93 6.92,-12.3 17.1,-20.94 10.18,-8.65 20.37,-15.95 10.18,-7.49 17.1,-16.14 6.92,-8.65 6.92,-21.33 0,-16.33 -13.06,-25.17 -13.06,-9.03 -32.47,-9.03 -13.64,0 -28.05,6.72 -14.41,6.53 -26.32,21.33l-23.06,-16.91q16.91,-19.79 37.08,-28.82 20.37,-9.22 43.61,-9.22zM249.83,341.29q11.14,0 18.64,7.49 7.49,7.49 7.49,18.06 0,10.76 -7.49,18.44 -7.49,7.49 -18.64,7.49 -10.76,0 -18.06,-7.49 -7.3,-7.69 -7.3,-18.44 0,-10.57 7.3,-18.06 7.3,-7.49 18.06,-7.49z"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#ffffff"
|
||||
android:strokeColor="#000000"/>
|
||||
</vector>
|
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:drawable="@drawable/circle_guess"
|
||||
android:width="40dp"
|
||||
android:height="40dp"
|
||||
/>
|
||||
</layer-list>
|
@ -0,0 +1,18 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="512.35dp"
|
||||
android:height="512.35dp"
|
||||
android:viewportWidth="512.35"
|
||||
android:viewportHeight="512.35">
|
||||
<path
|
||||
android:pathData="M256.17,256.17m-255.17,0a255.17,255.17 0,1 1,510.35 0a255.17,255.17 0,1 1,-510.35 0"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#d0b7fb"
|
||||
android:strokeColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M171.42,85 L213.79,169.85 256.17,254.69 298.55,169.85 340.93,85L256.17,85ZM256.17,257.66 L213.79,342.5 171.42,427.34L256.17,427.34 340.93,427.34l-42.38,-84.84z"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="6"
|
||||
android:fillColor="#ffffff"
|
||||
android:strokeColor="#000000"
|
||||
android:strokeLineCap="butt"/>
|
||||
</vector>
|
@ -0,0 +1,25 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="512.35dp"
|
||||
android:height="512.35dp"
|
||||
android:viewportWidth="512.35"
|
||||
android:viewportHeight="512.35">
|
||||
<path
|
||||
android:pathData="M256.17,256.17m-255.17,0a255.17,255.17 0,1 1,510.35 0a255.17,255.17 0,1 1,-510.35 0"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#bafbb7"
|
||||
android:strokeColor="#000000"/>
|
||||
<path
|
||||
android:pathData="m159.28,190.03v227.07h65.16l0.41,-1.07L256.17,335.25l31.32,80.78 0.41,1.07h65.16L353.07,190.03Z"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="6.21387"
|
||||
android:fillColor="#ffffff"
|
||||
android:strokeColor="#000000"
|
||||
android:strokeLineCap="butt"/>
|
||||
<path
|
||||
android:pathData="M256.17,192.14m-96.63,0a96.63,96.63 0,1 1,193.27 0a96.63,96.63 0,1 1,-193.27 0"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="6.73409"
|
||||
android:fillColor="#ffffff"
|
||||
android:strokeColor="#000000"
|
||||
android:strokeLineCap="butt"/>
|
||||
</vector>
|
@ -0,0 +1,16 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="512.35dp"
|
||||
android:height="512.35dp"
|
||||
android:viewportWidth="512.35"
|
||||
android:viewportHeight="512.35">
|
||||
<path
|
||||
android:pathData="M256.17,256.17m-255.17,0a255.17,255.17 0,1 1,510.35 0a255.17,255.17 0,1 1,-510.35 0"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#ff7f7f"
|
||||
android:strokeColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M325.85,129.86L255.11,200.6L185.43,130.92L140.95,175.39l69.68,69.68l-69.04,69.04l44.47,44.47l69.04,-69.04l70.1,70.1l44.47,-44.47l-70.1,-70.1l70.74,-70.75z"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#ffffff"
|
||||
android:strokeColor="#000000"/>
|
||||
</vector>
|
@ -0,0 +1,59 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="64dp"
|
||||
android:height="64dp"
|
||||
android:viewportWidth="64"
|
||||
android:viewportHeight="64">
|
||||
<path
|
||||
android:pathData="M32,41L32,44"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#ffa500"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M25.636,38.364L23.515,40.485"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#ffa500"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M23,32L20,32"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#ffa500"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M25.636,25.636L23.515,23.515"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#ffa500"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M32,23L32,20"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#ffa500"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M38.364,25.636L40.485,23.515"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#ffa500"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M41,32L44,32"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#ffa500"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M38.364,38.364L40.485,40.485"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#ffa500"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M32,32m-5,0a5,5 0,1 1,10 0a5,5 0,1 1,-10 0"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#ffa500"
|
||||
android:strokeColor="#ffa500"/>
|
||||
</vector>
|
@ -0,0 +1,170 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#3DDC84"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M9,0L9,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,0L19,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,0L29,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,0L39,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,0L49,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,0L59,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,0L69,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,0L79,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M89,0L89,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M99,0L99,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,9L108,9"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,19L108,19"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,29L108,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,39L108,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,49L108,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,59L108,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,69L108,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,79L108,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,89L108,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,99L108,99"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,29L89,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,39L89,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,49L89,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,59L89,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,69L89,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,79L89,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,19L29,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,19L39,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,19L49,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,19L59,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,19L69,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,19L79,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
</vector>
|
@ -0,0 +1,30 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="85.84757"
|
||||
android:endY="92.4963"
|
||||
android:startX="42.9492"
|
||||
android:startY="49.59793"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#44000000"
|
||||
android:offset="0.0" />
|
||||
<item
|
||||
android:color="#00000000"
|
||||
android:offset="1.0" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeColor="#00000000" />
|
||||
</vector>
|
@ -0,0 +1,20 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="33.104dp"
|
||||
android:height="24.037dp"
|
||||
android:viewportWidth="33.104"
|
||||
android:viewportHeight="24.037">
|
||||
<path
|
||||
android:pathData="M6.043,10.713A6,6 0,0 0,7.492 22.537L23.992,22.537a7.5,7.5 0,1 0,-1.755 -14.793l-2,0.543"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="3"
|
||||
android:fillColor="?attr/colorControlNormal"
|
||||
android:strokeColor="?attr/colorControlNormal"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M22.237,7.744a8.25,8.25 0,0 0,-16.194 2.97,7.142 7.142,0 0,0 0.7,2.073"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="3"
|
||||
android:fillColor="?attr/colorControlNormal"
|
||||
android:strokeColor="?attr/colorControlNormal"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
@ -0,0 +1,11 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="33dp"
|
||||
android:height="37.5dp"
|
||||
android:viewportWidth="33"
|
||||
android:viewportHeight="37.5">
|
||||
<path
|
||||
android:pathData="M15.558,1.165 L5.109,12.661a1.623,1.623 0,0 0,1.2 2.714L8.063,15.375L2.902,20.536A1.591,1.591 0,0 0,4.027 23.25L6.375,23.25L1.13,29.543A1.653,1.653 0,0 0,2.402 32.25L14.25,32.25v2.25a2.25,2.25 0,0 0,4.5 0L18.75,32.25L30.598,32.25a1.653,1.653 0,0 0,1.273 -2.707L26.625,23.25h2.348a1.591,1.591 0,0 0,1.125 -2.714l-5.161,-5.161h1.751A1.625,1.625 0,0 0,28.313 13.75a1.6,1.6 0,0 0,-0.422 -1.09L17.442,1.165a1.277,1.277 0,0 0,-1.884 0Z"
|
||||
android:strokeWidth="1.5"
|
||||
android:fillColor="?attr/colorControlNormal"
|
||||
android:strokeColor="?attr/colorControlNormal"/>
|
||||
</vector>
|
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="360.5dp"
|
||||
android:height="37.24dp"
|
||||
android:viewportWidth="360.5"
|
||||
android:viewportHeight="37.24">
|
||||
<path
|
||||
android:fillColor="?attr/colorControlNormal"
|
||||
android:pathData="M360.14,23.7h0ZM60.38,20.31c21.45,0 25.6,3.39 60.02,3.39 35.15,0 37.67,-3.39 60.02,-3.39 21.45,0 25.6,3.39 60.02,3.39 35.15,0 37.67,-3.39 60.02,-3.39 21.45,0 25.24,3.32 59.66,3.39L360.14,16.93c-21.45,0 -25.6,-3.39 -60.02,-3.39 -35.15,0 -37.67,3.39 -60.02,3.39 -21.45,0 -25.6,-3.39 -60.02,-3.39 -35.15,0 -37.67,3.39 -60.02,3.39 -21.45,0 -25.6,-3.39 -60.02,-3.39 -35.15,0 -37.67,3.39 -60.02,3.39L0,23.7c34.25,0 39.11,-3.39 60.38,-3.39ZM300.48,27.08c-35.15,0 -37.67,3.39 -60.02,3.39 -21.45,0 -25.6,-3.39 -60.02,-3.39 -35.15,0 -37.85,3.39 -60.2,3.39S95.35,27.09 60.2,27.09 22.35,30.47 0,30.47v6.77c35.15,0 38.03,-3.39 60.2,-3.39 22.35,0 24.88,3.39 60.02,3.39s37.85,-3.39 60.2,-3.39c21.45,0 25.6,3.39 60.02,3.39 34.97,0 37.67,-3.39 60.02,-3.39 21.45,0 25.6,3.39 60.02,3.39L360.49,30.47c-22.35,0 -24.88,-3.39 -60.02,-3.39ZM60.38,6.77c21.45,0 25.6,3.39 60.02,3.39 35.15,0 37.67,-3.39 60.02,-3.39 21.45,0 25.6,3.39 60.02,3.39 35.15,0 37.67,-3.39 60.02,-3.39 21.45,0 25.24,3.32 59.66,3.39L360.14,3.39c-21.45,0 -25.6,-3.39 -60.02,-3.39 -35.15,0 -37.67,3.39 -60.02,3.39 -21.45,0 -25.6,-3.39 -60.02,-3.39 -35.15,0 -37.67,3.39 -60.02,3.39 -21.45,0 -25.6,-3.39 -60.02,-3.39C24.88,0 22.35,3.39 0,3.39v6.77C34.25,10.16 39.11,6.77 60.38,6.77Z"/>
|
||||
</vector>
|
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="26.652dp"
|
||||
android:height="17.46dp"
|
||||
android:viewportWidth="26.652"
|
||||
android:viewportHeight="17.46">
|
||||
<path
|
||||
android:fillColor="?attr/colorControlNormal"
|
||||
android:pathData="M0,12.36A5.1,5.1 0,0 1,3.972 7.392a6.091,6.091 0,0 1,6 -4.8,5.942 5.942,0 0,1 3.792,1.308 5.88,5.88 0,0 1,2.16 3.36h0.324a5.1,5.1 0,0 1,5.112 5.088,5.121 5.121,0 0,1 -5.112,5.112L5.124,17.46a4.836,4.836 0,0 1,-1.98 -0.408,5.328 5.328,0 0,1 -1.644,-1.068 5.333,5.333 0,0 1,-1.092 -1.632,4.933 4.933,0 0,1 -0.408,-1.992ZM1.74,12.36a3.228,3.228 0,0 0,0.984 2.352,3.27 3.27,0 0,0 2.388,0.984L16.248,15.696a3.262,3.262 0,0 0,2.388 -0.984,3.212 3.212,0 0,0 1,-2.352 3.148,3.148 0,0 0,-1 -2.352,3.289 3.289,0 0,0 -2.388,-0.984L14.58,9.024a0.159,0.159 0,0 1,-0.18 -0.18l-0.084,-0.588a4.363,4.363 0,0 0,-4.368 -3.936,4.267 4.267,0 0,0 -2.952,1.128 4.169,4.169 0,0 0,-1.416 2.808l-0.084,0.5c0,0.12 -0.06,0.18 -0.192,0.18l-0.54,0.084A3.228,3.228 0,0 0,2.592 10.092a3.367,3.367 0,0 0,-0.852 2.268ZM12.36,1.896c-0.12,0.108 -0.1,0.192 0.084,0.252a10.7,10.7 0,0 1,1.3 0.66c0.132,0.036 0.228,0.024 0.264,-0.036a3.7,3.7 0,0 1,6.252 2.34l0.108,0.768h1.7a2.747,2.747 0,0 1,2.016 0.84,2.77 2.77,0 0,1 0.1,3.876 2.83,2.83 0,0 1,-1.836 0.924c-0.12,0 -0.18,0.06 -0.18,0.192v1.356c0,0.132 0.06,0.192 0.18,0.192a4.577,4.577 0,0 0,4.308 -4.548,4.589 4.589,0 0,0 -4.584,-4.584h-0.18a5.588,5.588 0,0 0,-5.34 -4.128,5.27 5.27,0 0,0 -4.188,1.9Z"/>
|
||||
</vector>
|
@ -0,0 +1,11 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="360dp"
|
||||
android:height="1dp"
|
||||
android:viewportWidth="360"
|
||||
android:viewportHeight="1">
|
||||
<path
|
||||
android:pathData="M0,0.5L360,0.5"
|
||||
android:strokeWidth="1"
|
||||
android:fillColor="?attr/colorControlNormal"
|
||||
android:strokeColor="#707070"/>
|
||||
</vector>
|
@ -0,0 +1,16 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="301.68dp"
|
||||
android:height="82.76dp"
|
||||
android:viewportWidth="301.68"
|
||||
android:viewportHeight="82.76">
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M10.3,0.5L291.38,0.5A9.8,9.8 0,0 1,301.18 10.3L301.18,72.46A9.8,9.8 0,0 1,291.38 82.26L10.3,82.26A9.8,9.8 0,0 1,0.5 72.46L0.5,10.3A9.8,9.8 0,0 1,10.3 0.5z"
|
||||
android:fillColor="#fff"
|
||||
android:strokeColor="#231f20"/>
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M33.55,41.38h174.32H33.55Z"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#231f20"/>
|
||||
</vector>
|
After Width: | Height: | Size: 944 KiB |
After Width: | Height: | Size: 416 KiB |
After Width: | Height: | Size: 671 KiB |
After Width: | Height: | Size: 889 KiB |
After Width: | Height: | Size: 375 KiB |
After Width: | Height: | Size: 146 KiB |
After Width: | Height: | Size: 126 KiB |
After Width: | Height: | Size: 450 KiB |
After Width: | Height: | Size: 866 KiB |
After Width: | Height: | Size: 1.2 MiB |
After Width: | Height: | Size: 986 KiB |
After Width: | Height: | Size: 744 KiB |
After Width: | Height: | Size: 961 KiB |
After Width: | Height: | Size: 557 KiB |
After Width: | Height: | Size: 1.7 MiB |
After Width: | Height: | Size: 493 KiB |
After Width: | Height: | Size: 176 KiB |
After Width: | Height: | Size: 164 KiB |
After Width: | Height: | Size: 1.0 MiB |
After Width: | Height: | Size: 312 KiB |
After Width: | Height: | Size: 365 KiB |
After Width: | Height: | Size: 594 KiB |
After Width: | Height: | Size: 635 KiB |
@ -0,0 +1,21 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="32dp"
|
||||
android:height="32dp"
|
||||
android:viewportWidth="32"
|
||||
android:viewportHeight="32">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M20.33,2a9.72,9.72 0,0 0,-6.12 2.17A5,5 0,0 0,6 7.59a7.08,7.08 0,0 0,-4 6.23v0.08H2v0a7,7 0,0 0,0.56 2.74,2.15 2.15,0 0,0 0.11,0.22A7.11,7.11 0,0 0,9.1 21H21.66a7.05,7.05 0,0 0,6 -3.32A9.38,9.38 0,0 0,30 11.5,9.6 9.6,0 0,0 20.33,2ZM11,5a3,3 0,0 1,1.8 0.6,9.58 9.58,0 0,0 -1.13,1.69 7.43,7.43 0,0 0,-2.48 -0.43,7.93 7.93,0 0,0 -1,0.07A3,3 0,0 1,11 5ZM26.11,16.42a0.71,0.71 0,0 0,-0.1 0.14A5.08,5.08 0,0 1,21.66 19H9.1a5.13,5.13 0,0 1,-4.62 -2.95l-0.05,-0.11 0,-0.1A4.74,4.74 0,0 1,4 14v-0.14a5.15,5.15 0,0 1,5.19 -5,5.27 5.27,0 0,1 2.52,0.64 1,1 0,0 0,0.83 0.05A1,1 0,0 0,13.13 9a7.68,7.68 0,0 1,7.2 -5A7.59,7.59 0,0 1,28 11.5,7.4 7.4,0 0,1 26.11,16.42Z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M23.55,6.17a1,1 0,0 0,-1.1 1.66A4.59,4.59 0,0 1,24 12.76,1 1,0 0,0 24.76,14L25,14a1,1 0,0 0,1 -0.76A6.51,6.51 0,0 0,23.55 6.17Z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M11,23a1,1 0,0 0,-1 1v5a1,1 0,0 0,2 0V24A1,1 0,0 0,11 23Z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M16,23a1,1 0,0 0,-1 1v2.5a1,1 0,0 0,2 0V24A1,1 0,0 0,16 23Z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M21,23a1,1 0,0 0,-1 1v3.75a1,1 0,0 0,2 0V24A1,1 0,0 0,21 23Z"/>
|
||||
</vector>
|
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="800dp"
|
||||
android:height="800dp"
|
||||
android:viewportWidth="1920"
|
||||
android:viewportHeight="1920">
|
||||
<path
|
||||
android:pathData="m1739.3,1293.4 l-105.8,180.8 -240.2,-80.2 -24.5,22.3c-69.9,63.6 -150.2,109.7 -238.6,136.8l-32.1,9.9 -49.5,244.1L835.6,1807.1l-49.5,-244.2 -32.1,-9.9c-88.4,-27.1 -168.7,-73.2 -238.6,-136.8l-24.5,-22.3 -240.2,80.2 -105.8,-180.8 189.7,-164.4 -7.5,-33c-10.4,-45.7 -15.6,-91.5 -15.6,-135.9 0,-44.4 5.2,-90.1 15.6,-135.9l7.5,-33 -189.7,-164.4 105.8,-180.8 240.2,80.1 24.5,-22.3c69.9,-63.6 150.2,-109.7 238.6,-136.9l32.1,-9.8 49.5,-244.1h213l49.5,244.2 32.1,9.8c88.4,27.2 168.7,73.2 238.6,136.9l24.5,22.3 240.2,-80.2 105.8,180.8 -189.7,164.4 7.5,33c10.4,45.7 15.6,91.5 15.6,135.9 0,44.4 -5.2,90.1 -15.6,135.9l-7.5,33 189.7,164.6ZM1685.6,960c0,-41.8 -3.8,-84.5 -11.6,-127.3l210.2,-182.1 -199.5,-340.9 -265.2,88.4c-67,-55.6 -143.3,-99.4 -223.9,-128.4L1141,0L743.2,0l-54.7,269.7c-81.4,29.1 -156.4,72.3 -224,128.4L199.5,309.8 0,650.7l210.1,182.1c-7.7,42.8 -11.5,85.5 -11.5,127.3 0,41.8 3.8,84.5 11.5,127.2L0,1269.4 199.5,1610.2l265.2,-88.5c67,55.7 143.3,99.4 223.9,128.5l54.7,269.8h397.8l54.7,-269.7c81.3,-29.3 156.4,-72.3 223.9,-128.5l265.2,88.5 199.5,-340.9 -210.2,-182.2c7.8,-42.8 11.6,-85.5 11.6,-127.3ZM942.1,564.7C724.1,564.7 546.8,742 546.8,960c0,218 177.3,395.3 395.3,395.3 218,0 395.3,-177.3 395.3,-395.3 0,-218 -177.3,-395.3 -395.3,-395.3m0,677.6c-155.6,0 -282.4,-126.7 -282.4,-282.4s126.7,-282.4 282.4,-282.4S1224.4,804.4 1224.4,960s-126.7,282.4 -282.4,282.4"
|
||||
android:fillColor="?attr/colorControlNormal"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- No fill color -->
|
||||
<solid android:color="@android:color/transparent"/>
|
||||
<!-- Border style -->
|
||||
<stroke android:width="1dp" android:color="#000000"/>
|
||||
</shape>
|
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="#FFFFFF" /> <!-- Cell background color -->
|
||||
<stroke android:width="1dp" android:color="#000000" /> <!-- Border color and width -->
|
||||
</shape>
|
@ -0,0 +1,14 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="800dp"
|
||||
android:height="800dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M16,13.385C16,14.609 15.526,15.783 14.682,16.648C14.203,17.14 13.627,17.509 13,17.736M19,13.692C19,7.115 12,2 12,2C12,2 5,7.115 5,13.692C5,15.63 5.738,17.489 7.05,18.86C8.363,20.23 10.144,20.999 12,20.999C13.857,20.999 15.637,20.23 16.95,18.859C18.263,17.489 19,15.63 19,13.692Z"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="?attr/colorControlNormal"
|
||||
android:strokeLineCap="round"/>
|
||||
|
||||
</vector>
|
After Width: | Height: | Size: 16 KiB |
@ -0,0 +1,11 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="196.27dp"
|
||||
android:height="25.32dp"
|
||||
android:viewportWidth="196.27"
|
||||
android:viewportHeight="25.32">
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M10.3,0.5L185.97,0.5A9.8,9.8 0,0 1,195.77 10.3L195.77,15.02A9.8,9.8 0,0 1,185.97 24.82L10.3,24.82A9.8,9.8 0,0 1,0.5 15.02L0.5,10.3A9.8,9.8 0,0 1,10.3 0.5z"
|
||||
android:fillColor="?attr/colorControlNormal"
|
||||
android:strokeColor="?attr/colorControlNormal"/>
|
||||
</vector>
|
@ -0,0 +1,12 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="800dp"
|
||||
android:height="800dp"
|
||||
android:viewportWidth="512"
|
||||
android:viewportHeight="512">
|
||||
<path
|
||||
android:pathData="M55.13,261.43c4.61,-4.72 9.61,-9.05 14.93,-12.96C54.27,232.77 44.49,211.05 44.49,187.03c0,-47.84 38.79,-86.63 86.63,-86.63c32.81,0 61.35,18.25 76.05,45.15c11.37,-7.76 23.87,-13.95 37.21,-18.3l0.64,-1.79c0.48,-1.32 0.34,-2.79 -0.35,-4c-0.71,-1.22 -1.91,-2.07 -3.29,-2.31l-32.66,-5.87c-1.96,-0.35 -3.5,-1.89 -3.86,-3.86l-5.87,-32.66c-0.25,-1.38 -1.09,-2.59 -2.31,-3.29c-1.21,-0.71 -2.67,-0.85 -4,-0.36l-31.22,11.21c-1.87,0.68 -3.97,0.12 -5.26,-1.4l-21.44,-25.33c-0.9,-1.07 -2.25,-1.69 -3.64,-1.69c-1.41,0 -2.74,0.62 -3.65,1.69L106.04,82.93c-1.29,1.53 -3.39,2.08 -5.27,1.42L69.55,73.13c-1.32,-0.48 -2.79,-0.35 -4.01,0.35c-1.22,0.7 -2.05,1.91 -2.31,3.29l-5.86,32.66c-0.36,1.97 -1.9,3.5 -3.86,3.86l-32.66,5.86c-1.39,0.25 -2.58,1.1 -3.29,2.31c-0.71,1.22 -0.84,2.68 -0.36,4.01l11.22,31.22c0.67,1.88 0.12,3.97 -1.41,5.27L1.69,183.39C0.62,184.29 0,185.63 0,187.03c0,1.41 0.62,2.74 1.69,3.65l25.33,21.44c1.53,1.29 2.08,3.39 1.41,5.27l-11.22,31.22c-0.48,1.33 -0.34,2.79 0.36,4.01c0.71,1.21 1.9,2.05 3.29,2.31l32.66,5.86C54.1,260.88 54.64,261.13 55.13,261.43z"
|
||||
android:fillColor="?attr/colorControlNormal"/>
|
||||
<path
|
||||
android:pathData="M483.62,290.7c-14.77,-14.78 -34.42,-24.74 -56.25,-27.51c-1.9,-34.17 -16.4,-65.07 -39.1,-87.75c-24.35,-24.37 -58.13,-39.49 -95.31,-39.48c-34.83,-0.01 -66.68,13.26 -90.57,34.97c-20.55,18.65 -35.28,43.64 -41.23,71.84c-5.78,-0.96 -11.69,-1.5 -17.73,-1.5c-29.62,-0.01 -56.55,12.03 -75.95,31.46c-19.42,19.39 -31.46,46.33 -31.46,75.94c0,29.62 12.04,56.56 31.46,75.95c19.4,19.42 46.33,31.46 75.95,31.46H415.12c26.72,0 51.01,-10.86 68.5,-28.38c17.51,-17.49 28.39,-41.79 28.38,-68.5C512.01,332.49 501.14,308.18 483.62,290.7zM465.75,409.83c-13,12.98 -30.82,20.97 -50.63,20.98H143.44c-22.72,-0.01 -43.18,-9.17 -58.08,-24.06c-14.89,-14.9 -24.05,-35.35 -24.06,-58.08c0.01,-22.72 9.17,-43.18 24.06,-58.08c14.9,-14.88 35.36,-24.05 58.08,-24.06c8.48,0 16.62,1.28 24.29,3.66l14.73,4.55l1.58,-15.34c2.82,-27.57 15.88,-52.09 35.34,-69.77c19.48,-17.66 45.22,-28.4 73.59,-28.4c30.27,0 57.58,12.23 77.44,32.08c19.85,19.85 32.07,47.16 32.08,77.44c0,1.07 -0.04,2.31 -0.1,3.77l-0.48,13.59l13.42,-0.52c19.73,0.07 37.47,8.04 50.43,20.97c12.98,13 20.96,30.82 20.98,50.63C486.71,379.02 478.73,396.84 465.75,409.83z"
|
||||
android:fillColor="?attr/colorControlNormal"/>
|
||||
</vector>
|
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="82.8dp" android:viewportHeight="82.76" android:viewportWidth="301.68" android:width="301.8258dp">
|
||||
|
||||
<path android:fillColor="#fff" android:pathData="M10.3,0.5L291.38,0.5A9.8,9.8 0,0 1,301.18 10.3L301.18,72.46A9.8,9.8 0,0 1,291.38 82.26L10.3,82.26A9.8,9.8 0,0 1,0.5 72.46L0.5,10.3A9.8,9.8 0,0 1,10.3 0.5z" android:strokeColor="#231f20" android:strokeWidth="1"/>
|
||||
|
||||
<path android:fillColor="#fff" android:pathData="M242.39,41.38m-20.27,0a20.27,20.27 0,1 1,40.54 0a20.27,20.27 0,1 1,-40.54 0" android:strokeColor="#231f20" android:strokeWidth="1"/>
|
||||
|
||||
<path android:fillColor="#00000000" android:pathData="M33.55,41.38h174.32H33.55Z" android:strokeColor="#231f20" android:strokeWidth="1"/>
|
||||
|
||||
</vector>
|
@ -0,0 +1,285 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".BoatRampActivity">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/rampName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:text="Ramp Name"
|
||||
android:textColor="@color/textColor"
|
||||
android:textSize="34sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/rampImage"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="270dp"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:scaleType="fitXY"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/rampName"
|
||||
app:srcCompat="@mipmap/ic_launcher" />
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/rampImage">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/statusCard"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginBottom="10dp"
|
||||
app:cardBackgroundColor="#E9E9E9"
|
||||
app:cardCornerRadius="3000dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="60dp"
|
||||
android:background="#00FFFFFF"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<HorizontalScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginLeft="3dp"
|
||||
android:layout_marginRight="3dp"
|
||||
android:layout_weight="1"
|
||||
android:background="#00FFFFFF"
|
||||
android:fillViewport="true">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/statusText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#00FFFFFF"
|
||||
android:gravity="center"
|
||||
android:text="Unknown"
|
||||
android:textColor="@color/rampStatusText"
|
||||
android:textSize="24sp" />
|
||||
</HorizontalScrollView>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/statusIcon"
|
||||
android:layout_width="120dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="end"
|
||||
android:layout_weight="1"
|
||||
android:background="#00FFFFFF"
|
||||
app:srcCompat="@drawable/circle_guess" />
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/openTimes"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginBottom="10dp"
|
||||
app:cardCornerRadius="3000dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="60dp"
|
||||
android:background="#00FFFFFF"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<HorizontalScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginLeft="3dp"
|
||||
android:layout_marginRight="3dp"
|
||||
android:layout_weight="1"
|
||||
android:background="#00FFFFFF"
|
||||
android:fillViewport="true">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/openTimesText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:background="#00FFFFFF"
|
||||
android:gravity="center"
|
||||
android:text="Unknown"
|
||||
android:textAlignment="gravity"
|
||||
android:textColor="@color/rampStatusText"
|
||||
android:textSize="24sp" />
|
||||
</HorizontalScrollView>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="120dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="end"
|
||||
android:layout_weight="1"
|
||||
android:background="#00FFFFFF"
|
||||
app:srcCompat="@drawable/circle_hour_glass" />
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/address"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginBottom="10dp"
|
||||
app:cardCornerRadius="3000dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="60dp"
|
||||
android:background="#00FFFFFF"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<HorizontalScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginLeft="3dp"
|
||||
android:layout_marginRight="3dp"
|
||||
android:layout_weight="1"
|
||||
android:background="#00FFFFFF"
|
||||
android:fillViewport="true">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/addressText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:background="#00FFFFFF"
|
||||
android:gravity="center"
|
||||
android:text="Unknown"
|
||||
android:textAlignment="gravity"
|
||||
android:textColor="@color/rampStatusText"
|
||||
android:textSize="24sp" />
|
||||
</HorizontalScrollView>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="120dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="end"
|
||||
android:layout_weight="1"
|
||||
android:background="#00FFFFFF"
|
||||
app:srcCompat="@drawable/circle_copy" />
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/operator"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginBottom="10dp"
|
||||
app:cardCornerRadius="3000dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="60dp"
|
||||
android:background="#00FFFFFF"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<HorizontalScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginLeft="3dp"
|
||||
android:layout_marginRight="3dp"
|
||||
android:layout_weight="1"
|
||||
android:background="#00FFFFFF"
|
||||
android:fillViewport="true">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/operatorText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:background="#00FFFFFF"
|
||||
android:gravity="center"
|
||||
android:text="Unknown"
|
||||
android:textAlignment="gravity"
|
||||
android:textColor="@color/rampStatusText"
|
||||
android:textSize="24sp" />
|
||||
</HorizontalScrollView>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="120dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="end"
|
||||
android:layout_weight="1"
|
||||
android:background="#00FFFFFF"
|
||||
app:srcCompat="@drawable/circle_ops" />
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/lastUpdated"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginBottom="10dp"
|
||||
app:cardCornerRadius="3000dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="60dp"
|
||||
android:background="#00FFFFFF"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<HorizontalScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginLeft="3dp"
|
||||
android:layout_marginRight="3dp"
|
||||
android:layout_weight="1"
|
||||
android:background="#00FFFFFF"
|
||||
android:fillViewport="true">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/lastUpdatedText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:background="#00FFFFFF"
|
||||
android:gravity="center"
|
||||
android:text="Unknown"
|
||||
android:textAlignment="gravity"
|
||||
android:textColor="@color/rampStatusText"
|
||||
android:textSize="24sp" />
|
||||
</HorizontalScrollView>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="120dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="end"
|
||||
android:layout_weight="1"
|
||||
android:background="#00FFFFFF"
|
||||
app:srcCompat="@drawable/calendar" />
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -0,0 +1,264 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:outlineProvider="none"
|
||||
tools:context=".MainActivity">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:text="LAKEWATCH"
|
||||
android:textSize="34sp"
|
||||
android:textStyle="bold|italic"
|
||||
android:textColor="@color/textColor"
|
||||
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.497"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.125" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:text="CANYON LAKE"
|
||||
android:textSize="34sp"
|
||||
android:textColor="@color/textColor"
|
||||
android:textStyle="bold|italic"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.177" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView"
|
||||
android:layout_width="45sp"
|
||||
android:layout_height="48sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.136"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.281"
|
||||
app:srcCompat="@drawable/icon_fa_solid_tree" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView2"
|
||||
android:layout_width="45sp"
|
||||
android:layout_height="48sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.89"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.281"
|
||||
app:srcCompat="@drawable/icon_fa_solid_tree" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView3"
|
||||
android:layout_width="400sp"
|
||||
android:layout_height="wrap_content"
|
||||
android:scaleY="2"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.454"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.318"
|
||||
app:srcCompat="@drawable/line_1" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView4"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:scaleX="1.1"
|
||||
android:scaleY="1.1"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.49"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.981"
|
||||
app:srcCompat="@drawable/icon_material_sharp_water" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView5"
|
||||
android:layout_width="40sp"
|
||||
android:layout_height="45sp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/imageView4"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.706"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.023"
|
||||
app:srcCompat="@drawable/icon_akar_cloud" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView6"
|
||||
android:layout_width="40sp"
|
||||
android:layout_height="45sp"
|
||||
android:layout_marginBottom="220sp"
|
||||
android:rotationY="177"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/imageView4"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.148"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.069"
|
||||
app:srcCompat="@drawable/icon_akar_cloud" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView7"
|
||||
android:layout_width="35sp"
|
||||
android:layout_height="45sp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/imageView4"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.252"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.018"
|
||||
app:srcCompat="@drawable/icon_weather_cloudy" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView8"
|
||||
android:layout_width="35sp"
|
||||
android:layout_height="45sp"
|
||||
android:rotationY="182"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/imageView4"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.617"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.051"
|
||||
app:srcCompat="@drawable/icon_weather_cloudy" />
|
||||
|
||||
<Button
|
||||
style="@style/BaseButtonStyle"
|
||||
android:id="@+id/button"
|
||||
android:layout_width="200sp"
|
||||
android:layout_height="90sp"
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:text="WEATHER"
|
||||
android:textSize="25sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/imageView4"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.483"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.441"
|
||||
app:strokeColor="#000000"
|
||||
app:strokeWidth="7sp" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/button2"
|
||||
android:layout_width="200sp"
|
||||
android:layout_height="90sp"
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:text="WATER STATUS"
|
||||
android:textSize="25sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="@+id/button"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toStartOf="@+id/button"
|
||||
app:layout_constraintTop_toBottomOf="@+id/button"
|
||||
app:layout_constraintVertical_bias="0.019"
|
||||
app:strokeColor="#000000"
|
||||
app:strokeWidth="7sp" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/button3"
|
||||
android:layout_width="200sp"
|
||||
android:layout_height="90sp"
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:text="BOAT RAMPS"
|
||||
android:textSize="25sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/imageView4"
|
||||
app:layout_constraintEnd_toEndOf="@+id/button2"
|
||||
app:layout_constraintHorizontal_bias="1.0"
|
||||
app:layout_constraintStart_toStartOf="@+id/button2"
|
||||
app:layout_constraintTop_toBottomOf="@+id/button2"
|
||||
app:layout_constraintVertical_bias="0.032"
|
||||
app:strokeColor="#000000"
|
||||
app:strokeWidth="7sp" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/settingsButton"
|
||||
android:layout_width="200sp"
|
||||
android:layout_height="90sp"
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:text="SETTINGS"
|
||||
android:textSize="25sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/imageView4"
|
||||
app:layout_constraintEnd_toEndOf="@+id/button3"
|
||||
app:layout_constraintHorizontal_bias="1.0"
|
||||
app:layout_constraintStart_toStartOf="@+id/button3"
|
||||
app:layout_constraintTop_toBottomOf="@+id/button3"
|
||||
app:layout_constraintVertical_bias="0.049"
|
||||
app:strokeColor="#000000"
|
||||
app:strokeWidth="7sp" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView9"
|
||||
android:layout_width="75sp"
|
||||
android:layout_height="100sp"
|
||||
android:layout_marginTop="540dp"
|
||||
android:rotationY="177"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/button3"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.235"
|
||||
app:layout_constraintStart_toEndOf="@+id/button3"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="1.0"
|
||||
app:srcCompat="@drawable/boat_ramp_svgrepo_com" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView11"
|
||||
android:layout_width="75sp"
|
||||
android:layout_height="100sp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/button"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.922"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="1.0"
|
||||
app:srcCompat="@drawable/weather_symbol_4_svgrepo_com" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView12"
|
||||
android:layout_width="75sp"
|
||||
android:layout_height="100sp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/button2"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.922"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/imageView11"
|
||||
app:layout_constraintVertical_bias="0.0"
|
||||
app:srcCompat="@drawable/water_drop_svgrepo_com" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView14"
|
||||
android:layout_width="75sp"
|
||||
android:layout_height="100sp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/settingsButton"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.235"
|
||||
app:layout_constraintStart_toEndOf="@+id/settingsButton"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="1.0"
|
||||
app:srcCompat="@drawable/settings_2_svgrepo_com" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".MainRampMenuActivity">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/rampsOpen"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:text="\?/\? Ramps Open"
|
||||
android:textColor="@color/textColor"
|
||||
android:textSize="34sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ScrollView
|
||||
android:id="@+id/rampListingScrollView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/rampsOpen"
|
||||
app:layout_constraintVertical_bias="0.0">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/rampListing"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical" />
|
||||
</ScrollView>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -0,0 +1,194 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".SettingsActivity">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView3"
|
||||
android:layout_width="400sp"
|
||||
android:layout_height="wrap_content"
|
||||
android:scaleY="2"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.454"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.318"
|
||||
app:srcCompat="@drawable/line_1" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView"
|
||||
android:layout_width="45sp"
|
||||
android:layout_height="48sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.136"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.281"
|
||||
app:srcCompat="@drawable/icon_fa_solid_tree" />
|
||||
<ImageView
|
||||
android:id="@+id/imageView2"
|
||||
android:layout_width="45sp"
|
||||
android:layout_height="48sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.89"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.281"
|
||||
app:srcCompat="@drawable/icon_fa_solid_tree" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView6"
|
||||
android:layout_width="40sp"
|
||||
android:layout_height="45sp"
|
||||
android:layout_marginTop="52dp"
|
||||
android:rotationY="177"
|
||||
app:layout_constraintBottom_toTopOf="@+id/imageView"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.11"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.0"
|
||||
app:srcCompat="@drawable/icon_akar_cloud" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView7"
|
||||
android:layout_width="35sp"
|
||||
android:layout_height="45sp"
|
||||
android:layout_marginTop="16dp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/imageView6"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.252"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.0"
|
||||
app:srcCompat="@drawable/icon_weather_cloudy" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView8"
|
||||
android:layout_width="35sp"
|
||||
android:layout_height="45sp"
|
||||
android:rotationY="182"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/imageView6"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.651"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/imageView5"
|
||||
app:layout_constraintVertical_bias="1.0"
|
||||
app:srcCompat="@drawable/icon_weather_cloudy" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView5"
|
||||
android:layout_width="40sp"
|
||||
android:layout_height="45sp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/imageView2"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.77"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.08"
|
||||
app:srcCompat="@drawable/icon_akar_cloud" />
|
||||
|
||||
<Button
|
||||
style="@style/BaseButtonStyle"
|
||||
android:id="@+id/button5"
|
||||
android:layout_width="200sp"
|
||||
android:layout_height="90sp"
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:text="DARK MODE"
|
||||
android:textSize="25sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.497"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/imageView3"
|
||||
app:layout_constraintVertical_bias="0.084"
|
||||
app:strokeColor="#000000"
|
||||
app:strokeWidth="7sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:text="SETTINGS"
|
||||
android:textSize="40sp"
|
||||
android:textStyle="bold|italic"
|
||||
android:textColor="@color/textColor"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.422"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.155" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView14"
|
||||
android:layout_width="75sp"
|
||||
android:layout_height="100sp"
|
||||
android:layout_marginTop="112dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/imageView2"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.88"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.0"
|
||||
app:srcCompat="@drawable/settings_2_svgrepo_com" />
|
||||
|
||||
<Button
|
||||
style="@style/BaseButtonStyle"
|
||||
android:id="@+id/button6"
|
||||
android:layout_width="200sp"
|
||||
android:layout_height="90sp"
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:text="FORCE DATA FETCH"
|
||||
android:textSize="25sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.497"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/button5"
|
||||
app:layout_constraintVertical_bias="0.047"
|
||||
app:strokeColor="#000000"
|
||||
app:strokeWidth="7sp" />
|
||||
|
||||
<Button
|
||||
style="@style/BaseButtonStyle"
|
||||
android:id="@+id/button7"
|
||||
android:layout_width="200sp"
|
||||
android:layout_height="90sp"
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:text="CLEAR CACHE"
|
||||
android:textSize="25sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.497"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/button6"
|
||||
app:layout_constraintVertical_bias="0.065"
|
||||
app:strokeColor="#000000"
|
||||
app:strokeWidth="7sp" />
|
||||
<ImageView
|
||||
android:id="@+id/imageView4"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:scaleX="1.1"
|
||||
android:scaleY="1.1"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.49"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.981"
|
||||
app:srcCompat="@drawable/icon_material_sharp_water" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -0,0 +1,184 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".WaterLevelActivity">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView10"
|
||||
android:layout_width="376dp"
|
||||
android:layout_height="251dp"
|
||||
android:layout_marginStart="21dp"
|
||||
android:layout_marginTop="341dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="182dp"
|
||||
android:background="#FFFFFF"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="1.0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.726"
|
||||
app:srcCompat="@drawable/waterlevelgraph" />
|
||||
|
||||
<TableLayout
|
||||
android:id="@+id/tableLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:background="@android:color/transparent"
|
||||
android:padding="1dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent">
|
||||
|
||||
<TableRow>
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/table_border_with_lines"
|
||||
android:padding="8dp"
|
||||
android:text="Percent Full"
|
||||
android:textColor="@color/textColor" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/table_border_with_lines"
|
||||
android:padding="8dp"
|
||||
android:textColor="@color/textColor" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow>
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/table_border_with_lines"
|
||||
android:padding="8dp"
|
||||
android:text="Mean Water Level (ft)"
|
||||
android:textColor="@color/textColor" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/table_border_with_lines"
|
||||
android:padding="8dp"
|
||||
android:textColor="@color/textColor" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow>
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/table_border_with_lines"
|
||||
android:padding="8dp"
|
||||
android:text="Surface Area (acres)"
|
||||
android:textColor="@color/textColor" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/table_border_with_lines"
|
||||
android:padding="8dp"
|
||||
android:textColor="@color/textColor" />
|
||||
</TableRow>
|
||||
|
||||
</TableLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/your_text_view_id"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="92dp"
|
||||
android:layout_marginTop="300dp"
|
||||
android:layout_marginEnd="93dp"
|
||||
android:layout_marginBottom="399dp"
|
||||
android:text="Historical Water Data"
|
||||
android:textColor="@color/textColor"
|
||||
android:textSize="24sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/cardView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
app:cardCornerRadius="16dp"
|
||||
app:cardElevation="4dp"
|
||||
app:cardBackgroundColor="@android:color/white"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp"
|
||||
android:gravity="center">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/waterLevelTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="WATER LEVEL"
|
||||
android:textSize="20sp"
|
||||
android:textColor="@color/textColor"
|
||||
android:textStyle="bold"
|
||||
android:gravity="center"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/waterLevelValue"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="36sp"
|
||||
android:textColor="@color/textColor"
|
||||
android:gravity="center"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/feetMsl"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Feet MSL"
|
||||
android:textSize="16sp"
|
||||
android:textColor="@color/textColor"
|
||||
android:gravity="center"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dateTime"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="16sp"
|
||||
android:textColor="@color/textColor"
|
||||
android:gravity="center"
|
||||
android:layout_marginTop="16dp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/levelStatus"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="16sp"
|
||||
android:textColor="@color/textColor"
|
||||
android:gravity="center"
|
||||
android:layout_marginTop="16dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -0,0 +1,175 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".WeatherActivity">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/day_svg"
|
||||
android:layout_width="409dp"
|
||||
android:layout_height="212dp"
|
||||
android:src="@drawable/day"
|
||||
app:layout_constraintBottom_toTopOf="@id/weather_description"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.723" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/weather_description"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:textColor="@color/textColor"
|
||||
android:textSize="24sp"
|
||||
app:layout_constraintBottom_toTopOf="@id/temperature"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.497"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/temperature"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="44dp"
|
||||
android:textColor="@color/textColor"
|
||||
android:textSize="24sp"
|
||||
app:layout_constraintBottom_toTopOf="@id/wind_svg"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.498"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/wind_svg"
|
||||
android:layout_width="332dp"
|
||||
android:layout_height="110dp"
|
||||
android:src="@drawable/wind_svg"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.504"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.615" />
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView1"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="@android:color/transparent"
|
||||
android:text="WIND"
|
||||
android:textColor="#000000"
|
||||
android:textSize="10dp"
|
||||
app:layout_constraintStart_toStartOf="@id/wind_svg"
|
||||
app:layout_constraintTop_toTopOf="@id/wind_svg" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/wind_direction"
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="216dp"
|
||||
android:background="@android:color/transparent"
|
||||
android:gravity="center"
|
||||
android:textColor="#000000"
|
||||
app:layout_constraintBottom_toBottomOf="@id/wind_svg"
|
||||
app:layout_constraintLeft_toLeftOf="@id/wind_svg"
|
||||
app:layout_constraintRight_toRightOf="@id/wind_svg"
|
||||
app:layout_constraintStart_toStartOf="@id/wind_svg"
|
||||
app:layout_constraintTop_toTopOf="@id/wind_svg"
|
||||
app:layout_constraintVertical_bias="0.494" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/wind_speed"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="44dp"
|
||||
android:layout_marginTop="36dp"
|
||||
android:textColor="#000000"
|
||||
android:background="@android:color/transparent"
|
||||
app:layout_constraintStart_toStartOf="@id/wind_svg"
|
||||
app:layout_constraintTop_toTopOf="@id/wind_svg" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/wind_gust"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="44dp"
|
||||
android:layout_marginBottom="36dp"
|
||||
android:background="@android:color/transparent"
|
||||
android:textColor="#000000"
|
||||
app:layout_constraintBottom_toBottomOf="@id/wind_svg"
|
||||
app:layout_constraintStart_toStartOf="@id/wind_svg" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/precipitation_svg"
|
||||
android:layout_width="334dp"
|
||||
android:layout_height="119dp"
|
||||
android:src="@drawable/precipitation_svg"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.493"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/wind_svg"
|
||||
app:layout_constraintVertical_bias="0.104" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="55dp"
|
||||
android:layout_height="49dp"
|
||||
android:layout_marginStart="240dp"
|
||||
android:layout_marginTop="36dp"
|
||||
android:background="@android:color/transparent"
|
||||
android:src="@drawable/reshot_icon_weather_yrq52ezutw"
|
||||
app:layout_constraintStart_toStartOf="@id/precipitation_svg"
|
||||
app:layout_constraintTop_toTopOf="@id/precipitation_svg" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/chance_precipitation"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="36dp"
|
||||
android:layout_marginTop="40dp"
|
||||
android:background="@android:color/transparent"
|
||||
android:textColor="#000000"
|
||||
app:layout_constraintStart_toStartOf="@id/precipitation_svg"
|
||||
app:layout_constraintTop_toTopOf="@id/precipitation_svg" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/amount_precipitation"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="36dp"
|
||||
android:layout_marginBottom="40dp"
|
||||
android:background="@android:color/transparent"
|
||||
android:textColor="#000000"
|
||||
app:layout_constraintBottom_toBottomOf="@id/precipitation_svg"
|
||||
app:layout_constraintStart_toStartOf="@id/precipitation_svg" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView5"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginTop="20dp"
|
||||
android:background="@android:color/transparent"
|
||||
android:text="PRECIPITATION"
|
||||
android:textSize="10dp"
|
||||
android:textColor="#000000"
|
||||
app:layout_constraintStart_toStartOf="@id/precipitation_svg"
|
||||
app:layout_constraintTop_toTopOf="@id/precipitation_svg" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/weather_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="weather.gov"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.498"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/precipitation_svg" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -0,0 +1,96 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#00FFFFFF"
|
||||
android:backgroundTint="#949494"
|
||||
android:padding="16dp">
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/rampMenuCard"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:cardBackgroundColor="#E9E9E9"
|
||||
app:cardCornerRadius="30dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:background="#00FFFFFF"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/statusIcon"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="150dp"
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:background="#00FFFFFF"
|
||||
app:srcCompat="@drawable/circle_guess" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:background="#00FFFFFF"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#00FFFFFF"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/rampName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:layout_weight="1"
|
||||
android:background="#00FFFFFF"
|
||||
android:text="Unknown Ramp"
|
||||
android:textAlignment="center"
|
||||
android:textColor="#7F7F7F"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:id="@+id/divider2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="2dp"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:background="#7F7F7F" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#00FFFFFF"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/rampStatus"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_weight="1"
|
||||
android:background="#00FFFFFF"
|
||||
android:text="Unknown Status"
|
||||
android:textAlignment="center"
|
||||
android:textColor="#7F7F7F"
|
||||
android:textSize="24sp" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 982 B |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 5.8 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 7.6 KiB |
@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorPrimary">#BB86FC</color>
|
||||
<color name="colorPrimaryDark">#3700B3</color>
|
||||
<color name="colorAccent">#03DAC5</color>
|
||||
<color name="backgroundColor">#08446F</color>
|
||||
<color name="textColor">#FFFFFFFF</color>
|
||||
|
||||
<color name="buttonBackgroundColor">#9DCFED</color>
|
||||
<color name="rampInfoText">#515151</color>
|
||||
<color name="rampInfoBackground">#DAF0FD</color>
|
||||
<color name="buttonTextColor">#FFFFFFFF</color>
|
||||
<color name="rampStatusOpen">#C9FFD9</color>
|
||||
<color name="rampStatusClosed">#FFCBC9</color>
|
||||
<color name="rampStatusUnknown">#E9E9E9</color>
|
||||
</resources>
|
@ -0,0 +1,13 @@
|
||||
<resources>
|
||||
<style name="Theme.LakeWatch" parent="Theme.AppCompat.DayNight.NoActionBar">
|
||||
<!-- Other theme attributes -->
|
||||
<item name="android:background">@color/backgroundColor</item> <!-- Set background color here -->
|
||||
<item name="buttonStyle">@style/BaseButtonStyle</item>
|
||||
|
||||
</style>
|
||||
|
||||
<style name="BaseButtonStyle" parent="Widget.AppCompat.Button">
|
||||
<item name="android:backgroundTint">@color/buttonBackgroundColor</item>
|
||||
<item name="android:textColor">@color/buttonTextColor</item>
|
||||
</style>
|
||||
</resources>
|
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorPrimary" >#6200EE</color>
|
||||
<color name="colorPrimaryDark">#3700B3</color>
|
||||
<color name="colorAccent">#03DAC5</color>
|
||||
|
||||
<!-- Normal/Light mode -->
|
||||
<color name="backgroundColor">#ECF7FF</color>
|
||||
<color name="textColor">#000000</color>
|
||||
<color name="buttonBackgroundColor">#FFFFFF</color>
|
||||
<color name="buttonTextColor">#000000</color>
|
||||
<color name="rampInfoBackground">#FFFFFF</color>
|
||||
<color name="rampStatusText">#8D8D8D</color>
|
||||
<color name="rampInfoText">#8D8D8D</color>
|
||||
<color name="rampStatusOpen">#C9FFD9</color>
|
||||
<color name="rampStatusClosed">#FFCBC9</color>
|
||||
<color name="rampStatusUnknown">#E9E9E9</color>
|
||||
</resources>
|
@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<string name="app_name">LakeWatch</string>
|
||||
</resources>
|
@ -0,0 +1,12 @@
|
||||
<resources>
|
||||
<style name="Theme.LakeWatch" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
<!-- Other theme attributes -->
|
||||
<item name="android:background">@color/backgroundColor</item> <!-- Set background color here -->
|
||||
<item name="buttonStyle">@style/BaseButtonStyle</item>
|
||||
</style>
|
||||
|
||||
<style name="BaseButtonStyle" parent="Widget.AppCompat.Button">
|
||||
<item name="android:backgroundTint">@color/buttonBackgroundColor</item>
|
||||
<item name="android:textColor">@color/buttonTextColor</item>
|
||||
</style>
|
||||
</resources>
|
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Sample backup rules file; uncomment and customize as necessary.
|
||||
See https://developer.android.com/guide/topics/data/autobackup
|
||||
for details.
|
||||
Note: This file is ignored for devices older that API 31
|
||||
See https://developer.android.com/about/versions/12/backup-restore
|
||||
-->
|
||||
<full-backup-content>
|
||||
<!--
|
||||
<include domain="sharedpref" path="."/>
|
||||
<exclude domain="sharedpref" path="device.xml"/>
|
||||
-->
|
||||
</full-backup-content>
|
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Sample data extraction rules file; uncomment and customize as necessary.
|
||||
See https://developer.android.com/about/versions/12/backup-restore#xml-changes
|
||||
for details.
|
||||
-->
|
||||
<data-extraction-rules>
|
||||
<cloud-backup>
|
||||
<!-- TODO: Use <include> and <exclude> to control what is backed up.
|
||||
<include .../>
|
||||
<exclude .../>
|
||||
-->
|
||||
</cloud-backup>
|
||||
<!--
|
||||
<device-transfer>
|
||||
<include .../>
|
||||
<exclude .../>
|
||||
</device-transfer>
|
||||
-->
|
||||
</data-extraction-rules>
|
@ -0,0 +1,17 @@
|
||||
package edu.utsa.cs3443.lakewatch;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
5
Summer-2024/CS-3443/LakeWatch/LakeWatch/build.gradle.kts
Normal file
@ -0,0 +1,5 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
plugins {
|
||||
alias(libs.plugins.android.application) apply false
|
||||
|
||||
}
|
@ -0,0 +1,145 @@
|
||||
* Lake Watch Architecture
|
||||
|
||||
** Classes
|
||||
|
||||
*** MainMenuActivity
|
||||
|
||||
- Responsible for launching all other activities (the following):
|
||||
- ~BoatRampMenuActivity~
|
||||
- ~WeatherActivity~
|
||||
- ~SettingsActivity~
|
||||
- ~WaterLevelActivity~
|
||||
- Primary entrypoint for the application
|
||||
|
||||
*** SettingsActivity
|
||||
- Responsible for the following:
|
||||
- Handling dark mode and light mode toggling
|
||||
- Forcing a data fetch
|
||||
- Forcefully clearing the cache
|
||||
- Data fetching
|
||||
- Our app will be talking to external data sources beyond local files on the system
|
||||
- Due to this external data sourcing, the app may end up out of synchronization with those sources and thus we want to allow a given user to force an update if it ever becomes necessary. It's a escape hatch of sorts.
|
||||
- Cache clearing
|
||||
- To avoid overloading those external data sources we're going to maintain a cache on the local device that will be preferenced for data fetching under certain criteria (such as difference in dates, a generated hash for a given data set, and potentially more).
|
||||
- As such, it would be useful if anything ever ends up in a bad state to forcefully clear out this cache to allow data to be reindexed.
|
||||
*** Settings Data
|
||||
|
||||
- Handle the logic behind the scenes for the ~SettingsActivity~ class. It's responsible for actually driving the data behind the view, like the data fetching and caching.
|
||||
- Likewise, it's also responsible for setting the global lightmode and darkmode for the entire application.
|
||||
|
||||
*** WeatherActivity
|
||||
|
||||
- Controls general weather information local to Canyon Lake. That information includes:
|
||||
- High and Low temperature
|
||||
- Wind speed and direction
|
||||
- Precipitation chances along with the amount of said precipitation
|
||||
- A short weather description
|
||||
- This view will also include a link to [[https://weather.gov]] for Canyon Lake which may include further information.
|
||||
|
||||
*** WeatherData
|
||||
|
||||
- This is the Model for the ~WeatherActivity~ view.
|
||||
- Responsible for fetching the local weather conditions at Canyon Lake.
|
||||
|
||||
*** MainRampMenuActivity
|
||||
|
||||
- Responsible for displaying a vertically scrollable view of all simple boat ramp details, those details include:
|
||||
- The name of the ramp
|
||||
- The status of the ramp, is it open, closed, or is its status unknown?
|
||||
- Color coding based on the status
|
||||
- Also responsible for launching each individual ramp subview with more details on a given ramp when the ramp is tapped/clicked on
|
||||
- Contains a list of ~BoatRampData~ objects to pull said data from
|
||||
|
||||
*** BoatRampActivity
|
||||
- Responsible for actually displaying the long information on a given boat ramp, that information includes:
|
||||
- The boat ramp name, which may be different from the address
|
||||
- The boat ramp status, open/closed/unknown
|
||||
- The boat ramp opening times (the officially stated times when the ramp is open if its status shows it as open)
|
||||
- The address, that has an included copy button to make it easy to paste into a GPS app
|
||||
- The operator of the ramp, who is actually in charge of it
|
||||
- The last time the information on the ramp was updated
|
||||
- Useful for determining if the ramp status may be incorrect
|
||||
- An image of the boat ramp
|
||||
|
||||
*** BoatRampData
|
||||
- The Model class responsible for handling data interactions for both the ~MainRampMenuActivity~ and the ~BoatRampActivity~
|
||||
- It pulls data from various data sources that are all kludged together or, unfortunately, sometimes /manually/ gathered for display in the view classes
|
||||
- The ~BoatRampData~ is intended to be used as a collection, thus the ~loadBoatRampData~ returns an ~ArrayList~ of the data so the ~MainRampMenuActivity~ can easily use it
|
||||
|
||||
**** BoatRampStatus
|
||||
- Contained within the ~BoatRampData~ class
|
||||
- It's an Enumeration for determining the various states of the ramp
|
||||
- Since a given ramp can have more than two states (open/closed/unknown), it had to be an Enumeration
|
||||
|
||||
*** WaterLevelActivity
|
||||
- Responsible for displaying information on Canyon Lake's current water level and other data; furthermore, it will show historical water level and other data as well as showing a water level graph for the past three years
|
||||
- Its information includes:
|
||||
- How full the lake is as a percentage and in feet
|
||||
- The surface area covered by the lake in acres of the total lake
|
||||
- How many feet below (or above) the full pool the current water level is
|
||||
- The last time the information was updated
|
||||
|
||||
*** WaterLevelData
|
||||
|
||||
- Contains the following information:
|
||||
- ~date~: the date for the given water data record
|
||||
- ~waterLevel~: the level of the water for the given record as feet above the vertical datum
|
||||
- ~surfaceArea~: the acres covered by the lake surface
|
||||
- ~reservoirStorage~: actual storage at measured lake elevation
|
||||
- ~conservationStorage~: reservoir storage - dead pool capacity (note: conservation storage is capped at conservation capacity)
|
||||
- ~percentFull~: 100 * conservation storage/conservation capacity
|
||||
- ~conservationCapacity~: storage at conservation pool elevation - dead pool capacity
|
||||
- ~deadPoolCapacity~: storage at dead pool elevation
|
||||
- ~WaterLevelData~ is intended to be used as a collection of objects because each object represents a single date's information, as such the ~loadWaterLevelData~ method returns an ~ArrayList~ of ~WaterLevelData~ with the newest data being the first object in the list.
|
||||
- Some of the data may not be used, but it's /much/ faster and easier to load the full CSV without cutting out some columns of data, thus that's what we do to populate it
|
||||
|
||||
** Data Files
|
||||
|
||||
*** ~weather-gov-forcast.json~
|
||||
|
||||
This file was pulled from [[https://api.weather.gov/gridpoints/EWX/136,74/forecast]].
|
||||
|
||||
It contains all the relevant weather data for Canyon Lake. And yes, before you ask, EWX, 136,74 is the correct station for Canyon Lake.
|
||||
|
||||
We intend to hit that API as needed for the data, but will include a preloaded set as well in case something goes wrong with that endpoint.
|
||||
|
||||
*** ~waterdata.csv~
|
||||
|
||||
Please see the file, the entire top of that CSV includes comments as to its exact functionality and numbers as provided to us from following URL: [[https://www.waterdatafortexas.org/reservoirs/individual/canyon.csv]].
|
||||
|
||||
To summarize the data is in the following format:
|
||||
| date | water_level | surface_area | reservoir_storage | conservation_storage | percent_full | conservation_capacity | dead_pool_capacity |
|
||||
|------------|-------------|--------------|-------------------|----------------------|--------------|-----------------------|--------------------|
|
||||
| 2024-06-19 | 885.41 | 5689.16 | 215710 | 215639 | 56.9 | 378781 | 71 |
|
||||
|
||||
All data is using feet or acres as relevant.
|
||||
|
||||
*** ~Canyon-Lake-WaterData-Graph.png~
|
||||
|
||||
That PNG is an example of the actual graph we intend to show in the app, it will serve as a fallback in case data can't be pulled for any reason such that the user isn't left with nothing on screen.
|
||||
|
||||
It contains the water level per month for the last three years plotted out based on the data in the ~waterdata.csv~ file.
|
||||
|
||||
*** BOAT RAMP DATA FROM HELL
|
||||
|
||||
We are breaking the boat ramp data down into its own section because of how ad-hoc the data consistency and availability is.
|
||||
|
||||
Some operators, like Comal County, are angels and have the data in a simple JSON serving API that we can hit and get up to date statuses for.
|
||||
|
||||
Another operator who at least posts data, is the United States Army Core of Engineers. It's great that they do post data, but! They post their data in a manually updated HTML page — so we get to go through the /fun/, /_fun_/ experience of doing data scrapping against those pages to get up to date information.
|
||||
|
||||
The worst operators, like WORD (yes, that's an operator), told me (Price Hiller) to effectively pound sand after waiting on hold for the better part of two hours. So for the WORD ramps, and others like them the data status is in god's hands.
|
||||
|
||||
For the record, we only have up to date data we can pull on the ramps for approximately 14 ramps give or take a few. There's 23 total. A lot of the data will be manually entered for the purposes of this section based on news articles and hearsay.
|
||||
|
||||
Do take note that the ~Canyon-Lake-USACE-...~ directories are included for the relevant HTML files and are not going to be discussed below as during our scrape those directories won't exist, our scraper will see the full webpage.
|
||||
|
||||
**** ~Comal-County-Ramp-Info.json~
|
||||
Comal County posts their ramp information off of their GIS system for the public to use. Under the ~features~ key within the JSON, they have per ramp statuses which we will be leveraging for their ramp information.
|
||||
|
||||
**** ~Canyon-Lake-USACE-Operators.html~
|
||||
|
||||
This file includes *all* of the operators of every Canyon Lake boat ramp. This makes it easy on us to assign names to ramps, and determine who actually owns the ramps.
|
||||
|
||||
**** ~Canyon-Lake-USACE.html~
|
||||
This file has all the United States Army Corps of Engineer's data on their ramps. That data has the names of the ramps and whether they are closed or not and (sometimes) operating hours.
|