Merge pull request #3 from Tucan444/Development

First working version of WikiSpot Server and app
This commit is contained in:
Untriex Programming 2021-05-09 12:05:55 +02:00 committed by GitHub
commit 048342375e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
169 changed files with 10061 additions and 679 deletions

174
README.md

@ -1,21 +1,169 @@
# Mabasej_Team # Mabasej_Team
We are working on system, that will help tourists in cities to get information about city more easily. We are working on system, that will help tourists in cities to get information about city more easily.
# Hardware ## Hardware
- work in progress (but probabbly we will use rpi with external antena for wifi) - Raspberry PI (for now tested only on rpi4. Works on rpi zero too, but it will be slow if more devices are connected)
- External/Internal WiFi antena
# Software ## Software
- python 3.9.2 compatible server with basic web interface - python 3.9.2 compatible server with basic web interface
- python 3.x based mobile app with help of android studio - Kotlin based mobile app
# Server ## Install
To run server you need to install Wikispot is in testing stages, but it is possible to install it using our .img file (link coming soon) based on DietPi or custom script.
- hypercorn - "pip install hypercorn"
- fastapi - "pip install fastapi"
- requests - "pip install requests"
- aiofiles - "pip install aiofiles"
then run by command - "hypercorn main:app --bind <IP:port>" | Device | Server compatible | Instalation |
To connect to another rpi you need to edit settings.json with different ID and fill heartbeat table. | :-------------------- | :------------------------------------------------------------------------------------------ | :-----------: |
| Ubuntu (I7, 16GB ram) | :heavy_check_mark: WORKING (Only server) | Manual/script |
| RPI 4b (2GB) | :heavy_check_mark: WORKING | .img/script |
| RPI 400 (4GB) | :grey_question: Untested. Should work. | .img/script |
| RPI 3b+ | :grey_question: Untested. Should work. | .img/script |
| RPI zero w | :white_check_mark: Working with fewer devices (Only server. No AP, Computer vision) | .img/script |
| RPI 2 | :question: Untested. | :x: |
| RPI | :question: Untested. | :x: |
This is not finished product
### Fresh istall (.img) Only RPI
login credentials
> login: dietpi
> password: WikiSpot2021
requirements:
1. WikiSpot image file (download: *soon*)
2. MicroSd card (recommended: >=16GB, :exclamation: ALL DATA STORED ON SD CARD WILL BE FORMATED :exclamation:)
3. BalenaEtcher (or another sd card flasher) *link:* https://www.balena.io/etcher/
4. SD card reader
Install:
1. Download all required files (wikispot.img and balenaetcher) and install BalenaEtcher
2. Insert SD card into computer/reader, open BalenaEtcher -> chose Flash from file -> chose downloaded wikispot.img -> Select your sd in *Select target* -> Flash!
3. :exclamation: WINDOWS will show unformated drive. Cancel it. It is because of uncompatible format for windows :exclamation:
4. After flashing open partition *boot* (should apear as USB), find file *dietpi.txt* and open it in text editor.
- Accept license by changing `AUTO_SETUP_ACCEPT_LICENSE=0` to `AUTO_SETUP_ACCEPT_LICENSE=1`
- Change name of WikiSpot `AUTO_SETUP_NET_HOSTNAME=WikiSpot-CHANGE_ME` by changing only *CHANGE_ME* or leave *CHANGE_ME* for random number name *WikiSpot-54346
- You can set static ip address by changing `AUTO_SETUP_NET_USESTATIC=0` to `AUTO_SETUP_NET_USESTATIC=1` And entering your setting into required lines.
- If you want to use computer vision plugin with rpi camera set `ENABLE_COMPUTER_VISION_PLUGIN=0` to `ENABLE_COMPUTER_VISION_PLUGIN=1` (*recommended only on RPI4)
- If you want to use RPI as access point to WikiSpot change `#AUTO_SETUP_INSTALL_SOFTWARE_ID=60` to `AUTO_SETUP_INSTALL_SOFTWARE_ID=60`
- *wifi setup in testing*
5. :grey_exclamation:For advanced users:grey_exclamation: You can now change contens of WikiSpot server in `/boot/WikiSpot`according to an example in server filesystem
6. Eject sd card from computer, insert it in Raspberry Pi and power it on. :bangbang:Raspberry Pi needs to be connected to intenet via Ethernet (*wifi coming soon*) othervise the setup will crash.
7. The setup will take approximately 25-40 min (RPI 4b (2gb) and 70 mb download speed)
8. Done you can start using WikiSpot and edit contents of WikiSpot with our app (*coming soon*)
### Script install
*coming soon*
### Manual install
*coming soon*
## Server filesystem
```
└── test_directory
├── cache # files forwarded from another servers to client
├── engine.py # engine for server (log, recovery, update)
├── files # content of WikiSpot server
│   └── test.jpg
├── filesystem.json # data settings of server (name, description, files)
├── main.py # main server file
├── plugins # plugins file
│   └── computer_vision # oficial WikiSpot computer vision plugin for RPI 4
│   ├── MobileNetSSD_deploy.caffemodel
│   ├── MobileNetSSD_deploy.prototxt
│   └── com_vision.py
├── run.py # start script for server
├── settings.json # settings (log, debug, connected WikiSpots, cache size,...)
├── system.py # update and clean script
└── version.json # version of WikiSpot
```
### filesystem
```
{
"ID": 0, # ID of WikiSpots, Needs to be different, because network will crash
"location": "25.997417761947318, -97.15738221291177", # Location of WikiSpot server copied from google maps
"description": {
"title": "WikiSpot demo", # Name of WikiSpot server (swiming pool, school, ...)
"description_s": "This is showcase of WikiSpot", # Short description showed on web/app in list of servers
"description_l": "This will show after opening server in app", # Long description showed after opening the server in web/app
"photo_s": "test.jpg", # Small image showed on web/app in list of servers
"photo_b": "test.png" # Big image showed after opening the server in web/app
},
"files": [ # files on server in /files that will be mediated to the web/app
{
"name": "test", # Name of the file, without spaces. App will change "_" to spaces
"format": ".jpg", # Format of the file
"description": "This is test file" # Description showed next to the file
}
]
}
```
To manualy add new file to server (on setup or via ssh) add file to `server_directory/files`
and add record for file into `files` list in `filesystem.json`. :exclamation:do not forget "," after last record:exclamation:
```
{
"name": "new_file_name",
"format": ".txt",
"description": "This is how you add new file"
}
```
### settings.json
```
{
"time_to_heartbeat": 20, # Time to ping of another online servers in seconds
"time_to_heartbeat_offline": 25, # Time to ping of another offline servers in seconds
"save_table": true, # Save connected servers to reconnect after restart
"time_to_save": 60, # Time to save server in seconds
"max_mess": 20, # Maximum messages stored in RAM
"cache_size_mb": 1000, # Maximum size of cache directory in mb
"clear_cache_on_startup": false, # Remove contents of cache on startup (slower first downloads)
"log": { # Log settings
"save_error": true, # Save errors into log.txt
"print_error": true, # Print errors into console (if running as service into linux log)
"save_warning": true, # Save warnings into log.txt
"print_warning": true, # Print warnings into console (if running as service into linux log)
"save_message": false, # Save messages (new server, etc. not messages from clients) into log.txt
"print_message": true, # Print messages into console (if running as service into linux log)
"enable_debug": false # Enable debug into console (if running as service into linux log)
},
"heartbeat_table": { # Saved servers
"ID": [],
"IP": [],
"location": [],
"file_system": [],
"last_heartbeat": []
}
}
```
If you want to manually add server on first setup or via ssh fill heartbeat table like this.
```
"heartbeat_table": { # Saved servers
"ID": [1], # ID of server as integer (number)
"IP": ["192.168.1.2"], # IP of server as string
"location": [""], # Empty string as placeholder. location will be downloaded after first connection
"file_system": [""], # Empty string as placeholder. filesystem will be downloaded after first connection
"last_heartbeat": [10] # After how many seconds will server try to connect for the first time
}
```
:bangbang:If the server will be offline for long time (heartbeat + offline heartbeat) it will be removed from heartbeat table. If the save function is disabled server will trying to connect after restart:bangbang:
*This is not finished product*

@ -0,0 +1,123 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<JetCodeStyleSettings>
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<codeStyleSettings language="XML">
<option name="FORCE_REARRANGE_MODE" value="1" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>ANDROID_ATTRIBUTE_ORDER</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
<codeStyleSettings language="kotlin">
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</codeStyleSettings>
</code_scheme>
</component>

@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="CompilerConfiguration"> <component name="CompilerConfiguration">
<bytecodeTargetLevel target="1.8" /> <bytecodeTargetLevel target="11" />
</component> </component>
</project> </project>

@ -0,0 +1,10 @@
<component name="ProjectDictionaryState">
<dictionary name="ben44">
<words>
<w>backstack</w>
<w>datatype</w>
<w>filetype</w>
<w>initing</w>
</words>
</dictionary>
</component>

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="PyInterpreterInspection" enabled="false" level="WARNING" enabled_by_default="false" />
</profile>
</component>

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" /> <output url="file://$PROJECT_DIR$/build/classes" />
</component> </component>
<component name="ProjectType"> <component name="ProjectType">

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="com.android.tools.idea.compose.preview.runconfiguration.ComposePreviewRunConfigurationProducer" />
</set>
</option>
</component>
</project>

@ -1,7 +1,8 @@
plugins { plugins {
id 'com.android.application' id 'com.android.application'
id 'kotlin-android' id 'kotlin-android'
id 'com.chaquo.python' id 'kotlin-android-extensions'
id 'androidx.navigation.safeargs.kotlin'
} }
android { android {
@ -10,28 +11,14 @@ android {
defaultConfig { defaultConfig {
applicationId "com.example.wikispot" applicationId "com.example.wikispot"
minSdkVersion 16 minSdkVersion 23
targetSdkVersion 30 targetSdkVersion 30
versionCode 1 versionCode 1
versionName "1.0" versionName "1.0"
vectorDrawables.useSupportLibrary = true
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
sourceSets {
main {
python {
srcDirs = ["src/main/python"]
}
}
}
python {
buildPython "/urs/local/bin/python3"
buildPython "python3"
}
ndk {
abiFilters "armeabi-v7a", "x86"
}
} }
buildFeatures { buildFeatures {
@ -49,12 +36,11 @@ android {
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8
} }
kotlinOptions { kotlinOptions {
jvmTarget = '1.8' jvmTarget = JavaVersion.VERSION_1_8.toString()
} }
} }
dependencies { dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.2' implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0' implementation 'androidx.appcompat:appcompat:1.2.0'
@ -64,7 +50,14 @@ dependencies {
implementation 'androidx.navigation:navigation-ui-ktx:2.3.4' implementation 'androidx.navigation:navigation-ui-ktx:2.3.4'
implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.preference:preference:1.1.1' implementation 'androidx.preference:preference:1.1.1'
implementation 'com.squareup.okhttp3:okhttp:4.9.0'
implementation 'com.google.android.gms:play-services-maps:17.0.0'
testImplementation 'junit:junit:4.13.2' testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.2' androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
implementation 'com.github.bumptech.glide:glide:4.12.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
implementation 'com.github.barteksc:android-pdf-viewer:2.8.2'
} }

@ -0,0 +1,24 @@
<resources>
<!--
Before you run your application, you need a Google Maps API key.
To get one, follow this link, follow the directions and press "Create" at the end:
https://console.developers.google.com/flows/enableapi?apiid=maps_android_backend&keyType=CLIENT_SIDE_ANDROID&r=65:0D:80:30:57:9C:7B:EB:35:6F:CC:61:21:5D:C1:2A:73:E1:D8:20%3Bcom.example.wikispot
You can also add your credentials to an existing key, using these values:
Package name:
com.example.wikispot
SHA-1 certificate fingerprint:
65:0D:80:30:57:9C:7B:EB:35:6F:CC:61:21:5D:C1:2A:73:E1:D8:20
Alternatively, follow the directions here:
https://developers.google.com/maps/documentation/android/start#get-key
Once you have your key (it starts with "AIza"), replace the "google_maps_key"
string in this file.
-->
<string name="google_maps_key" templateMergeStrategy="preserve" translatable="false">AIzaSyAixBio8FevppLsncIkFUQarx2kUB-0dW0</string>
</resources>

@ -2,15 +2,40 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.wikispot"> package="com.example.wikispot">
<!--
The ACCESS_COARSE/FINE_LOCATION permissions are not required to use
Google Maps Android API v2, but you must specify either coarse or fine
location permissions for the "MyLocation" functionality.
-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application <application
android:allowBackup="true" android:allowBackup="false"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.WikiSpotWithActionBar"> android:theme="@style/Theme.WikiSpot"
<activity android:name=".activities.MainActivity" android:usesCleartextTraffic="true">
android:launchMode="singleTask">
<!--
The API key for Google Maps-based APIs is defined as a string resource.
(See the file "res/values/google_maps_api.xml").
Note that the API key is linked to the encryption key used to sign the APK.
You need a different API key for each encryption key, including the release key that is used to
sign the APK for publishing.
You can define the keys for the debug and release targets in src/debug/ and src/release/.
-->
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="@string/google_maps_key" />
<activity
android:name=".activities.MainActivity">
<!--android:screenOrientation="portrait"> -->
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

@ -1,3 +0,0 @@
package com.example.wikispot
object Constants {}

@ -0,0 +1,117 @@
package com.example.wikispot
import android.app.Activity
import android.app.AlertDialog
import android.content.Context
import android.os.Environment
import android.view.View
import android.widget.Toast
import com.google.android.material.snackbar.Snackbar
import java.io.File
import java.io.FileInputStream
import java.nio.channels.FileChannel
import java.nio.charset.Charset
import java.util.*
// for showing messages
fun Context.showToast(message: String, length: Int = Toast.LENGTH_SHORT) {
Toast.makeText(this, message, length).show()
}
fun Context.showSnack(message: String, view: View, length: Int = Snackbar.LENGTH_LONG) {
Snackbar.make(this, view, message, length).show()
}
// for theme
fun Context.getThemeId(): Int {
if (ThemeOptions.darkTheme) {
if (!ThemeOptions.moreColors) {
return R.style.Theme_WikiSpotDark
} else {
return R.style.Theme_WikiSpotDark_
}
} else {
if (!ThemeOptions.moreColors) {
return R.style.Theme_WikiSpot
} else {
return R.style.Theme_WikiSpot_
}
}
}
// working with files
fun Context.createFile(filename: String, filetype: String): File {
val storageDir = getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS)
if (!storageDir?.exists()!!) {
storageDir.mkdir()
}
return File(storageDir, "$filename.$filetype")
}
fun Context.getFile(filename: String, filetype: String): String {
val file = (getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS)?.absolutePath ?: "") + "/$filename.$filetype"
val stream = FileInputStream(file)
var returnString = ""
stream.use { streamInUse ->
val fileChannel = streamInUse.channel
val mappedByteBuffer = fileChannel.map(
FileChannel.MapMode.READ_ONLY,
0,
fileChannel.size()
)
returnString = Charset.defaultCharset().decode(mappedByteBuffer).toString()
}
return returnString
}
fun Context.saveString(accessKey: String, stringValue: String, preferencesFilename: String="generalPreferences") {
val sharedPreferences = getSharedPreferences(preferencesFilename, Context.MODE_PRIVATE)
val editor = sharedPreferences.edit()
editor.apply{
putString(accessKey, stringValue)
}.apply()
}
fun Context.getStringFromSharedPreferences(accessKey: String, preferencesFilename: String="generalPreferences"): String {
val sharedPreferences = getSharedPreferences(preferencesFilename, Context.MODE_PRIVATE)
val returnedString = sharedPreferences.getString(accessKey, "")
returnedString?.let {
return returnedString
}
return ""
}
// Other
fun Context.getRandomGenerator(seedString: String): Random {
var n: Long = 0
for (element in seedString) {
n += element.toInt()
}
println(n)
return Random(n)
}
// Activity extensions
fun Activity.askToQuit() {
val builder = AlertDialog.Builder(this)
builder.setTitle("Confirm")
builder.setMessage("Do you want to quit the application?")
builder.setPositiveButton("Yes") { _, _ -> finish()}
builder.setNegativeButton("No") { _, _ -> }
builder.show()
}

@ -1,31 +1,195 @@
package com.example.wikispot.activities package com.example.wikispot.activities
import androidx.appcompat.app.AppCompatActivity import android.graphics.Bitmap
import android.os.Bundle import android.os.Bundle
import androidx.appcompat.app.AlertDialog import android.util.DisplayMetrics
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.fragment.app.Fragment
import androidx.navigation.findNavController import androidx.navigation.findNavController
import androidx.navigation.ui.setupWithNavController import androidx.navigation.ui.setupWithNavController
import com.example.wikispot.R import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.bottomnavigation.BottomNavigationView import com.example.wikispot.*
import com.example.wikispot.adapters.FileViewsAdapter
import com.example.wikispot.adapters.LabeledValuesAdapter
import com.example.wikispot.adapters.PlacePreviewsAdapter
import com.example.wikispot.fragments.*
import com.example.wikispot.modelClasses.JsonManager
import com.example.wikispot.modelClasses.JsonManagerLite
import com.example.wikispot.modelClasses.SettingsSaveManager
import com.example.wikispot.modelsForAdapters.*
import com.google.android.gms.maps.model.LatLng
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.fragment_explore.*
import kotlinx.android.synthetic.main.fragment_home.*
import kotlinx.android.synthetic.main.fragment_info.*
import kotlinx.android.synthetic.main.fragment_info.view.*
class MainActivity : AppCompatActivity() { class MainActivity : AppCompatActivity() {
override fun onBackPressed() { override fun onBackPressed() {
val builder = AlertDialog.Builder(this)
builder.setTitle("Confirm") try {
builder.setMessage("Do you want to quit the application?") when (val currentlyShownFragment = mainFragmentHost.childFragmentManager.fragments[0]) {
builder.setPositiveButton("Yes") {_, _ -> finish()} is chatFragment -> {
builder.setNegativeButton("No") {_, _ -> } askToQuit()
builder.show() }
is exploreFragment -> {
askToQuit()
}
is homeFragment -> {
askToQuit()
}
is mapFragment -> {
askToQuit()
}
is settingsFragment -> {
askToQuit()
}
is infoFragment -> {
when (CustomBackstackVariables.infoFragmentBackDestination) {
"exploreFragment" -> { currentlyShownFragment.goExploreFragment() }
"mapFragment" -> {currentlyShownFragment.goMapFragment()}
}
}
}
} catch (e: Throwable) { println(e) }
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
doPreparations()
loadSettings()
setTheme(getThemeId())
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main) setContentView(R.layout.activity_main)
val navController = findNavController(R.id.mainFragmentHost) val navController = findNavController(R.id.mainFragmentHost)
val bottomNavView = findViewById<BottomNavigationView>(R.id.mainBottomNavigationView) mainBottomNavigationView.setupWithNavController(navController)
bottomNavView.setupWithNavController(navController) handleExtras()
}
private fun doPreparations() {
val displayMetrics = DisplayMetrics()
windowManager.defaultDisplay.getMetrics(displayMetrics)
ScreenParameters.height = displayMetrics.heightPixels
ScreenParameters.width = displayMetrics.widthPixels
val savedBaseUrl = this.getStringFromSharedPreferences("baseUrlSave")
if (savedBaseUrl != "") {
ServerManagement.baseUrl = savedBaseUrl
} }
} }
override fun onResume() {
super.onResume()
// server communication
connectExploreFragmentAdapterModel()
}
override fun onPause() {
PlaceSupplier.saveToCache(this)
ServerManagement.serverManager.deleteConnection("exploreListConnection")
super.onPause()
}
private fun handleExtras() {
when (intent.getStringExtra(IntentsKeys.startFragment)) {
"chatFragment" -> {
mainBottomNavigationView.selectedItemId = R.id.chatFragment
}
"exploreFragment" -> {
mainBottomNavigationView.selectedItemId = R.id.exploreFragment
}
// skipping home fragment because were already here
"mapFragment" -> {
mainBottomNavigationView.selectedItemId = R.id.mapFragment
}
"settingsFragment" -> {
mainBottomNavigationView.selectedItemId = R.id.settingsFragment
}
"debugFragment" -> {
StartDirections.settingsFragmentStartDirection = "debugFragment"
mainBottomNavigationView.selectedItemId = R.id.settingsFragment
}
}
}
private fun loadSettings() {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
val settingsSaveManager = SettingsSaveManager(this)
settingsSaveManager.loadSettings()
}
private fun connectExploreFragmentAdapterModel () {
// loading from cache
PlaceSupplier.loadFromCache(this)
// connecting to server
val dataReceiver: (String) -> Unit = { data: String ->
val json = JsonManager(this, data)
PlaceSupplier.controlJson = JsonManagerLite(data)
for (i in 1 until json.getLengthOfJsonArray()) {
json.getJsonObject(i)
val id = json.getAttributeContent("ID").toInt()
val location = json.getAttributeContent("location")
json.getAttributeContent("description")
val title = json.getAttributeContent("title")
val shortDescription = json.getAttributeContent("description_s")
val place = PlacePreview(title, shortDescription, location, null, id)
if (!PlaceSupplier.checkIfContains(place)) {
val imageReceiver: (Bitmap) -> Unit = { bitmap: Bitmap ->
place.img = bitmap
}
ServerManagement.serverManager.getImage(imageReceiver, id, json.getAttributeContentByPath("description/photo_s"), 3)
PlaceSupplier.appendPlace(place)
} else {
val containingPlace = PlaceSupplier.getContainingInstance(place)
if ((containingPlace != null) and (containingPlace?.img == null)) {
val imageReceiver: (Bitmap) -> Unit = { bitmap: Bitmap ->
containingPlace?.img = bitmap
}
ServerManagement.serverManager.getImage(imageReceiver, id, json.getAttributeContentByPath("description/photo_s"), 3)
}
// checking if location wasn't changed
if ((containingPlace != null) and (containingPlace?.location != location)) {
containingPlace?.location = location
try {
mainFragmentHost.childFragmentManager.fragments[0]?.let {
if (it is exploreFragment) {
it.explore_recycler_view.post {
val layoutManager = LinearLayoutManager(it.requireContext())
layoutManager.orientation = LinearLayoutManager.VERTICAL
it.explore_recycler_view.layoutManager = layoutManager
val adapter = PlacePreviewsAdapter(it.requireContext(), PlaceSupplier.places)
it.explore_recycler_view.adapter = adapter
}
}
}
} catch (e: Throwable) { println("[debug] e4 that i couldnt fix si try catch Exception: $e") }
}
}
json.clearSelectedAttribute()
}
}
ServerManagement.serverManager.addReceiverConnection(dataReceiver, this, "exploreListConnection", 0, "", "GET_WHOLE_ARRAY", 10000)
}
}

@ -0,0 +1,51 @@
package com.example.wikispot.adapters
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.wikispot.GeneralVariables
import com.example.wikispot.R
import com.example.wikispot.modelsForAdapters.Message
import kotlinx.android.synthetic.main.message.view.*
class ChatMessagesAdapter(private val context: Context, private val messages: Array<Message?>) : RecyclerView.Adapter<ChatMessagesAdapter.MyViewHolder>() {
inner class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
var message: Message? = null
var pos: Int = 0
fun setData(message: Message?, pos: Int) {
message?.let {
itemView.message_author_text.text = message.senderName
itemView.message_content_text.text = message.content
if (GeneralVariables.id == message.senderId) {
itemView.message_author_text.textAlignment = View.TEXT_ALIGNMENT_VIEW_END
itemView.message_content_text.textAlignment = View.TEXT_ALIGNMENT_VIEW_END
}
}
this.message = message
this.pos = pos
}
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val message = messages[position]
holder.setIsRecyclable(false)
holder.setData(message, position)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val view = LayoutInflater.from(context).inflate(R.layout.message, parent, false)
return MyViewHolder(view)
}
override fun getItemCount(): Int {
return messages.size
}
}

@ -0,0 +1,192 @@
package com.example.wikispot.adapters
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.net.Uri
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.animation.Animation
import android.view.animation.AnimationUtils
import android.widget.RelativeLayout
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat.startActivity
import androidx.core.view.setPadding
import androidx.recyclerview.widget.RecyclerView
import com.example.wikispot.R
import com.example.wikispot.ServerManagement
import com.example.wikispot.modelsForAdapters.FileView
import kotlinx.android.synthetic.main.file_view.view.*
class FileViewsAdapter(private val context: Context, private val fileViews: Array<FileView?>) : RecyclerView.Adapter<FileViewsAdapter.MyViewHolder>() {
private val rotateOpen: Animation by lazy { AnimationUtils.loadAnimation(context, R.anim.open_rotation_anim) }
private val rotateClose: Animation by lazy { AnimationUtils.loadAnimation(context, R.anim.close_rotation_anim) }
private val fadeIn: Animation by lazy {AnimationUtils.loadAnimation(context, R.anim.fade_in) }
private val fadeOut: Animation by lazy {AnimationUtils.loadAnimation(context, R.anim.fade_out) }
inner class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
var fileView: FileView? = null
var pos: Int = 0
var textInfo: String? = null
var imgInfo: String? = null
var pdfUrl: String? = null
var generalUrl: String? = null
var opened = false
init {
itemView.setOnClickListener {
if (!opened) {
itemView.downloadFileBtn.visibility = View.VISIBLE
itemView.downloadFileBtn.startAnimation(fadeIn)
if (generalUrl == null) {
itemView.showFileBtn.startAnimation(rotateOpen)
}
fileView?.let {
textInfo?.let {
itemView.textContent.textSize = 18F
itemView.textContent.setPadding(32)
val dataReceiver: (String) -> Unit = { data: String ->
itemView.textContent.post {
itemView.textContent.text = data
}
}
val textInformation = textInfo!!.split("|||||")
ServerManagement.serverManager.getData(dataReceiver, itemView.context, textInformation[0].toInt(), textInformation[1])
}
imgInfo?.let {
itemView.imageContent.visibility = View.VISIBLE
val imageReceiver2: (Bitmap) -> Unit = { bitmap: Bitmap ->
itemView.imageContent.post {
itemView.imageContent.setImageBitmap(bitmap)
}
}
val imgInformation = imgInfo!!.split("|||||")
ServerManagement.serverManager.getImage(imageReceiver2, imgInformation[0].toInt(), imgInformation[1])
}
pdfUrl?.let {
itemView.pdfContent.visibility = View.VISIBLE
ServerManagement.serverManager.loadPdfView(itemView.pdfContent, pdfUrl!!, true)
}
}
} else {
if (generalUrl == null) {
itemView.showFileBtn.startAnimation(rotateClose)
}
val downloadBtnVanishActionThread = Thread(DownloadBtnVanishAction())
downloadBtnVanishActionThread.start()
itemView.textContent.textSize = 0F
itemView.textContent.setPadding(0)
itemView.imageContent.visibility = View.GONE
itemView.pdfContent.visibility = View.GONE
}
opened = !opened
}
itemView.downloadFileBtn.setOnClickListener {
textInfo?.let {
val textInformation = textInfo!!.split("|||||")
val url = "${ServerManagement.baseUrl}files/${textInformation[0]}/${textInformation[1]}"
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
startActivity(context, browserIntent, null)
}
imgInfo?.let {
val imgInformation = imgInfo!!.split("|||||")
val url = "${ServerManagement.baseUrl}files/${imgInformation[0]}/${imgInformation[1]}"
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
startActivity(context, browserIntent, null)
}
pdfUrl?.let {
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(pdfUrl))
startActivity(context, browserIntent, null)
}
generalUrl?.let {
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(generalUrl))
startActivity(context, browserIntent, null)
}
}
}
fun setData(fileView: FileView?, pos: Int) {
fileView?.let {
fileView.textInfo?.let {
textInfo = it
}
fileView.imgInfo?.let {
imgInfo = it
}
fileView.pdfUrl?.let {
pdfUrl = it
}
fileView.generalUrl?.let {
generalUrl = it
itemView.showFileBtn.visibility = View.INVISIBLE
// setting new margins
var params = itemView.downloadFileBtn.layoutParams as ConstraintLayout.LayoutParams
params.marginEnd = 24
itemView.downloadFileBtn.layoutParams = params
params = itemView.showFileBtn.layoutParams as ConstraintLayout.LayoutParams
params.marginEnd = 0
itemView.showFileBtn.layoutParams = params
}
itemView.filenameText.text = fileView.filename.replace("_", " ")
itemView.fileDescription.text = fileView.fileDescription
}
this.fileView = fileView
this.pos = pos
}
inner class DownloadBtnVanishAction: Runnable {
override fun run() {
itemView.post {
itemView.downloadFileBtn.startAnimation(fadeOut)
}
Thread.sleep(600)
itemView.post {
itemView.downloadFileBtn.clearAnimation()
itemView.downloadFileBtn.visibility = View.GONE
}
}
}
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val fileView = fileViews[position]
holder.setData(fileView, position)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val view = LayoutInflater.from(context).inflate(R.layout.file_view, parent, false)
return MyViewHolder(view)
}
override fun getItemCount(): Int {
return fileViews.size
}
}

@ -0,0 +1,79 @@
package com.example.wikispot.adapters
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.navigation.Navigation
import androidx.recyclerview.widget.RecyclerView
import com.example.wikispot.CustomBackstackVariables
import com.example.wikispot.R
import com.example.wikispot.ServerManagement
import com.example.wikispot.fragments.exploreFragmentDirections
import com.example.wikispot.modelsForAdapters.PlacePreview
import com.example.wikispot.showToast
import com.google.android.gms.maps.model.LatLng
import kotlinx.android.synthetic.main.explore_list_item.view.*
class PlacePreviewsAdapter(private val context: Context, private val placePreviews: Array<PlacePreview?>) : RecyclerView.Adapter<PlacePreviewsAdapter.MyViewHolder>() {
inner class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
var currentPlacePreview: PlacePreview? = null
var pos: Int = 0
var location: LatLng? = null
init {
itemView.setOnClickListener {
CustomBackstackVariables.infoFragmentBackDestination = "exploreFragment"
ServerManagement.selectedServerId = currentPlacePreview?.id!!
val action = exploreFragmentDirections.navigateToInfoFragment(true)
Navigation.findNavController(it).navigate(action)
}
itemView.item_location_img.setOnClickListener {
if (location != null) {
val action = exploreFragmentDirections.navigateToMapFragment(location!!)
Navigation.findNavController(it).navigate(action)
}
}
}
fun setData(placePreview: PlacePreview?, pos: Int) {
placePreview?.let {
itemView.item_title.text = placePreview.title
itemView.item_description.text = placePreview.description
try {
val coordinates = placePreview.location!!.split(",")
location = LatLng(coordinates[0].toDouble(), coordinates[1].toDouble())
} catch (e: Throwable) {
println("[debug] Failed getting coordinates in ${placePreview.title}, explore fragment list. Exception: $e")}
placePreview.img?.let {
itemView.item_img.setImageBitmap(placePreview.img)
}
}
this.currentPlacePreview = placePreview
this.pos = pos
}
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val placePreview = placePreviews[position]
holder.setData(placePreview, position)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val view = LayoutInflater.from(context).inflate(R.layout.explore_list_item, parent, false)
return MyViewHolder(view)
}
override fun getItemCount(): Int {
return placePreviews.size
}
}

@ -0,0 +1,56 @@
package com.example.wikispot.adapters
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.wikispot.GeneralVariables
import com.example.wikispot.R
import com.example.wikispot.modelsForAdapters.LabeledValue
import kotlinx.android.synthetic.main.labeled_value_item.view.*
class LabeledValuesAdapter(private val context: Context, private val labeledValues: Array<LabeledValue?>) : RecyclerView.Adapter<LabeledValuesAdapter.MyViewHolder>() {
inner class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
var currentLabeledValue: LabeledValue? = null
var pos: Int = 0
fun setData(labeledValue: LabeledValue?, pos: Int) {
labeledValue?.let {
if (labeledValue.label.startsWith(GeneralVariables.translatePrefix)) {
itemView.label.text = context.resources.getString(context.resources.getIdentifier(labeledValue.label.slice(GeneralVariables.translatePrefix.length until labeledValue.label.length),
"string", context.packageName))
} else {
itemView.label.text = labeledValue.label
}
if (labeledValue.value.startsWith(GeneralVariables.translatePrefix)) {
itemView.value.text = context.resources.getString(context.resources.getIdentifier(labeledValue.value.slice(GeneralVariables.translatePrefix.length until labeledValue.value.length),
"string", context.packageName))
} else {
itemView.value.text = labeledValue.value
}
}
this.currentLabeledValue = labeledValue
this.pos = pos
}
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val labeledValue = labeledValues[position]
holder.setData(labeledValue, position)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val view = LayoutInflater.from(context).inflate(R.layout.labeled_value_item, parent, false)
return MyViewHolder(view)
}
override fun getItemCount(): Int {
return labeledValues.size
}
}

File diff suppressed because it is too large Load Diff

@ -1,14 +0,0 @@
package com.example.wikispot
import android.content.Context
import android.view.View
import android.widget.Toast
import com.google.android.material.snackbar.Snackbar
fun Context.showToast(message: String, length: Int=Toast.LENGTH_SHORT) {
Toast.makeText(this, message, length).show()
}
fun Context.showSnack(message: String, view: View, length: Int=Snackbar.LENGTH_LONG) {
Snackbar.make(this, view, message, length).show()
}

@ -0,0 +1,70 @@
package com.example.wikispot.fragments
import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment
import androidx.navigation.Navigation
import com.example.wikispot.R
import com.example.wikispot.ServerManagement
import com.example.wikispot.modelClasses.JsonManager
import kotlinx.android.synthetic.main.fragment_another_debug.*
import org.json.JSONObject
import kotlin.random.Random
class anotherDebugFragment : Fragment(R.layout.fragment_another_debug) {
private lateinit var jsonManager: JsonManager
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
jsonManager = JsonManager(requireContext(), "[]", "JSONArray", true)
generateAndSaveDataBtn.setOnClickListener {
val generatedData = JSONObject()
for (n in 0 until 10) {
generatedData.put("$n", Random.nextInt(0, 100))
}
context?.let {
jsonManager = JsonManager(requireContext(), generatedData.toString(), "JSONObject", true)
jsonManager.saveJson("test")
}
println("[debug] saved generated data")
}
loadAndShowDataBtn.setOnClickListener {
val json = jsonManager.loadJson(requireContext(), "test", "JSONObject")
dataContentView.text = json.currentJsonObject.toString()
}
stopConnectionBtn.setOnClickListener {
ServerManagement.serverManager.deleteConnection("debug", "view")
}
createViewConnectionBtn.setOnClickListener {
val attributePath = attributePathInput.text.toString()
val filePath = filePathInput.text.toString()
ServerManagement.serverManager.deleteConnection("debug", "view")
ServerManagement.serverManager.addViewConnection(requireContext(), dataContentView, "debug",0, filePath, attributePath)
}
// handling navigation between debug fragments
goFirstDegubFragmentBtn.setOnClickListener {
cleanup()
Navigation.findNavController(it).navigate(R.id.navigateBackToDebugFragment)
}
}
override fun onDestroy() {
super.onDestroy()
cleanup()
}
private fun cleanup() {
ServerManagement.serverManager.deleteConnection("debug")
}
}

@ -1,12 +1,204 @@
package com.example.wikispot.fragments package com.example.wikispot.fragments
import android.os.Bundle import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import androidx.fragment.app.Fragment
import com.example.wikispot.R import androidx.recyclerview.widget.LinearLayoutManager
import com.example.wikispot.*
import com.example.wikispot.adapters.ChatMessagesAdapter
import com.example.wikispot.databases.NamesDatabase
import com.example.wikispot.modelClasses.JsonManager
import com.example.wikispot.modelsForAdapters.Message
import com.example.wikispot.modelsForAdapters.MessagesSupplier
import kotlinx.android.synthetic.main.fragment_chat.*
import okhttp3.*
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import org.json.JSONObject
import java.io.IOException
class chatFragment : Fragment(R.layout.fragment_chat) { class chatFragment : Fragment(R.layout.fragment_chat) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
updateRecyclerView()
writeBar.setOnClickListener {
val scrollDownThread = Thread(ScrollDown(300))
scrollDownThread.start()
}
userMessageText.setOnClickListener {
val scrollDownThread = Thread(ScrollDown(300))
scrollDownThread.start()
}
userMessageText.setOnFocusChangeListener { _, _ ->
val scrollDownThread = Thread(ScrollDown(300))
scrollDownThread.start()
}
sendMessageBtn.setOnClickListener {
GeneralVariables.id?.let {
if (userMessageText.text.toString() != "" ) {
val message = Message(GeneralVariables.id!!, userMessageText.text.toString(), "waiting")
MessagesSupplier.appendMessage(message)
userMessageText.setText("")
val messagePostThread = Thread(MessagePost(message))
messagePostThread.start()
updateRecyclerView()
} else {
requireContext().showToast("sending empty messages is not permitted")
}
}
}
}
override fun onResume() {
super.onResume()
loadIdFromCache()
val dataReceiver: (String) -> Unit = { data: String ->
try {
val json = JsonManager(requireContext(), data, "JSONObject")
when (json.getAttributeContent("source")) {
"messages/register" -> {
GeneralVariables.id = json.getAttributeContentByPath("data/0")
val r = requireContext().getRandomGenerator(GeneralVariables.id!!)
GeneralVariables.name = "${NamesDatabase.names[r.nextInt(NamesDatabase.names.size)]} - ${r.nextInt(9999)}"
json.getAttributeContent("data")
json.getAttributeContent("1")
val length = json.currentJsonAttribute1!!.length()
json.clearSelectedAttribute()
for (i in 0 until length) {
val jsonOfMessage = JsonManager(requireContext(), json.getAttributeContentByPath("data/1/$i"), "JSONObject")
val message = Message(jsonOfMessage.getAttributeContent("sender"),
jsonOfMessage.getAttributeContent("message"),
jsonOfMessage.getAttributeContent("timestamp"))
if (!MessagesSupplier.checkIfContains(message)) {
MessagesSupplier.appendMessage(message)
updateRecyclerView()
}
}
}
"messages/get" -> {
json.getAttributeContent("data")
val length = json.currentJsonAttribute1!!.length()
json.clearSelectedAttribute()
MessagesSupplier.clearWaitingMessages()
for (i in 0 until length) {
val jsonOfMessage = JsonManager(requireContext(), json.getAttributeContentByPath("data/$i"), "JSONObject")
val message = Message(jsonOfMessage.getAttributeContent("sender"),
jsonOfMessage.getAttributeContent("message"),
jsonOfMessage.getAttributeContent("timestamp"))
if (!MessagesSupplier.checkIfContains(message)) {
MessagesSupplier.appendMessage(message)
updateRecyclerView()
}
}
}
}
} catch (e: Throwable) { println("[debug][chat fragment] Exception: $e") }
}
ServerManagement.serverManager.addChatConnection(dataReceiver, requireContext(), "chatConnection")
}
override fun onPause() {
super.onPause()
saveIdToCache()
ServerManagement.serverManager.deleteConnection("chatConnection")
}
private fun updateRecyclerView() {
try {
chat_messages_recycler_view.post {
val layoutManager = LinearLayoutManager(context)
layoutManager.orientation = LinearLayoutManager.VERTICAL
chat_messages_recycler_view.layoutManager = layoutManager
val adapter = context?.let { ChatMessagesAdapter(it, MessagesSupplier.messages) }
chat_messages_recycler_view.adapter = adapter
chat_messages_recycler_view.scrollToPosition(MessagesSupplier.messages.size - 1)
}
} catch (e: Throwable) { println("[debug] e5 Exception: $e") }
}
inner class ScrollDown(private val after: Long): Runnable {
override fun run() {
Thread.sleep(after)
try {
chat_messages_recycler_view.post {
chat_messages_recycler_view.scrollToPosition(MessagesSupplier.messages.size - 1)
}
} catch (e: Throwable) { println("[debug] e6 Exception: $e") }
}
}
inner class MessagePost(private val message: Message): Runnable {
override fun run() {
val url = "${ServerManagement.baseUrl}messages/post"
val json: MediaType? = "application/json; charset=utf-8".toMediaTypeOrNull()
val body:RequestBody = RequestBody.create(json, JSONObject()
.put("m_sender", message.senderId)
.put("message", message.content).toString())
val request: Request = Request.Builder()
.url(url)
.post(body)
.build()
val client = OkHttpClient()
client.newCall(request).enqueue(object : Callback {
override fun onResponse(call: Call, response: Response) {
response.body?.let {
val receivedString = response.body!!.string()
println("[debug][message post] received string from post request: $receivedString")
}
}
override fun onFailure(call: Call, e: IOException) {
println("Request Failed")
println(e)
}
})
}
}
// loading and saving last names
private fun loadIdFromCache() {
val id = requireContext().getStringFromSharedPreferences("id")
if (id != "") {
GeneralVariables.id = id
}
}
private fun saveIdToCache() {
GeneralVariables.id?.let {
requireContext().saveString("id", GeneralVariables.id!!)
}
}
} }

@ -0,0 +1,63 @@
package com.example.wikispot.fragments
import android.annotation.SuppressLint
import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment
import androidx.navigation.Navigation
import com.example.wikispot.*
import com.example.wikispot.activities.MainActivity
import com.example.wikispot.modelsForAdapters.MessagesSupplier
import kotlinx.android.synthetic.main.fragment_debug.*
class debugFragment : Fragment(R.layout.fragment_debug) {
@SuppressLint("SetTextI18n")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
goSecondDebugFragmentBtn.setOnClickListener {
Navigation.findNavController(it).navigate(R.id.navigateToAnotherDebugFragment)
}
getNumberOfSentRequestsBtn.setOnClickListener {
outputText.text = ServerManagement.totalNumberOfRequestsSent.toString()
}
clearServerConnectionsBtn.setOnClickListener {
ServerManagement.serverManager.clearConnections()
}
editTextIp.setText(ServerManagement.baseUrl)
changeUrlBtn.setOnClickListener {
ServerManagement.baseUrl = editTextIp.text.toString()
requireContext().saveString("baseUrlSave", editTextIp.text.toString())
restartAppPartially()
}
restartAppPartiallyBtn.setOnClickListener {
restartAppPartially()
}
closeAppBtn.setOnClickListener {
activity?.finish()
}
}
private fun restartAppPartially() {
val intent = Intent(context?.applicationContext, MainActivity::class.java)
intent.putExtra(IntentsKeys.startFragment, "debugFragment")
ServerManagement.serverManager.clearConnections()
MessagesSupplier.wipeData()
GeneralVariables.id = null
startActivity(intent)
activity?.finish()
}
}

@ -1,8 +1,36 @@
package com.example.wikispot.fragments package com.example.wikispot.fragments
import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.wikispot.GeneralVariables
import com.example.wikispot.IntentsKeys
import com.example.wikispot.R import com.example.wikispot.R
import com.example.wikispot.ServerManagement
import com.example.wikispot.activities.MainActivity
import com.example.wikispot.adapters.PlacePreviewsAdapter
import com.example.wikispot.modelsForAdapters.PlaceSupplier
import kotlinx.android.synthetic.main.fragment_explore.*
class exploreFragment : Fragment(R.layout.fragment_explore) { class exploreFragment : Fragment(R.layout.fragment_explore) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupRecyclerView()
}
private fun setupRecyclerView() {
val layoutManager = LinearLayoutManager(context)
layoutManager.orientation = LinearLayoutManager.VERTICAL
explore_recycler_view.layoutManager = layoutManager
val adapter = context?.let { PlacePreviewsAdapter(it, PlaceSupplier.places) }
explore_recycler_view.adapter = adapter
}
} }

@ -1,12 +1,251 @@
package com.example.wikispot.fragments package com.example.wikispot.fragments
import android.graphics.Bitmap
import android.os.Bundle import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import androidx.fragment.app.Fragment
import androidx.navigation.Navigation
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.wikispot.GeneralVariables
import com.example.wikispot.MapManagement
import com.example.wikispot.R import com.example.wikispot.R
import com.example.wikispot.ServerManagement
import com.example.wikispot.adapters.FileViewsAdapter
import com.example.wikispot.adapters.LabeledValuesAdapter
import com.example.wikispot.modelClasses.JsonManager
import com.example.wikispot.modelClasses.JsonManagerLite
import com.example.wikispot.modelsForAdapters.FileView
import com.example.wikispot.modelsForAdapters.FileViewsSupplier
import com.example.wikispot.modelsForAdapters.LabeledValue
import com.example.wikispot.modelsForAdapters.LabeledValuesSupplier
import com.google.android.gms.maps.model.LatLng
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.fragment_home.*
import kotlinx.android.synthetic.main.fragment_info.*
import kotlinx.android.synthetic.main.fragment_info.view.*
class homeFragment : Fragment(R.layout.fragment_home) { class homeFragment : Fragment(R.layout.fragment_home) {
var infoFragmentLoadedIn = false
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
loadCache()
chatBtn.setOnClickListener {
Navigation.findNavController(it).navigate(R.id.homeFragment_to_chatFragment)
}
}
override fun onResume() {
super.onResume()
// connecting to server
val serverConnectorThread = Thread(ServerConnector())
serverConnectorThread.start()
val dataReceiver0: (String) -> Unit = { data: String ->
try {
val json = JsonManager(requireContext(), data)
json.findJsonObjectByAttribute("ID", json.getAttributeContent("connected_id"))
if (!infoFragmentLoadedIn) {
infoFragmentLoadedIn = true
val phoneNumberLoaded = json.getAttributeContentByPath("description/phone_number")
if (phoneNumberLoaded != GeneralVariables.variableMissingKeyword) {
GeneralVariables.phoneNumber = phoneNumberLoaded.toInt()
}
val emailLoaded = json.getAttributeContentByPath("description/email")
if (emailLoaded != GeneralVariables.variableMissingKeyword) {
GeneralVariables.email = emailLoaded
}
homeFragmentInnerFragment.post {
homeFragmentInnerFragment?.let { fragment ->
val title = json.getAttributeContentByPath("description/title")
val description = json.getAttributeContentByPath("description/description_l")
if (title != GeneralVariables.variableMissingKeyword) {
fragment.mainTitle.text = title
}
if (description != GeneralVariables.variableMissingKeyword) {
fragment.mainDescription.text = description
}
}
}
val imageReceiver: (Bitmap) -> Unit = { bitmap: Bitmap ->
homeFragmentInnerFragment?.let {
homeFragmentInnerFragment.post {
try {
homeFragmentInnerFragment.mainImage.setImageBitmap(bitmap)
} catch (e: Throwable) { println("[debug] e7 Exception: $e") }
}
}
}
ServerManagement.serverManager.getImage(imageReceiver, json.getAttributeContent("ID").toInt(),
json.getAttributeContentByPath("description/photo_b"), 3)
}
} catch (e: Throwable) { println(e) }
}
val dataReceiver1: (String) -> Unit = {connectedId: String ->
ServerManagement.connectedServerId = connectedId.toInt()
}
ServerManagement.serverManager.getData(dataReceiver0, requireContext(), 0, "", "GET_WHOLE_ARRAY", 4)
ServerManagement.serverManager.getData(dataReceiver1, requireContext(), 0, "", "connected_id", 3)
}
override fun onPause() {
super.onPause()
ServerManagement.serverManager.deleteConnection("sensorsConnection")
ServerManagement.serverManager.deleteConnection("mapConnection")
ServerManagement.serverManager.deleteConnection("fileViewsConnection")
saveCache()
}
private fun tryConnectingToServer() {
ServerManagement.connectedServerId?.let{ connectedServerId: Int ->
context?.let {
val dataReceiver1: (String) -> Unit = { data1: String ->
try {
val json = JsonManager(requireContext(), data1, "JSONObject")
val names = json.currentJsonObject!!.names()
names?.let {
LabeledValuesSupplier.wipeData()
for (n in 0 until names.length()) {
val labeledValue = LabeledValue(names[n].toString(), json.getAttributeContent(names[n].toString()))
if (!LabeledValuesSupplier.checkIfContains(labeledValue)) {
LabeledValuesSupplier.appendLabeledValue(labeledValue)
}
}
labeled_values_recycler_view.post {
val layoutManager = LinearLayoutManager(requireContext())
layoutManager.orientation = LinearLayoutManager.VERTICAL
labeled_values_recycler_view.layoutManager = layoutManager
val adapter = LabeledValuesAdapter(requireContext(), LabeledValuesSupplier.labeledValues)
labeled_values_recycler_view.adapter = adapter
}
}
} catch (e: Throwable) { println("[debug] Exception in main activity, sensors connection : $e") }
}
if (!ServerManagement.serverManager.checkIfConnectionAlreadyExists("sensorsConnection")){
ServerManagement.serverManager.addReceiverConnection(dataReceiver1, requireContext(), "sensorsConnection", connectedServerId, ServerManagement.sensors_keyword)
}
// getting other needed information
val dataReceiver2: (String) -> Unit = {data1: String ->
context?.let {
var json = JsonManager(requireContext(), data1)
json = JsonManager(requireContext(), json.findJsonObjectByAttribute("ID", connectedServerId), "JSONObject") // todo doesnt return correct result
val positionsList = json.getAttributeContent("location").split(",")
MapManagement.connectedServerPosition = LatLng(positionsList[0].toDouble(), positionsList[1].toDouble())
}
}
if (!ServerManagement.serverManager.checkIfConnectionAlreadyExists("mapConnection")){
ServerManagement.serverManager.addReceiverConnection(dataReceiver2, requireContext(), "mapConnection", connectedServerId, "", "GET_WHOLE_ARRAY")
}
val dataReceiver3: (String) -> Unit = { data1: String ->
fun updateFileViewsRecyclerView(fragment: Fragment) {
try {
fragment.context?.let {
fragment.homeFragmentInnerFragment?.let {
it.file_views_recycler_view.post {
val layoutManager = LinearLayoutManager(fragment.requireContext())
layoutManager.orientation = LinearLayoutManager.VERTICAL
it.file_views_recycler_view.layoutManager = layoutManager
val adapter = FileViewsAdapter(fragment.requireContext(), FileViewsSupplier.fileViews)
it.file_views_recycler_view.adapter = adapter
}
}
}
} catch (e: Throwable) { println("[debug] e1 that i couldnt fix so try catch Exception: $e") }
}
try {
val json = JsonManager(requireContext(), data1)
json.findJsonObjectByAttribute("ID", connectedServerId)
json.getAttributeContent("files")
for (n in 0 until json.currentJsonAttribute1!!.length()) {
val fileInfo = JsonManagerLite(json.getAttributeContentByPath("files/$n"), "JSONObject")
val filetype = fileInfo.getAttributeContentByPath("format").split(".")[1]
val filename = fileInfo.getAttributeContentByPath("name")
val fileDescription = fileInfo.getAttributeContentByPath("description")
// handling text
if ("txt json".contains(filetype)) {
val fileView = FileView(filetype, filename, fileDescription, "$connectedServerId|||||$filename.$filetype")
if (!FileViewsSupplier.checkIfContains(fileView)) {
FileViewsSupplier.appendFileView(fileView)
updateFileViewsRecyclerView(this)
}
}
// handling images
if ("jpg png".contains(filetype)) {
val fileView = FileView(filetype, filename, fileDescription, null, "$connectedServerId|||||$filename.$filetype")
if (!FileViewsSupplier.checkIfContains(fileView)) {
FileViewsSupplier.appendFileView(fileView)
updateFileViewsRecyclerView(this)
}
}
// handling pdf files
if ("pdf".contains(filetype)) {
val fileView = FileView(filetype, filename, fileDescription, null, null, "${ServerManagement.baseUrl}files/$connectedServerId/$filename.$filetype")
if (!FileViewsSupplier.checkIfContains(fileView)) {
FileViewsSupplier.appendFileView(fileView)
updateFileViewsRecyclerView(this)
}
}
}
} catch (e: Throwable) { println("[debug] Exception in home fragment, files data request : $e") }
}
if (!ServerManagement.serverManager.checkIfConnectionAlreadyExists("fileViewsConnection")) {
ServerManagement.serverManager.addReceiverConnection(dataReceiver3, requireContext(), "fileViewsConnection", connectedServerId, "", "GET_WHOLE_ARRAY")
}
}
}
}
inner class ServerConnector(private val numberOfAttempts: Int=3): Runnable {
override fun run() {
for (n in 0 until numberOfAttempts) {
tryConnectingToServer()
Thread.sleep(1000)
}
}
}
private fun loadCache() {}
private fun saveCache() {}
} }

@ -0,0 +1,323 @@
package com.example.wikispot.fragments
import android.content.Intent
import android.graphics.Bitmap
import android.net.Uri
import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment
import androidx.navigation.Navigation
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.wikispot.GeneralVariables
import com.example.wikispot.R
import com.example.wikispot.ServerManagement
import com.example.wikispot.adapters.FileViewsAdapter
import com.example.wikispot.adapters.LabeledValuesAdapter
import com.example.wikispot.modelClasses.JsonManager
import com.example.wikispot.modelClasses.JsonManagerLite
import com.example.wikispot.modelsForAdapters.FileView
import com.example.wikispot.modelsForAdapters.FileViewsSupplier
import com.example.wikispot.modelsForAdapters.LabeledValue
import com.example.wikispot.modelsForAdapters.LabeledValuesSupplier
import com.example.wikispot.showToast
import com.google.android.gms.maps.model.LatLng
import kotlinx.android.synthetic.main.fragment_info.*
class infoFragment : Fragment(R.layout.fragment_info) {
private val args: infoFragmentArgs by navArgs()
var location: LatLng? = null
var phoneNumber: Int? = null
var email: String? = null
var executeLoadFunction = false
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
LabeledValuesSupplier.wipeData()
FileViewsSupplier.wipeData()
updateSensorsRecyclerView()
updateFileViewsRecyclerView()
//file_views_recycler_view.isNestedScrollingEnabled = false
try {
executeLoadFunction = args.executeLoadFuntion
} catch (e: Throwable) {
if (!e.toString().contains("has null arguments")){
println("[debug] Exception in Info Fragment while getting args: $e")
}
}
if (executeLoadFunction) {
load()
} else {
getContactInfoFromGeneralVariables()
}
locationBtn.setOnClickListener {
if (executeLoadFunction) {
if (location != null) {
val action = infoFragmentDirections.infoFragmentToMapFragment(location!!)
Navigation.findNavController(it).navigate(action)
}
} else {
Navigation.findNavController(it).navigate(R.id.homeFragment_to_mapFragment)
}
}
phoneBtn.setOnClickListener {
if (phoneNumber != null) {
phoneNumber?.let {
val intent = Intent(Intent.ACTION_DIAL)
intent.data = Uri.parse("tel:$phoneNumber")
startActivity(intent)
}
} else {
requireContext().showToast("Phone number not found.")
}
}
emailBtn.setOnClickListener {
if (email != null) {
email?.let{
val intent = Intent(Intent.ACTION_SEND)
intent.putExtra(Intent.EXTRA_EMAIL, arrayOf(email))
intent.type = "text/plain"
startActivity(Intent.createChooser(intent, "Send Email"))
}
} else {
requireContext().showToast("Email address not found.")
}
}
}
private fun load() {
val serverId = ServerManagement.selectedServerId
val dataReceiver: (String) -> Unit = { data: String ->
context?.let {
try {
val json = JsonManager(requireContext(), data)
json.findJsonObjectByAttribute("ID", serverId)
val phoneNumberLoaded = json.getAttributeContentByPath("description/phone_number")
if (phoneNumberLoaded != GeneralVariables.variableMissingKeyword) {
phoneNumber = phoneNumberLoaded.toInt()
checkContactInformation()
}
val emailLoaded = json.getAttributeContentByPath("description/email")
if (emailLoaded != GeneralVariables.variableMissingKeyword) {
email = emailLoaded
checkContactInformation()
}
mainTitle?.let {
mainTitle.post {
val title = json.getAttributeContentByPath("description/title")
if (title != GeneralVariables.variableMissingKeyword) {
mainTitle.text = title
}
}
}
mainDescription?.let {
mainDescription.post {
val description = json.getAttributeContentByPath("description/description_l")
if (description != GeneralVariables.variableMissingKeyword) {
mainDescription.text = description
}
}
}
val imageReceiver1: (Bitmap) -> Unit = { bitmap: Bitmap ->
mainImage.post {
mainImage?.let {
mainImage.setImageBitmap(bitmap)
}
}
}
val coordinates = json.getAttributeContent("location").split(",")
location = LatLng(coordinates[0].toDouble(), coordinates[1].toDouble())
ServerManagement.serverManager.getImage(imageReceiver1, json.getAttributeContent("ID").toInt(),
json.getAttributeContentByPath("description/photo_b"), 2)
// getting files
json.getAttributeContent("files")
for (n in 0 until json.currentJsonAttribute1!!.length()) {
val fileInfo = JsonManagerLite(json.getAttributeContentByPath("files/$n"), "JSONObject")
val filetype = fileInfo.getAttributeContentByPath("format").split(".")[1]
val filename = fileInfo.getAttributeContentByPath("name")
val fileDescription = fileInfo.getAttributeContent("description")
// handling text
if ("txt json".contains(filetype)) {
val fileView = FileView(filetype, filename, fileDescription, "$serverId|||||$filename.$filetype")
if (!FileViewsSupplier.checkIfContains(fileView)) {
FileViewsSupplier.appendFileView(fileView)
updateFileViewsRecyclerView()
}
}
// handling images
else if ("jpg png".contains(filetype)) {
val fileView = FileView(filetype, filename, fileDescription, null, "$serverId|||||$filename.$filetype")
if (!FileViewsSupplier.checkIfContains(fileView)) {
FileViewsSupplier.appendFileView(fileView)
updateFileViewsRecyclerView()
}
}
// handling pdf files
else if ("pdf".contains(filetype)) {
val fileView = FileView(filetype, filename, fileDescription, null, null, "${ServerManagement.baseUrl}files/$serverId/$filename.$filetype")
if (!FileViewsSupplier.checkIfContains(fileView)) {
FileViewsSupplier.appendFileView(fileView)
updateFileViewsRecyclerView()
}
}
else {
val fileView = FileView(filetype, filename, fileDescription, null, null, null, "${ServerManagement.baseUrl}files/$serverId/$filename.$filetype")
if (!FileViewsSupplier.checkIfContains(fileView)) {
FileViewsSupplier.appendFileView(fileView)
updateFileViewsRecyclerView()
}
}
}
} catch (e: Throwable) { println("[debug] exception in infoFragment load data request Exception: $e") }
}
}
val sensorsDataReceiver: (String) -> Unit = { data: String ->
try {
context?.let {
val json = JsonManager(requireContext(), data, "JSONObject")
val names = json.currentJsonObject!!.names()
LabeledValuesSupplier.wipeData()
if (names != null) {
for (n in 0 until names.length()) {
val labeledValue = LabeledValue(names[n].toString(), json.getAttributeContent(names[n].toString()))
if (!LabeledValuesSupplier.checkIfContains(labeledValue)) {
LabeledValuesSupplier.appendLabeledValue(labeledValue)
}
}
}
updateSensorsRecyclerView()
}
} catch (e: Throwable) { println("[debug] Exception in info fragment, load, sensorsDataReceiver : $e") }
}
context?.let {
ServerManagement.serverManager.getData(dataReceiver, requireContext(), serverId, "", "GET_WHOLE_ARRAY", 3)
ServerManagement.serverManager.addReceiverConnection(sensorsDataReceiver, requireContext(), "infoFragmentSensorsConnection",
serverId, ServerManagement.sensors_keyword)
}
}
private fun getContactInfoFromGeneralVariables(numberOfAttempts: Int = 8) {
class RetrieveContactInfoFromGeneralVariables(val attemptsCount: Int): Runnable {
override fun run() {
for (i in 0 until attemptsCount) {
phoneNumber = GeneralVariables.phoneNumber
email = GeneralVariables.email
if (phoneNumber != null) {
checkContactInformation()
break
}
if (email != null) {
checkContactInformation()
break
}
Thread.sleep(300)
}
}
}
val retrieveContactInfoFromGeneralVariables = Thread(RetrieveContactInfoFromGeneralVariables(numberOfAttempts))
retrieveContactInfoFromGeneralVariables.start()
}
private fun checkContactInformation() {
phoneNumber?.let {
try {
phoneBtn.post {
phoneBtn?.let {
phoneBtn.visibility = View.VISIBLE
}
}
} catch (e: Throwable) { println("[debug] Exception in checkContactInformation: $e") }
}
email?.let {
try {
emailBtn.post {
emailBtn?.let {
emailBtn.visibility = View.VISIBLE
}
}
} catch (e: Throwable) { println("[debug] Exception in checkContactInformation: $e") }
}
}
override fun onPause() {
ServerManagement.serverManager.deleteConnection("infoFragmentSensorsConnection")
super.onPause()
}
private fun updateSensorsRecyclerView() {
try {
labeled_values_recycler_view.post {
val layoutManager = LinearLayoutManager(context)
layoutManager.orientation = LinearLayoutManager.VERTICAL
labeled_values_recycler_view.layoutManager = layoutManager
val adapter = context?.let { LabeledValuesAdapter(it, LabeledValuesSupplier.labeledValues) }
labeled_values_recycler_view.adapter = adapter
}
} catch (e: Throwable) { println("[debug] e3 Exception: $e") }
}
private fun updateFileViewsRecyclerView() {
try {
file_views_recycler_view.post {
val layoutManager = LinearLayoutManager(context)
layoutManager.orientation = LinearLayoutManager.VERTICAL
file_views_recycler_view.layoutManager = layoutManager
val adapter = context?.let { FileViewsAdapter(it, FileViewsSupplier.fileViews) }
file_views_recycler_view.adapter = adapter
}
} catch (e: Throwable) { println("[debug] e2 Exception: $e") }
}
fun goExploreFragment() {
Navigation.findNavController(mainTitle).navigate(R.id.navigateBackToExploreFragment)
}
fun goMapFragment() {
val action = infoFragmentDirections.infoFragmentToMapFragment(LatLng(0.toDouble(), 0.toDouble()), true)
Navigation.findNavController(mainTitle).navigate(action)
}
}

@ -1,12 +1,99 @@
package com.example.wikispot.fragments package com.example.wikispot.fragments
import android.os.Bundle import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.navigation.Navigation
import androidx.navigation.fragment.navArgs
import com.example.wikispot.CustomBackstackVariables
import com.example.wikispot.MapManagement
import com.example.wikispot.R import com.example.wikispot.R
import com.example.wikispot.ServerManagement
import com.example.wikispot.modelsForAdapters.PlaceSupplier
import com.google.android.gms.maps.CameraUpdateFactory
import com.google.android.gms.maps.GoogleMap
import com.google.android.gms.maps.OnMapReadyCallback
import com.google.android.gms.maps.SupportMapFragment
import com.google.android.gms.maps.model.LatLng
import com.google.android.gms.maps.model.Marker
import com.google.android.gms.maps.model.MarkerOptions
import kotlinx.android.synthetic.main.fragment_map.*
import java.time.Clock
class mapFragment : Fragment(), GoogleMap.OnMarkerClickListener {
class mapFragment : Fragment(R.layout.fragment_map) { val args: mapFragmentArgs by navArgs()
private var loadFromMapManager = true
private var loadLastCoordinates = false
var location: LatLng? = null
var lastClickedMarkerTitle = ""
private val callback = OnMapReadyCallback { googleMap ->
/**
* Manipulates the map once available.
* This callback is triggered when the map is ready to be used.
* This is where we can add markers or lines, add listeners or move the camera.
* In this case, we just add a marker near Sydney, Australia.
* If Google Play services is not installed on the device, the user will be prompted to
* install it inside the SupportMapFragment. This method will only be triggered once the
* user has installed Google Play services and returned to the app.
*/
/*val pb = LatLng(49.11274928646463, 18.443442353021045)
googleMap.addMarker(MarkerOptions().position(pb).title("Povazska Bystrica"))
googleMap.moveCamera(CameraUpdateFactory.newLatLng(pb))
googleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(pb, 16.0f)) */
try {
location = args.location
loadLastCoordinates = args.loadLastCoordinates
loadFromMapManager = false
} catch (e: Throwable) { println("[debug] Exception in Map Fragment while getting args: $e") }
if (loadFromMapManager) {
googleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(MapManagement.connectedServerPosition, 15.0F))
} else if (loadLastCoordinates){
googleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(MapManagement.lastCoordinates, 15F))
} else {
googleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(location, 15.0F))
}
// loading other markers
for (n in PlaceSupplier.places.indices) {
val coordinates = PlaceSupplier.places[n]?.location!!.split(",")
googleMap.addMarker(MarkerOptions().position(LatLng(coordinates[0].toDouble(), coordinates[1].toDouble())).title(PlaceSupplier.places[n]?.title)
.snippet(PlaceSupplier.places[n]!!.description))
}
googleMap.setOnMarkerClickListener(this)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_map, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val mapFragment = childFragmentManager.findFragmentById(R.id.map) as SupportMapFragment?
mapFragment?.getMapAsync(callback)
}
override fun onMarkerClick(marker: Marker?): Boolean {
marker?.let {
if (marker.title == lastClickedMarkerTitle) {
for (n in PlaceSupplier.places.indices) {
if (marker.title == PlaceSupplier.places[n]!!.title) {
CustomBackstackVariables.infoFragmentBackDestination = "mapFragment"
MapManagement.lastCoordinates = marker.position
ServerManagement.selectedServerId = PlaceSupplier.places[n]!!.id!!
val action = mapFragmentDirections.mapFragmentToInfoFragment(true)
Navigation.findNavController(navControllerView).navigate(action)
}
}
}
lastClickedMarkerTitle = marker.title
}
return false
}
} }

@ -1,12 +1,69 @@
package com.example.wikispot.fragments package com.example.wikispot.fragments
import android.content.Intent
import android.content.res.Resources
import android.os.Bundle import android.os.Bundle
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import androidx.navigation.Navigation
import com.example.wikispot.R import com.example.wikispot.*
import com.example.wikispot.activities.MainActivity
import com.example.wikispot.modelClasses.ServerManager
import com.example.wikispot.modelClasses.SettingsSaveManager
import kotlinx.android.synthetic.main.fragment_settings.*
import javax.xml.transform.sax.TemplatesHandler
class settingsFragment : Fragment(R.layout.fragment_settings) { class settingsFragment : Fragment(R.layout.fragment_settings) {
private lateinit var settingsSaveManager: SettingsSaveManager
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
StartDirections.settingsFragmentStartDirection?.let {
when (StartDirections.settingsFragmentStartDirection) {
"debugFragment" -> {
Navigation.findNavController(debugBtn).navigate(R.id.navigateToDebugFragment)
StartDirections.settingsFragmentStartDirection = null
}
}
}
settingsSaveManager = SettingsSaveManager(requireContext())
loadSettings()
debugBtn.setOnClickListener {
Navigation.findNavController(it).navigate(R.id.navigateToDebugFragment)
}
darkThemeSwitch.setOnCheckedChangeListener { _, isChecked ->
ThemeOptions.darkTheme = isChecked
settingsSaveManager.saveSettings()
restartAppPartially()
}
moreColorsSwitch.setOnCheckedChangeListener { _, isChecked ->
ThemeOptions.moreColors = isChecked
settingsSaveManager.saveSettings()
restartAppPartially()
}
}
private fun loadSettings() {
darkThemeSwitch.isChecked = ThemeOptions.darkTheme
moreColorsSwitch.isChecked = ThemeOptions.moreColors
}
private fun restartAppPartially() {
val intent = Intent(context?.applicationContext, MainActivity::class.java)
intent.putExtra(IntentsKeys.startFragment, "settingsFragment")
ServerManagement.serverManager.clearConnections()
startActivity(intent)
activity?.finish()
}
} }

@ -0,0 +1,226 @@
package com.example.wikispot.modelClasses
import android.content.Context
import com.example.wikispot.GeneralVariables
import com.example.wikispot.getStringFromSharedPreferences
import com.example.wikispot.saveString
import com.example.wikispot.showToast
import org.json.JSONArray
import org.json.JSONObject
data class JsonManager(private val context: Context, val data: String, val inputType: String = "JSONArray", val debug: Boolean = false) {
var jsonArray: JSONArray? = null
var currentJsonObject: JSONObject? = null
var currentJsonAttribute0: JSONObject? = null
var currentJsonAttribute1: JSONArray? = null
init {
if (inputType == "JSONArray") {
jsonArray = JSONArray(data)
try {
currentJsonObject = jsonArray!!.getJSONObject(0)
} catch (exception: Throwable) {}
if (debug) {
println("[debug] Content of received JSONArray is ${jsonArray.toString()}")
}
} else if (inputType == "JSONObject") {
currentJsonObject = JSONObject(data)
if (debug) {
println("[debug] Content of received JSONObject is ${currentJsonObject.toString()}")
}
}
}
fun getJsonObject(i: Int): JSONObject? {
jsonArray?.let {
if ((i >= jsonArray!!.length()) or (i < 0)) {
context.showToast("Index out of range")
} else {
currentJsonObject = jsonArray?.getJSONObject(i)
return currentJsonObject
}
}
if (jsonArray == null) {
context.showToast("Json Array is null")
}
return null
}
fun getAttributeContent(name: String): String {
if (currentJsonObject != null) {
if (currentJsonAttribute0 != null) {
try {
currentJsonAttribute0 = currentJsonAttribute0!!.getJSONObject(name)
return currentJsonAttribute0.toString()
} catch (exception: Throwable) {
try {
currentJsonAttribute1 = currentJsonAttribute0!!.getJSONArray(name)
currentJsonAttribute0 = null
return currentJsonAttribute1.toString()
} catch (exception: Throwable) {
try {
return currentJsonAttribute0!!.get(name).toString()
} catch (exception: Throwable) {
if (debug) {
context.showToast("Invalid attribute name: $name")
}
}
}
}
}
if (currentJsonAttribute1 != null) {
try {
currentJsonAttribute0 = currentJsonAttribute1!!.getJSONObject(name.toInt())
return currentJsonAttribute0.toString()
} catch (exception: Throwable) {
try {
currentJsonAttribute1 = currentJsonAttribute1!!.getJSONArray(name.toInt())
currentJsonAttribute0 = null
return currentJsonAttribute1.toString()
} catch (exception: Throwable) {
try {
return currentJsonAttribute1!!.get(name.toInt()).toString()
} catch (exception: Throwable) {
if (debug) {
context.showToast("Invalid attribute name: $name")
}
}
}
}
} else {
try {
currentJsonAttribute0 = currentJsonObject!!.getJSONObject(name)
return currentJsonAttribute0.toString()
} catch (exception: Throwable) {
try {
currentJsonAttribute1 = currentJsonObject!!.getJSONArray(name)
currentJsonAttribute0 = null
return currentJsonAttribute1.toString()
} catch (exception: Throwable) {
try {
return currentJsonObject!!.get(name).toString()
} catch (exception: Throwable) {
if (debug) {
context.showToast("Invalid attribute name: $name")
}
}
}
}
}
} else {
if (debug) {
context.showToast("Invalid attribute name: $name")
}
}
return GeneralVariables.variableMissingKeyword
}
fun getAttributeContentByPath(path: String): String {
val steps = path.split("/")
val currentJsonAttributesBackup = listOf(currentJsonAttribute0, currentJsonAttribute1) // backing up selected jsonAttributes
// getting the attribute
clearSelectedAttribute()
var result: Any? = null
for (step in steps) {
try {
result = getAttributeContent(step)
} catch (exception: Throwable) {
context.showToast("Invalid path")
// loading back saved json attributes
currentJsonAttribute0 = currentJsonAttributesBackup[0] as JSONObject?
currentJsonAttribute1 = currentJsonAttributesBackup[1] as JSONArray?
return GeneralVariables.variableMissingKeyword
}
}
// loading back saved json attributes
currentJsonAttribute0 = currentJsonAttributesBackup[0] as JSONObject?
currentJsonAttribute1 = currentJsonAttributesBackup[1] as JSONArray?
// returning result
return result.toString()
}
fun clearSelectedAttribute() {
currentJsonAttribute0 = null
currentJsonAttribute1 = null
}
fun getLengthOfJsonArray(): Int {
return if (jsonArray != null) {
if (debug) {
println("[debug] Length of json array is ${jsonArray!!.length()}")
}
jsonArray!!.length()
} else {
println("[debug] Length of json array is 0")
0
}
}
fun getCurrentJsonAttributeContent(): String {
if (currentJsonAttribute0 != null) {
return currentJsonAttribute0.toString()
} else if (currentJsonAttribute1 != null) {
return currentJsonAttribute1.toString()
}
return "get json attribute first"
}
fun findJsonObjectByAttribute(attributePath: String, value: Any): String {
val currentJsonObjectSave = currentJsonObject
for (i in 0 until getLengthOfJsonArray()) {
getJsonObject(i)
val attributeContent = getAttributeContentByPath(attributePath)
if (attributeContent != GeneralVariables.variableMissingKeyword) {
if (attributeContent == value.toString()) {
return currentJsonObject.toString()
}
}
}
currentJsonObject = currentJsonObjectSave
return currentJsonObject.toString()
}
// saving and loading
fun saveJson(accessKey: String) {
// finding data that could be saved
if (jsonArray != null) {
context.saveString(accessKey, jsonArray.toString(), "jsonStrings")
} else if (currentJsonObject != null) {
context.saveString(accessKey, currentJsonObject.toString(), "jsonStrings")
} else if (currentJsonAttribute0 != null) {
context.saveString(accessKey, currentJsonAttribute0.toString(), "jsonStrings")
} else if (currentJsonAttribute1 != null) {
context.saveString(accessKey, currentJsonAttribute1.toString(), "jsonStrings")
} else {
context.showToast("Nothing to save")
}
}
fun loadJson(context: Context, accessKey: String, inputType: String = "JSONArray", debug: Boolean = false): JsonManager {
return JsonManager(context, context.getStringFromSharedPreferences(accessKey, "jsonStrings"), inputType, debug)
}
}

@ -0,0 +1,135 @@
package com.example.wikispot.modelClasses
import org.json.JSONArray
import org.json.JSONObject
data class JsonManagerLite(val data: String, val inputType: String = "JSONArray") {
var jsonArray: JSONArray? = null
var currentJsonObject: JSONObject? = null
private var currentJsonAttribute0: JSONObject? = null
private var currentJsonAttribute1: JSONArray? = null
init {
if (inputType == "JSONArray") {
jsonArray = JSONArray(data)
try {
currentJsonObject = jsonArray!!.getJSONObject(0)
} catch (exception: Throwable) {}
} else if (inputType == "JSONObject") {
currentJsonObject = JSONObject(data)
}
}
fun getJsonObject(i: Int): JSONObject? {
jsonArray?.let {
currentJsonObject = jsonArray?.getJSONObject(i)
return currentJsonObject
}
return null
}
fun getAttributeContent(name: String): String {
if (currentJsonObject != null) {
if (currentJsonAttribute0 != null) {
try {
currentJsonAttribute0 = currentJsonAttribute0!!.getJSONObject(name)
return currentJsonAttribute0.toString()
} catch (exception: Throwable) {
try {
currentJsonAttribute1 = currentJsonAttribute0!!.getJSONArray(name)
currentJsonAttribute0 = null
return currentJsonAttribute1.toString()
} catch (exception: Throwable) {
try {
return currentJsonAttribute0!!.get(name).toString()
} catch (exception: Throwable) { }
}
}
} else if (currentJsonAttribute1 != null) {
try {
currentJsonAttribute0 = currentJsonAttribute1!!.getJSONObject(name.toInt())
return currentJsonAttribute0.toString()
} catch (exception: Throwable) {
try {
currentJsonAttribute1 = currentJsonAttribute1!!.getJSONArray(name.toInt())
currentJsonAttribute0 = null
return currentJsonAttribute1.toString()
} catch (exception: Throwable) {
try {
return currentJsonAttribute1!!.get(name.toInt()).toString()
} catch (exception: Throwable) { }
}
}
} else {
try {
currentJsonAttribute0 = currentJsonObject!!.getJSONObject(name)
return currentJsonAttribute0.toString()
} catch (exception: Throwable) {
try {
currentJsonAttribute1 = currentJsonObject!!.getJSONArray(name)
currentJsonAttribute0 = null
return currentJsonAttribute1.toString()
} catch (exception: Throwable) {
try {
return currentJsonObject!!.get(name).toString()
} catch (exception: Throwable) { }
}
}
}
}
return ""
}
fun getAttributeContentByPath(path: String): String {
val steps = path.split("/")
val currentJsonAttributesBackup = listOf(currentJsonAttribute0, currentJsonAttribute1) // backing up selected jsonAttributes
// getting the attribute
clearSelectedAttributes()
var result: Any? = null
for (step in steps) {
try {
result = getAttributeContent(step)
} catch (exception: Throwable) {
// loading back saved json attributes
currentJsonAttribute0 = currentJsonAttributesBackup[0] as JSONObject?
currentJsonAttribute1 = currentJsonAttributesBackup[1] as JSONArray?
return ""
}
}
// loading back saved json attributes
currentJsonAttribute0 = currentJsonAttributesBackup[0] as JSONObject?
currentJsonAttribute1 = currentJsonAttributesBackup[1] as JSONArray?
// returning result
return result.toString()
}
fun clearSelectedAttributes() {
currentJsonAttribute0 = null
currentJsonAttribute1 = null
}
fun getLengthOfJsonArray(): Int {
return if (jsonArray != null) {
jsonArray!!.length()
} else {
0
}
}
fun getCurrentJsonAttributeContent(): String {
if (currentJsonAttribute0 != null) {
return currentJsonAttribute0.toString()
} else if (currentJsonAttribute1 != null) {
return currentJsonAttribute1.toString()
}
return "get json attribute first"
}
}

@ -0,0 +1,551 @@
package com.example.wikispot.modelClasses
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.widget.TextView
import com.example.wikispot.GeneralVariables
import com.example.wikispot.ScreenParameters
import com.example.wikispot.ServerManagement
import com.example.wikispot.modelsForAdapters.MessagesSupplier
import com.github.barteksc.pdfviewer.PDFView
import okhttp3.*
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import org.json.JSONArray
import org.json.JSONObject
import java.io.IOException
class ServerManager {
private var receiverConnections = mutableListOf<ReceiverConnection>()
private var viewConnections = mutableListOf<ViewConnection>()
private var chatConnections = mutableListOf<ChatConnection>()
// single time requests
fun getData(dataReceiver: (String) -> Unit, context: Context, serverId: Int, path: String, attributePath: String = "", numberOfAttempts: Int = 2) {
val dataRequestThread = Thread(DataRequest(dataReceiver, context, serverId, path, attributePath, numberOfAttempts))
dataRequestThread.start()
}
inner class DataRequest(val dataReceiver: (String) -> Unit, val context: Context, val serverId: Int, val path: String = "", val attributePath: String, private val numberOfAttempts: Int = 2): Runnable{
override fun run() {
for (n in 0 until numberOfAttempts) {
var url = "${ServerManagement.baseUrl}devices_list"
if (path != "") {
if (path.contains(ServerManagement.sensors_keyword)){
url = "${ServerManagement.baseUrl}$serverId/sensors"
} else {
url = "${ServerManagement.baseUrl}files/$serverId/$path"
}
}
val request = Request.Builder().url(url).build()
val client = OkHttpClient()
client.newCall(request).enqueue(object : Callback {
override fun onResponse(call: Call, response: Response) {
response.body?.let {
val receivedString = response.body!!.string()
if (receivedString == "Internal Server Error") {
return
}
try {
JSONArray(receivedString)
val jsonManager = JsonManager(context, receivedString)
if (path == "") {
if (attributePath == "GET_WHOLE_ARRAY") {
dataReceiver(jsonManager.jsonArray.toString())
return
}
jsonManager.getJsonObject(0)
} else {
if (attributePath == "") {
throw Throwable()
}
}
if (attributePath != "") {
dataReceiver(jsonManager.getAttributeContentByPath(attributePath))
} else {
dataReceiver(jsonManager.currentJsonObject.toString())
}
} catch (exception: Throwable) {
try {
JSONObject(receivedString)
val jsonManager = JsonManager(context, receivedString, "JSONObject")
if (attributePath != "") {
dataReceiver(jsonManager.getAttributeContentByPath(attributePath))
} else {
dataReceiver(jsonManager.currentJsonObject.toString())
}
} catch (exception: Throwable) {
dataReceiver(receivedString)
}
}
}
}
override fun onFailure(call: Call, e: IOException) {
println("Request Failed")
println(e)
}
})
ServerManagement.totalNumberOfRequestsSent += 1
Thread.sleep(ServerManagement.dataRequestOnAttemptWait)
}
}
}
fun getImage(imageReceiver: (Bitmap) -> Unit, serverId: Int, path: String, numberOfAttempts: Int = 2) {
val imageRequestThread = Thread(ImageRequest(imageReceiver, serverId, path, numberOfAttempts))
imageRequestThread.start()
}
inner class ImageRequest(val imageReceiver: (Bitmap) -> Unit, val serverId: Int, val path: String, private val numberOfAttempts: Int): Runnable {
override fun run() {
for (i in 0 until numberOfAttempts) {
val url = "${ServerManagement.baseUrl}files/$serverId/$path"
try {
val inputStream = java.net.URL(url).openStream()
var bitmap = BitmapFactory.decodeStream(inputStream)
if (bitmap.width > ScreenParameters.width) {
val ratio = bitmap.height.toFloat() / bitmap.width.toFloat()
bitmap = Bitmap.createScaledBitmap(bitmap, ScreenParameters.width, (ScreenParameters.width * ratio).toInt(), false)
}
imageReceiver(bitmap)
break
} catch (e: Throwable) { println(e) }
ServerManagement.totalNumberOfRequestsSent += 1
Thread.sleep(ServerManagement.imageRequestOnAttemptWait)
}
}
}
fun loadPdfView(view: PDFView, url: String, swipeHorizontal: Boolean = false) {
val pdfLoadingRequestThread = Thread(PdfLoadingRequest(view, url, swipeHorizontal))
pdfLoadingRequestThread.start()
}
inner class PdfLoadingRequest(val view: PDFView, val url: String, private val swipeHorizontal: Boolean = false): Runnable {
override fun run() {
val inputStream = java.net.URL(url).openStream()
view.post {
view.fromStream(inputStream).swipeHorizontal(swipeHorizontal).load()
view.zoomTo(view.width / 490.0F)
}
}
}
// connections
fun clearConnections() {
for (i in 0 until receiverConnections.size) {
try {
receiverConnections[i].running = false
} catch (e: Throwable) { println("In clearConnections: $e") }
}
receiverConnections = mutableListOf()
for (i in 0 until viewConnections.size) {
try {
viewConnections[i].running = false
} catch (e: Throwable) { println("In clearConnections: $e") }
}
viewConnections = mutableListOf()
for (i in 0 until chatConnections.size) {
try {
chatConnections[i].running = false
} catch (e: Throwable) { println("In clearConnections: $e") }
}
chatConnections = mutableListOf()
}
fun checkIfConnectionAlreadyExists(connectionName: String, connectionType: String = "any"): Boolean{
if ((connectionType == "any") or (connectionType == "receiver")) {
for (n in 0 until receiverConnections.size) {
if (receiverConnections[n].connectionName == connectionName) {
return true
}
}
}
if ((connectionType == "any") or (connectionType == "view")) {
for (n in 0 until viewConnections.size) {
if (viewConnections[n].connectionName == connectionName) {
return true
}
}
}
if ((connectionType == "any") or (connectionType == "chat")) {
for (n in 0 until chatConnections.size) {
if (chatConnections[n].connectionName == connectionName) {
return true
}
}
}
return false
}
fun deleteConnection(connectionName: String, connectionType: String = "any") { // other types are any, activity and view
if ((connectionType == "any") or (connectionType == "receiver")) {
val indexesToRemove = mutableListOf<Int>()
for (i in 0 until receiverConnections.size) { // checking in connections
try {
if (receiverConnections[i].connectionName == connectionName) {
receiverConnections[i].running = false
indexesToRemove.add(i)
}
} catch (e: Throwable) { println("In deleteConnection: $e") }
}
for (i in 0 until indexesToRemove.size) {
receiverConnections.removeAt(indexesToRemove[i] - i)
}
}
if ((connectionType == "any") or (connectionType == "view")) {
val indexesToRemove = mutableListOf<Int>()
for (i in 0 until viewConnections.size) { // checking in connections
try {
if (viewConnections[i].connectionName == connectionName) {
viewConnections[i].running = false
indexesToRemove.add(i)
}
} catch (e: Throwable) { println("In deleteConnection: $e") }
}
for (i in 0 until indexesToRemove.size) {
viewConnections.removeAt(indexesToRemove[i] - i)
}
}
if ((connectionType == "any") or (connectionType == "chat")) {
val indexesToRemove = mutableListOf<Int>()
for (i in 0 until chatConnections.size) { // checking in connections
try {
if (chatConnections[i].connectionName == connectionName) {
chatConnections[i].running = false
indexesToRemove.add(i)
}
} catch (e: Throwable) { println("In deleteConnection: $e") }
}
for (i in 0 until indexesToRemove.size) {
chatConnections.removeAt(indexesToRemove[i] - i)
}
}
}
fun addReceiverConnection(dataReceiver: (String) -> Unit, context: Context, connectionName: String, serverId: Int, path: String = "", attributePath: String = "", waitTime: Long = ServerManagement.receiverConnectionOnCheckWait) {
receiverConnections.add(ReceiverConnection(dataReceiver, context, connectionName, serverId, path, attributePath, waitTime))
}
inner class ReceiverConnection(val dataReceiver: (String) -> Unit, val context: Context, val connectionName: String, val serverId: Int, val path: String = "", val attributePath: String, val waitTime: Long) {
var running = true
init {
val checkingServerDataThread = Thread(CheckingServerData())
checkingServerDataThread.start()
}
inner class CheckingServerData : Runnable {
override fun run() {
while (running) {
var url = "${ServerManagement.baseUrl}devices_list"
if (path != "") {
if (path.contains(ServerManagement.sensors_keyword)){
url = "${ServerManagement.baseUrl}$serverId/sensors"
} else {
url = "${ServerManagement.baseUrl}files/$serverId/$path"
}
}
val request = Request.Builder().url(url).build()
val client = OkHttpClient()
client.newCall(request).enqueue(object : Callback {
override fun onResponse(call: Call, response: Response) {
response.body?.let {
val receivedString = response.body!!.string()
if (receivedString == "Internal Server Error") {
return
}
try {
JSONArray(receivedString)
val jsonManager = JsonManager(context, receivedString)
if (path == "") {
if (attributePath == "GET_WHOLE_ARRAY") {
dataReceiver(jsonManager.jsonArray.toString())
return
}
jsonManager.getJsonObject(0)
} else {
if (attributePath == "") {
throw Throwable()
}
}
if (attributePath != "") {
dataReceiver(jsonManager.getAttributeContentByPath(attributePath))
} else {
dataReceiver(jsonManager.currentJsonObject.toString())
}
} catch (exception: Throwable) {
try {
JSONObject(receivedString)
val jsonManager = JsonManager(context, receivedString, "JSONObject")
if (attributePath != "") {
dataReceiver(jsonManager.getAttributeContentByPath(attributePath))
} else {
dataReceiver(jsonManager.currentJsonObject.toString())
}
} catch (exception: Throwable) {
dataReceiver(receivedString)
}
}
}
}
override fun onFailure(call: Call, e: IOException) {
println("Request Failed")
println(e)
}
})
ServerManagement.totalNumberOfRequestsSent += 1
Thread.sleep(waitTime)
}
}
}
}
fun addViewConnection(context: Context, view: TextView, connectionName: String, serverId: Int, path: String = "", attributePath: String = "") {
viewConnections.add(ViewConnection(context, view, connectionName, serverId, path, attributePath))
}
inner class ViewConnection(val context: Context, val view: TextView, val connectionName: String, val serverId: Int, val path: String = "", var attributePath: String) {
var running = true
init {
val checkingServerDataThread = Thread(CheckingServerData())
checkingServerDataThread.start()
}
inner class CheckingServerData: Runnable {
override fun run() {
while (running) {
var url = "${ServerManagement.baseUrl}devices_list"
if (path != "") {
if (path.contains(ServerManagement.sensors_keyword)){
url = "${ServerManagement.baseUrl}$serverId/sensors"
} else {
url = "${ServerManagement.baseUrl}files/$serverId/$path"
}
}
val request = Request.Builder().url(url).build()
val client = OkHttpClient()
client.newCall(request).enqueue(object : Callback {
override fun onResponse(call: Call, response: Response) {
response.body?.let {
val receivedString = response.body!!.string()
if (receivedString == "Internal Server Error") {
return
}
try {
JSONArray(receivedString)
val jsonManager = JsonManager(context, receivedString)
if (path == "") {
if (attributePath == "GET_WHOLE_ARRAY") {
view.text = jsonManager.jsonArray.toString()
return
}
jsonManager.getJsonObject(0)
} else {
if (attributePath == "") {
throw Throwable()
}
}
if (attributePath != "") {
view.post {
view.text = jsonManager.getAttributeContentByPath(attributePath)
}
} else {
view.post {
view.text = jsonManager.currentJsonObject.toString()
}
}
} catch (exception: Throwable) {
try {
JSONObject(receivedString)
val jsonManager = JsonManager(context, receivedString, "JSONObject")
if (attributePath != "") {
view.post {
view.text = jsonManager.getAttributeContentByPath(attributePath)
}
} else {
view.post {
view.text = jsonManager.currentJsonObject.toString()
}
}
} catch (exception: Throwable) {
view.post {
view.text = receivedString
}
}
}
}
}
override fun onFailure(call: Call, e: IOException) {
println("Request Failed")
println(e)
}
})
ServerManagement.totalNumberOfRequestsSent += 1
Thread.sleep(ServerManagement.viewConnectionOnCheckWait)
}
}
}
}
fun addChatConnection(dataReceiver: (String) -> Unit, context: Context, connectionName: String) {
chatConnections.add(ChatConnection(dataReceiver, context, connectionName))
}
inner class ChatConnection(val dataReceiver: (String) -> Unit, val context: Context, val connectionName: String) {
var running = true
init {
val checkingServerDataThread = Thread(CheckingServerData())
checkingServerDataThread.start()
}
inner class CheckingServerData : Runnable {
override fun run() {
while (running) {
if (GeneralVariables.id == null) {
val url = "${ServerManagement.baseUrl}messages/register"
println(url)
val request = Request.Builder().url(url).build()
val client = OkHttpClient()
client.newCall(request).enqueue(object : Callback {
override fun onResponse(call: Call, response: Response) {
response.body?.let {
val receivedString = response.body!!.string()
if (receivedString == "Internal Server Error") {
return
}
val returnJsonObject = JSONObject()
returnJsonObject.put("source", "messages/register")
returnJsonObject.put("data", JSONArray(receivedString))
dataReceiver(returnJsonObject.toString())
}
}
override fun onFailure(call: Call, e: IOException) {
println("Request Failed")
println(e)
}
})
} else {
var timestamp = "0"
if (MessagesSupplier.messages.isNotEmpty()) {
val messagesReversed = MessagesSupplier.messages.reversed()
for (i in messagesReversed.indices) {
if (messagesReversed[i]!!.timestamp != "waiting") {
timestamp = messagesReversed[i]!!.timestamp
break
}
}
}
val urlBuilder: HttpUrl.Builder = "${ServerManagement.baseUrl}messages/get".toHttpUrlOrNull()!!.newBuilder()
urlBuilder.addQueryParameter("timestamp", timestamp)
val url: String = urlBuilder.build().toString()
val request: Request = Request.Builder()
.url(url)
.build()
val client = OkHttpClient()
client.newCall(request).enqueue(object : Callback {
override fun onResponse(call: Call, response: Response) {
response.body?.let {
val receivedString = response.body!!.string()
if (receivedString == "Internal Server Error") {
return
}
val returnJsonObject = JSONObject()
returnJsonObject.put("source", "messages/get")
returnJsonObject.put("data", JSONArray(receivedString))
dataReceiver(returnJsonObject.toString())
}
}
override fun onFailure(call: Call, e: IOException) {
println("Request Failed")
println(e)
}
})
}
Thread.sleep(ServerManagement.chatConnectionOnCheckWait)
}
}
}
}
}

@ -0,0 +1,46 @@
package com.example.wikispot.modelClasses
import android.content.Context
import android.content.Intent
import android.content.res.Configuration
import androidx.appcompat.app.AppCompatDelegate
import com.example.wikispot.GeneralVariables
import com.example.wikispot.IntentsKeys
import com.example.wikispot.ThemeOptions
import com.example.wikispot.activities.MainActivity
class SettingsSaveManager(val context: Context) {
fun loadSettings() {
val sharedPreferences = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
ThemeOptions.darkTheme = sharedPreferences.getBoolean("darkMode", ThemeOptions.darkTheme)
ThemeOptions.moreColors = sharedPreferences.getBoolean("moreColors", ThemeOptions.moreColors)
// checking if we want to use system default theme
try {
GeneralVariables.appRunningFirstTime = sharedPreferences.getBoolean("appRunningFirstTime", true)
if (GeneralVariables.appRunningFirstTime) {
ThemeOptions.darkTheme = (context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES)
}
} catch (e: Throwable) {
println(e)
}
// saving settings cause some things might change based on system preferences
saveSettings()
}
fun saveSettings() {
val sharedPreferences = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
val editor = sharedPreferences.edit()
editor.apply{
putBoolean("appRunningFirstTime", false)
putBoolean("darkMode", ThemeOptions.darkTheme)
putBoolean("moreColors", ThemeOptions.moreColors)
}.apply()
}
}

@ -0,0 +1,107 @@
package com.example.wikispot.modelsForAdapters
import android.content.Context
import android.graphics.Bitmap
import com.example.wikispot.getStringFromSharedPreferences
import com.example.wikispot.modelClasses.JsonManager
import com.example.wikispot.modelClasses.JsonManagerLite
import com.example.wikispot.saveString
import org.json.JSONArray
data class PlacePreview(var title: String, var description: String, var location: String? = null, var img: Bitmap? = null, val id: Int?=null)
object PlaceSupplier {
var controlJson: JsonManagerLite? = null
var places = arrayOf<PlacePreview?>()
fun appendPlace(place: PlacePreview) {
val array = places.copyOf(places.size + 1)
array[places.size] = place
places = array
}
fun checkIfContains(place: PlacePreview): Boolean {
for (n in places.indices) {
places[n]?.let {
if (places[n]?.title == place.title) {
if (places[n]?.description == place.description) {
return true
}
}
}
}
return false
}
fun getContainingInstance(place: PlacePreview): PlacePreview? {
for (n in places.indices) {
places[n]?.let {
if (places[n]?.title == place.title) {
if (places[n]?.description == place.description) {
return places[n]
}
}
}
}
return null
}
// loading from and saving to cache
fun loadFromCache(context: Context) {
println("loading")
var save = context.getStringFromSharedPreferences("placePreviews", "exploreFragmentCache")
if (save.isEmpty()) {
save = "[]"
}
val jsonManager = JsonManager(context, save)
for (n in 0 until jsonManager.getLengthOfJsonArray()) {
val savedData = jsonManager.jsonArray?.get(n).toString().split("|||||")
val place = PlacePreview(savedData[0], savedData[1], savedData[2], null, savedData[3].toInt())
if (!checkIfContains(place)) {
appendPlace(place)
}
}
}
fun saveToCache(context: Context) {
val save = JSONArray()
var i = 0
for (n in places.indices) {
val place = places[n]
if (getSavePermission(place)) {
save.put(i, "${place!!.title}|||||${place.description}|||||${place.location}|||||${place.id}")
i++
}
}
context.saveString("placePreviews", save.toString(), "exploreFragmentCache")
}
private fun getSavePermission(place: PlacePreview?): Boolean {
if (controlJson == null) {
return true
}
place?.let {
for (n in 1 until controlJson!!.getLengthOfJsonArray()) {
controlJson!!.getJsonObject(n)
if (place.id == controlJson!!.getAttributeContent("ID").toInt()) {
if (place.title == controlJson!!.getAttributeContentByPath("description/title")) {
val tempPlace = PlacePreview("", controlJson!!.getAttributeContentByPath("description/description_s"),
controlJson!!.getAttributeContentByPath("location"))
if (place.description == tempPlace.description) {
if (place.location == tempPlace.location) {
return true
}
}
}
}
}
}
return false
}
}

@ -0,0 +1,34 @@
package com.example.wikispot.modelsForAdapters
data class FileView(val filetype: String, val filename: String, val fileDescription: String,
var textInfo: String? = null, var imgInfo: String? = null, var pdfUrl: String? = null,
var generalUrl: String? = null)
object FileViewsSupplier {
var fileViews = arrayOf<FileView?>()
fun appendFileView(fileView: FileView) {
val array = fileViews.copyOf(fileViews.size + 1)
array[fileViews.size] = fileView
fileViews = array
}
fun checkIfContains(fileView: FileView): Boolean{
for (n in fileViews.indices) {
if (fileViews[n]!!.filename == fileView.filename) {
if (fileViews[n]!!.filetype == fileView.filetype) {
return true
}
}
}
return false
}
fun wipeData() {
fileViews = arrayOf()
}
}

@ -0,0 +1,97 @@
package com.example.wikispot.modelsForAdapters
import com.example.wikispot.GeneralVariables
import com.example.wikispot.databases.NamesDatabase
import java.util.*
data class Message(var senderId: String, val content: String, var timestamp: String="0") {
var senderName: String
init {
val r = getRandomGenerator(senderId)
senderName = "${NamesDatabase.names[r.nextInt(NamesDatabase.names.size)]} - ${r.nextInt(9999)}"
}
private fun getRandomGenerator(seedString: String): Random {
var n: Long = 0
for (element in seedString) {
n += element.toInt()
}
return Random(n)
}
}
object MessagesSupplier {
var messages = arrayOf<Message?>()
fun appendMessage(message: Message) {
val array = messages.copyOf(messages.size + 1)
array[messages.size] = message
messages = array
if (messages.size > GeneralVariables.max_amount_of_saved_messages) {
deleteMessageByIndex(0)
println(messages.size)
}
}
private fun deleteMessageByIndex(i: Int) {
messages = messages.copyOfRange(0, i) + messages.copyOfRange(i + 1, messages.size)
}
fun checkIfContains(message: Message, checkTimestamp: Boolean=true): Boolean {
for (i in messages.indices) {
messages[i]?.let {
if (message.senderId == it.senderId) {
if (message.content == it.content) {
if (checkTimestamp) {
if (message.timestamp == it.timestamp) {
return true
}
} else {
return true
}
}
}
}
}
return false
}
fun clearWaitingMessages() {
val positionsOfItemsToRemove = mutableListOf<Int>()
for (i in messages.indices) {
if (messages[i]!!.timestamp == "waiting") {
positionsOfItemsToRemove.add(i)
println("waiting at: $i")
}
}
var subtractAmount = 0
for (index in positionsOfItemsToRemove) {
deleteMessageByIndex(index - subtractAmount)
subtractAmount += 1
}
}
fun getIndexOfLastMessageFromSelf(): Int? {
var i: Int? = null
for (n in messages.indices) {
if (messages[n]!!.senderId == GeneralVariables.id) {
i = n
}
}
return i
}
fun wipeData() {
messages = arrayOf()
}
}

@ -0,0 +1,30 @@
package com.example.wikispot.modelsForAdapters
data class LabeledValue(val label: String, val value: String)
object LabeledValuesSupplier {
var labeledValues = arrayOf<LabeledValue?>()
fun appendLabeledValue(labeledValue: LabeledValue) {
val array = labeledValues.copyOf(labeledValues.size + 1)
array[labeledValues.size] = labeledValue
labeledValues = array
}
fun checkIfContains(labeledValue: LabeledValue): Boolean{
for (n in labeledValues.indices) {
if (labeledValues[n]!!.label == labeledValue.label) {
return true
}
}
return false
}
fun wipeData() {
labeledValues = arrayOf()
}
}

@ -0,0 +1,73 @@
package com.example.wikispot
import com.example.wikispot.modelClasses.ServerManager
import com.google.android.gms.maps.model.LatLng
import org.json.JSONArray
object GeneralVariables {
var appRunningFirstTime = true
var id: String? = null
var name: String? = null
var email:String? = null
var phoneNumber: Int? = null
const val max_amount_of_saved_messages = 32
const val variableMissingKeyword = "_[{(V,a,r,i,a,b,l,e, ,m,i,s,s,i,n,g)}]_"
const val translatePrefix = "[translate]-"
}
object IntentsKeys {
const val startFragment = "start_fragment"
}
object ServerManagement {
var serverManager = ServerManager()
const val receiverConnectionOnCheckWait: Long = 4000
const val viewConnectionOnCheckWait: Long = 5000
const val chatConnectionOnCheckWait: Long = 1000
const val dataRequestOnAttemptWait: Long = 2000
const val imageRequestOnAttemptWait: Long = 2000
var baseUrl = "http://192.168.1.156:8000/"
var connectedServerId: Int? = null
var selectedServerId = 0
const val sensors_keyword = "_[{(S,e,n,s,o,r,s)}]_"
var totalNumberOfRequestsSent = 0
}
object MapManagement {
var connectedServerPosition: LatLng? = LatLng(0.toDouble(), 0.toDouble())
var lastCoordinates = LatLng(0.toDouble(), 0.toDouble())
}
object ScreenParameters {
var height = 1
var width = 1
}
object CustomBackstackVariables {
var infoFragmentBackDestination = "exploreFragment"
}
object ThemeOptions {
var darkTheme = false
var moreColors = false
}
object StartDirections {
var settingsFragmentStartDirection: String? = null
}

@ -0,0 +1,15 @@
import requests
json_list = []
def init():
global json_list
json_list = eval(requests.get("http://192.168.1.230:8000/devices_list").text)
def get_length():
return len(json_list)
def get_json(i):
return json_list[i]

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" android:fillAfter="true">
<rotate
android:fromDegrees="-180"
android:toDegrees="0"
android:pivotX="50%"
android:pivotY="50%"
android:duration="600"/>
</set>

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" android:fillAfter="true">
<alpha
android:fromAlpha="0"
android:toAlpha="1"
android:duration="600"/>
</set>

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" android:fillAfter="true">
<alpha
android:fromAlpha="1"
android:toAlpha="0"
android:duration="600"/>
</set>

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" android:fillAfter="true">
<rotate
android:fromDegrees="0"
android:toDegrees="-180"
android:pivotX="50%"
android:pivotY="50%"
android:duration="600"/>
</set>

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true" android:color="?attr/bottomNavBarCheckedItemColor" />
<item android:color="?attr/colorOnPrimary" />
</selector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 304 B

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:centerX="1"
android:startColor="?attr/bottomNavBarGradientStartColor"
android:endColor="?attr/bottomNavBarGradientEndColor"
android:angle="45"
android:type="sweep" />
</shape>
<!-- <gradient
android:centerX="1"
android:startColor="#FCDD94"
android:endColor="#C5F8AB"
android:angle="45"
android:type="sweep" /> -->

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:startColor="?attr/chatFragmentGradientStartColor"
android:centerColor="?attr/chatFragmentGradientCenterColor"
android:endColor="?attr/chatFragmentGradientEndColor"
android:angle="90"
/>
<stroke
android:color="?attr/chatFragmentBgStrokeColor"
android:width="10dp"/>
</shape>

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:startColor="?attr/contrastingGradientStartColor"
android:centerColor="?attr/contrastingGradientCenterColor"
android:endColor="?attr/contrastingGradientEndColor"/>
</shape>

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M20,8h-2.81c-0.45,-0.78 -1.07,-1.45 -1.82,-1.96L17,4.41 15.59,3l-2.17,2.17C12.96,5.06 12.49,5 12,5c-0.49,0 -0.96,0.06 -1.41,0.17L8.41,3 7,4.41l1.62,1.63C7.88,6.55 7.26,7.22 6.81,8L4,8v2h2.09c-0.05,0.33 -0.09,0.66 -0.09,1v1L4,12v2h2v1c0,0.34 0.04,0.67 0.09,1L4,16v2h2.81c1.04,1.79 2.97,3 5.19,3s4.15,-1.21 5.19,-3L20,18v-2h-2.09c0.05,-0.33 0.09,-0.66 0.09,-1v-1h2v-2h-2v-1c0,-0.34 -0.04,-0.67 -0.09,-1L20,10L20,8zM14,16h-4v-2h4v2zM14,12h-4v-2h4v2z"/>
</vector>

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:startColor="?attr/exploreFragmentGradientStartColor"
android:centerColor="?attr/exploreFragmentGradientCenterColor"
android:endColor="?attr/exploreFragmentGradientEndColor"
android:type="linear"
android:angle="-90"
/>
</shape>

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:startColor="?attr/homeFragmentGradientStartColor"
android:centerColor="?attr/homeFragmentGradientCenterColor"
android:endColor="?attr/homeFragmentGradientEndColor"
android:angle="135" />
</shape>
<!-- <gradient
android:startColor="#79AFFB"
android:centerColor="#9E94F8"
android:endColor="#EB8FFB"
android:angle="135" /> -->

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/generalIconsColor">
<path
android:fillColor="@android:color/white"
android:pathData="M7,10l5,5 5,-5z"/>
</vector>

@ -3,7 +3,7 @@
android:height="24dp" android:height="24dp"
android:viewportWidth="24" android:viewportWidth="24"
android:viewportHeight="24" android:viewportHeight="24"
android:tint="?attr/colorControlNormal"> android:tint="?attr/generalIconsColor">
<path <path
android:fillColor="@android:color/white" android:fillColor="@android:color/white"
android:pathData="M20,2L4,2c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM6,9h12v2L6,11L6,9zM14,14L6,14v-2h8v2zM18,8L6,8L6,6h12v2z"/> android:pathData="M20,2L4,2c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM6,9h12v2L6,11L6,9zM14,14L6,14v-2h8v2zM18,8L6,8L6,6h12v2z"/>

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/generalIconsColor">
<path
android:fillColor="@android:color/white"
android:pathData="M5,20h14v-2H5V20zM19,9h-4V3H9v6H5l7,7L19,9z"/>
</vector>

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/generalIconsColor">
<path
android:fillColor="@android:color/white"
android:pathData="M20,4L4,4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2zM20,8l-8,5 -8,-5L4,6l8,5 8,-5v2z"/>
</vector>

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/generalIconsColor">
<path
android:fillColor="@android:color/white"
android:pathData="M21,19V5c0,-1.1 -0.9,-2 -2,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM8.5,13.5l2.5,3.01L14.5,12l4.5,6H5l3.5,-4.5z"/>
</vector>

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/generalIconsColor">
<path
android:fillColor="@android:color/white"
android:pathData="M6.62,10.79c1.44,2.83 3.76,5.14 6.59,6.59l2.2,-2.2c0.27,-0.27 0.67,-0.36 1.02,-0.24 1.12,0.37 2.33,0.57 3.57,0.57 0.55,0 1,0.45 1,1V20c0,0.55 -0.45,1 -1,1 -9.39,0 -17,-7.61 -17,-17 0,-0.55 0.45,-1 1,-1h3.5c0.55,0 1,0.45 1,1 0,1.25 0.2,2.45 0.57,3.57 0.11,0.35 0.03,0.74 -0.25,1.02l-2.2,2.2z"/>
</vector>

@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/generalIconsColor"
android:autoMirrored="true">
<path
android:fillColor="@android:color/white"
android:pathData="M2.01,21L23,12 2.01,3 2,10l15,2 -15,2z"/>
</vector>

@ -1,170 +0,0 @@
<?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,19 @@
<?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"
android:tint="#2E0F4B">
<group android:scaleX="0.21506493"
android:scaleY="0.21506493"
android:translateX="29.16"
android:translateY="36.080723">
<group android:translateY="136.26562">
<path android:pathData="M83.765625,-16.046875L67.109375,-78L50.234375,-16.046875Q48.265625,-9,47.09375,-5.921875Q45.9375,-2.859375,43.046875,-0.421875Q40.171875,2,35.390625,2Q31.53125,2,29.03125,0.546875Q26.53125,-0.890625,24.984375,-3.53125Q23.4375,-6.1875,22.453125,-9.8125Q21.46875,-13.453125,20.703125,-16.5625L3.546875,-86.21875Q2,-92.296875,2,-95.46875Q2,-99.5,4.8125,-102.25Q7.625,-105,11.765625,-105Q17.46875,-105,19.4375,-101.3125Q21.40625,-97.640625,22.875,-90.625L36.375,-30L51.5,-86.734375Q53.1875,-93.25,54.515625,-96.640625Q55.859375,-100.046875,58.875,-102.515625Q61.90625,-105,67.109375,-105Q72.375,-105,75.296875,-102.40625Q78.21875,-99.828125,79.34375,-96.78125Q80.46875,-93.734375,82.359375,-86.734375L97.625,-30L111.125,-90.625Q112.109375,-95.375,112.984375,-98.0625Q113.859375,-100.75,116,-102.875Q118.15625,-105,122.234375,-105Q126.3125,-105,129.15625,-102.28125Q132,-99.5625,132,-95.46875Q132,-92.578125,130.45312,-86.21875L113.296875,-16.5625Q111.546875,-9.5,110.375,-6.21875Q109.21875,-2.9375,106.4375,-0.46875Q103.671875,2,98.609375,2Q93.828125,2,90.9375,-0.390625Q88.0625,-2.796875,86.9375,-5.78125Q85.8125,-8.78125,83.765625,-16.046875Z"
android:fillColor="#2E0F4B"/>
<path android:pathData="M224,-30.5Q224,-21.203125,219.23438,-13.796875Q214.46875,-6.390625,205.28125,-2.1875Q196.10938,2,183.51562,2Q168.42188,2,158.60938,-3.640625Q151.6875,-7.78125,147.35938,-14.703125Q143.03125,-21.625,143.03125,-28.171875Q143.03125,-31.96875,145.625,-34.671875Q148.21875,-37.375,152.23438,-37.375Q155.48438,-37.375,157.73438,-35.25Q159.98438,-33.125,161.57812,-28.9375Q163.54688,-23.984375,165.82812,-20.65625Q168.10938,-17.328125,172.25,-15.15625Q176.39062,-13,183.14062,-13Q192.40625,-13,198.20312,-17.359375Q204,-21.71875,204,-28.234375Q204,-33.40625,200.875,-36.625Q197.75,-39.84375,192.78125,-41.546875Q187.82812,-43.25,179.53125,-45.15625Q168.53125,-47.78125,161.10938,-51.28125Q153.70312,-54.796875,149.34375,-60.859375Q145,-66.921875,145,-75.921875Q145,-84.515625,149.59375,-91.171875Q154.1875,-97.84375,162.875,-101.421875Q171.5625,-105,183.32812,-105Q192.70312,-105,199.54688,-102.625Q206.40625,-100.25,210.92188,-96.3125Q215.45312,-92.375,217.53125,-88.046875Q219.625,-83.71875,219.625,-79.609375Q219.625,-75.84375,217.04688,-72.828125Q214.48438,-69.8125,210.65625,-69.8125Q207.15625,-69.8125,205.34375,-71.71875Q203.53125,-73.640625,201.40625,-78Q198.59375,-83.65625,194.65625,-86.828125Q190.71875,-90,182,-90Q173.92188,-90,168.95312,-86.421875Q164,-82.84375,164,-77.8125Q164,-74.703125,165.6875,-72.4375Q167.375,-70.171875,170.32812,-68.53125Q173.28125,-66.90625,176.29688,-65.984375Q179.32812,-65.0625,186.28125,-63.296875Q194.85938,-61.25,201.8125,-58.765625Q208.78125,-56.28125,213.65625,-52.734375Q218.53125,-49.203125,221.26562,-43.78125Q224,-38.359375,224,-30.5Z"
android:fillColor="#2E0F4B"/>
</group>
</group>
</vector>

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:startColor="?attr/infoFragmentGradientStartColor"
android:centerColor="?attr/infoFragmentGradientCenterColor"
android:endColor="?attr/infoFragmentGradientEndColor"
android:angle="225"/>
</shape>

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/generalIconsColor">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C8.13,2 5,5.13 5,9c0,5.25 7,13 7,13s7,-7.75 7,-13c0,-3.87 -3.13,-7 -7,-7zM12,11.5c-1.38,0 -2.5,-1.12 -2.5,-2.5s1.12,-2.5 2.5,-2.5 2.5,1.12 2.5,2.5 -1.12,2.5 -2.5,2.5z"/>
</vector>

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:centerY="0.7"
android:centerX="1.1"
android:startColor="?attr/settingsFragmentGradientStartColor"
android:centerColor="?attr/settingsFragmentGradientCenterColor"
android:endColor="?attr/settingsFragmentGradientEndColor"
android:type="sweep"/>
</shape>

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:startColor="?attr/textBackgroundGradientStartColor"
android:endColor="?attr/textBackgroundGradientEndColor"
android:angle="45"
android:type="linear" />
</shape>

@ -10,9 +10,13 @@
android:id="@+id/mainBottomNavigationView" android:id="@+id/mainBottomNavigationView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="70dp" android:layout_height="70dp"
app:itemRippleColor="?attr/bottomNavBarRippleColor"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:itemIconTint="@color/bottom_nav_bar_item_color"
app:itemTextColor="@color/bottom_nav_bar_item_color"
android:background="@drawable/bottom_nav_bar_gradient_background"
app:menu="@menu/main_bottom_nav_menu" /> app:menu="@menu/main_bottom_nav_menu" />
<fragment <fragment
@ -27,4 +31,5 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:navGraph="@navigation/main_activity_navigation" /> app:navGraph="@navigation/main_activity_navigation" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:backgroundTint="?attr/exploreFragmentListItemBackgroundColor"
card_view:cardCornerRadius="10dp"
card_view:cardElevation="4dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="4dp">
<ImageView
android:id="@+id/item_img"
android:layout_width="80dp"
android:layout_height="80dp"
card_view:layout_constraintBottom_toBottomOf="parent"
card_view:layout_constraintStart_toStartOf="parent"
card_view:layout_constraintTop_toTopOf="parent"
card_view:srcCompat="@drawable/ic_baseline_image_24" />
<TextView
android:id="@+id/item_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginTop="8dp"
android:textColor="?attr/generalTextColor"
android:textSize="20sp"
android:textStyle="bold"
card_view:layout_constraintStart_toEndOf="@+id/item_img"
card_view:layout_constraintTop_toTopOf="parent"
tools:text="Title" />
<TextView
android:id="@+id/item_description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:textColor="?attr/secondaryTextColor"
card_view:layout_constraintBottom_toBottomOf="parent"
card_view:layout_constraintEnd_toStartOf="@+id/item_location_img"
card_view:layout_constraintHorizontal_bias="0.15"
card_view:layout_constraintStart_toEndOf="@+id/item_img"
card_view:layout_constraintTop_toBottomOf="@+id/item_title"
tools:text="Description" />
<ImageView
android:id="@+id/item_location_img"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
card_view:layout_constraintBottom_toBottomOf="parent"
card_view:layout_constraintEnd_toEndOf="parent"
card_view:layout_constraintTop_toTopOf="parent"
card_view:srcCompat="@drawable/loacation_vector_asset" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>

@ -0,0 +1,107 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:layout_marginTop="2dp"
android:layout_marginRight="4dp"
android:layout_marginBottom="2dp"
android:backgroundTint="?attr/fileViewBackgroundColor"
card_view:cardCornerRadius="10dp"
card_view:cardElevation="0dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="4dp">
<TextView
android:id="@+id/filenameText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="28dp"
android:layout_marginTop="16dp"
android:textColor="?attr/generalTextColor"
android:textSize="24sp"
android:textStyle="bold"
card_view:layout_constraintStart_toStartOf="parent"
card_view:layout_constraintTop_toTopOf="parent"
tools:text="Filename" />
<ImageView
android:id="@+id/showFileBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="36dp"
android:layout_marginEnd="32dp"
android:scaleX="4"
android:scaleY="4"
card_view:layout_constraintEnd_toEndOf="parent"
card_view:layout_constraintTop_toTopOf="parent"
card_view:srcCompat="@drawable/ic_baseline_arrow_drop_down_24" />
<TextView
android:id="@+id/textContent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="52dp"
android:textColor="?attr/secondaryTextColor"
android:textSize="0sp"
card_view:layout_constraintBottom_toTopOf="@+id/imageContent"
card_view:layout_constraintEnd_toEndOf="parent"
card_view:layout_constraintHorizontal_bias="0.497"
card_view:layout_constraintStart_toStartOf="parent"
card_view:layout_constraintTop_toBottomOf="@+id/filenameText"
tools:ignore="SmallSp" />
<ImageView
android:id="@+id/imageContent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
card_view:layout_constraintBottom_toTopOf="@+id/pdfContent"
card_view:layout_constraintEnd_toEndOf="parent"
card_view:layout_constraintHorizontal_bias="0.5"
card_view:layout_constraintStart_toEndOf="@+id/filenameText"
card_view:layout_constraintStart_toStartOf="parent"
card_view:srcCompat="@drawable/blank" />
<com.github.barteksc.pdfviewer.PDFView
android:id="@+id/pdfContent"
android:layout_width="match_parent"
android:layout_height="300dp"
android:layout_margin="5dp"
android:visibility="gone"
card_view:layout_constraintBottom_toBottomOf="parent"
card_view:layout_constraintEnd_toEndOf="parent"
card_view:layout_constraintHorizontal_bias="0.5"
card_view:layout_constraintStart_toStartOf="parent" />
<ImageView
android:id="@+id/downloadFileBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="32dp"
android:visibility="gone"
card_view:layout_constraintBottom_toBottomOf="@+id/showFileBtn"
card_view:layout_constraintEnd_toStartOf="@+id/showFileBtn"
card_view:layout_constraintTop_toTopOf="@+id/showFileBtn"
card_view:srcCompat="@drawable/ic_baseline_download_24" />
<TextView
android:id="@+id/fileDescription"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:textColor="?attr/secondaryTextColor"
card_view:layout_constraintEnd_toStartOf="@+id/showFileBtn"
card_view:layout_constraintStart_toStartOf="@+id/filenameText"
card_view:layout_constraintTop_toBottomOf="@+id/filenameText"
tools:text="file_description" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>

@ -0,0 +1,124 @@
<?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="?attr/debugFragmentBackground"
tools:context=".fragments.anotherDebugFragment">
<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="another debug fragment"
android:textColor="#FFFFFF"
android:textSize="30sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/goFirstDegubFragmentBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginEnd="32dp"
android:textColor="@color/white"
android:text="1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView3" />
<Button
android:id="@+id/generateAndSaveDataBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:textColor="@color/white"
android:text="generate and save data"
app:layout_constraintEnd_toStartOf="@+id/goFirstDegubFragmentBtn"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView3" />
<Button
android:id="@+id/loadAndShowDataBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginTop="16dp"
android:textColor="@color/white"
android:text="load and show data"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/generateAndSaveDataBtn" />
<ScrollView
android:id="@+id/scrollView4"
android:layout_width="120dp"
android:layout_height="80dp"
android:layout_marginTop="16dp"
android:background="#E66C6C"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.05"
app:layout_constraintStart_toEndOf="@+id/loadAndShowDataBtn"
app:layout_constraintTop_toBottomOf="@+id/goFirstDegubFragmentBtn">
<TextView
android:id="@+id/dataContentView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="data content"
android:textAlignment="center" />
</ScrollView>
<EditText
android:id="@+id/attributePathInput"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="50dp"
android:ems="10"
android:textColor="@color/white"
android:hint="attribute path"
android:inputType="textPersonName"
app:layout_constraintStart_toStartOf="@+id/loadAndShowDataBtn"
app:layout_constraintTop_toBottomOf="@+id/loadAndShowDataBtn" />
<Button
android:id="@+id/createViewConnectionBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:textColor="@color/white"
android:text="create view connection"
android:textSize="12sp"
app:layout_constraintStart_toStartOf="@+id/attributePathInput"
app:layout_constraintTop_toBottomOf="@+id/attributePathInput" />
<Button
android:id="@+id/stopConnectionBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:textColor="@color/white"
android:text="stop connection"
android:textSize="13sp"
app:layout_constraintBottom_toBottomOf="@+id/attributePathInput"
app:layout_constraintStart_toEndOf="@+id/attributePathInput"
app:layout_constraintTop_toTopOf="@+id/attributePathInput" />
<EditText
android:id="@+id/filePathInput"
android:layout_width="160dp"
android:layout_height="wrap_content"
android:ems="10"
android:hint="file path"
android:inputType="textPersonName"
app:layout_constraintBottom_toTopOf="@+id/attributePathInput"
app:layout_constraintStart_toStartOf="@+id/loadAndShowDataBtn"
app:layout_constraintTop_toBottomOf="@+id/loadAndShowDataBtn" />
</androidx.constraintlayout.widget.ConstraintLayout>

@ -4,21 +4,55 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="#E11976D2" android:background="@drawable/chat_fragment_gradient_background"
tools:background="@drawable/chat_fragment_gradient_background"
tools:context=".fragments.chatFragment"> tools:context=".fragments.chatFragment">
<TextView <androidx.recyclerview.widget.RecyclerView
android:layout_width="wrap_content" android:id="@+id/chat_messages_recycler_view"
android:layout_height="wrap_content" android:layout_width="match_parent"
android:text="Chat Fragment" android:layout_height="0dp"
android:textStyle="bold" android:layout_margin="16dp"
android:textSize="24sp" app:layout_constraintBottom_toTopOf="@+id/writeBar"
android:textColor="@android:color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5" app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent" />
tools:text="Chat Fragment" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/writeBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:background="?attr/chatFragmentWriteBarBgColor"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent">
<EditText
android:id="@+id/userMessageText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_marginEnd="5dp"
android:ems="10"
android:inputType="textPersonName"
android:textColor="?attr/generalTextColor"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/sendMessageBtn"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/sendMessageBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:src="@drawable/ic_baseline_send_24"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

@ -0,0 +1,132 @@
<?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="?attr/debugFragmentBackground"
tools:context=".fragments.debugFragment">
<Button
android:id="@+id/goSecondDebugFragmentBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:textColor="@color/white"
android:text="2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/debugFragmentTitle" />
<Button
android:id="@+id/getNumberOfSentRequestsBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:textColor="@color/white"
android:text="get number of sent requests"
android:textSize="12sp"
app:layout_constraintEnd_toStartOf="@+id/goSecondDebugFragmentBtn"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/debugFragmentTitle" />
<ScrollView
android:id="@+id/scrollView3"
android:layout_width="252dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:textColor="?attr/generalTextColor"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/clearServerConnectionsBtn">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/outputText"
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="#6A5252"
android:backgroundTint="#E39595"
android:padding="4dp"
android:text="output" />
</LinearLayout>
</ScrollView>
<Button
android:id="@+id/changeUrlBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:textColor="@color/white"
android:text="change url"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/editTextIp" />
<EditText
android:id="@+id/editTextIp"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:ems="10"
android:inputType="textPersonName"
app:layout_constraintEnd_toEndOf="@+id/scrollView3"
app:layout_constraintStart_toStartOf="@+id/scrollView3"
app:layout_constraintTop_toBottomOf="@+id/scrollView3" />
<Button
android:id="@+id/clearServerConnectionsBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:textColor="@color/white"
android:text="clear server connections"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/getNumberOfSentRequestsBtn" />
<Button
android:id="@+id/restartAppPartiallyBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:textColor="@color/white"
android:text="restart app partially"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/changeUrlBtn" />
<TextView
android:id="@+id/debugFragmentTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:text="debug fragment"
android:textColor="#FFFFFF"
android:textSize="30sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/closeAppBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:textColor="@color/white"
android:text="close app"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/restartAppPartiallyBtn" />
</androidx.constraintlayout.widget.ConstraintLayout>

@ -4,20 +4,32 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="#C200897B" android:background="@drawable/explore_fragment_gradient_background"
tools:background="@drawable/explore_fragment_gradient_background"
tools:context=".fragments.exploreFragment"> tools:context=".fragments.exploreFragment">
<TextView <ScrollView
android:layout_width="wrap_content" android:id="@+id/scrollView2"
android:layout_height="wrap_content" android:layout_width="match_parent"
android:text="Explore Fragment" android:layout_height="0dp"
android:textStyle="bold" android:orientation="vertical"
android:textSize="24sp"
android:textColor="@android:color/white"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5" app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/explore_recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView" />
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

@ -1,23 +1,63 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="#7CB342" android:background="@drawable/home_fragment_gradient_background"
tools:context=".fragments.homeFragment"> tools:background="@drawable/home_fragment_gradient_background"
tools:context=".fragments.homeFragment"
android:orientation="vertical">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView <TextView
android:id="@+id/home_title"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Home Fragment" android:layout_marginTop="60dp"
android:textColor="@android:color/white" android:background="@drawable/text_background_gradient"
android:textSize="24sp" android:padding="5dp"
android:text="@string/home"
android:textColor="?attr/textOnTextBgColor"
android:textSize="28sp"
android:textStyle="bold" android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5" app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/homeFragmentInnerFragment"
android:name="com.example.wikispot.fragments.infoFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="32dp"
app:layout_constraintBottom_toTopOf="@+id/chatBtn"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/home_title"
app:layout_constraintVertical_bias="0.0">
</androidx.fragment.app.FragmentContainerView>
<ImageView
android:id="@+id/chatBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginBottom="32dp"
android:src="@drawable/ic_baseline_chat_24"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>

@ -0,0 +1,144 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView 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="@drawable/info_fragment_gradient_background"
tools:background="@drawable/info_fragment_gradient_background"
tools:context=".fragments.infoFragment">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/mainTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:background="@drawable/text_background_gradient"
android:padding="5dp"
android:text=""
android:textColor="?attr/textOnTextBgColor"
android:textSize="24sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/mainImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:src="@drawable/ic_baseline_image_24"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/mainTitle" />
<TextView
android:id="@+id/mainDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="16dp"
android:text=""
android:textAlignment="center"
android:textColor="?attr/secondaryTextColor"
android:textSize="18sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/mainImage" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/labeled_values_recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nestedScrollingEnabled="false"/>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:background="@drawable/text_background_gradient"
android:padding="4dp"
android:text="@string/files"
android:textAlignment="center"
android:textColor="?attr/textOnTextBgColor"
android:textSize="24sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/file_views_recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nestedScrollingEnabled="false" />
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/locationBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:src="@drawable/loacation_vector_asset"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/phoneBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:src="@drawable/ic_baseline_phone_24"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/emailBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="32dp"
android:src="@drawable/ic_baseline_email_24"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>

@ -1,23 +1,20 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent"
xmlns:tools="http://schemas.android.com/tools" xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal">
<fragment xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="#FBC02D" tools:context=".fragments.mapFragment" />
tools:context=".fragments.mapFragment">
<TextView <TextView
android:layout_width="wrap_content" android:id="@+id/navControllerView"
android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Map Fragment" android:layout_weight="1"
android:textColor="@android:color/white" android:text=""
android:textSize="24sp" android:visibility="gone" />
android:textStyle="bold" </LinearLayout>
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

@ -4,20 +4,67 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="#F57C00" android:background="@drawable/settings_fragment_gradient_background"
tools:background="@drawable/settings_fragment_gradient_background"
tools:context=".fragments.settingsFragment"> tools:context=".fragments.settingsFragment">
<ImageView
android:id="@+id/debugBtn"
android:layout_width="30dp"
android:layout_height="30dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/debug_vector_asset" />
<TextView <TextView
android:id="@+id/darkThemeSwitchText"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Settings Fragment" android:layout_marginStart="64dp"
android:textColor="@android:color/white" android:layout_marginTop="88dp"
android:text="@string/dark_theme"
android:background="@drawable/text_background_gradient"
android:padding="5dp"
android:textColor="?attr/textOnTextBgColor"
android:textSize="24sp" android:textSize="24sp"
android:textStyle="bold" android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<Switch
android:id="@+id/darkThemeSwitch"
android:layout_width="50dp"
android:layout_height="21dp"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
app:layout_constraintBottom_toBottomOf="@+id/darkThemeSwitchText"
app:layout_constraintStart_toEndOf="@+id/darkThemeSwitchText"
app:layout_constraintTop_toTopOf="@+id/darkThemeSwitchText"
tools:ignore="UseSwitchCompatOrMaterialXml" />
<TextView
android:id="@+id/moreColorsSwitchText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:text="@string/more_colors"
android:background="@drawable/text_background_gradient"
android:padding="5dp"
android:textColor="?attr/textOnTextBgColor"
android:textSize="24sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="@+id/darkThemeSwitchText"
app:layout_constraintStart_toStartOf="@+id/darkThemeSwitchText"
app:layout_constraintTop_toBottomOf="@+id/darkThemeSwitchText" />
<Switch
android:id="@+id/moreColorsSwitch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
app:layout_constraintBottom_toBottomOf="@+id/moreColorsSwitchText"
app:layout_constraintStart_toEndOf="@+id/moreColorsSwitchText"
app:layout_constraintTop_toTopOf="@+id/moreColorsSwitchText"
tools:ignore="UseSwitchCompatOrMaterialXml" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:backgroundTint="@color/invisible"
card_view:cardElevation="0dp"
card_view:cardCornerRadius="5dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/contrasting_gradient_background"
android:padding="4dp">
<TextView
android:id="@+id/label"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:textColor="?attr/generalTextColor"
android:textSize="30sp"
card_view:layout_constraintBottom_toBottomOf="parent"
card_view:layout_constraintEnd_toStartOf="@+id/value"
card_view:layout_constraintStart_toStartOf="parent"
card_view:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginEnd="32dp"
android:layout_marginBottom="8dp"
android:textColor="?attr/generalTextColor"
android:textSize="30sp"
card_view:layout_constraintBottom_toBottomOf="parent"
card_view:layout_constraintEnd_toEndOf="parent"
card_view:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:backgroundTint="?attr/messageBackground"
card_view:cardCornerRadius="6dp"
card_view:cardElevation="0dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="4dp"
android:textAlignment="textStart">
<TextView
android:id="@+id/message_author_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="2dp"
android:textSize="12sp"
android:textStyle="bold"
android:textColor="?attr/generalTextColor"
card_view:layout_constraintEnd_toEndOf="parent"
card_view:layout_constraintHorizontal_bias="0.5"
card_view:layout_constraintStart_toStartOf="parent"
card_view:layout_constraintTop_toTopOf="parent"
tools:text="Author" />
<TextView
android:id="@+id/message_content_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="2dp"
android:textAlignment="textStart"
android:textSize="18sp"
android:textStyle="bold"
android:textColor="?attr/generalTextColor"
card_view:layout_constraintBottom_toBottomOf="parent"
card_view:layout_constraintEnd_toEndOf="parent"
card_view:layout_constraintHorizontal_bias="0.5"
card_view:layout_constraintStart_toStartOf="parent"
card_view:layout_constraintTop_toBottomOf="@+id/message_author_text"
tools:text="message content" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" /> <background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground" /> <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon> </adaptive-icon>

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" /> <background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground" /> <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon> </adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 26 KiB

@ -16,14 +16,24 @@
android:label="fragment_explore" android:label="fragment_explore"
tools:layout="@layout/fragment_explore"> tools:layout="@layout/fragment_explore">
<action <action
android:id="@+id/action_exploreFragment_to_mapFragment" android:id="@+id/navigateToMapFragment"
app:destination="@id/mapFragment" /> app:destination="@id/mapFragment" />
<action
android:id="@+id/navigateToInfoFragment"
app:destination="@id/infoFragment" />
</fragment> </fragment>
<fragment <fragment
android:id="@+id/homeFragment" android:id="@+id/homeFragment"
android:name="com.example.wikispot.fragments.homeFragment" android:name="com.example.wikispot.fragments.homeFragment"
android:label="fragment_home" android:label="fragment_home"
tools:layout="@layout/fragment_home" /> tools:layout="@layout/fragment_home" >
<action
android:id="@+id/homeFragment_to_chatFragment"
app:destination="@id/chatFragment" />
<action
android:id="@+id/homeFragment_to_mapFragment"
app:destination="@id/mapFragment" />
</fragment>
<fragment <fragment
android:id="@+id/mapFragment" android:id="@+id/mapFragment"
android:name="com.example.wikispot.fragments.mapFragment" android:name="com.example.wikispot.fragments.mapFragment"
@ -32,10 +42,59 @@
<action <action
android:id="@+id/action_mapFragment_to_exploreFragment" android:id="@+id/action_mapFragment_to_exploreFragment"
app:destination="@id/exploreFragment" /> app:destination="@id/exploreFragment" />
<argument
android:name="location"
app:argType="com.google.android.gms.maps.model.LatLng" />
<action
android:id="@+id/mapFragment_to_infoFragment"
app:destination="@id/infoFragment" >
</action>
<argument
android:name="loadLastCoordinates"
app:argType="boolean"
android:defaultValue="false" />
</fragment> </fragment>
<fragment <fragment
android:id="@+id/settingsFragment" android:id="@+id/settingsFragment"
android:name="com.example.wikispot.fragments.settingsFragment" android:name="com.example.wikispot.fragments.settingsFragment"
android:label="fragment_settings" android:label="fragment_settings"
tools:layout="@layout/fragment_settings" /> tools:layout="@layout/fragment_settings" >
<action
android:id="@+id/navigateToDebugFragment"
app:destination="@id/debugFragment" />
</fragment>
<fragment
android:id="@+id/debugFragment"
android:name="com.example.wikispot.fragments.debugFragment"
android:label="fragment_debug"
tools:layout="@layout/fragment_debug" >
<action
android:id="@+id/navigateToAnotherDebugFragment"
app:destination="@id/anotherDebugFragment" />
</fragment>
<fragment
android:id="@+id/anotherDebugFragment"
android:name="com.example.wikispot.fragments.anotherDebugFragment"
android:label="fragment_another_debug"
tools:layout="@layout/fragment_another_debug" >
<action
android:id="@+id/navigateBackToDebugFragment"
app:destination="@id/debugFragment" />
</fragment>
<fragment
android:id="@+id/infoFragment"
android:name="com.example.wikispot.fragments.infoFragment"
android:label="fragment_info"
tools:layout="@layout/fragment_info" >
<argument
android:name="executeLoadFuntion"
app:argType="boolean"
android:defaultValue="false" />
<action
android:id="@+id/navigateBackToExploreFragment"
app:destination="@id/exploreFragment" />
<action
android:id="@+id/infoFragment_to_mapFragment"
app:destination="@id/mapFragment" />
</fragment>
</navigation> </navigation>

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">WikiSpot</string>
<string name="chat">Plaudern</string>
<string name="explore">Erkunden</string>
<string name="home">Start</string>
<string name="map">Karte</string>
<string name="settings">Einstellungen</string>
<string name="files">Dateien</string>
<string name="dark_theme">Dunkles Thema</string>
<string name="closed">Geschlossen</string>
<string name="open">Öffnen</string>
<string name="number_of_people">Anzahl der Personen :</string>
<string name="status">Status :</string>
<string name="temperature">Temperatur :</string>
<string name="more_colors">Mehr Farben</string>
</resources>

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">WikiSpot</string>
<string name="chat">Chat</string>
<string name="explore">Explorar</string>
<string name="home">Casa</string>
<string name="map">Mapa</string>
<string name="settings">Ajustes</string>
<string name="files">Archivos</string>
<string name="dark_theme">Tema oscuro</string>
<string name="temperature">Temperatura :</string>
<string name="status">Estado :</string>
<string name="number_of_people">Número de personas:</string>
<string name="open">Abierto</string>
<string name="closed">Cerrado</string>
<string name="more_colors">Mas colores</string>
</resources>

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">WikiSpot</string>
<string name="dark_theme">Thème sombre</string>
<string name="files">Des dossiers</string>
<string name="settings">Paramètres</string>
<string name="map">Carte</string>
<string name="home">Domicile</string>
<string name="explore">Explorer</string>
<string name="chat">Discuter</string>
<string name="status">Statut :</string>
<string name="temperature">Température :</string>
<string name="number_of_people">Nombre de personnes :</string>
<string name="open">Ouvert</string>
<string name="closed">Fermé</string>
<string name="more_colors">Plus de couleurs</string>
</resources>

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">ウィキすぽっと</string>
<string name="map">地図</string>
<string name="chat">チャット</string>
<string name="explore">見る</string>
<string name="home"></string>
<string name="files">ファイル</string>
<string name="settings">設定</string>
<string name="dark_theme">暗いテーマ</string>
<string name="closed">閉まっている</string>
<string name="open">開いた</string>
<string name="number_of_people">人々の数 </string>
<string name="status">状態 </string>
<string name="temperature">温度:</string>
<string name="more_colors">より多くの色</string>
</resources>

@ -1,16 +0,0 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.WikiSpot" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/purple_200</item>
<item name="colorPrimaryVariant">@color/purple_700</item>
<item name="colorOnPrimary">@color/black</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">@color/teal_200</item>
<item name="colorSecondaryVariant">@color/teal_200</item>
<item name="colorOnSecondary">@color/black</item>
<!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
</style>
</resources>

Some files were not shown because too many files have changed in this diff Show More