Compare commits
121 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d929397492 | |||
| 2217f80f72 | |||
| d062158c7c | |||
| 26dcfb8f34 | |||
| a0978d8a54 | |||
| fac521e0e3 | |||
| 5cf1f72d9c | |||
| ce1b921447 | |||
| 0de02c6d44 | |||
| 1bbf8d0c5c | |||
| 8b470c1927 | |||
| 904cafcf48 | |||
| 3361ec8965 | |||
| c5e05e4b8a | |||
| 25f7b05fd0 | |||
| 443e954c66 | |||
| 28258d8c15 | |||
| d74462a66c | |||
| 572095a7c3 | |||
| 32b005599a | |||
| 49e291881a | |||
| 5949099d91 | |||
| 13287f5b05 | |||
| acf5090489 | |||
| 7b26938efb | |||
| 0e8f9e2dfa | |||
| 513bc368b2 | |||
| a752a85a3f | |||
| 053f7401b8 | |||
| b6ce246a74 | |||
| ae9274d646 | |||
| 739399f54b | |||
| fd4a9c1f5d | |||
| d762ffe40a | |||
| ebbd4c823b | |||
| e1747b73f3 | |||
| e1b1adf9ad | |||
| a396406877 | |||
| ca2b7a7064 | |||
| fa65c33ec6 | |||
| a1d6f1dabe | |||
| 367d6d0a74 | |||
| b15236580b | |||
| 334b540721 | |||
| cd576ada12 | |||
| cf7d38d872 | |||
| a13e601eea | |||
| 7efd2a2f8b | |||
| 4f64ef00b9 | |||
| e595b12705 | |||
| 73f597fb54 | |||
| 3ac332535d | |||
| 4f59359f5e | |||
| 713e47b20a | |||
| 9dfcfa064a | |||
| 61263c191e | |||
| b7f61846e3 | |||
| 4aae6e90be | |||
| d5b6158364 | |||
| b9980206f5 | |||
| 6336c4d61b | |||
| 864802cdae | |||
| 907fcb4cb1 | |||
| d3a21c93c1 | |||
| f57abf150a | |||
| a40b64baca | |||
| 15c11a33ee | |||
| d888485fc2 | |||
| d009237679 | |||
| c3e2496596 | |||
| 607bbd0c2b | |||
| 90c4b998e7 | |||
| 8f054dd324 | |||
| 83b3b8a48d | |||
| e64b89bce6 | |||
| fd14eea4b9 | |||
| 065ef2bea6 | |||
| 308474246b | |||
| 96b88a398c | |||
| 15cea67818 | |||
| 9e89879d5c | |||
| a0c397b194 | |||
| cdb146bffb | |||
| 7ac35f21cf | |||
| 760f7f572a | |||
| 9aa2652059 | |||
| 718c654056 | |||
| 63b60a46ae | |||
| a694246a56 | |||
| 8b7e89f43b | |||
| cf7f96efad | |||
| b9dbde7f7c | |||
| 4592bb3382 | |||
| 88cf7711b0 | |||
| 82a5eb343b | |||
| ae37f14067 | |||
| c7212fbeba | |||
| f21e5221ba | |||
| fdb314c48b | |||
| b8541c994a | |||
| 2a62da3cf2 | |||
| 7c5de6ccd7 | |||
| 4ff6c569d0 | |||
| c58041fc20 | |||
| b3169f6525 | |||
| 6bca4462a3 | |||
| e558fa28b5 | |||
| 809616aefa | |||
| b8551cbb13 | |||
| 9161b61b6e | |||
| 9c83707bfc | |||
| bd3ca1740a | |||
| 5d4e56135e | |||
| bac3639c3f | |||
| e89ce8e7da | |||
| 79ad2633e7 | |||
| a327cf7a21 | |||
| b3c63df32e | |||
| 76e99b7700 | |||
| 369c842a14 | |||
| 29bf3c80d3 |
@@ -96,3 +96,10 @@ lint/generated/
|
||||
lint/outputs/
|
||||
lint/tmp/
|
||||
# lint/reports/
|
||||
|
||||
app/release/output-metadata.json
|
||||
|
||||
.idea/deploymentTargetDropDown.xml
|
||||
.idea/misc.xml
|
||||
.idea/deploymentTargetSelector.xml
|
||||
.idea/other.xml
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<bytecodeTargetLevel target="11" />
|
||||
<bytecodeTargetLevel target="21" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectMigrations">
|
||||
<option name="MigrateToGradleLocalJavaHome">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="11" project-jdk-type="JavaSDK" />
|
||||
</project>
|
||||
@@ -1,10 +0,0 @@
|
||||
<?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,9 +1,36 @@
|
||||
# Nextcloud Maps Geobookmarks Android app
|
||||

|
||||
|
||||
Android app to show your Nextcloud Maps geobookmarks list. Geobookmarks can be opened in all apps supporting geo links (i.e. Google Maps, Organic Maps etc...).
|
||||
A new geobookmark can be created on current location.
|
||||
# Nextcloud Maps Geofavorites Android app
|
||||
|
||||
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" alt="Get it on F-Droid" height="80">](https://f-droid.org/it/packages/it.danieleverducci.nextcloudmaps)
|
||||
[<img src="https://cdn.rawgit.com/steverichey/google-play-badge-svg/master/img/en_get.svg" height="80">](https://play.google.com/store/apps/details?id=it.danieleverducci.nextcloudmaps)
|
||||
[<img src="https://raw.githubusercontent.com/andOTP/andOTP/master/assets/badges/get-it-on-github.png" height="80">](https://github.com/penguin86/nextcloud-maps-client/releases/latest)
|
||||
|
||||
(Always prefer [F-Droid](https://f-droid.org) build, when possible).
|
||||
|
||||
UNOFFICIAL and FOSS Nextcloud Maps client at its earliest stages of developement. Shows your Nextcloud Maps geofavorites in a list and a map.
|
||||
Geofavorites can be opened in all apps supporting geo links (i.e. Google Maps, Organic Maps etc...).
|
||||
A new geofavorite can be created on current location, by sharing a "geo:" uri from another app or manually picking from the map.
|
||||
|
||||
**Requires Maps app to be installed on the Nextcloud instance.**
|
||||
|
||||
This work is heavily based on [matiasdelellis's Nextcloud SSO example](https://github.com/matiasdelellis/app-tutorial-android) to implement [Nextcloud single sign on](https://github.com/nextcloud/Android-SingleSignOn).
|
||||
|
||||
  
|
||||
|
||||
## Contributions
|
||||
|
||||
### Why isn't this hosted on GitHub?
|
||||
|
||||
I'm using a private git server just because I'm worried for the vast majority of open source code being hosted in a server property of Microsoft and being used to train theirs AI.
|
||||
I didn't find a better option, BTW the Gitea project is working on implementing federation, so soon it will be possible to contribute using any other gitea server, selfhosted or not.
|
||||
|
||||
### How to contribute
|
||||
|
||||
The project is open to contribution, but with some limits:
|
||||
|
||||
- I'm sorry I can't accept AI-generated contributions. Reviewing a contribution requires time and effort from my side, while generating code with AI requires very little time and produces non reliable code that must be reviewed in detail. This is effectively shifting the work on my side, and in a forced way. If you feel you need a feature but you're not able to implement it by yourself, I prefer you to create an issue in the repository so I can implement it when I can, in a more mantainable way.
|
||||
- I prefer to make project-wide changes (i.e. updating Android target, app name and icon, release number...) by myself.
|
||||
|
||||
To contribute, you'll have to create an account on this git instance. Unfortunately, I had to disable registration to avoid huge waves of fake accounts created by bots.
|
||||
You can request an account writing to daniele.verducci@ichibi.eu
|
||||
|
||||
@@ -18,14 +18,13 @@
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
compileSdkVersion 30
|
||||
|
||||
defaultConfig {
|
||||
applicationId "it.danieleverducci.nextcloudmaps"
|
||||
minSdkVersion 23
|
||||
targetSdkVersion 30
|
||||
versionCode 1
|
||||
versionName "0.1"
|
||||
targetSdkVersion 34
|
||||
compileSdkVersion 34
|
||||
versionCode 9
|
||||
versionName "0.4.0"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
@@ -37,13 +36,18 @@ android {
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
// Flag to enable support for the new language APIs
|
||||
coreLibraryDesugaringEnabled true
|
||||
// Sets Java compatibility to Java 8
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
dataBinding true
|
||||
buildConfig true
|
||||
}
|
||||
namespace 'it.danieleverducci.nextcloudmaps'
|
||||
}
|
||||
|
||||
repositories {
|
||||
@@ -54,21 +58,40 @@ repositories {
|
||||
dependencies {
|
||||
implementation fileTree(dir: "libs", include: ["*.jar"])
|
||||
|
||||
implementation 'com.android.support:design:30.0.1'
|
||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.1.0'
|
||||
implementation "androidx.cardview:cardview:1.0.0"
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
|
||||
implementation "androidx.preference:preference:1.1.1"
|
||||
// Desugaring lib: see https://developer.android.com/studio/write/java8-support
|
||||
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.2")
|
||||
|
||||
testImplementation 'junit:junit:4.13'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
|
||||
implementation 'com.android.support:design:34.0.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.7.0'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.3.2'
|
||||
implementation "androidx.cardview:cardview:1.0.0"
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||
implementation "androidx.preference:preference:1.2.1"
|
||||
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
||||
|
||||
// Retrofif2
|
||||
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
|
||||
implementation 'com.squareup.retrofit2:retrofit:2.11.0'
|
||||
implementation 'com.squareup.retrofit2:converter-gson:2.6.1'
|
||||
|
||||
// Nextcloud SSO
|
||||
implementation "com.github.nextcloud:Android-SingleSignOn:0.5.6"
|
||||
implementation "com.github.nextcloud:Android-SingleSignOn:1.3.2"
|
||||
|
||||
// OSMDroid
|
||||
implementation 'org.osmdroid:osmdroid-android:6.1.18'
|
||||
|
||||
//Threeten-Backport (ports Java 8 Date API on Java 6+)
|
||||
implementation 'org.threeten:threetenbp:1.5.1'
|
||||
|
||||
// https://mvnrepository.com/artifact/commons-io/commons-io
|
||||
implementation 'commons-io:commons-io:2.13.0'
|
||||
|
||||
// Picasso (image loader)
|
||||
implementation 'com.squareup.picasso:picasso:2.8'
|
||||
|
||||
configurations.all {
|
||||
resolutionStrategy {
|
||||
force 'commons-io:commons-io:2.11.0'
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,39 +17,99 @@
|
||||
-->
|
||||
|
||||
<manifest
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="it.danieleverducci.nextcloudmaps">
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
|
||||
<queries>
|
||||
<package android:name="com.nextcloud.client" />
|
||||
<!-- To see if google maps is installed, as it needs a specific intent Uri) -->
|
||||
<package android:name="com.google.android.apps.maps" />
|
||||
</queries>
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:label="@string/app_name"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
|
||||
<activity android:name=".activity.login.LoginActivity">
|
||||
<activity android:name=".activity.login.LoginActivity"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name=".activity.main.MainActivity">
|
||||
<activity android:name=".activity.main.MainActivity"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".activity.main.GeofavoriteDetailActivity"
|
||||
android:theme="@style/AppTheme"/>
|
||||
android:name=".activity.detail.GeofavoriteDetailActivity"
|
||||
android:exported="true">
|
||||
<!-- standard "geo" scheme -->
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
|
||||
<data android:scheme="geo"/>
|
||||
</intent-filter>
|
||||
|
||||
<!-- Google Maps -->
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
<data android:scheme="google.navigation"/>
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
<data
|
||||
android:host="maps.google.com"
|
||||
android:scheme="https"/>
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
<data
|
||||
android:host="www.google.com"
|
||||
android:pathPrefix="/maps"
|
||||
android:scheme="https"/>
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
<data
|
||||
android:host="www.openstreetmap.org"
|
||||
android:scheme="https"/>
|
||||
</intent-filter>
|
||||
|
||||
</activity>
|
||||
|
||||
<activity android:name=".activity.mappicker.MapPickerActivity"
|
||||
android:exported="true"
|
||||
android:theme="@style/AppTheme">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".activity.about.AboutActivity"
|
||||
|
||||
|
After Width: | Height: | Size: 14 KiB |
@@ -0,0 +1,26 @@
|
||||
package it.danieleverducci.nextcloudmaps.activity;
|
||||
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import org.osmdroid.views.overlay.TilesOverlay;
|
||||
|
||||
public class NextcloudMapsStyledActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// For whatever reason, android:windowLightStatusBar is ignored in styles.xml
|
||||
int currentNightMode = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
|
||||
if (currentNightMode == Configuration.UI_MODE_NIGHT_YES)
|
||||
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -30,8 +30,9 @@ import androidx.appcompat.widget.Toolbar;
|
||||
|
||||
import it.danieleverducci.nextcloudmaps.BuildConfig;
|
||||
import it.danieleverducci.nextcloudmaps.R;
|
||||
import it.danieleverducci.nextcloudmaps.activity.NextcloudMapsStyledActivity;
|
||||
|
||||
public class AboutActivity extends AppCompatActivity {
|
||||
public class AboutActivity extends NextcloudMapsStyledActivity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@@ -62,6 +63,10 @@ public class AboutActivity extends AppCompatActivity {
|
||||
TextView tvIssues = findViewById(R.id.about_issues);
|
||||
tvIssues.setText(Html.fromHtml(getString(R.string.about_issues, getString(R.string.url_issues))));
|
||||
tvIssues.setOnClickListener(view -> openUtl(getString(R.string.url_issues)));
|
||||
|
||||
TextView tvMaps = findViewById(R.id.about_maps);
|
||||
tvMaps.setText(Html.fromHtml(getString(R.string.about_maps)));
|
||||
tvMaps.setOnClickListener(view -> openUtl(getString(R.string.url_maps)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package it.danieleverducci.nextcloudmaps.activity.detail;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.graphics.drawable.DrawableCompat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
|
||||
import it.danieleverducci.nextcloudmaps.R;
|
||||
import it.danieleverducci.nextcloudmaps.model.Geofavorite;
|
||||
|
||||
public class CategoriesAdapter extends ArrayAdapter<String> {
|
||||
|
||||
public CategoriesAdapter(@NonNull Context context) {
|
||||
super(context, R.layout.category_listitem, R.id.category_name, new ArrayList<>());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
|
||||
View v = super.getView(position, convertView, parent);
|
||||
TextView categoryName = v.findViewById(R.id.category_name);
|
||||
Drawable backgroundDrawable = categoryName.getBackground();
|
||||
DrawableCompat.setTint(
|
||||
backgroundDrawable,
|
||||
Geofavorite.categoryColorFromName(categoryName.getText().toString()) == 0
|
||||
? v.getContext().getColor(R.color.defaultBrand)
|
||||
: Geofavorite.categoryColorFromName(categoryName.getText().toString())
|
||||
);
|
||||
return v;
|
||||
}
|
||||
|
||||
public void setCategoriesList(HashSet<String> categories) {
|
||||
clear();
|
||||
addAll(categories);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,441 @@
|
||||
/*
|
||||
* Nextcloud Maps Geofavorites for Android
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package it.danieleverducci.nextcloudmaps.activity.detail;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.location.Location;
|
||||
import android.location.LocationListener;
|
||||
import android.location.LocationManager;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.content.res.AppCompatResources;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.lifecycle.Observer;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import org.osmdroid.api.IMapController;
|
||||
import org.osmdroid.config.Configuration;
|
||||
import org.osmdroid.util.GeoPoint;
|
||||
import org.osmdroid.views.CustomZoomButtonsController;
|
||||
import org.osmdroid.views.overlay.Marker;
|
||||
import org.threeten.bp.format.DateTimeFormatter;
|
||||
import org.threeten.bp.format.FormatStyle;
|
||||
|
||||
import java.util.HashSet;
|
||||
|
||||
import it.danieleverducci.nextcloudmaps.R;
|
||||
import it.danieleverducci.nextcloudmaps.activity.NextcloudMapsStyledActivity;
|
||||
import it.danieleverducci.nextcloudmaps.databinding.ActivityGeofavoriteDetailBinding;
|
||||
import it.danieleverducci.nextcloudmaps.model.Geofavorite;
|
||||
import it.danieleverducci.nextcloudmaps.utils.GeoUriParser;
|
||||
import it.danieleverducci.nextcloudmaps.utils.IntentGenerator;
|
||||
import it.danieleverducci.nextcloudmaps.utils.MapUtils;
|
||||
|
||||
public class GeofavoriteDetailActivity extends NextcloudMapsStyledActivity implements LocationListener, ActivityCompat.OnRequestPermissionsResultCallback {
|
||||
|
||||
public static final String TAG = "GeofavDetail";
|
||||
public static final String ARG_GEOFAVORITE = "geofav";
|
||||
private static final int PERMISSION_REQUEST_CODE = 9999;
|
||||
|
||||
private ViewHolder mViewHolder;
|
||||
private Geofavorite mGeofavorite;
|
||||
private GeofavoriteDetailActivityViewModel mViewModel;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
MapUtils.configOsmdroid(this);
|
||||
|
||||
mViewHolder = new ViewHolder(getLayoutInflater());
|
||||
setContentView(mViewHolder.getRootView());
|
||||
mViewHolder.setOnSubmitListener(new OnSubmitListener() {
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActionIconShareClicked() {
|
||||
startActivity(Intent.createChooser(IntentGenerator.newShareIntent(GeofavoriteDetailActivity.this, mGeofavorite), getString(R.string.share_via)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActionIconNavClicked() {
|
||||
startActivity(IntentGenerator.newGeoUriIntent(GeofavoriteDetailActivity.this, mGeofavorite));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActionIconDeleteClicked() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSubmit() {
|
||||
saveGeofavorite();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMapClicked() {
|
||||
// TODO: Open map activity with pin
|
||||
startActivity(IntentGenerator.newGeoUriIntent(GeofavoriteDetailActivity.this, mGeofavorite));
|
||||
}
|
||||
});
|
||||
|
||||
mViewModel = new ViewModelProvider(this).get(GeofavoriteDetailActivityViewModel.class);
|
||||
mViewModel.init(getApplicationContext());
|
||||
mViewModel.getCategories().observe(this, new Observer<HashSet<String>>() {
|
||||
@Override
|
||||
public void onChanged(HashSet<String> categories) {
|
||||
mViewHolder.setCategories(categories);
|
||||
}
|
||||
});
|
||||
mViewModel.getIsUpdating().observe(this, new Observer<Boolean>() {
|
||||
@Override
|
||||
public void onChanged(@Nullable Boolean updating) {
|
||||
mViewHolder.setUpdating(updating);
|
||||
}
|
||||
});
|
||||
mViewModel.getOnFinished().observe(this, new Observer<Boolean>() {
|
||||
@Override
|
||||
public void onChanged(@Nullable Boolean success) {
|
||||
if(success){
|
||||
Toast.makeText(GeofavoriteDetailActivity.this, R.string.geofavorite_saved, Toast.LENGTH_SHORT).show();
|
||||
finish();
|
||||
} else {
|
||||
Toast.makeText(GeofavoriteDetailActivity.this, R.string.error_saving_geofavorite, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (getIntent().hasExtra(ARG_GEOFAVORITE) && getIntent().getIntExtra(ARG_GEOFAVORITE, 0) != 0) {
|
||||
// Opening geofavorite from list
|
||||
mGeofavorite = mViewModel.getGeofavorite(
|
||||
getIntent().getIntExtra(ARG_GEOFAVORITE, 0)
|
||||
);
|
||||
mViewHolder.hideAccuracy();
|
||||
} else {
|
||||
// New geofavorite
|
||||
mGeofavorite = new Geofavorite();
|
||||
mGeofavorite.setCategory(Geofavorite.DEFAULT_CATEGORY);
|
||||
mGeofavorite.setDateCreated(System.currentTimeMillis() / 1000);
|
||||
mGeofavorite.setDateModified(System.currentTimeMillis() / 1000);
|
||||
mViewHolder.hideActions();
|
||||
|
||||
if (getIntent().getData() != null) {
|
||||
// Opened by external generic intent: parse URI
|
||||
try {
|
||||
double[] coords = GeoUriParser.parseUri(getIntent().getData(), false);
|
||||
mGeofavorite.setLat(coords[0]);
|
||||
mGeofavorite.setLng(coords[1]);
|
||||
mViewHolder.hideAccuracy();
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.e(TAG, e.getMessage());
|
||||
Toast.makeText(this, R.string.error_unsupported_uri, Toast.LENGTH_SHORT).show();
|
||||
finish();
|
||||
}
|
||||
} else {
|
||||
// Precompile location with current one
|
||||
getLocation();
|
||||
}
|
||||
}
|
||||
|
||||
mViewHolder.updateView(mGeofavorite);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
// OSMDroid config
|
||||
Configuration.getInstance().load(getApplicationContext(),
|
||||
PreferenceManager.getDefaultSharedPreferences(getApplicationContext()));
|
||||
mViewHolder.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
// OSMDroid config
|
||||
Configuration.getInstance().save(getApplicationContext(),
|
||||
PreferenceManager.getDefaultSharedPreferences(getApplicationContext()));
|
||||
mViewHolder.onPause();
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the submit button is clicked
|
||||
* @param v The button
|
||||
*/
|
||||
public void onSubmit(View v) {
|
||||
saveGeofavorite();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks fields and sends updated geofavorite to Nextcloud instance
|
||||
*/
|
||||
private void saveGeofavorite() {
|
||||
mViewHolder.updateModel(mGeofavorite);
|
||||
|
||||
if (!mGeofavorite.valid()) {
|
||||
Toast.makeText(GeofavoriteDetailActivity.this, R.string.incomplete_geofavorite, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
mViewModel.saveGeofavorite(mGeofavorite);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the current location (requesting user's permission, if necessary)
|
||||
* and calls updateLocationField()
|
||||
*/
|
||||
private void getLocation() {
|
||||
// Check if user granted location permission
|
||||
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
|
||||
// User didn't grant permission. Ask it.
|
||||
requestPermissions(new String[] {Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSION_REQUEST_CODE);
|
||||
return;
|
||||
}
|
||||
|
||||
LocationManager locationManager = (LocationManager)
|
||||
getSystemService(Context.LOCATION_SERVICE);
|
||||
// Try to use last available location
|
||||
Location lastKnown = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
|
||||
if (lastKnown != null)
|
||||
updateLocationField(lastKnown);
|
||||
// Register for location updates in case the user moves before saving
|
||||
locationManager.requestLocationUpdates(
|
||||
LocationManager.GPS_PROVIDER, 1000, 10, this
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the geofavorite location field with the provided location object
|
||||
* @param location to set in the geofavorite
|
||||
*/
|
||||
private void updateLocationField(Location location) {
|
||||
// Update model
|
||||
mGeofavorite.setLat(location.getLatitude());
|
||||
mGeofavorite.setLng(location.getLongitude());
|
||||
// Update view
|
||||
mViewHolder.updateViewCoords(mGeofavorite);
|
||||
mViewHolder.setAccuracy(location.getAccuracy());
|
||||
}
|
||||
|
||||
|
||||
/** Location updates callbacks **/
|
||||
|
||||
@Override
|
||||
public void onLocationChanged(@NonNull Location location) {
|
||||
updateLocationField(location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStatusChanged(String provider, int status, Bundle extras) {}
|
||||
|
||||
@Override
|
||||
public void onProviderEnabled(@NonNull String provider) {}
|
||||
|
||||
@Override
|
||||
public void onProviderDisabled(@NonNull String provider) {}
|
||||
|
||||
@Override
|
||||
public void onPointerCaptureChanged(boolean hasCapture) {}
|
||||
|
||||
|
||||
/** Position permission request result **/
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
if (requestCode == PERMISSION_REQUEST_CODE && permissions[0].equals(Manifest.permission.ACCESS_FINE_LOCATION)) {
|
||||
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
getLocation();
|
||||
} else {
|
||||
Toast.makeText(this, R.string.location_permission_required, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private class ViewHolder implements View.OnClickListener {
|
||||
private final ActivityGeofavoriteDetailBinding binding;
|
||||
private DateTimeFormatter dateFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG);
|
||||
private OnSubmitListener listener;
|
||||
private Marker mapMarker;
|
||||
|
||||
|
||||
public ViewHolder(LayoutInflater inflater) {
|
||||
|
||||
this.binding = ActivityGeofavoriteDetailBinding.inflate(inflater);
|
||||
this.binding.submitBt.setOnClickListener(this);
|
||||
this.binding.mapBt.setOnClickListener(this);
|
||||
this.binding.backBt.setOnClickListener(this);
|
||||
this.binding.actionIconShare.setOnClickListener(this);
|
||||
this.binding.actionIconDelete.setOnClickListener(this);
|
||||
this.binding.actionIconNav.setOnClickListener(this);
|
||||
|
||||
// Set categories adapter
|
||||
CategoriesAdapter categoriesAdapter = new CategoriesAdapter(binding.root.getContext());
|
||||
this.binding.categoryAt.setAdapter(categoriesAdapter);
|
||||
this.binding.categoryAt.setText(Geofavorite.DEFAULT_CATEGORY);
|
||||
this.binding.categoryAtClear.setOnClickListener((v) -> {
|
||||
if (this.binding.categoryAt.getText().toString().isEmpty())
|
||||
this.binding.categoryAt.setText(Geofavorite.DEFAULT_CATEGORY);
|
||||
else
|
||||
this.binding.categoryAt.setText("");
|
||||
});
|
||||
|
||||
// Set map properties
|
||||
this.binding.map.getZoomController().setVisibility(CustomZoomButtonsController.Visibility.NEVER);
|
||||
this.binding.map.setMultiTouchControls(true);
|
||||
MapUtils.setTheme(this.binding.map);
|
||||
// this.binding.map.setTilesScaledToDpi(true);
|
||||
|
||||
// Create marker
|
||||
mapMarker = new Marker(binding.map);
|
||||
mapMarker.setIcon(AppCompatResources.getDrawable(GeofavoriteDetailActivity.this, R.drawable.ic_map_pin));
|
||||
mapMarker.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM);
|
||||
binding.map.getOverlays().add(mapMarker);
|
||||
}
|
||||
|
||||
public View getRootView() {
|
||||
return this.binding.root;
|
||||
}
|
||||
|
||||
public void updateView(Geofavorite item) {
|
||||
binding.collapsingToolbar.setTitle(item.getName() != null ? item.getName() : getString(R.string.new_geobookmark));
|
||||
binding.nameEt.setText(item.getName() != null ? item.getName() : "");
|
||||
binding.descriptionEt.setText(item.getComment() != null ? item.getComment() : "");
|
||||
binding.createdTv.setText(item.getLocalDateCreated().format(dateFormatter));
|
||||
binding.modifiedTv.setText(item.getLocalDateCreated().format(dateFormatter));
|
||||
binding.categoryAt.setText(item.getCategory() != null ? item.getCategory() : Geofavorite.DEFAULT_CATEGORY);
|
||||
updateViewCoords(item);
|
||||
}
|
||||
|
||||
public void updateViewCoords(Geofavorite item) {
|
||||
binding.coordsTv.setText(item.getCoordinatesString());
|
||||
binding.coordsTv.setOnClickListener((v) -> {
|
||||
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
ClipData clip = ClipData.newPlainText(item.getCoordinatesString(), item.getCoordinatesString());
|
||||
clipboard.setPrimaryClip(clip);
|
||||
Toast.makeText(GeofavoriteDetailActivity.this, R.string.coords_copied, Toast.LENGTH_SHORT).show();
|
||||
});
|
||||
|
||||
// Center map
|
||||
GeoPoint position = new GeoPoint(item.getLat(), item.getLng());
|
||||
IMapController mapController = binding.map.getController();
|
||||
mapController.setZoom(19.0f);
|
||||
mapController.setCenter(position);
|
||||
|
||||
// Set pin
|
||||
mapMarker.setPosition(position);
|
||||
}
|
||||
|
||||
public void updateModel(Geofavorite item) {
|
||||
item.setName(binding.nameEt.getText().toString());
|
||||
item.setComment(binding.descriptionEt.getText().toString());
|
||||
item.setCategory(binding.categoryAt.getText().toString());
|
||||
item.setDateModified(System.currentTimeMillis() / 1000);
|
||||
}
|
||||
|
||||
public void setUpdating(boolean updating) {
|
||||
binding.progress.setVisibility(updating ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
public void setAccuracy(float accuracy) {
|
||||
// Display accuracy in meters
|
||||
binding.accuracyTv.setText(getString(R.string.accuracy).replace("{accuracy}", ((int)accuracy) + ""));
|
||||
// Display accuracy in progress bar
|
||||
int accuracyPercent = (int)accuracy > 100 ? 0 : Math.abs((int)accuracy - 100);
|
||||
binding.accuracyProgress.setIndeterminate(false);
|
||||
binding.accuracyProgress.setProgress(accuracyPercent);
|
||||
}
|
||||
|
||||
public void setCategories(HashSet<String> categories) {
|
||||
((CategoriesAdapter)binding.categoryAt.getAdapter()).setCategoriesList(categories);
|
||||
}
|
||||
|
||||
public void hideAccuracy() {
|
||||
binding.accuracyTv.setVisibility(View.GONE);
|
||||
binding.accuracyProgressContainer.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
public void hideActions() {
|
||||
binding.actionIcons.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
public void setOnSubmitListener(OnSubmitListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public void onResume() {
|
||||
binding.map.onResume();
|
||||
}
|
||||
|
||||
public void onPause() {
|
||||
binding.map.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (v.getId() == R.id.submit_bt && this.listener != null) {
|
||||
this.listener.onSubmit();
|
||||
}
|
||||
if (v.getId() == R.id.map_bt && this.listener != null) {
|
||||
this.listener.onMapClicked();
|
||||
}
|
||||
if (v.getId() == R.id.back_bt && this.listener != null) {
|
||||
this.listener.onBackPressed();
|
||||
}
|
||||
|
||||
// Actions
|
||||
if (v.getId() == R.id.action_icon_share && this.listener != null) {
|
||||
this.listener.onActionIconShareClicked();
|
||||
}
|
||||
if (v.getId() == R.id.action_icon_nav && this.listener != null) {
|
||||
this.listener.onActionIconNavClicked();
|
||||
}
|
||||
if (v.getId() == R.id.action_icon_delete && this.listener != null) {
|
||||
this.listener.onActionIconDeleteClicked();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected interface OnSubmitListener {
|
||||
void onSubmit();
|
||||
void onMapClicked();
|
||||
void onBackPressed();
|
||||
void onActionIconShareClicked();
|
||||
void onActionIconNavClicked();
|
||||
void onActionIconDeleteClicked();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package it.danieleverducci.nextcloudmaps.activity.detail;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import it.danieleverducci.nextcloudmaps.model.Geofavorite;
|
||||
import it.danieleverducci.nextcloudmaps.repository.GeofavoriteRepository;
|
||||
|
||||
public class GeofavoriteDetailActivityViewModel extends ViewModel {
|
||||
private GeofavoriteRepository mRepo;
|
||||
|
||||
public void init(Context applicationContext) {
|
||||
mRepo = GeofavoriteRepository.getInstance(applicationContext);
|
||||
}
|
||||
|
||||
public Geofavorite getGeofavorite(int id) {
|
||||
return mRepo.getGeofavorite(id);
|
||||
}
|
||||
|
||||
public void saveGeofavorite(Geofavorite geofav) {
|
||||
mRepo.saveGeofavorite(geofav);
|
||||
}
|
||||
|
||||
public LiveData<HashSet<String>> getCategories(){
|
||||
return mRepo.getCategories();
|
||||
}
|
||||
|
||||
public LiveData<Boolean> getIsUpdating(){
|
||||
return mRepo.isUpdating();
|
||||
}
|
||||
|
||||
public LiveData<Boolean> getOnFinished(){
|
||||
return mRepo.onFinished();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,6 +20,8 @@ package it.danieleverducci.nextcloudmaps.activity.login;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.ProgressBar;
|
||||
@@ -39,12 +41,14 @@ import com.nextcloud.android.sso.model.SingleSignOnAccount;
|
||||
import com.nextcloud.android.sso.ui.UiExceptionManager;
|
||||
|
||||
import it.danieleverducci.nextcloudmaps.R;
|
||||
import it.danieleverducci.nextcloudmaps.activity.NextcloudMapsStyledActivity;
|
||||
import it.danieleverducci.nextcloudmaps.activity.main.MainActivity;
|
||||
import it.danieleverducci.nextcloudmaps.api.API;
|
||||
import it.danieleverducci.nextcloudmaps.api.ApiProvider;
|
||||
|
||||
public class LoginActivity extends AppCompatActivity {
|
||||
public class LoginActivity extends NextcloudMapsStyledActivity {
|
||||
private static final String TAG = "LoginActivity";
|
||||
|
||||
protected ApiProvider mApi;
|
||||
protected ProgressBar progress;
|
||||
protected Button button;
|
||||
|
||||
@@ -62,18 +66,25 @@ public class LoginActivity extends AppCompatActivity {
|
||||
openAccountChooser();
|
||||
});
|
||||
|
||||
Handler h = new Handler();
|
||||
h.post(() -> {
|
||||
try {
|
||||
ssoAccount = SingleAccountHelper.getCurrentSingleSignOnAccount(getApplicationContext());
|
||||
SingleAccountHelper.setCurrentAccount(getApplicationContext(), ssoAccount.name);
|
||||
SingleAccountHelper.applyCurrentAccount(getApplicationContext(), ssoAccount.name);
|
||||
accountAccessDone();
|
||||
} catch (NextcloudFilesAppAccountNotFoundException | NoCurrentAccountSelectedException e) {
|
||||
Log.e(TAG, "Autologin: " + e.toString());
|
||||
}
|
||||
});
|
||||
}
|
||||
private void openAccountChooser() {
|
||||
try {
|
||||
AccountImporter.pickNewAccount(this);
|
||||
} catch (NextcloudFilesAppNotInstalledException | AndroidGetAccountsPermissionNotGranted e) {
|
||||
UiExceptionManager.showDialogForException(this, e);
|
||||
|
||||
Log.e(TAG, "openAccountChooser: " + e.toString());
|
||||
progress.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +108,7 @@ public class LoginActivity extends AppCompatActivity {
|
||||
@Override
|
||||
public void accountAccessGranted(SingleSignOnAccount account) {
|
||||
Context l_context = getApplicationContext();
|
||||
SingleAccountHelper.setCurrentAccount(l_context, account.name);
|
||||
SingleAccountHelper.applyCurrentAccount(l_context, account.name);
|
||||
|
||||
accountAccessDone();
|
||||
}
|
||||
@@ -114,9 +125,6 @@ public class LoginActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
private void accountAccessDone() {
|
||||
Context l_context = getApplicationContext();
|
||||
mApi = new ApiProvider(l_context);
|
||||
|
||||
Intent intent = new Intent(LoginActivity.this, MainActivity.class);
|
||||
startActivity(intent);
|
||||
|
||||
|
||||
@@ -21,53 +21,65 @@
|
||||
package it.danieleverducci.nextcloudmaps.activity.main;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.text.Html;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Filter;
|
||||
import android.widget.Filterable;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.PopupMenu;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.cardview.widget.CardView;
|
||||
import androidx.core.graphics.drawable.DrawableCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import org.threeten.bp.format.DateTimeFormatter;
|
||||
import org.threeten.bp.format.FormatStyle;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import it.danieleverducci.nextcloudmaps.R;
|
||||
import it.danieleverducci.nextcloudmaps.model.Geofavorite;
|
||||
|
||||
public class GeofavoriteAdapter extends RecyclerView.Adapter<GeofavoriteAdapter.RecyclerViewAdapter> implements Filterable {
|
||||
public class GeofavoriteAdapter extends RecyclerView.Adapter<GeofavoriteAdapter.GeofavoriteViewHolder> {
|
||||
|
||||
public static final String TAG = "GeofavoriteAdapter";
|
||||
|
||||
public static final int SORT_BY_TITLE = 0;
|
||||
public static final int SORT_BY_CREATED = 1;
|
||||
public static final int SORT_BY_CATEGORY = 2;
|
||||
public static final int SORT_BY_DISTANCE = 3;
|
||||
|
||||
private Context context;
|
||||
private ItemClickListener itemClickListener;
|
||||
private final Context context;
|
||||
private final ItemClickListener itemClickListener;
|
||||
private final DateTimeFormatter dateFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT);
|
||||
|
||||
private List<Geofavorite> geofavoriteList = new ArrayList<>();
|
||||
private List<Geofavorite> geofavoriteListFiltered = new ArrayList<>();
|
||||
private List<Geofavorite> items = new ArrayList<>();
|
||||
private int sortRule = SORT_BY_CREATED;
|
||||
|
||||
// Contains the position of the element containing the overflow menu clicked
|
||||
private int overflowMenuSelectedPosition = -1;
|
||||
|
||||
public GeofavoriteAdapter(Context context, ItemClickListener itemClickListener) {
|
||||
this.context = context;
|
||||
this.itemClickListener = itemClickListener;
|
||||
}
|
||||
|
||||
public void setGeofavoriteList(@NonNull List<Geofavorite> geofavoriteList) {
|
||||
this.geofavoriteList = geofavoriteList;
|
||||
this.geofavoriteListFiltered = new ArrayList<>(geofavoriteList);
|
||||
this.items.clear();
|
||||
this.items.addAll(geofavoriteList);
|
||||
|
||||
performSort();
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public Geofavorite get(int position) {
|
||||
return geofavoriteListFiltered.get(position);
|
||||
return items.get(position);
|
||||
}
|
||||
|
||||
public int getSortRule() {
|
||||
@@ -83,95 +95,120 @@ public class GeofavoriteAdapter extends RecyclerView.Adapter<GeofavoriteAdapter.
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public RecyclerViewAdapter onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(context).inflate(R.layout.item_note, parent, false);
|
||||
return new RecyclerViewAdapter(view, itemClickListener);
|
||||
public GeofavoriteViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(context).inflate(R.layout.item_geofav, parent, false);
|
||||
return new GeofavoriteViewHolder(view, itemClickListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull RecyclerViewAdapter holder, int position) {
|
||||
Geofavorite geofavorite = geofavoriteListFiltered.get(position);
|
||||
public void onBindViewHolder(@NonNull GeofavoriteViewHolder holder, int position) {
|
||||
Geofavorite geofavorite = items.get(position);
|
||||
|
||||
holder.tv_title.setText(Html.fromHtml(geofavorite.getName()));
|
||||
holder.tv_content.setText(geofavorite.getComment());
|
||||
holder.tv_category.setText(geofavorite.categoryLetter());
|
||||
holder.setCategoryColor(
|
||||
geofavorite.categoryColor() == 0 ? context.getColor(R.color.defaultBrand) : geofavorite.categoryColor());
|
||||
holder.tv_title.setText(Html.fromHtml(geofavorite.getName() == null ? "" : geofavorite.getName()));
|
||||
holder.tv_content.setText(geofavorite.getComment() == null ? "" : geofavorite.getComment());
|
||||
holder.tv_date.setText(geofavorite.getLocalDateCreated().format(dateFormatter));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return geofavoriteListFiltered.size();
|
||||
return items.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Filter getFilter() {
|
||||
return filter;
|
||||
}
|
||||
|
||||
Filter filter = new Filter() {
|
||||
@Override
|
||||
// Run on Background thread.
|
||||
protected FilterResults performFiltering(CharSequence charSequence) {
|
||||
FilterResults filterResults = new FilterResults();
|
||||
List <Geofavorite> filteredGeofavorites = new ArrayList<>();
|
||||
|
||||
if (charSequence.toString().isEmpty()) {
|
||||
filteredGeofavorites.addAll(geofavoriteList);
|
||||
} else {
|
||||
for (Geofavorite geofavorite : geofavoriteList) {
|
||||
String query = charSequence.toString().toLowerCase();
|
||||
if (geofavorite.getName() != null && geofavorite.getName().toLowerCase().contains(query)) {
|
||||
filteredGeofavorites.add(geofavorite);
|
||||
} else if (geofavorite.getComment() != null && geofavorite.getComment().toLowerCase().contains(query)) {
|
||||
filteredGeofavorites.add(geofavorite);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
filterResults.values = filteredGeofavorites;
|
||||
return filterResults;
|
||||
}
|
||||
//Run on ui thread
|
||||
@Override
|
||||
protected void publishResults(CharSequence charSequence, FilterResults filterResults) {
|
||||
geofavoriteListFiltered.clear();
|
||||
geofavoriteListFiltered.addAll((Collection<? extends Geofavorite>) filterResults.values);
|
||||
|
||||
performSort();
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
};
|
||||
|
||||
class RecyclerViewAdapter extends RecyclerView.ViewHolder implements View.OnClickListener {
|
||||
TextView tv_title, tv_content;
|
||||
class GeofavoriteViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
|
||||
TextView tv_category, tv_title, tv_content, tv_date;
|
||||
ImageView bt_context_menu;
|
||||
ImageView bt_nav;
|
||||
|
||||
ItemClickListener itemClickListener;
|
||||
|
||||
RecyclerViewAdapter(@NonNull View itemView, ItemClickListener itemClickListener) {
|
||||
|
||||
GeofavoriteViewHolder(@NonNull View itemView, ItemClickListener itemClickListener) {
|
||||
super(itemView);
|
||||
|
||||
tv_category = itemView.findViewById(R.id.tv_category);
|
||||
tv_title = itemView.findViewById(R.id.title);
|
||||
tv_content = itemView.findViewById(R.id.content);
|
||||
tv_date = itemView.findViewById(R.id.date);
|
||||
bt_context_menu = itemView.findViewById(R.id.geofav_context_menu_bt);
|
||||
bt_nav = itemView.findViewById(R.id.geofav_nav_bt);
|
||||
|
||||
this.itemClickListener = itemClickListener;
|
||||
itemView.setOnClickListener(this);
|
||||
|
||||
tv_content.setOnClickListener(this);
|
||||
bt_context_menu.setOnClickListener(this);
|
||||
bt_nav.setOnClickListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
itemClickListener.onItemClick(view, getAdapterPosition());
|
||||
switch (view.getId()) {
|
||||
case R.id.geofav_context_menu_bt:
|
||||
onOverflowIconClicked(view, getBindingAdapterPosition());
|
||||
break;
|
||||
case R.id.geofav_nav_bt:
|
||||
if (itemClickListener != null)
|
||||
itemClickListener.onItemNavClick(get(getBindingAdapterPosition()));
|
||||
break;
|
||||
default:
|
||||
if (itemClickListener != null)
|
||||
itemClickListener.onItemClick(get(getBindingAdapterPosition()));
|
||||
}
|
||||
}
|
||||
|
||||
public void setCategoryColor(int ccTint) {
|
||||
Drawable bg = DrawableCompat.wrap(this.tv_category.getContext().getDrawable(R.drawable.ic_list_pin));
|
||||
this.tv_category.setBackground(bg);
|
||||
DrawableCompat.setTint(bg, ccTint);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void performSort() {
|
||||
if (sortRule == SORT_BY_TITLE) {
|
||||
Collections.sort(geofavoriteListFiltered, Geofavorite.ByTitleAZ);
|
||||
Collections.sort(items, Geofavorite.ByTitleAZ);
|
||||
} else if (sortRule == SORT_BY_CREATED) {
|
||||
Collections.sort(geofavoriteListFiltered, Geofavorite.ByLastCreated);
|
||||
Collections.sort(items, Geofavorite.ByLastCreated);
|
||||
} else if (sortRule == SORT_BY_CATEGORY) {
|
||||
Collections.sort(items, Geofavorite.ByCategory);
|
||||
} else if (sortRule == SORT_BY_DISTANCE) {
|
||||
Collections.sort(items, Geofavorite.ByDistance);
|
||||
}
|
||||
}
|
||||
|
||||
public interface ItemClickListener {
|
||||
void onItemClick(View view, int position);
|
||||
|
||||
private void onOverflowIconClicked(View view, int position) {
|
||||
// Save selected item
|
||||
overflowMenuSelectedPosition = position;
|
||||
// Open menu
|
||||
PopupMenu popup = new PopupMenu(context, view);
|
||||
popup.inflate(R.menu.list_context_menu);
|
||||
popup.setOnMenuItemClickListener(this::optionsItemSelected);
|
||||
popup.show();
|
||||
}
|
||||
|
||||
private boolean optionsItemSelected(MenuItem item) {
|
||||
if (overflowMenuSelectedPosition < 0) {
|
||||
Log.e(TAG, "No overflow menu selected position saved!");
|
||||
return false;
|
||||
}
|
||||
Geofavorite gf = get(overflowMenuSelectedPosition);
|
||||
overflowMenuSelectedPosition = -1;
|
||||
if (item.getItemId() == R.id.list_context_menu_share && itemClickListener != null)
|
||||
itemClickListener.onItemShareClick(gf);
|
||||
if (item.getItemId() == R.id.list_context_menu_delete && itemClickListener != null)
|
||||
itemClickListener.onItemDeleteClick(gf);
|
||||
return true;
|
||||
}
|
||||
|
||||
public interface ItemClickListener {
|
||||
void onItemClick(Geofavorite item);
|
||||
void onItemShareClick(Geofavorite item);
|
||||
void onItemNavClick(Geofavorite item);
|
||||
void onItemDeleteClick(Geofavorite item);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,188 +0,0 @@
|
||||
/*
|
||||
* Nextcloud Maps Geofavorites for Android
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package it.danieleverducci.nextcloudmaps.activity.main;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.location.Location;
|
||||
import android.location.LocationListener;
|
||||
import android.location.LocationManager;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
|
||||
import it.danieleverducci.nextcloudmaps.R;
|
||||
import it.danieleverducci.nextcloudmaps.api.ApiProvider;
|
||||
import it.danieleverducci.nextcloudmaps.databinding.ActivityGeofavoriteDetailBinding;
|
||||
import it.danieleverducci.nextcloudmaps.model.Geofavorite;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
||||
public class GeofavoriteDetailActivity extends AppCompatActivity implements LocationListener, ActivityCompat.OnRequestPermissionsResultCallback {
|
||||
|
||||
public static final String TAG = "GeofavDetail";
|
||||
public static final String ARG_GEOFAVORITE_ID = "geofavid";
|
||||
private static final int PERMISSION_REQUEST_CODE = 9999;
|
||||
|
||||
private ActivityGeofavoriteDetailBinding binding;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
binding = ActivityGeofavoriteDetailBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.root);
|
||||
|
||||
int id = getIntent().getIntExtra(ARG_GEOFAVORITE_ID, 0);
|
||||
if (id == 0) {
|
||||
// New geofavorite
|
||||
Geofavorite gf = new Geofavorite();
|
||||
gf.setDateCreated(System.currentTimeMillis());
|
||||
gf.setDateModified(System.currentTimeMillis());
|
||||
binding.setGeofavorite(gf);
|
||||
// Precompile location
|
||||
getLocation();
|
||||
} else {
|
||||
// TODO: Load geofavorite from cache for edit
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the submit button is clicked
|
||||
* @param v The button
|
||||
*/
|
||||
public void onSubmit(View v) {
|
||||
saveGeofavorite();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks fields and sends updated geofavorite to Nextcloud instance
|
||||
*/
|
||||
private void saveGeofavorite() {
|
||||
Geofavorite gf = binding.getGeofavorite();
|
||||
gf.setName(binding.nameEt.getText().toString());
|
||||
gf.setComment(binding.descriptionEt.getText().toString());
|
||||
gf.setDateModified(System.currentTimeMillis());
|
||||
|
||||
if (!gf.valid()) {
|
||||
Toast.makeText(GeofavoriteDetailActivity.this, R.string.incomplete_geofavorite, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
Call<Geofavorite> call;
|
||||
if (gf.getId() == 0) {
|
||||
// New geofavorite
|
||||
call = ApiProvider.getAPI().createGeofavorite(gf);
|
||||
} else {
|
||||
// Update existing geofavorite
|
||||
call = ApiProvider.getAPI().updateGeofavorite(gf.getId(), gf);
|
||||
}
|
||||
call.enqueue(new Callback<Geofavorite>() {
|
||||
@Override
|
||||
public void onResponse(Call<Geofavorite> call, Response<Geofavorite> response) {
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<Geofavorite> call, Throwable t) {
|
||||
Toast.makeText(GeofavoriteDetailActivity.this, R.string.error_saving_geofavorite, Toast.LENGTH_SHORT).show();
|
||||
Log.e(TAG, "Unable to update geofavorite: " + t.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the current location (requesting user's permission, if necessary)
|
||||
* and calls updateLocationField()
|
||||
*/
|
||||
private void getLocation() {
|
||||
// Check if user granted location permission
|
||||
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
|
||||
// User didn't grant permission. Ask it.
|
||||
requestPermissions(new String[] {Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSION_REQUEST_CODE);
|
||||
return;
|
||||
}
|
||||
|
||||
LocationManager locationManager = (LocationManager)
|
||||
getSystemService(Context.LOCATION_SERVICE);
|
||||
// Try to use last available location
|
||||
Location lastKnown = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
|
||||
if (lastKnown != null)
|
||||
updateLocationField(lastKnown);
|
||||
// Register for location updates in case the user moves before saving
|
||||
locationManager.requestLocationUpdates(
|
||||
LocationManager.GPS_PROVIDER, 1000, 10, this
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the geofavorite location field with the provided location object
|
||||
* @param location to set in the geofavorite
|
||||
*/
|
||||
private void updateLocationField(Location location) {
|
||||
binding.getGeofavorite().setLat(location.getLatitude());
|
||||
binding.getGeofavorite().setLng(location.getLongitude());
|
||||
binding.notifyChange();
|
||||
}
|
||||
|
||||
|
||||
/** Location updates callbacks **/
|
||||
|
||||
@Override
|
||||
public void onLocationChanged(@NonNull Location location) {
|
||||
updateLocationField(location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStatusChanged(String provider, int status, Bundle extras) {}
|
||||
|
||||
@Override
|
||||
public void onProviderEnabled(@NonNull String provider) {}
|
||||
|
||||
@Override
|
||||
public void onProviderDisabled(@NonNull String provider) {}
|
||||
|
||||
@Override
|
||||
public void onPointerCaptureChanged(boolean hasCapture) {}
|
||||
|
||||
|
||||
/** Position permission request result **/
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
if (requestCode == PERMISSION_REQUEST_CODE && permissions[0].equals(Manifest.permission.ACCESS_FINE_LOCATION)) {
|
||||
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
getLocation();
|
||||
} else {
|
||||
Toast.makeText(this, R.string.location_permission_required, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package it.danieleverducci.nextcloudmaps.activity.main;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import it.danieleverducci.nextcloudmaps.model.Geofavorite;
|
||||
import it.danieleverducci.nextcloudmaps.repository.GeofavoriteRepository;
|
||||
|
||||
public class GeofavoritesFragmentViewModel extends ViewModel {
|
||||
private GeofavoriteRepository mRepo;
|
||||
|
||||
public void init(Context applicationContext) {
|
||||
mRepo = GeofavoriteRepository.getInstance(applicationContext);
|
||||
}
|
||||
|
||||
public LiveData<List<Geofavorite>> getGeofavorites(){
|
||||
mRepo.updateGeofavorites();
|
||||
return mRepo.getGeofavorites();
|
||||
}
|
||||
|
||||
public void updateGeofavorites() {
|
||||
mRepo.updateGeofavorites();
|
||||
}
|
||||
|
||||
public LiveData<HashSet<String>> getCategories(){
|
||||
return mRepo.getCategories();
|
||||
}
|
||||
|
||||
public void deleteGeofavorite(Geofavorite geofav) {
|
||||
mRepo.deleteGeofavorite(geofav);
|
||||
}
|
||||
|
||||
public LiveData<Boolean> getIsUpdating(){
|
||||
return mRepo.isUpdating();
|
||||
}
|
||||
|
||||
public LiveData<Boolean> getOnFinished(){
|
||||
return mRepo.onFinished();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,166 +17,137 @@
|
||||
|
||||
package it.danieleverducci.nextcloudmaps.activity.main;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.widget.Toast;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.AppCompatImageButton;
|
||||
import androidx.appcompat.widget.AppCompatImageView;
|
||||
import androidx.appcompat.widget.SearchView;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.view.GravityCompat;
|
||||
import androidx.drawerlayout.widget.DrawerLayout;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import com.google.android.material.card.MaterialCardView;
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
import com.nextcloud.android.sso.helper.SingleAccountHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import it.danieleverducci.nextcloudmaps.R;
|
||||
import it.danieleverducci.nextcloudmaps.activity.NextcloudMapsStyledActivity;
|
||||
import it.danieleverducci.nextcloudmaps.activity.about.AboutActivity;
|
||||
import it.danieleverducci.nextcloudmaps.activity.detail.GeofavoriteDetailActivity;
|
||||
import it.danieleverducci.nextcloudmaps.activity.login.LoginActivity;
|
||||
import it.danieleverducci.nextcloudmaps.activity.main.NavigationAdapter.NavigationItem;
|
||||
import it.danieleverducci.nextcloudmaps.activity.main.SortingOrderDialogFragment.OnSortingOrderListener;
|
||||
import it.danieleverducci.nextcloudmaps.activity.mappicker.MapPickerActivity;
|
||||
import it.danieleverducci.nextcloudmaps.api.ApiProvider;
|
||||
import it.danieleverducci.nextcloudmaps.model.Geofavorite;
|
||||
import it.danieleverducci.nextcloudmaps.fragments.GeofavoriteListFragment;
|
||||
import it.danieleverducci.nextcloudmaps.fragments.GeofavoriteMapFragment;
|
||||
import it.danieleverducci.nextcloudmaps.repository.GeofavoriteRepository;
|
||||
import it.danieleverducci.nextcloudmaps.utils.SettingsManager;
|
||||
|
||||
import static android.view.View.GONE;
|
||||
import static android.view.View.VISIBLE;
|
||||
import static it.danieleverducci.nextcloudmaps.activity.main.GeofavoriteAdapter.*;
|
||||
import static it.danieleverducci.nextcloudmaps.activity.main.GeofavoriteAdapter.SORT_BY_CREATED;
|
||||
import static it.danieleverducci.nextcloudmaps.activity.main.GeofavoriteAdapter.SORT_BY_TITLE;
|
||||
public class MainActivity extends NextcloudMapsStyledActivity {
|
||||
|
||||
public class MainActivity extends AppCompatActivity implements MainView, OnSortingOrderListener {
|
||||
private static final String TAG = "MainActivity";
|
||||
private static final int PERMISSION_REQUEST_CODE = 3890;
|
||||
|
||||
private static final int INTENT_ADD = 100;
|
||||
private static final int INTENT_EDIT = 200;
|
||||
|
||||
private static final String NAVIGATION_KEY_ADD_GEOFAVORITE = "add";
|
||||
private static final String NAVIGATION_KEY_ADD_GEOFAVORITE_FROM_GPS = "add_from_gps";
|
||||
private static final String NAVIGATION_KEY_ADD_GEOFAVORITE_FROM_MAP = "add_from_map";
|
||||
private static final String NAVIGATION_KEY_SHOW_ABOUT = "about";
|
||||
private static final String NAVIGATION_KEY_SWITCH_ACCOUNT = "switch_account";
|
||||
|
||||
private SharedPreferences preferences;
|
||||
|
||||
private ArrayList<OnGpsPermissionGrantedListener> onGpsPermissionGrantedListener = new ArrayList<>();
|
||||
private DrawerLayout drawerLayout;
|
||||
private Toolbar toolbar;
|
||||
private MaterialCardView homeToolbar;
|
||||
private SearchView searchView;
|
||||
private SwipeRefreshLayout swipeRefresh;
|
||||
private RecyclerView recyclerView;
|
||||
private StaggeredGridLayoutManager layoutManager;
|
||||
private FloatingActionButton fab;
|
||||
|
||||
private MainPresenter presenter;
|
||||
private GeofavoriteAdapter geofavoriteAdapter;
|
||||
private ItemClickListener itemClickListener;
|
||||
private boolean isFabOpen = false;
|
||||
|
||||
NavigationAdapter navigationCommonAdapter;
|
||||
|
||||
private ApiProvider mApi;
|
||||
public void openDrawer() {
|
||||
drawerLayout.openDrawer(GravityCompat.START);
|
||||
}
|
||||
|
||||
public void showMap() {
|
||||
replaceFragment(new GeofavoriteMapFragment());
|
||||
SettingsManager.setGeofavoriteListShownAsMap(this, true);
|
||||
}
|
||||
|
||||
public void showList() {
|
||||
replaceFragment(new GeofavoriteListFragment());
|
||||
SettingsManager.setGeofavoriteListShownAsMap(this, false);
|
||||
}
|
||||
|
||||
public void addOnGpsPermissionGrantedListener(OnGpsPermissionGrantedListener l) {
|
||||
onGpsPermissionGrantedListener.add(l);
|
||||
}
|
||||
|
||||
public void removeOnGpsPermissionGrantedListener(OnGpsPermissionGrantedListener l) {
|
||||
onGpsPermissionGrantedListener.remove(l);
|
||||
}
|
||||
|
||||
public void requestGpsPermissions() {
|
||||
ActivityCompat.requestPermissions(
|
||||
this,
|
||||
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
|
||||
PERMISSION_REQUEST_CODE
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
|
||||
if (requestCode == PERMISSION_REQUEST_CODE && permissions[0].equals(Manifest.permission.ACCESS_FINE_LOCATION)) {
|
||||
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
for (OnGpsPermissionGrantedListener l : onGpsPermissionGrantedListener) {
|
||||
l.onGpsPermissionGranted();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
|
||||
boolean showMap = SettingsManager.isGeofavoriteListShownAsMap(this);
|
||||
if (showMap)
|
||||
showMap();
|
||||
else
|
||||
showList();
|
||||
|
||||
int sortRule = preferences.getInt(getString(R.string.setting_sort_by), SORT_BY_CREATED);
|
||||
boolean gridViewEnabled = preferences.getBoolean(getString(R.string.setting_grid_view_enabled), true);
|
||||
FloatingActionButton fab = findViewById(R.id.open_fab);
|
||||
fab.setOnClickListener(view -> openFab(!this.isFabOpen));
|
||||
|
||||
recyclerView = findViewById(R.id.recycler_view);
|
||||
layoutManager = new StaggeredGridLayoutManager(gridViewEnabled ? 2 : 1, StaggeredGridLayoutManager.VERTICAL);
|
||||
recyclerView.setLayoutManager(layoutManager);
|
||||
fab = findViewById(R.id.add_from_gps);
|
||||
fab.setOnClickListener(view -> addGeofavoriteFromGps());
|
||||
|
||||
presenter = new MainPresenter(this);
|
||||
fab = findViewById(R.id.add_from_map);
|
||||
fab.setOnClickListener(view -> addGeofavoriteFromMap());
|
||||
|
||||
itemClickListener = ((view, position) -> {
|
||||
Geofavorite geofavorite = geofavoriteAdapter.get(position);
|
||||
Intent i = new Intent();
|
||||
i.setAction(Intent.ACTION_VIEW);
|
||||
i.setData(geofavorite.getGeoUri());
|
||||
startActivity(i);
|
||||
});
|
||||
|
||||
geofavoriteAdapter = new GeofavoriteAdapter(getApplicationContext(), itemClickListener);
|
||||
recyclerView.setAdapter(geofavoriteAdapter);
|
||||
|
||||
geofavoriteAdapter.setSortRule(sortRule);
|
||||
|
||||
swipeRefresh = findViewById(R.id.swipe_refresh);
|
||||
swipeRefresh.setOnRefreshListener(() -> presenter.getGeofavorites());
|
||||
|
||||
fab = findViewById(R.id.add);
|
||||
fab.setOnClickListener(view -> addGeofavorite());
|
||||
|
||||
toolbar = findViewById(R.id.toolbar);
|
||||
homeToolbar = findViewById(R.id.home_toolbar);
|
||||
|
||||
searchView = findViewById(R.id.search_view);
|
||||
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
|
||||
@Override
|
||||
public boolean onQueryTextSubmit(String query) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onQueryTextChange(String query) {
|
||||
geofavoriteAdapter.getFilter().filter(query);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
searchView.setOnCloseListener(() -> {
|
||||
if (toolbar.getVisibility() == VISIBLE && TextUtils.isEmpty(searchView.getQuery())) {
|
||||
updateToolbars(true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
setSupportActionBar(toolbar);
|
||||
setupNavigationMenu();
|
||||
|
||||
homeToolbar.setOnClickListener(view -> updateToolbars(false));
|
||||
|
||||
AppCompatImageView sortButton = findViewById(R.id.sort_mode);
|
||||
sortButton.setOnClickListener(view -> openSortingOrderDialogFragment(getSupportFragmentManager(), geofavoriteAdapter.getSortRule()));
|
||||
|
||||
drawerLayout = findViewById(R.id.drawerLayout);
|
||||
AppCompatImageButton menuButton = findViewById(R.id.menu_button);
|
||||
menuButton.setOnClickListener(view -> drawerLayout.openDrawer(GravityCompat.START));
|
||||
|
||||
AppCompatImageView viewButton = findViewById(R.id.view_mode);
|
||||
viewButton.setOnClickListener(view -> {
|
||||
boolean gridEnabled = layoutManager.getSpanCount() == 1;
|
||||
onGridIconChosen(gridEnabled);
|
||||
});
|
||||
|
||||
updateSortingIcon(sortRule);
|
||||
updateGridIcon(gridViewEnabled);
|
||||
|
||||
mApi = new ApiProvider(getApplicationContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
protected void onPause() {
|
||||
openFab(false);
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
// Update list
|
||||
presenter.getGeofavorites();
|
||||
private void replaceFragment(Fragment fragment) {
|
||||
FragmentManager manager = getSupportFragmentManager();
|
||||
FragmentTransaction transaction = manager.beginTransaction();
|
||||
transaction.replace(R.id.fragment_container, fragment);
|
||||
transaction.commit();
|
||||
}
|
||||
|
||||
private void setupNavigationMenu() {
|
||||
@@ -184,8 +155,11 @@ public class MainActivity extends AppCompatActivity implements MainView, OnSorti
|
||||
|
||||
navigationCommonAdapter = new NavigationAdapter(this, item -> {
|
||||
switch (item.id) {
|
||||
case NAVIGATION_KEY_ADD_GEOFAVORITE:
|
||||
addGeofavorite();
|
||||
case NAVIGATION_KEY_ADD_GEOFAVORITE_FROM_GPS:
|
||||
addGeofavoriteFromGps();
|
||||
break;
|
||||
case NAVIGATION_KEY_ADD_GEOFAVORITE_FROM_MAP:
|
||||
addGeofavoriteFromMap();
|
||||
break;
|
||||
case NAVIGATION_KEY_SHOW_ABOUT:
|
||||
show_about();
|
||||
@@ -196,7 +170,8 @@ public class MainActivity extends AppCompatActivity implements MainView, OnSorti
|
||||
}
|
||||
});
|
||||
|
||||
navItems.add(new NavigationItem(NAVIGATION_KEY_ADD_GEOFAVORITE, getString(R.string.new_geobookmark), R.drawable.ic_add));
|
||||
navItems.add(new NavigationItem(NAVIGATION_KEY_ADD_GEOFAVORITE_FROM_GPS, getString(R.string.new_geobookmark_gps), R.drawable.ic_add_gps));
|
||||
navItems.add(new NavigationItem(NAVIGATION_KEY_ADD_GEOFAVORITE_FROM_MAP, getString(R.string.new_geobookmark_map), R.drawable.ic_add_map));
|
||||
navItems.add(new NavigationItem(NAVIGATION_KEY_SHOW_ABOUT, getString(R.string.about), R.drawable.ic_info_grey));
|
||||
navItems.add(new NavigationItem(NAVIGATION_KEY_SWITCH_ACCOUNT, getString(R.string.switch_account), R.drawable.ic_logout_grey));
|
||||
navigationCommonAdapter.setItems(navItems);
|
||||
@@ -205,97 +180,52 @@ public class MainActivity extends AppCompatActivity implements MainView, OnSorti
|
||||
navigationMenuCommon.setAdapter(navigationCommonAdapter);
|
||||
}
|
||||
|
||||
private void updateToolbars(boolean disableSearch) {
|
||||
homeToolbar.setVisibility(disableSearch ? VISIBLE : GONE);
|
||||
toolbar.setVisibility(disableSearch ? GONE : VISIBLE);
|
||||
if (disableSearch) {
|
||||
searchView.setQuery(null, true);
|
||||
}
|
||||
searchView.setIconified(disableSearch);
|
||||
private void addGeofavoriteFromGps() {
|
||||
startActivity(
|
||||
new Intent(this, GeofavoriteDetailActivity.class)
|
||||
);
|
||||
}
|
||||
|
||||
private void openSortingOrderDialogFragment(FragmentManager supportFragmentManager, int sortOrder) {
|
||||
FragmentTransaction fragmentTransaction = supportFragmentManager.beginTransaction();
|
||||
fragmentTransaction.addToBackStack(null);
|
||||
|
||||
SortingOrderDialogFragment.newInstance(sortOrder).show(fragmentTransaction, SortingOrderDialogFragment.SORTING_ORDER_FRAGMENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (requestCode == INTENT_ADD && resultCode == RESULT_OK) {
|
||||
presenter.getGeofavorites();
|
||||
} else if (requestCode == INTENT_EDIT && resultCode == RESULT_OK) {
|
||||
presenter.getGeofavorites();
|
||||
}
|
||||
}
|
||||
|
||||
private void addGeofavorite() {
|
||||
startActivityForResult(
|
||||
new Intent(this, GeofavoriteDetailActivity.class), INTENT_ADD);
|
||||
private void addGeofavoriteFromMap() {
|
||||
startActivity(
|
||||
new Intent(this, MapPickerActivity.class)
|
||||
);
|
||||
}
|
||||
|
||||
private void show_about() {
|
||||
startActivity(new Intent(this, AboutActivity.class));
|
||||
}
|
||||
|
||||
private void switch_account() {
|
||||
SingleAccountHelper.setCurrentAccount(this, null);
|
||||
public void switch_account() {
|
||||
ApiProvider.logout();
|
||||
GeofavoriteRepository.resetInstance();
|
||||
SingleAccountHelper.applyCurrentAccount(this, null);
|
||||
Intent intent = new Intent(MainActivity.this, LoginActivity.class);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showLoading() {
|
||||
swipeRefresh.setRefreshing(true);
|
||||
private void openFab(boolean open) {
|
||||
View fab = findViewById(R.id.open_fab);
|
||||
View addFromGpsFab = findViewById(R.id.add_from_gps);
|
||||
View addFromMapFab = findViewById(R.id.add_from_map);
|
||||
|
||||
if (open) {
|
||||
this.isFabOpen = true;
|
||||
fab.animate().rotation(45.0f);
|
||||
addFromGpsFab.animate().translationY(-getResources().getDimension(R.dimen.fab_vertical_offset));
|
||||
addFromMapFab.animate().translationY(-getResources().getDimension(R.dimen.fab_vertical_offset) * 2);
|
||||
} else {
|
||||
this.isFabOpen = false;
|
||||
fab.animate().rotation(0f);
|
||||
addFromGpsFab.animate().translationY(0);
|
||||
addFromMapFab.animate().translationY(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hideLoading() {
|
||||
swipeRefresh.setRefreshing(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGetResult(List<Geofavorite> geofavorite_list) {
|
||||
geofavoriteAdapter.setGeofavoriteList(geofavorite_list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onErrorLoading(String message) {
|
||||
Toast.makeText(MainActivity.this, message, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSortingOrderChosen(int sortSelection) {
|
||||
geofavoriteAdapter.setSortRule(sortSelection);
|
||||
updateSortingIcon(sortSelection);
|
||||
|
||||
preferences.edit().putInt(getString(R.string.setting_sort_by), sortSelection).apply();
|
||||
}
|
||||
|
||||
public void updateSortingIcon(int sortSelection) {
|
||||
AppCompatImageView sortButton = findViewById(R.id.sort_mode);
|
||||
switch (sortSelection) {
|
||||
case SORT_BY_TITLE:
|
||||
sortButton.setImageResource(R.drawable.ic_alphabetical_asc);
|
||||
break;
|
||||
case SORT_BY_CREATED:
|
||||
sortButton.setImageResource(R.drawable.ic_modification_asc);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void onGridIconChosen(boolean gridEnabled) {
|
||||
layoutManager.setSpanCount(gridEnabled ? 2 : 1);
|
||||
updateGridIcon(gridEnabled);
|
||||
|
||||
preferences.edit().putBoolean(getString(R.string.setting_grid_view_enabled), gridEnabled).apply();
|
||||
}
|
||||
|
||||
public void updateGridIcon(boolean gridEnabled) {
|
||||
AppCompatImageView viewButton = findViewById(R.id.view_mode);
|
||||
viewButton.setImageResource(gridEnabled ? R.drawable.ic_view_list : R.drawable.ic_view_module);
|
||||
public interface OnGpsPermissionGrantedListener {
|
||||
public void onGpsPermissionGranted();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Nextcloud Maps Geofavorites for Android
|
||||
*
|
||||
* @copyright Copyright (c) 2020 John Doe <john@doe.com>
|
||||
* @author John Doe <john@doe.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package it.danieleverducci.nextcloudmaps.activity.main;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import it.danieleverducci.nextcloudmaps.api.ApiProvider;
|
||||
import it.danieleverducci.nextcloudmaps.model.Geofavorite;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
||||
public class MainPresenter {
|
||||
private MainView view;
|
||||
|
||||
public MainPresenter(MainView view) {
|
||||
this.view = view;
|
||||
}
|
||||
|
||||
public void getGeofavorites() {
|
||||
view.showLoading();
|
||||
Call<List<Geofavorite>> call = ApiProvider.getAPI().getGeofavorites();
|
||||
call.enqueue(new Callback<List<Geofavorite>>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<List<Geofavorite>> call, @NonNull Response<List<Geofavorite>> response) {
|
||||
((AppCompatActivity) view).runOnUiThread(() -> {
|
||||
view.hideLoading();
|
||||
if (response.isSuccessful() && response.body() != null) {
|
||||
view.onGetResult(response.body());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<List<Geofavorite>> call, @NonNull Throwable t) {
|
||||
((AppCompatActivity) view).runOnUiThread(() -> {
|
||||
view.hideLoading();
|
||||
view.onErrorLoading(t.getLocalizedMessage());
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Nextcloud Maps Geofavorites for Android
|
||||
*
|
||||
* @copyright Copyright (c) 2020 John Doe <john@doe.com>
|
||||
* @author John Doe <john@doe.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package it.danieleverducci.nextcloudmaps.activity.main;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import it.danieleverducci.nextcloudmaps.model.Geofavorite;
|
||||
|
||||
public interface MainView {
|
||||
void showLoading();
|
||||
void hideLoading();
|
||||
void onGetResult(List<Geofavorite> geofavorites);
|
||||
void onErrorLoading(String message);
|
||||
}
|
||||
@@ -80,15 +80,12 @@ public class NavigationAdapter extends RecyclerView.Adapter<NavigationAdapter.Vi
|
||||
}
|
||||
|
||||
private void bind(@NonNull NavigationItem item) {
|
||||
int color = view.getResources().getColor(R.color.accent);
|
||||
|
||||
currentItem = item;
|
||||
name.setText(item.label);
|
||||
|
||||
name.setTextColor(color);
|
||||
|
||||
icon.setImageDrawable(DrawableCompat.wrap(icon.getResources().getDrawable(item.icon)));
|
||||
icon.setColorFilter(color);
|
||||
icon.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ import android.widget.ImageButton;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.graphics.drawable.DrawableCompat;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
|
||||
@@ -47,6 +48,7 @@ public class SortingOrderDialogFragment extends DialogFragment {
|
||||
public static final String SORTING_ORDER_FRAGMENT = "SORTING_ORDER_FRAGMENT";
|
||||
private static final String KEY_SORT_ORDER = "SORT_ORDER";
|
||||
|
||||
private OnSortingOrderListener onSortingOrderListener;
|
||||
private View mView;
|
||||
private View[] mTaggedViews;
|
||||
private Button mCancel;
|
||||
@@ -89,15 +91,20 @@ public class SortingOrderDialogFragment extends DialogFragment {
|
||||
return mView;
|
||||
}
|
||||
|
||||
public void setOnSortingOrderListener(OnSortingOrderListener listener) {
|
||||
this.onSortingOrderListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* find all relevant UI elements and set their values.
|
||||
* TODO: this is REALLY ugly.
|
||||
*
|
||||
* @param view the parent view
|
||||
*/
|
||||
private void setupDialogElements(View view) {
|
||||
mCancel = view.findViewById(R.id.cancel);
|
||||
|
||||
mTaggedViews = new View[4];
|
||||
mTaggedViews = new View[8];
|
||||
mTaggedViews[0] = view.findViewById(R.id.sortByTitleAscending);
|
||||
mTaggedViews[0].setTag(GeofavoriteAdapter.SORT_BY_TITLE);
|
||||
mTaggedViews[1] = view.findViewById(R.id.sortByTitleAscendingText);
|
||||
@@ -106,6 +113,14 @@ public class SortingOrderDialogFragment extends DialogFragment {
|
||||
mTaggedViews[2].setTag(GeofavoriteAdapter.SORT_BY_CREATED);
|
||||
mTaggedViews[3] = view.findViewById(R.id.sortByCreationDateDescendingText);
|
||||
mTaggedViews[3].setTag(GeofavoriteAdapter.SORT_BY_CREATED);
|
||||
mTaggedViews[4] = view.findViewById(R.id.sortByCategoryAscending);
|
||||
mTaggedViews[4].setTag(GeofavoriteAdapter.SORT_BY_CATEGORY);
|
||||
mTaggedViews[5] = view.findViewById(R.id.sortByCategoryAscendingText);
|
||||
mTaggedViews[5].setTag(GeofavoriteAdapter.SORT_BY_CATEGORY);
|
||||
mTaggedViews[6] = view.findViewById(R.id.sortByDistanceAscending);
|
||||
mTaggedViews[6].setTag(GeofavoriteAdapter.SORT_BY_DISTANCE);
|
||||
mTaggedViews[7] = view.findViewById(R.id.sortByDistanceAscendingText);
|
||||
mTaggedViews[7].setTag(GeofavoriteAdapter.SORT_BY_DISTANCE);
|
||||
|
||||
setupActiveOrderSelection();
|
||||
}
|
||||
@@ -121,10 +136,10 @@ public class SortingOrderDialogFragment extends DialogFragment {
|
||||
if (view instanceof ImageButton) {
|
||||
Drawable normalDrawable = ((ImageButton) view).getDrawable();
|
||||
Drawable wrapDrawable = DrawableCompat.wrap(normalDrawable);
|
||||
DrawableCompat.setTint(wrapDrawable, this.getResources().getColor(R.color.defaultTint));
|
||||
DrawableCompat.setTint(wrapDrawable, ContextCompat.getColor(getContext(), R.color.selector_item_selected));
|
||||
}
|
||||
if (view instanceof TextView) {
|
||||
((TextView)view).setTextColor(this.getResources().getColor(R.color.defaultTint));
|
||||
((TextView)view).setTextColor(ContextCompat.getColor(getContext(), R.color.selector_item_selected));
|
||||
((TextView)view).setTypeface(Typeface.DEFAULT_BOLD);
|
||||
}
|
||||
}
|
||||
@@ -161,8 +176,8 @@ public class SortingOrderDialogFragment extends DialogFragment {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
dismissAllowingStateLoss();
|
||||
((SortingOrderDialogFragment.OnSortingOrderListener) getActivity())
|
||||
.onSortingOrderChosen((int) v.getTag());
|
||||
if (onSortingOrderListener != null)
|
||||
onSortingOrderListener.onSortingOrderChosen((int) v.getTag());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
package it.danieleverducci.nextcloudmaps.activity.mappicker;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.location.Location;
|
||||
import android.location.LocationManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
|
||||
import org.osmdroid.api.IGeoPoint;
|
||||
import org.osmdroid.api.IMapController;
|
||||
import org.osmdroid.events.MapListener;
|
||||
import org.osmdroid.events.ScrollEvent;
|
||||
import org.osmdroid.events.ZoomEvent;
|
||||
import org.osmdroid.util.GeoPoint;
|
||||
import org.osmdroid.views.CustomZoomButtonsController;
|
||||
import org.osmdroid.views.MapView;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import it.danieleverducci.nextcloudmaps.R;
|
||||
import it.danieleverducci.nextcloudmaps.activity.NextcloudMapsStyledActivity;
|
||||
import it.danieleverducci.nextcloudmaps.activity.detail.GeofavoriteDetailActivity;
|
||||
import it.danieleverducci.nextcloudmaps.databinding.ActivityMapPickerBinding;
|
||||
import it.danieleverducci.nextcloudmaps.utils.GeoUriParser;
|
||||
import it.danieleverducci.nextcloudmaps.utils.MapUtils;
|
||||
|
||||
public class MapPickerActivity extends NextcloudMapsStyledActivity {
|
||||
public static final String TAG = "MapPickerActivity";
|
||||
private static final int PERMISSION_REQUEST_CODE = 8888;
|
||||
|
||||
private ViewHolder mViewHolder;
|
||||
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
MapUtils.configOsmdroid(this);
|
||||
|
||||
mViewHolder = new MapPickerActivity.ViewHolder(getLayoutInflater());
|
||||
mViewHolder.setViewEventListener(new ViewEventListener() {
|
||||
@Override
|
||||
public void onExitButtonPressed() {
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfirmButtonPressed() {
|
||||
double[] coords = mViewHolder.getCurrentCoordinates();
|
||||
Uri geoUri = GeoUriParser.createGeoUri(coords[0], coords[1], null);
|
||||
Intent i = new Intent(MapPickerActivity.this, GeofavoriteDetailActivity.class);
|
||||
i.setData(geoUri);
|
||||
startActivity(i);
|
||||
finish();
|
||||
}
|
||||
});
|
||||
Location l = getLastKnownPosition();
|
||||
if (l != null)
|
||||
mViewHolder.centerMapOn(l.getLatitude(), l.getLongitude());
|
||||
|
||||
setContentView(mViewHolder.getRootView());
|
||||
}
|
||||
|
||||
/**
|
||||
* May return last known GPS position or null. Used to center the map.
|
||||
* @return last known GPS position or null
|
||||
*/
|
||||
private Location getLastKnownPosition() {
|
||||
// Check if user granted location permission
|
||||
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
|
||||
// User didn't grant permission. Ask it.
|
||||
requestPermissions(new String[] {Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSION_REQUEST_CODE);
|
||||
return null;
|
||||
}
|
||||
|
||||
LocationManager locationManager = (LocationManager)
|
||||
getSystemService(Context.LOCATION_SERVICE);
|
||||
// Try to use last available location
|
||||
return locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
|
||||
}
|
||||
|
||||
private class ViewHolder implements View.OnClickListener {
|
||||
private final ActivityMapPickerBinding binding;
|
||||
private ViewEventListener listener;
|
||||
|
||||
private final MapView map;
|
||||
private boolean coordsEditMode = false;
|
||||
|
||||
public ViewHolder(LayoutInflater inflater) {
|
||||
this.binding = ActivityMapPickerBinding.inflate(inflater);
|
||||
|
||||
// Show confirm button on lat/lng edit
|
||||
View.OnFocusChangeListener latlonFocusListener = new View.OnFocusChangeListener() {
|
||||
@Override
|
||||
public void onFocusChange(View view, boolean focused) {
|
||||
if (!focused)
|
||||
return;
|
||||
|
||||
(ViewHolder.this).coordsEditMode(true);
|
||||
}
|
||||
};
|
||||
this.binding.latEt.setOnFocusChangeListener(latlonFocusListener);
|
||||
this.binding.lonEt.setOnFocusChangeListener(latlonFocusListener);
|
||||
|
||||
// Setup map
|
||||
this.map = this.binding.map;
|
||||
this.map.getZoomController().setVisibility(CustomZoomButtonsController.Visibility.NEVER);
|
||||
this.map.setMultiTouchControls(true);
|
||||
MapUtils.setTheme(this.map);
|
||||
this.map.addMapListener(new MapListener() {
|
||||
@Override
|
||||
public boolean onScroll(ScrollEvent event) {
|
||||
// Disable edit mode
|
||||
if ((ViewHolder.this).coordsEditMode)
|
||||
(ViewHolder.this).coordsEditMode(false);
|
||||
// Write coords on edittext
|
||||
IGeoPoint igp = (ViewHolder.this).map.getMapCenter();
|
||||
(ViewHolder.this).binding.latEt.setText(String.format(Locale.ENGLISH, "%.06f", igp.getLatitude()));
|
||||
(ViewHolder.this).binding.lonEt.setText(String.format(Locale.ENGLISH, "%.06f", igp.getLongitude()));
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onZoom(ZoomEvent event) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
IMapController mapController = binding.map.getController();
|
||||
mapController.setZoom(7.0f);
|
||||
|
||||
// Setup onClick
|
||||
this.binding.latlonConfirmBtn.setOnClickListener(this);
|
||||
this.binding.backBt.setOnClickListener(this);
|
||||
this.binding.okBt.setOnClickListener(this);
|
||||
|
||||
}
|
||||
|
||||
public void setViewEventListener(ViewEventListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public void centerMapOn(Double lat, Double lon ) {
|
||||
IMapController mapController = binding.map.getController();
|
||||
mapController.setCenter(new GeoPoint(lat, lon));
|
||||
}
|
||||
|
||||
public View getRootView() {
|
||||
return this.binding.root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (view == this.binding.latlonConfirmBtn) {
|
||||
this.coordsEditMode(false);
|
||||
Double lat;
|
||||
Double lon;
|
||||
try {
|
||||
lat = Double.parseDouble(this.binding.latEt.getText().toString());
|
||||
lon = Double.parseDouble(this.binding.lonEt.getText().toString());
|
||||
} catch (NumberFormatException e) {
|
||||
Log.e(TAG, "Unable to parse coordinates: " + e.getLocalizedMessage());
|
||||
Toast.makeText(MapPickerActivity.this, R.string.coordinates_parse_error, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
// Validate coordinates
|
||||
if (lon <= -180 || lon >= 180 || lat <= -90 || lat >= 90) {
|
||||
Toast.makeText(MapPickerActivity.this, R.string.coordinates_invalid_error, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
// Move map to coordinates
|
||||
this.centerMapOn(lat, lon);
|
||||
}
|
||||
|
||||
if (view == this.binding.backBt)
|
||||
listener.onExitButtonPressed();
|
||||
|
||||
if (view == this.binding.okBt)
|
||||
listener.onConfirmButtonPressed();
|
||||
}
|
||||
|
||||
public double[] getCurrentCoordinates() {
|
||||
IGeoPoint igp = (ViewHolder.this).map.getMapCenter();
|
||||
return new double[]{igp.getLatitude(), igp.getLongitude()};
|
||||
}
|
||||
|
||||
/**
|
||||
* Enters/exits coordinates edit mode.
|
||||
* On exit, removes focus from the coordinates ET and hides the button
|
||||
*/
|
||||
private void coordsEditMode(boolean active) {
|
||||
this.coordsEditMode = active;
|
||||
View btn = this.binding.latlonConfirmBtn;
|
||||
if (active) {
|
||||
btn.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
this.binding.latEt.clearFocus();
|
||||
this.binding.lonEt.clearFocus();
|
||||
btn.setVisibility(View.GONE);
|
||||
// Hide soft keyboard
|
||||
InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(btn.getWindowToken(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected interface ViewEventListener {
|
||||
public void onExitButtonPressed();
|
||||
public void onConfirmButtonPressed();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.nextcloud.android.sso.api.NextcloudAPI;
|
||||
@@ -35,47 +36,27 @@ import com.nextcloud.android.sso.model.SingleSignOnAccount;
|
||||
import retrofit2.NextcloudRetrofitApiBuilder;
|
||||
|
||||
public class ApiProvider {
|
||||
private final String TAG = ApiProvider.class.getCanonicalName();
|
||||
private static final String TAG = ApiProvider.class.getCanonicalName();
|
||||
|
||||
@NonNull
|
||||
protected Context context;
|
||||
protected static API mApi;
|
||||
|
||||
protected static String ssoAccountName;
|
||||
|
||||
public ApiProvider(Context context) {
|
||||
this.context = context;
|
||||
initSsoApi(new NextcloudAPI.ApiConnectedListener() {
|
||||
@Override
|
||||
public void onConnected() {
|
||||
// Ignore..
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception ex) {
|
||||
// Ignore...
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void initSsoApi(final NextcloudAPI.ApiConnectedListener callback) {
|
||||
@Nullable
|
||||
public static API getAPI(Context context) {
|
||||
if (mApi == null) {
|
||||
try {
|
||||
SingleSignOnAccount ssoAccount = SingleAccountHelper.getCurrentSingleSignOnAccount(context);
|
||||
NextcloudAPI nextcloudAPI = new NextcloudAPI(context, ssoAccount, new GsonBuilder().create(), callback);
|
||||
NextcloudAPI nextcloudAPI = new NextcloudAPI(context, ssoAccount, new GsonBuilder().create());
|
||||
|
||||
ssoAccountName = ssoAccount.name;
|
||||
mApi = new NextcloudRetrofitApiBuilder(nextcloudAPI, API.mApiEndpoint).create(API.class);
|
||||
} catch (NextcloudFilesAppAccountNotFoundException | NoCurrentAccountSelectedException e) {
|
||||
Log.d(TAG, "setAccout() called with: ex = [" + e + "]");
|
||||
}
|
||||
}
|
||||
|
||||
public static API getAPI() {
|
||||
return mApi;
|
||||
}
|
||||
|
||||
public static String getAccountName() {
|
||||
return ssoAccountName;
|
||||
public static void logout() {
|
||||
mApi = null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
package it.danieleverducci.nextcloudmaps.fragments;
|
||||
|
||||
import static it.danieleverducci.nextcloudmaps.activity.main.GeofavoriteAdapter.SORT_BY_CATEGORY;
|
||||
import static it.danieleverducci.nextcloudmaps.activity.main.GeofavoriteAdapter.SORT_BY_CREATED;
|
||||
import static it.danieleverducci.nextcloudmaps.activity.main.GeofavoriteAdapter.SORT_BY_DISTANCE;
|
||||
import static it.danieleverducci.nextcloudmaps.activity.main.GeofavoriteAdapter.SORT_BY_TITLE;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.widget.AppCompatImageView;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
import androidx.lifecycle.Observer;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import it.danieleverducci.nextcloudmaps.R;
|
||||
import it.danieleverducci.nextcloudmaps.activity.main.GeofavoriteAdapter;
|
||||
import it.danieleverducci.nextcloudmaps.activity.main.MainActivity;
|
||||
import it.danieleverducci.nextcloudmaps.activity.main.SortingOrderDialogFragment;
|
||||
import it.danieleverducci.nextcloudmaps.model.Geofavorite;
|
||||
import it.danieleverducci.nextcloudmaps.utils.GeofavoritesFilter;
|
||||
import it.danieleverducci.nextcloudmaps.utils.SettingsManager;
|
||||
|
||||
public class GeofavoriteListFragment extends GeofavoritesFragment implements SortingOrderDialogFragment.OnSortingOrderListener {
|
||||
|
||||
private SwipeRefreshLayout swipeRefresh;
|
||||
private GeofavoriteAdapter geofavoriteAdapter;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
View v = inflater.inflate(R.layout.fragment_geofavorite_list, container, false);
|
||||
|
||||
// Setup list
|
||||
int sortRule = SettingsManager.getGeofavoriteListSortBy(requireContext());
|
||||
|
||||
RecyclerView recyclerView = v.findViewById(R.id.recycler_view);
|
||||
LinearLayoutManager layoutManager = new LinearLayoutManager(requireContext());
|
||||
recyclerView.setLayoutManager(layoutManager);
|
||||
|
||||
GeofavoriteAdapter.ItemClickListener rvItemClickListener = new GeofavoriteAdapter.ItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(Geofavorite item) {
|
||||
openGeofavorite(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemShareClick(Geofavorite item) {
|
||||
shareGeofavorite(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemNavClick(Geofavorite item) {
|
||||
navigateToGeofavorite(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemDeleteClick(Geofavorite item) {
|
||||
deleteGeofavorite(item);
|
||||
}
|
||||
};
|
||||
|
||||
geofavoriteAdapter = new GeofavoriteAdapter(requireContext(), rvItemClickListener);
|
||||
recyclerView.setAdapter(geofavoriteAdapter);
|
||||
geofavoriteAdapter.setSortRule(sortRule);
|
||||
|
||||
// Register for data source events
|
||||
mGeofavoritesFragmentViewModel.getIsUpdating().observe(getViewLifecycleOwner(), new Observer<Boolean>() {
|
||||
@Override
|
||||
public void onChanged(@Nullable Boolean changed) {
|
||||
if(Boolean.TRUE.equals(changed)){
|
||||
swipeRefresh.setRefreshing(true);
|
||||
}
|
||||
else{
|
||||
swipeRefresh.setRefreshing(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Setup view listeners
|
||||
swipeRefresh = v.findViewById(R.id.swipe_refresh);
|
||||
swipeRefresh.setOnRefreshListener(() ->
|
||||
mGeofavoritesFragmentViewModel.updateGeofavorites());
|
||||
|
||||
AppCompatImageView sortButton = v.findViewById(R.id.sort_mode);
|
||||
sortButton.setOnClickListener(view -> openSortingOrderDialogFragment(geofavoriteAdapter.getSortRule()));
|
||||
|
||||
View showMapButton = v.findViewById(R.id.view_mode_map);
|
||||
showMapButton.setOnClickListener(View -> ((MainActivity)requireActivity()).showMap());
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
// Set icons
|
||||
int sortRule = SettingsManager.getGeofavoriteListSortBy(requireContext());
|
||||
updateSortingIcon(sortRule);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDatasetChange(List<Geofavorite> items) {
|
||||
// Called when the items are loaded or a filtering happens
|
||||
geofavoriteAdapter.setGeofavoriteList(items);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSortingOrderChosen(int sortSelection) {
|
||||
geofavoriteAdapter.setSortRule(sortSelection);
|
||||
updateSortingIcon(sortSelection);
|
||||
|
||||
SettingsManager.setGeofavoriteListSortBy(requireContext(), sortSelection);
|
||||
}
|
||||
|
||||
private void openSortingOrderDialogFragment(int sortOrder) {
|
||||
FragmentTransaction fragmentTransaction = requireActivity().getSupportFragmentManager().beginTransaction();
|
||||
fragmentTransaction.addToBackStack(null);
|
||||
|
||||
SortingOrderDialogFragment sodf = SortingOrderDialogFragment.newInstance(sortOrder);
|
||||
sodf.setOnSortingOrderListener(this);
|
||||
sodf.show(fragmentTransaction, SortingOrderDialogFragment.SORTING_ORDER_FRAGMENT);
|
||||
}
|
||||
|
||||
private void updateSortingIcon(int sortSelection) {
|
||||
AppCompatImageView sortButton = requireView().findViewById(R.id.sort_mode);
|
||||
switch (sortSelection) {
|
||||
case SORT_BY_TITLE:
|
||||
sortButton.setImageResource(R.drawable.ic_alphabetical_asc);
|
||||
break;
|
||||
case SORT_BY_CREATED:
|
||||
sortButton.setImageResource(R.drawable.ic_modification_asc);
|
||||
break;
|
||||
case SORT_BY_CATEGORY:
|
||||
sortButton.setImageResource(R.drawable.ic_category_asc);
|
||||
break;
|
||||
case SORT_BY_DISTANCE:
|
||||
sortButton.setImageResource(R.drawable.ic_distance_asc);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
package it.danieleverducci.nextcloudmaps.fragments;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.content.res.AppCompatResources;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.res.ResourcesCompat;
|
||||
import androidx.core.graphics.drawable.DrawableCompat;
|
||||
import androidx.lifecycle.Observer;
|
||||
|
||||
import org.osmdroid.api.IMapController;
|
||||
import org.osmdroid.events.MapEventsReceiver;
|
||||
import org.osmdroid.util.GeoPoint;
|
||||
import org.osmdroid.views.CustomZoomButtonsController;
|
||||
import org.osmdroid.views.MapView;
|
||||
import org.osmdroid.views.overlay.MapEventsOverlay;
|
||||
import org.osmdroid.views.overlay.Marker;
|
||||
import org.osmdroid.views.overlay.Overlay;
|
||||
import org.osmdroid.views.overlay.infowindow.InfoWindow;
|
||||
import org.osmdroid.views.overlay.mylocation.GpsMyLocationProvider;
|
||||
import org.osmdroid.views.overlay.mylocation.MyLocationNewOverlay;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import it.danieleverducci.nextcloudmaps.R;
|
||||
import it.danieleverducci.nextcloudmaps.activity.detail.GeofavoriteDetailActivity;
|
||||
import it.danieleverducci.nextcloudmaps.activity.main.MainActivity;
|
||||
import it.danieleverducci.nextcloudmaps.activity.mappicker.MapPickerActivity;
|
||||
import it.danieleverducci.nextcloudmaps.model.Geofavorite;
|
||||
import it.danieleverducci.nextcloudmaps.utils.GeoUriParser;
|
||||
import it.danieleverducci.nextcloudmaps.utils.MapUtils;
|
||||
import it.danieleverducci.nextcloudmaps.utils.SettingsManager;
|
||||
import it.danieleverducci.nextcloudmaps.views.GeofavMarkerInfoWindow;
|
||||
|
||||
public class GeofavoriteMapFragment extends GeofavoritesFragment implements MainActivity.OnGpsPermissionGrantedListener {
|
||||
|
||||
private MapView map;
|
||||
private MyLocationNewOverlay mLocationOverlay;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
MapUtils.configOsmdroid(requireContext());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
View v = inflater.inflate(R.layout.fragment_geofavorite_map, container, false);
|
||||
|
||||
// Register listeners
|
||||
v.findViewById(R.id.center_position).setOnClickListener((cpv) -> moveToUserPosition());
|
||||
|
||||
// Setup map
|
||||
map = v.findViewById(R.id.map);
|
||||
map.getZoomController().setVisibility(CustomZoomButtonsController.Visibility.NEVER);
|
||||
map.setMultiTouchControls(true);
|
||||
MapUtils.setTheme(map);
|
||||
MapEventsOverlay meo = new MapEventsOverlay(new MapEventsReceiver() {
|
||||
@Override
|
||||
public boolean singleTapConfirmedHelper(GeoPoint p) {
|
||||
InfoWindow.closeAllInfoWindowsOn(map);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean longPressHelper(GeoPoint p) {
|
||||
// Create new geofavorite (go to geofav creation activity)
|
||||
Uri geoUri = GeoUriParser.createGeoUri(p.getLatitude(), p.getLongitude(), null);
|
||||
Intent i = new Intent(requireActivity(), GeofavoriteDetailActivity.class);
|
||||
i.setData(geoUri);
|
||||
startActivity(i);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
map.getOverlays().add(0, meo);
|
||||
showUserPosition();
|
||||
|
||||
// Setup view listeners
|
||||
View showListButton = v.findViewById(R.id.view_mode_list);
|
||||
showListButton.setOnClickListener(View -> ((MainActivity)requireActivity()).showList());
|
||||
|
||||
View loadingWall = v.findViewById(R.id.loading_wall);
|
||||
|
||||
// Register for data source events
|
||||
mGeofavoritesFragmentViewModel.getIsUpdating().observe(getViewLifecycleOwner(), new Observer<Boolean>() {
|
||||
@Override
|
||||
public void onChanged(@Nullable Boolean changed) {
|
||||
if(Boolean.TRUE.equals(changed)){
|
||||
loadingWall.setVisibility(View.VISIBLE);
|
||||
}
|
||||
else{
|
||||
loadingWall.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
|
||||
((MainActivity)requireActivity()).addOnGpsPermissionGrantedListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDatasetChange(List<Geofavorite> items) {
|
||||
clearAllMarkers();
|
||||
for(Geofavorite gf : items)
|
||||
addMarker(gf);
|
||||
map.invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
|
||||
((MainActivity)requireActivity()).removeOnGpsPermissionGrantedListener(this);
|
||||
SettingsManager.setLastMapPosition(
|
||||
requireContext(),
|
||||
map.getMapCenter().getLatitude(),
|
||||
map.getMapCenter().getLongitude(),
|
||||
map.getZoomLevelDouble()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGpsPermissionGranted() {
|
||||
showUserPosition();
|
||||
}
|
||||
|
||||
private void showUserPosition() {
|
||||
// Center map on last position
|
||||
double[] pos = SettingsManager.getLastMapPosition(requireContext());
|
||||
IMapController mapController = map.getController();
|
||||
mapController.setCenter(new GeoPoint(pos[0], pos[1]));
|
||||
mapController.setZoom(pos[2]);
|
||||
|
||||
// Check if user granted location permission
|
||||
if (ActivityCompat.checkSelfPermission(requireContext(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
|
||||
ActivityCompat.checkSelfPermission(requireContext(), Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
// User didn't grant permission. Ask it.
|
||||
((MainActivity)requireActivity()).requestGpsPermissions();
|
||||
return;
|
||||
}
|
||||
|
||||
// Display user position on screen
|
||||
mLocationOverlay = new MyLocationNewOverlay(new GpsMyLocationProvider(requireContext()), map);
|
||||
Bitmap personIcon = ((BitmapDrawable)AppCompatResources.getDrawable(requireContext(), R.mipmap.ic_person)).getBitmap();
|
||||
mLocationOverlay.setPersonIcon(personIcon);
|
||||
mLocationOverlay.setDirectionIcon(personIcon);
|
||||
mLocationOverlay.setPersonAnchor(.5f, .5f);
|
||||
mLocationOverlay.setDirectionAnchor(.5f, .5f);
|
||||
// On first gps fix, show "center to my position" icon
|
||||
mLocationOverlay.runOnFirstFix(() -> {
|
||||
if(getActivity() != null) {
|
||||
getActivity().runOnUiThread(() -> {
|
||||
getView().findViewById(R.id.center_position).setVisibility(View.VISIBLE);
|
||||
});
|
||||
}
|
||||
});
|
||||
mLocationOverlay.enableMyLocation();
|
||||
map.getOverlays().add(mLocationOverlay);
|
||||
}
|
||||
|
||||
void moveToUserPosition() {
|
||||
if (mLocationOverlay != null)
|
||||
map.getController().animateTo(mLocationOverlay.getMyLocation());
|
||||
}
|
||||
|
||||
private void addMarker(Geofavorite geofavorite){
|
||||
GeoPoint pos = new GeoPoint(geofavorite.getLat(), geofavorite.getLng());
|
||||
|
||||
// Set icon and color
|
||||
Drawable icon = DrawableCompat.wrap(AppCompatResources.getDrawable(requireContext(), R.drawable.ic_map_pin));
|
||||
DrawableCompat.setTint(icon, geofavorite.categoryColor() == 0 ? requireContext().getColor(R.color.defaultBrand) : geofavorite.categoryColor());
|
||||
|
||||
// Set infowindow (popup opened on marker click) and its listeners
|
||||
GeofavMarkerInfoWindow iw = new GeofavMarkerInfoWindow(map, geofavorite);
|
||||
iw.setOnGeofavMarkerInfoWindowClickListener(new GeofavMarkerInfoWindow.OnGeofavMarkerInfoWindowClickListener() {
|
||||
@Override
|
||||
public void onGeofavMarkerInfoWindowEditClick() {
|
||||
openGeofavorite(geofavorite);
|
||||
}
|
||||
@Override
|
||||
public void onGeofavMarkerInfoWindowShareClick() {
|
||||
shareGeofavorite(geofavorite);
|
||||
}
|
||||
@Override
|
||||
public void onGeofavMarkerInfoWindowNavClick() {
|
||||
navigateToGeofavorite(geofavorite);
|
||||
|
||||
}
|
||||
@Override
|
||||
public void onGeofavMarkerInfoWindowDeleteClick() {
|
||||
deleteGeofavorite(geofavorite);
|
||||
}
|
||||
});
|
||||
|
||||
// Set marker
|
||||
Marker m = new Marker(map);
|
||||
m.setPosition(pos);
|
||||
m.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM);
|
||||
m.setIcon(icon);
|
||||
m.setTitle(geofavorite.getName());
|
||||
m.setSnippet(geofavorite.getComment());
|
||||
m.setSubDescription(geofavorite.getCategory());
|
||||
m.setInfoWindow(iw);
|
||||
map.getOverlays().add(m);
|
||||
}
|
||||
|
||||
private void clearAllMarkers() {
|
||||
// Close any open infowindow before removing related marker
|
||||
InfoWindow.closeAllInfoWindowsOn(map);
|
||||
// Remove all markers, leaving the other overlays (user position and tap listener ones)
|
||||
for(Overlay o : map.getOverlays()) {
|
||||
if (o instanceof Marker) {
|
||||
map.getOverlays().remove(o);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,270 @@
|
||||
package it.danieleverducci.nextcloudmaps.fragments;
|
||||
|
||||
import static android.view.View.GONE;
|
||||
import static android.view.View.VISIBLE;
|
||||
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.widget.AppCompatImageButton;
|
||||
import androidx.appcompat.widget.AppCompatImageView;
|
||||
import androidx.appcompat.widget.SearchView;
|
||||
import androidx.core.graphics.drawable.DrawableCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.Observer;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import com.nextcloud.android.sso.helper.SingleAccountHelper;
|
||||
import com.nextcloud.android.sso.model.SingleSignOnAccount;
|
||||
import com.squareup.picasso.Picasso;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import it.danieleverducci.nextcloudmaps.R;
|
||||
import it.danieleverducci.nextcloudmaps.activity.detail.CategoriesAdapter;
|
||||
import it.danieleverducci.nextcloudmaps.activity.detail.GeofavoriteDetailActivity;
|
||||
import it.danieleverducci.nextcloudmaps.activity.main.GeofavoritesFragmentViewModel;
|
||||
import it.danieleverducci.nextcloudmaps.activity.main.MainActivity;
|
||||
import it.danieleverducci.nextcloudmaps.model.Geofavorite;
|
||||
import it.danieleverducci.nextcloudmaps.utils.GeofavoritesFilter;
|
||||
import it.danieleverducci.nextcloudmaps.utils.IntentGenerator;
|
||||
|
||||
/**
|
||||
* Separates the specific list/map implementation details providing a standard interface
|
||||
* to communicate with the activity
|
||||
*/
|
||||
public abstract class GeofavoritesFragment extends Fragment {
|
||||
private final String TAG = "GeofavoritesFragment";
|
||||
|
||||
protected GeofavoritesFragmentViewModel mGeofavoritesFragmentViewModel;
|
||||
private View toolbar;
|
||||
private View homeToolbar;
|
||||
private SearchView searchView;
|
||||
private ImageButton filterButton;
|
||||
private List<Geofavorite> geofavorites = new ArrayList<>();
|
||||
private HashSet<String> categories = new HashSet<>();
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// Load data
|
||||
mGeofavoritesFragmentViewModel = new ViewModelProvider(this).get(GeofavoritesFragmentViewModel.class);
|
||||
mGeofavoritesFragmentViewModel.init(requireContext());
|
||||
|
||||
mGeofavoritesFragmentViewModel.getOnFinished().observe(this, new Observer<Boolean>() {
|
||||
@Override
|
||||
public void onChanged(@Nullable Boolean success) {
|
||||
if(success == null || !success){
|
||||
Toast.makeText(requireContext(), R.string.list_geofavorite_connection_error, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
// Set views
|
||||
AppCompatImageButton menuButton = view.findViewById(R.id.menu_button);
|
||||
menuButton.setOnClickListener(v -> ((MainActivity)requireActivity()).openDrawer());
|
||||
|
||||
View userBadgeContainer = view.findViewById(R.id.user_badge_container);
|
||||
userBadgeContainer.setOnClickListener(v -> showSwitchAccountDialog());
|
||||
|
||||
// Setup toolbar/searchbar
|
||||
toolbar = view.findViewById(R.id.toolbar);
|
||||
homeToolbar = view.findViewById(R.id.home_toolbar);
|
||||
filterButton = view.findViewById(R.id.search_filter);
|
||||
filterButton.setOnClickListener(v -> showCategoryFilterDialog());
|
||||
|
||||
searchView = view.findViewById(R.id.search_view);
|
||||
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
|
||||
@Override
|
||||
public boolean onQueryTextSubmit(String query) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onQueryTextChange(String query) {
|
||||
onDatasetChange(
|
||||
(new GeofavoritesFilter(geofavorites)).byText(query)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
mGeofavoritesFragmentViewModel.getGeofavorites().observe(getViewLifecycleOwner(), new Observer<List<Geofavorite>>() {
|
||||
@Override
|
||||
public void onChanged(List<Geofavorite> geofavorites) {
|
||||
GeofavoritesFragment.this.geofavorites = geofavorites;
|
||||
onDatasetChange(geofavorites);
|
||||
}
|
||||
});
|
||||
mGeofavoritesFragmentViewModel.getCategories().observe(getViewLifecycleOwner(), new Observer<HashSet<String>>() {
|
||||
@Override
|
||||
public void onChanged(HashSet<String> categories) {
|
||||
GeofavoritesFragment.this.categories = categories;
|
||||
}
|
||||
});
|
||||
|
||||
searchView.setOnCloseListener(() -> {
|
||||
if (toolbar.getVisibility() == VISIBLE && TextUtils.isEmpty(searchView.getQuery())) {
|
||||
updateToolbars(true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
homeToolbar.setOnClickListener(v -> updateToolbars(false));
|
||||
// Set user badge (async)
|
||||
Handler h = new Handler();
|
||||
h.post(() -> {
|
||||
try {
|
||||
SingleSignOnAccount ssoAccount = SingleAccountHelper.getCurrentSingleSignOnAccount(requireContext());
|
||||
String userBadgePath = ssoAccount.url + "/index.php/avatar/" + ssoAccount.userId + "/64";
|
||||
if (getActivity() != null)
|
||||
getActivity().runOnUiThread(
|
||||
() -> Picasso.get().load(userBadgePath).into((AppCompatImageView)userBadgeContainer.findViewById(R.id.user_badge))
|
||||
);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Unable to load user image: " + e.toString());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
|
||||
// Reset filter and update data
|
||||
filterButton.setImageResource(R.drawable.ic_filter_off);
|
||||
mGeofavoritesFragmentViewModel.updateGeofavorites();
|
||||
}
|
||||
|
||||
|
||||
abstract public void onDatasetChange(List<Geofavorite> items);
|
||||
|
||||
|
||||
protected void openGeofavorite(Geofavorite item) {
|
||||
showGeofavoriteDetailActivity(item);
|
||||
}
|
||||
|
||||
protected void shareGeofavorite(Geofavorite item) {
|
||||
startActivity(Intent.createChooser(IntentGenerator.newShareIntent(requireActivity(), item), getString(R.string.share_via)));
|
||||
}
|
||||
|
||||
protected void navigateToGeofavorite(Geofavorite item) {
|
||||
startActivity(IntentGenerator.newGeoUriIntent(requireActivity(), item));
|
||||
}
|
||||
|
||||
protected void deleteGeofavorite(Geofavorite item) {
|
||||
showGeofavoriteDeteleDialog(item);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void showGeofavoriteDetailActivity(Geofavorite item) {
|
||||
Intent i = new Intent(requireContext(), GeofavoriteDetailActivity.class);
|
||||
i.putExtra(GeofavoriteDetailActivity.ARG_GEOFAVORITE, item.getId());
|
||||
startActivity(i);
|
||||
}
|
||||
|
||||
private void showGeofavoriteDeteleDialog(Geofavorite item) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(requireContext());
|
||||
builder.setMessage(getString(R.string.dialog_delete_message).replace("{name}", item.getName() != null ? item.getName() : ""))
|
||||
.setTitle(R.string.dialog_delete_title)
|
||||
.setPositiveButton(R.string.dialog_delete_delete, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
mGeofavoritesFragmentViewModel.deleteGeofavorite(item);
|
||||
dialog.dismiss();
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.dialog_delete_cancel, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
dialog.dismiss();
|
||||
}
|
||||
});
|
||||
AlertDialog ad = builder.create();
|
||||
ad.show();
|
||||
}
|
||||
|
||||
private void showSwitchAccountDialog() {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(requireContext());
|
||||
builder.setMessage(R.string.dialog_logout_message)
|
||||
.setTitle(R.string.dialog_logout_title)
|
||||
.setPositiveButton(android.R.string.yes, (dialog, id) -> {
|
||||
if (getActivity() != null)
|
||||
((MainActivity) getActivity()).switch_account();
|
||||
dialog.dismiss();
|
||||
})
|
||||
.setNegativeButton(android.R.string.no, (dialog, id) -> dialog.dismiss());
|
||||
AlertDialog ad = builder.create();
|
||||
ad.show();
|
||||
}
|
||||
|
||||
private void showCategoryFilterDialog() {
|
||||
if (categories.isEmpty()) {
|
||||
Toast.makeText(requireContext(), R.string.filtering_unavailable, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(requireContext());
|
||||
builder.setTitle(R.string.filtering_dialog_title);
|
||||
CategoriesAdapter ca = new CategoriesAdapter(requireContext());
|
||||
ca.setCategoriesList(categories);
|
||||
builder.setAdapter(ca, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
// Set filter button enabled icon and color
|
||||
String categoryName = ca.getItem(which);
|
||||
filterButton.setImageResource(R.drawable.ic_filter);
|
||||
Drawable d = filterButton.getDrawable();
|
||||
DrawableCompat.setTint(
|
||||
d,
|
||||
Geofavorite.categoryColorFromName(categoryName) == 0
|
||||
? requireContext().getColor(R.color.defaultBrand)
|
||||
: Geofavorite.categoryColorFromName(categoryName)
|
||||
);
|
||||
filterByCategory(categoryName);
|
||||
}
|
||||
});
|
||||
|
||||
builder.setPositiveButton(R.string.filtering_dialog_cancel, (dialog, id) -> {
|
||||
dialog.dismiss();
|
||||
filterButton.setImageResource(R.drawable.ic_filter_off);
|
||||
filterByCategory(null);
|
||||
});
|
||||
AlertDialog dialog = builder.create();
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
private void updateToolbars(boolean disableSearch) {
|
||||
homeToolbar.setVisibility(disableSearch ? VISIBLE : GONE);
|
||||
toolbar.setVisibility(disableSearch ? GONE : VISIBLE);
|
||||
if (disableSearch) {
|
||||
searchView.setQuery(null, true);
|
||||
}
|
||||
searchView.setIconified(disableSearch);
|
||||
}
|
||||
|
||||
private void filterByCategory(String category) {
|
||||
onDatasetChange(
|
||||
(new GeofavoritesFilter(geofavorites)).byCategory(category)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,21 +20,31 @@
|
||||
|
||||
package it.danieleverducci.nextcloudmaps.model;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.net.Uri;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Filter;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.databinding.BindingAdapter;
|
||||
|
||||
import com.google.gson.annotations.Expose;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import org.threeten.bp.Instant;
|
||||
import org.threeten.bp.LocalDate;
|
||||
import org.threeten.bp.ZoneId;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import it.danieleverducci.nextcloudmaps.utils.GeoUriParser;
|
||||
|
||||
public class Geofavorite implements Serializable {
|
||||
public static final String DEFAULT_CATEGORY = "Personal";
|
||||
private static final double EARTH_RADIUS = 6371; // https://en.wikipedia.org/wiki/Earth_radius
|
||||
|
||||
/**
|
||||
* JSON Definition:
|
||||
* {
|
||||
@@ -90,20 +100,26 @@ public class Geofavorite implements Serializable {
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
if (!name.equals(this.name))
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public long getDateModified() {
|
||||
return dateModified;
|
||||
}
|
||||
|
||||
public void setDateModified(long dateModified) {
|
||||
this.dateModified = dateModified;
|
||||
}
|
||||
public LocalDate getLocalDateModified() {
|
||||
return Instant.ofEpochSecond(getDateCreated()).atZone(ZoneId.systemDefault()).toLocalDate();
|
||||
}
|
||||
|
||||
public long getDateCreated() {
|
||||
return dateCreated;
|
||||
}
|
||||
public LocalDate getLocalDateCreated() {
|
||||
return Instant.ofEpochSecond(getDateCreated()).atZone(ZoneId.systemDefault()).toLocalDate();
|
||||
}
|
||||
|
||||
public void setDateCreated(long dateCreated) {
|
||||
this.dateCreated = dateCreated;
|
||||
@@ -112,7 +128,6 @@ public class Geofavorite implements Serializable {
|
||||
public double getLat() {
|
||||
return lat;
|
||||
}
|
||||
|
||||
public void setLat(double lat) {
|
||||
this.lat = lat;
|
||||
}
|
||||
@@ -120,7 +135,6 @@ public class Geofavorite implements Serializable {
|
||||
public double getLng() {
|
||||
return lng;
|
||||
}
|
||||
|
||||
public void setLng(double lng) {
|
||||
this.lng = lng;
|
||||
}
|
||||
@@ -128,7 +142,6 @@ public class Geofavorite implements Serializable {
|
||||
public String getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
public void setCategory(String category) {
|
||||
this.category = category;
|
||||
}
|
||||
@@ -137,26 +150,67 @@ public class Geofavorite implements Serializable {
|
||||
public String getComment() {
|
||||
return comment;
|
||||
}
|
||||
|
||||
public void setComment(String comment) {
|
||||
this.comment = comment;
|
||||
}
|
||||
|
||||
public static Comparator<Geofavorite> ByTitleAZ = (note, t1) -> note.name.compareTo(t1.name);
|
||||
/**
|
||||
* Comparators for list order
|
||||
*/
|
||||
public static Comparator<Geofavorite> ByTitleAZ = (gf0, gf1) -> gf0.name.compareTo(gf1.name);
|
||||
public static Comparator<Geofavorite> ByLastCreated = (gf0, gf1) -> (int) (gf1.dateCreated - gf0.dateCreated);
|
||||
public static Comparator<Geofavorite> ByCategory = (gf0, gf1) -> (gf0.category + gf0.name).compareTo(gf1.category + gf1.name);
|
||||
public static Comparator<Geofavorite> ByDistance = (gf0, gf1) -> 0; // (int) ((gf1.getDistanceFrom(userPosition) - gf0.getDistanceFrom(userPosition)) * 1000);
|
||||
|
||||
public static Comparator<Geofavorite> ByLastCreated = (note, t1) -> t1.id - note.id;
|
||||
|
||||
public Uri getGeoUri() {
|
||||
return Uri.parse("geo:" + this.lat + "," + this.lng + "(" + this.name + ")");
|
||||
public String getCoordinatesString() {
|
||||
return this.lat + " N, " + this.lng + " E";
|
||||
}
|
||||
|
||||
@BindingAdapter("formatDate")
|
||||
public static void formatDate(@NonNull TextView textView, long timestamp) {
|
||||
textView.setText((new Date(timestamp)).toString());
|
||||
public Uri getGeoUri() {
|
||||
return GeoUriParser.createGeoUri(this.lat, this.lng, this.name);
|
||||
}
|
||||
public Uri getGmapsUri() {
|
||||
return GeoUriParser.createGmapsUri(this.lat, this.lng);
|
||||
}
|
||||
|
||||
public boolean valid() {
|
||||
return getLat() != 0 && getLng() != 0 && getName() != null && getName().length() > 0;
|
||||
return
|
||||
getLat() != 0 && getLng() != 0 &&
|
||||
getName() != null && getName().length() > 0 &&
|
||||
getCategory() != null && getCategory().length() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the distance between the current Geofavorite and the provided one, in kilometers.
|
||||
* @param other Geovavorite
|
||||
* @return the distance in kilometers
|
||||
*/
|
||||
public double getDistanceFrom(Geofavorite other) {
|
||||
double latDistance = (other.lat-lat) * Math.PI / 180;
|
||||
double lonDistance = (other.lng-lng) * Math.PI / 180;
|
||||
double a =
|
||||
Math.sin(latDistance / 2) * Math.sin(latDistance / 2) +
|
||||
Math.cos(lat * Math.PI / 180) * Math.cos(other.lat * Math.PI / 180) *
|
||||
Math.sin(lonDistance / 2) * Math.sin(lonDistance / 2);
|
||||
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
|
||||
return EARTH_RADIUS * c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on Nextcloud Maps's getLetterColor util.
|
||||
* Assigns a color to a category based on its two first letters.
|
||||
*
|
||||
* @see "https://github.com/nextcloud/maps/blob/master/src/utils.js"
|
||||
* @return the generated color or null for the default category
|
||||
*/
|
||||
public int categoryColor() {
|
||||
return categoryColorFromName(this.category);
|
||||
}
|
||||
|
||||
public String categoryLetter() {
|
||||
if (category == null || category.length() == 0 || category.equals(DEFAULT_CATEGORY))
|
||||
return "\u2022";
|
||||
return category.substring(0,1);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@@ -164,4 +218,28 @@ public class Geofavorite implements Serializable {
|
||||
public String toString() {
|
||||
return "[" + getName() + " (" + getLat() + "," + getLng() + ")]";
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Based on Nextcloud Maps's getLetterColor util.
|
||||
* Assigns a color to a category based on its two first letters.
|
||||
*
|
||||
* @see "https://github.com/nextcloud/maps/blob/master/src/utils.js"
|
||||
* @return the generated color or null for the default category
|
||||
*/
|
||||
public static int categoryColorFromName(String category) {
|
||||
// If category is default, return null: will be used Nextcloud's accent
|
||||
if (category == null || category.equals(DEFAULT_CATEGORY) || category.length() == 0)
|
||||
return 0;
|
||||
|
||||
float letter1Index = category.toLowerCase().charAt(0);
|
||||
float letter2Index = category.toLowerCase().charAt(1);
|
||||
float letterCoef = ((letter1Index * letter2Index) % 100) / 100;
|
||||
float h = letterCoef * 360;
|
||||
float s = 75 + letterCoef * 10;
|
||||
float l = 50 + letterCoef * 10;
|
||||
return Color.HSVToColor( new float[]{ Math.round(h), Math.round(s), Math.round(l) });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
package it.danieleverducci.nextcloudmaps.repository;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import it.danieleverducci.nextcloudmaps.api.ApiProvider;
|
||||
import it.danieleverducci.nextcloudmaps.model.Geofavorite;
|
||||
import it.danieleverducci.nextcloudmaps.utils.SingleLiveEvent;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
||||
/**
|
||||
* Singleton pattern
|
||||
*/
|
||||
public class GeofavoriteRepository {
|
||||
|
||||
private static final String TAG = "GeofavoriteRepository";
|
||||
private static GeofavoriteRepository instance;
|
||||
private MutableLiveData<List<Geofavorite>> mGeofavorites;
|
||||
private MutableLiveData<HashSet<String>> mCategories = new MutableLiveData<HashSet<String>>();
|
||||
private MutableLiveData<Boolean> mIsUpdating = new MutableLiveData<>(false);
|
||||
private SingleLiveEvent<Boolean> mOnFinished = new SingleLiveEvent<>();
|
||||
|
||||
private Context applicationContext;
|
||||
|
||||
public GeofavoriteRepository(Context applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
public static GeofavoriteRepository getInstance(Context applicationContext) {
|
||||
if(instance == null){
|
||||
instance = new GeofavoriteRepository(applicationContext);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static void resetInstance() {
|
||||
instance = null;
|
||||
}
|
||||
|
||||
public MutableLiveData<List<Geofavorite>> getGeofavorites(){
|
||||
if (mGeofavorites == null) {
|
||||
mGeofavorites = new MutableLiveData<>();
|
||||
mGeofavorites.setValue(new ArrayList<>());
|
||||
}
|
||||
return mGeofavorites;
|
||||
}
|
||||
|
||||
public MutableLiveData<HashSet<String>> getCategories() {
|
||||
return mCategories;
|
||||
}
|
||||
|
||||
public MutableLiveData<Boolean> isUpdating() {
|
||||
return mIsUpdating;
|
||||
}
|
||||
|
||||
public SingleLiveEvent<Boolean> onFinished() {
|
||||
return mOnFinished;
|
||||
}
|
||||
|
||||
public void updateGeofavorites() {
|
||||
mIsUpdating.postValue(true);
|
||||
// Obtain geofavorites
|
||||
Call<List<Geofavorite>> call = ApiProvider.getAPI(this.applicationContext).getGeofavorites();
|
||||
call.enqueue(new Callback<List<Geofavorite>>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<List<Geofavorite>> call, @NonNull Response<List<Geofavorite>> response) {
|
||||
if (response.isSuccessful() && response.body() != null) {
|
||||
mGeofavorites.postValue(response.body());
|
||||
updateCategories(response.body());
|
||||
mIsUpdating.postValue(false);
|
||||
mOnFinished.postValue(true);
|
||||
} else {
|
||||
onFailure(call, new Throwable("Dataset is empty"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<List<Geofavorite>> call, @NonNull Throwable t) {
|
||||
mIsUpdating.postValue(false);
|
||||
mOnFinished.postValue(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Geofavorite getGeofavorite(int id) {
|
||||
for (Geofavorite g : mGeofavorites.getValue()) {
|
||||
if (g.getId() == id)
|
||||
return g;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void saveGeofavorite(Geofavorite geofav) {
|
||||
mIsUpdating.postValue(true);
|
||||
Call<Geofavorite> call;
|
||||
if (geofav.getId() == 0) {
|
||||
// New geofavorite
|
||||
call = ApiProvider.getAPI(this.applicationContext).createGeofavorite(geofav);
|
||||
} else {
|
||||
// Update existing geofavorite
|
||||
call = ApiProvider.getAPI(this.applicationContext).updateGeofavorite(geofav.getId(), geofav);
|
||||
}
|
||||
call.enqueue(new Callback<Geofavorite>() {
|
||||
@Override
|
||||
public void onResponse(Call<Geofavorite> call, Response<Geofavorite> response) {
|
||||
if (response.isSuccessful()) {
|
||||
List<Geofavorite> geofavs = mGeofavorites.getValue();
|
||||
if (geofav.getId() != 0) {
|
||||
geofavs.remove(geofav);
|
||||
}
|
||||
geofavs.add(response.body());
|
||||
mGeofavorites.postValue(geofavs);
|
||||
mIsUpdating.postValue(false);
|
||||
mOnFinished.postValue(true);
|
||||
} else {
|
||||
mIsUpdating.postValue(false);
|
||||
mOnFinished.postValue(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<Geofavorite> call, Throwable t) {
|
||||
Log.e(TAG, t.getMessage());
|
||||
mIsUpdating.postValue(false);
|
||||
mOnFinished.postValue(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void deleteGeofavorite(Geofavorite geofav) {
|
||||
mIsUpdating.postValue(true);
|
||||
// Delete Geofavorite
|
||||
Call<Geofavorite> call = ApiProvider.getAPI(this.applicationContext).deleteGeofavorite(geofav.getId());
|
||||
call.enqueue(new Callback<Geofavorite>() {
|
||||
@Override
|
||||
public void onResponse(Call<Geofavorite> call, Response<Geofavorite> response) {
|
||||
List<Geofavorite> geofavs = mGeofavorites.getValue();
|
||||
if (geofavs.remove(geofav)) {
|
||||
mGeofavorites.postValue(geofavs);
|
||||
mIsUpdating.postValue(false);
|
||||
mOnFinished.postValue(true);
|
||||
} else {
|
||||
// Should never happen
|
||||
mIsUpdating.postValue(false);
|
||||
mOnFinished.postValue(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<Geofavorite> call, Throwable t) {
|
||||
mIsUpdating.postValue(false);
|
||||
mOnFinished.postValue(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateCategories(List<Geofavorite> geofavs) {
|
||||
HashSet<String> categories = new HashSet<>();
|
||||
for (Geofavorite g : geofavs) {
|
||||
String cat = g.getCategory();
|
||||
if (cat != null)
|
||||
categories.add(cat);
|
||||
}
|
||||
mCategories.postValue(categories);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package it.danieleverducci.nextcloudmaps.utils;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import it.danieleverducci.nextcloudmaps.model.Geofavorite;
|
||||
|
||||
public class GeoUriParser {
|
||||
private static final Pattern PATTERN_GEO = Pattern.compile("geo:(-?[\\d.]+),(-?[\\d.]+)");
|
||||
// Try to match not only geoUri but also Google Maps Uri
|
||||
private static final Pattern PATTERN_BROAD = Pattern.compile(
|
||||
"(?:@|&query=|&ce\nter=|geo:|#map=\\d{1,2}\\/)(-?\\d{1,2}\\.\\d+)(?:,|%2C|\\/)(-?\\d{1,3}\\.\\d{1,10})"
|
||||
);
|
||||
|
||||
/**
|
||||
* Parses an URI into latitude and longitude
|
||||
* @param uri to parse
|
||||
* @param strict if true, the uri must be a valid geo: uri, otherwise a broader check is applied to include other uris, like Google Maps ones
|
||||
* @return the parsed coordinates in an array [lat,lon]
|
||||
* @throws IllegalArgumentException if the url could not be parsed
|
||||
*/
|
||||
public static double[] parseUri(Uri uri, boolean strict) throws IllegalArgumentException {
|
||||
if (uri == null)
|
||||
throw new IllegalArgumentException("no uri");
|
||||
|
||||
// Try to extract coordinates in uri string with regexp
|
||||
Pattern pattern = strict ? PATTERN_GEO : PATTERN_BROAD;
|
||||
Matcher m = pattern.matcher(uri.toString());
|
||||
if (!m.find() || m.groupCount() != 2)
|
||||
throw new IllegalArgumentException("unable to parse uri: unable to find coordinates in uri");
|
||||
|
||||
// Obtain coordinates from regexp result
|
||||
String sLat = m.group(1);
|
||||
String sLon = m.group(2);
|
||||
double[] coords = null;
|
||||
try {
|
||||
// Check coordinates are numeric
|
||||
coords = new double[]{Double.parseDouble(sLat), Double.parseDouble(sLon)};
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("unable to parse uri: coordinates are not double");
|
||||
}
|
||||
|
||||
// Check coordinates validity
|
||||
String error = checkCoordsValidity(coords[0], coords[1]);
|
||||
if (error != null)
|
||||
throw new IllegalArgumentException(error);
|
||||
|
||||
return coords;
|
||||
}
|
||||
|
||||
public static Uri createGeoUri(double lat, double lon, String name) {
|
||||
String error = checkCoordsValidity(lat, lon);
|
||||
if (error != null)
|
||||
throw new IllegalArgumentException(error);
|
||||
|
||||
String uriStr = "geo:" + lat + "," + lon;
|
||||
if (name != null)
|
||||
uriStr += "(" + name + ")";
|
||||
return Uri.parse(uriStr);
|
||||
}
|
||||
|
||||
public static Uri createGmapsUri(double lat, double lon) {
|
||||
String error = checkCoordsValidity(lat, lon);
|
||||
if (error != null)
|
||||
throw new IllegalArgumentException(error);
|
||||
|
||||
String uriStr = "https://www.google.com/maps/search/?api=1&query=" + lat + "," + lon;
|
||||
return Uri.parse(uriStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks a latitude/longitude is valid
|
||||
* @param lat latitude
|
||||
* @param lon longitude
|
||||
* @return null if valid, a string containing an error otherwise
|
||||
*/
|
||||
private static String checkCoordsValidity(double lat, double lon) {
|
||||
// Check coords validity
|
||||
if (lon <= -180 || lon >= 180 )
|
||||
return "Invalid longitude: " + lon;
|
||||
if (lat <= -90 || lat >= 90)
|
||||
return "Invalid latitude: " + lat;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package it.danieleverducci.nextcloudmaps.utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import it.danieleverducci.nextcloudmaps.model.Geofavorite;
|
||||
|
||||
public class GeofavoritesFilter {
|
||||
List<Geofavorite> items;
|
||||
public GeofavoritesFilter(List<Geofavorite> items) {
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
public List<Geofavorite> byText(String text) {
|
||||
List<Geofavorite> filteredGeofavorites = new ArrayList<>();
|
||||
|
||||
if (text.isEmpty()) {
|
||||
return items;
|
||||
} else {
|
||||
for (Geofavorite geofavorite : items) {
|
||||
String query = text.toLowerCase();
|
||||
if (geofavorite.getName() != null && geofavorite.getName().toLowerCase().contains(query)) {
|
||||
filteredGeofavorites.add(geofavorite);
|
||||
} else if (geofavorite.getComment() != null && geofavorite.getComment().toLowerCase().contains(query)) {
|
||||
filteredGeofavorites.add(geofavorite);
|
||||
}
|
||||
}
|
||||
}
|
||||
return filteredGeofavorites;
|
||||
}
|
||||
|
||||
public List<Geofavorite> byCategory(String category) {
|
||||
List<Geofavorite> filteredGeofavorites = new ArrayList<>();
|
||||
|
||||
if (category == null) {
|
||||
return items;
|
||||
} else {
|
||||
for (Geofavorite geofavorite : items) {
|
||||
if (geofavorite.getCategory().equals(category)) {
|
||||
filteredGeofavorites.add(geofavorite);
|
||||
}
|
||||
}
|
||||
}
|
||||
return filteredGeofavorites;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package it.danieleverducci.nextcloudmaps.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.util.Log;
|
||||
|
||||
import it.danieleverducci.nextcloudmaps.R;
|
||||
import it.danieleverducci.nextcloudmaps.model.Geofavorite;
|
||||
|
||||
public class IntentGenerator {
|
||||
public static Intent newShareIntent(Context context, Geofavorite item) {
|
||||
Intent i = new Intent();
|
||||
i.setAction(Intent.ACTION_SEND);
|
||||
i.setType("text/plain");
|
||||
String shareMessage = context.getString(R.string.share_message)
|
||||
.replace("{title}", item.getName() == null ? context.getString(R.string.share_message_default_title) : item.getName())
|
||||
.replace("{lat}", ""+item.getLat())
|
||||
.replace("{lng}", ""+item.getLng());
|
||||
i.putExtra(Intent.EXTRA_TEXT, shareMessage );
|
||||
return i;
|
||||
}
|
||||
|
||||
public static Intent newGeoUriIntent(Context context, Geofavorite item) {
|
||||
Intent i = new Intent();
|
||||
i.setAction(Intent.ACTION_VIEW);
|
||||
i.setData(isGoogleMapsInstalled(context) ? item.getGmapsUri() : item.getGeoUri());
|
||||
return i;
|
||||
}
|
||||
|
||||
public static boolean isGoogleMapsInstalled(Context context) {
|
||||
try {
|
||||
context.getPackageManager().getApplicationInfo("com.google.android.apps.maps", 0);
|
||||
return true;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package it.danieleverducci.nextcloudmaps.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import org.osmdroid.config.IConfigurationProvider;
|
||||
import org.osmdroid.views.MapView;
|
||||
import org.osmdroid.views.overlay.TilesOverlay;
|
||||
|
||||
import it.danieleverducci.nextcloudmaps.BuildConfig;
|
||||
|
||||
public class MapUtils {
|
||||
|
||||
public static void configOsmdroid(Context context) {
|
||||
IConfigurationProvider osmdroidConfig = org.osmdroid.config.Configuration.getInstance();
|
||||
osmdroidConfig.load(context,
|
||||
PreferenceManager.getDefaultSharedPreferences(context));
|
||||
osmdroidConfig.setUserAgentValue(BuildConfig.APPLICATION_ID);
|
||||
}
|
||||
|
||||
public static void setTheme(MapView mapView) {
|
||||
int currentNightMode = mapView.getContext().getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
|
||||
switch (currentNightMode) {
|
||||
case Configuration.UI_MODE_NIGHT_NO:
|
||||
// Night mode is not active, we're using the light theme
|
||||
mapView.getOverlayManager().getTilesOverlay().setColorFilter(null);
|
||||
break;
|
||||
case Configuration.UI_MODE_NIGHT_YES:
|
||||
// Night mode is active, we're using dark theme
|
||||
mapView.getOverlayManager().getTilesOverlay().setColorFilter(TilesOverlay.INVERT_COLORS);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package it.danieleverducci.nextcloudmaps.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import androidx.preference.PreferenceManager;
|
||||
import it.danieleverducci.nextcloudmaps.activity.main.GeofavoriteAdapter;
|
||||
|
||||
public class SettingsManager {
|
||||
static private final String SETTING_SORT_BY = "SETTING_SORT_BY";
|
||||
static private final String SETTING_LAST_SELECTED_LIST_VIEW = "SETTING_LAST_SELECTED_LIST_VIEW";
|
||||
static private final String SETTING_LAST_MAP_POSITION_LAT = "SETTING_LAST_MAP_POSITION_LAT";
|
||||
static private final String SETTING_LAST_MAP_POSITION_LNG = "SETTING_LAST_MAP_POSITION_LNG";
|
||||
static private final String SETTING_LAST_MAP_POSITION_ZOOM = "SETTING_LAST_MAP_POSITION_ZOOM";
|
||||
|
||||
public static int getGeofavoriteListSortBy(Context context) {
|
||||
return PreferenceManager.getDefaultSharedPreferences(context)
|
||||
.getInt(SETTING_SORT_BY, GeofavoriteAdapter.SORT_BY_CREATED);
|
||||
}
|
||||
|
||||
public static void setGeofavoriteListSortBy(Context context, int value) {
|
||||
PreferenceManager.getDefaultSharedPreferences(context)
|
||||
.edit().putInt(SETTING_SORT_BY, value).apply();
|
||||
}
|
||||
|
||||
public static boolean isGeofavoriteListShownAsMap(Context context) {
|
||||
return PreferenceManager.getDefaultSharedPreferences(context)
|
||||
.getBoolean(SETTING_LAST_SELECTED_LIST_VIEW, false);
|
||||
}
|
||||
|
||||
public static void setGeofavoriteListShownAsMap(Context context, boolean value) {
|
||||
PreferenceManager.getDefaultSharedPreferences(context)
|
||||
.edit().putBoolean(SETTING_LAST_SELECTED_LIST_VIEW, value).apply();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last saved position
|
||||
* @param context
|
||||
* @return double[lat,lng,zoom]
|
||||
*/
|
||||
public static double[] getLastMapPosition(Context context) {
|
||||
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
return new double[] {
|
||||
(double) sp.getFloat(SETTING_LAST_MAP_POSITION_LAT, 0.0f),
|
||||
(double) sp.getFloat(SETTING_LAST_MAP_POSITION_LNG, 0.0f),
|
||||
(double) sp.getFloat(SETTING_LAST_MAP_POSITION_ZOOM, 10.0f),
|
||||
};
|
||||
}
|
||||
|
||||
public static void setLastMapPosition(Context context, double lat, double lng, double zoom) {
|
||||
PreferenceManager.getDefaultSharedPreferences(context).edit()
|
||||
.putFloat(SETTING_LAST_MAP_POSITION_LAT, (float)lat)
|
||||
.putFloat(SETTING_LAST_MAP_POSITION_LNG, (float)lng)
|
||||
.putFloat(SETTING_LAST_MAP_POSITION_ZOOM, (float)zoom)
|
||||
.apply();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package it.danieleverducci.nextcloudmaps.utils;
|
||||
|
||||
import androidx.annotation.MainThread;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.Observer;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* Events implementation in LiveData
|
||||
* From: https://gist.github.com/teegarcs/319a3e7e4736a0cce8eba2216c52b0ca#file-singleliveevent
|
||||
* @param <T>
|
||||
*/
|
||||
public class SingleLiveEvent<T> extends MutableLiveData<T> {
|
||||
AtomicBoolean mPending = new AtomicBoolean(false);
|
||||
|
||||
@MainThread
|
||||
@Override
|
||||
public void setValue(T value) {
|
||||
mPending.set(true);
|
||||
super.setValue(value);
|
||||
}
|
||||
|
||||
@MainThread
|
||||
@Override
|
||||
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
|
||||
super.observe(owner, new Observer<T>() {
|
||||
@Override
|
||||
public void onChanged(T t) {
|
||||
if (mPending.compareAndSet(true, false)) {
|
||||
observer.onChanged(t);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Util function for Void implementations.
|
||||
*/
|
||||
public void call() {
|
||||
setValue(null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package it.danieleverducci.nextcloudmaps.views;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.appcompat.content.res.AppCompatResources;
|
||||
import androidx.core.graphics.drawable.DrawableCompat;
|
||||
|
||||
import org.osmdroid.views.MapView;
|
||||
import org.osmdroid.views.overlay.infowindow.InfoWindow;
|
||||
import org.osmdroid.views.overlay.infowindow.MarkerInfoWindow;
|
||||
|
||||
import it.danieleverducci.nextcloudmaps.R;
|
||||
import it.danieleverducci.nextcloudmaps.model.Geofavorite;
|
||||
|
||||
public class GeofavMarkerInfoWindow extends MarkerInfoWindow implements View.OnClickListener {
|
||||
private OnGeofavMarkerInfoWindowClickListener onGeofavMarkerInfoWindowClickListener;
|
||||
|
||||
public GeofavMarkerInfoWindow(MapView mapView, Geofavorite geofavorite) {
|
||||
super(R.layout.infowindow_geofav, mapView);
|
||||
Context context = getView().getContext();
|
||||
|
||||
// Set category color
|
||||
View category = getView().findViewById(R.id.bubble_subdescription);
|
||||
Drawable backgroundDrawable = category.getBackground();
|
||||
DrawableCompat.setTint(backgroundDrawable, geofavorite.categoryColor() == 0 ? context.getColor(R.color.defaultBrand) : geofavorite.categoryColor());
|
||||
|
||||
// Set listeners
|
||||
getView().findViewById(R.id.action_icon_share).setOnClickListener(this);
|
||||
getView().findViewById(R.id.action_icon_nav).setOnClickListener(this);
|
||||
getView().findViewById(R.id.action_icon_delete).setOnClickListener(this);
|
||||
getView().findViewById(R.id.action_icon_edit).setOnClickListener(this);
|
||||
}
|
||||
|
||||
public void setOnGeofavMarkerInfoWindowClickListener(OnGeofavMarkerInfoWindowClickListener l) {
|
||||
this.onGeofavMarkerInfoWindowClickListener = l;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(Object item) {
|
||||
InfoWindow.closeAllInfoWindowsOn(getMapView());
|
||||
super.onOpen(item);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (onGeofavMarkerInfoWindowClickListener == null)
|
||||
return;
|
||||
|
||||
if (v.getId() == R.id.action_icon_share)
|
||||
onGeofavMarkerInfoWindowClickListener.onGeofavMarkerInfoWindowShareClick();
|
||||
|
||||
if (v.getId() == R.id.action_icon_nav)
|
||||
onGeofavMarkerInfoWindowClickListener.onGeofavMarkerInfoWindowNavClick();
|
||||
|
||||
if (v.getId() == R.id.action_icon_delete)
|
||||
onGeofavMarkerInfoWindowClickListener.onGeofavMarkerInfoWindowDeleteClick();
|
||||
|
||||
if (v.getId() == R.id.action_icon_edit)
|
||||
onGeofavMarkerInfoWindowClickListener.onGeofavMarkerInfoWindowEditClick();
|
||||
}
|
||||
|
||||
|
||||
public interface OnGeofavMarkerInfoWindowClickListener {
|
||||
public void onGeofavMarkerInfoWindowEditClick();
|
||||
public void onGeofavMarkerInfoWindowShareClick();
|
||||
public void onGeofavMarkerInfoWindowNavClick();
|
||||
public void onGeofavMarkerInfoWindowDeleteClick();
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
<!--
|
||||
~ Nextcloud Maps Geofavorites for Android
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<vector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="32dp"
|
||||
android:height="32dp"
|
||||
android:viewportWidth="32"
|
||||
android:viewportHeight="32">
|
||||
<path
|
||||
android:pathData="m6,2c-2.216,0 -4,1.784 -4,4v20c0,2.216 1.784,4 4,4h20c2.216,0 4,-1.784 4,-4v-16.719l-0.906,0.9068 -5.282,-5.2818 2.907,-2.9062h-20.719zM21.812,6.9062l5.282,5.2818 -8.313,8.312 -8.781,3.5 3.5,-8.781 8.312,-8.3128zM14.406,16.094l-2.656,4.406 1.75,1.75 4.406,-2.656 -3.5,-3.5z"
|
||||
android:fillColor="#FFF"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<shape
|
||||
android:shape="oval">
|
||||
<solid android:color="@color/defaultBrandAlpha"/>
|
||||
</shape>
|
||||
</item>
|
||||
</layer-list>
|
||||
@@ -0,0 +1,13 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="209.21dp"
|
||||
android:height="45.14dp"
|
||||
android:viewportWidth="209.21"
|
||||
android:viewportHeight="45.14">
|
||||
<path
|
||||
android:pathData="m204.31,0.14c-0.97,0.02 -2.1,0.07 -3.3,0.15l8.18,-0.01c0,0 -1.81,-0.2 -4.89,-0.14zM201.01,0.29 L0.02,0.46c0,0 32.67,5.29 57.83,17.7 25.15,12.42 45.33,26.81 45.33,26.81 0,0 18.75,-13.75 38.46,-26.28 20.46,-13 49.14,-17.73 59.37,-18.41z"
|
||||
android:strokeLineJoin="miter"
|
||||
android:strokeWidth="0.264583"
|
||||
android:fillColor="#000000"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeLineCap="butt"/>
|
||||
</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="?android:attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M20.94,11c-0.46,-4.17 -3.77,-7.48 -7.94,-7.94L13,1h-2v2.06c-1.13,0.12 -2.19,0.46 -3.16,0.97l1.5,1.5C10.16,5.19 11.06,5 12,5c3.87,0 7,3.13 7,7 0,0.94 -0.19,1.84 -0.52,2.65l1.5,1.5c0.5,-0.96 0.84,-2.02 0.97,-3.15L23,13v-2h-2.06zM3,4.27l2.04,2.04C3.97,7.62 3.25,9.23 3.06,11L1,11v2h2.06c0.46,4.17 3.77,7.48 7.94,7.94L11,23h2v-2.06c1.77,-0.2 3.38,-0.91 4.69,-1.98L19.73,21 21,19.73 4.27,3 3,4.27zM16.27,17.54C15.09,18.45 13.61,19 12,19c-3.87,0 -7,-3.13 -7,-7 0,-1.61 0.55,-3.09 1.46,-4.27l9.81,9.81z"/>
|
||||
</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="?android:attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M12,8c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4zM20.94,11c-0.46,-4.17 -3.77,-7.48 -7.94,-7.94L13,1h-2v2.06C6.83,3.52 3.52,6.83 3.06,11L1,11v2h2.06c0.46,4.17 3.77,7.48 7.94,7.94L11,23h2v-2.06c4.17,-0.46 7.48,-3.77 7.94,-7.94L23,13v-2h-2.06zM12,19c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M12,8c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4zM20.94,11c-0.46,-4.17 -3.77,-7.48 -7.94,-7.94L13,1h-2v2.06C6.83,3.52 3.52,6.83 3.06,11L1,11v2h2.06c0.46,4.17 3.77,7.48 7.94,7.94L11,23h2v-2.06c4.17,-0.46 7.48,-3.77 7.94,-7.94L23,13v-2h-2.06zM12,19c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M20.5,3l-0.16,0.03L15,5.1 9,3 3.36,4.9c-0.21,0.07 -0.36,0.25 -0.36,0.48V20.5c0,0.28 0.22,0.5 0.5,0.5l0.16,-0.03L9,18.9l6,2.1 5.64,-1.9c0.21,-0.07 0.36,-0.25 0.36,-0.48V3.5c0,-0.28 -0.22,-0.5 -0.5,-0.5zM15,19l-6,-2.11V5l6,2.11V19z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,16 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?android:attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M12,2l-5.5,9h11z"/>
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M17.5,17.5m-4.5,0a4.5,4.5 0,1 1,9 0a4.5,4.5 0,1 1,-9 0"/>
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M3,13.5h8v8H3z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="#000000"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="#000000"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M16,1L4,1c-1.1,0 -2,0.9 -2,2v14h2L4,3h12L16,1zM19,5L8,5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h11c1.1,0 2,-0.9 2,-2L21,7c0,-1.1 -0.9,-2 -2,-2zM19,21L8,21L8,7h11v14z"/>
|
||||
</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="?android:attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M21,3L3,10.53v0.98l6.84,2.65L12.48,21h0.98L21,3z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="#0082C9"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M4.25,5.61C6.27,8.2 10,13 10,13v6c0,0.55 0.45,1 1,1h2c0.55,0 1,-0.45 1,-1v-6c0,0 3.72,-4.8 5.74,-7.39C20.25,4.95 19.78,4 18.95,4H5.04C4.21,4 3.74,4.95 4.25,5.61z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,6 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M19.79,5.61C20.3,4.95 19.83,4 19,4H6.83l7.97,7.97L19.79,5.61z"/>
|
||||
<path android:fillColor="@android:color/white" android:pathData="M2.81,2.81L1.39,4.22L10,13v6c0,0.55 0.45,1 1,1h2c0.55,0 1,-0.45 1,-1v-2.17l5.78,5.78l1.41,-1.41L2.81,2.81z"/>
|
||||
</vector>
|
||||
@@ -1,74 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector
|
||||
android:height="108dp"
|
||||
android:width="108dp"
|
||||
android:viewportHeight="108"
|
||||
android:viewportWidth="108"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#3DDC84"
|
||||
android:pathData="M0,0h108v108h-108z"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M9,0L9,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,0L19,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M29,0L29,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M39,0L39,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M49,0L49,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M59,0L59,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M69,0L69,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M79,0L79,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M89,0L89,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M99,0L99,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,9L108,9"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,19L108,19"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,29L108,29"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,39L108,39"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,49L108,49"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,59L108,59"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,69L108,69"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,79L108,79"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,89L108,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,99L108,99"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,29L89,29"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,39L89,39"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,49L89,49"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,59L89,59"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,69L89,69"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,79L89,79"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M29,19L29,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M39,19L39,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M49,19L49,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M59,19L59,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M69,19L69,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M79,19L79,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
</vector>
|
||||
@@ -2,13 +2,14 @@
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<group android:scaleX="1.5525"
|
||||
android:scaleY="1.5525"
|
||||
android:translateX="29.16"
|
||||
android:translateY="29.16">
|
||||
android:viewportHeight="108"
|
||||
android:tint="#FFFFFF">
|
||||
<group android:scaleX="2.61"
|
||||
android:scaleY="2.61"
|
||||
android:translateX="22.68"
|
||||
android:translateY="22.68">
|
||||
<path
|
||||
android:pathData="m6,2c-2.216,0 -4,1.784 -4,4v20c0,2.216 1.784,4 4,4h20c2.216,0 4,-1.784 4,-4v-16.719l-0.906,0.9068 -5.282,-5.2818 2.907,-2.9062h-20.719zM21.812,6.9062l5.282,5.2818 -8.313,8.312 -8.781,3.5 3.5,-8.781 8.312,-8.3128zM14.406,16.094l-2.656,4.406 1.75,1.75 4.406,-2.656 -3.5,-3.5z"
|
||||
android:fillColor="#FFF"/>
|
||||
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"/>
|
||||
</group>
|
||||
</vector>
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<vector android:height="48dp"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="48dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<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,-7z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,5 @@
|
||||
<vector android:height="48dp" android:tint="#0082C9"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="48dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<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,5 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,16c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M17,1.01L7,1c-1.1,0 -1.99,0.9 -1.99,2v18c0,1.1 0.89,2 1.99,2h10c1.1,0 2,-0.9 2,-2L19,3c0,-1.1 -0.9,-1.99 -2,-1.99zM17,19L7,19L7,5h10v14zM12.8,13.22v1.75l3.2,-2.99L12.8,9v1.7c-3.11,0.43 -4.35,2.56 -4.8,4.7 1.11,-1.5 2.58,-2.18 4.8,-2.18z"/>
|
||||
</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="?android:attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M20.5,3l-0.16,0.03L15,5.1 9,3 3.36,4.9c-0.21,0.07 -0.36,0.25 -0.36,0.48V20.5c0,0.28 0.22,0.5 0.5,0.5l0.16,-0.03L9,18.9l6,2.1 5.64,-1.9c0.21,-0.07 0.36,-0.25 0.36,-0.48V3.5c0,-0.28 -0.22,-0.5 -0.5,-0.5zM15,19l-6,-2.11V5l6,2.11V19z"/>
|
||||
</vector>
|
||||
@@ -1,25 +0,0 @@
|
||||
<!--
|
||||
~ Nextcloud Maps Geofavorites for Android
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<vector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp"
|
||||
android:width="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path android:fillColor="#FFFFFF"
|
||||
android:pathData="M16,5V11H21V5M10,11H15V5H10M16,18H21V12H16M10,18H15V12H10M4,18H9V12H4M4,11H9V5H4V11Z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/defaultBackground"/>
|
||||
<stroke android:width="7dp" android:color="@color/defaultBrand" />
|
||||
<corners android:radius="20dp"/>
|
||||
</shape>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<shape
|
||||
android:shape="oval">
|
||||
<solid android:color="@color/defaultBrand"/>
|
||||
</shape>
|
||||
</item>
|
||||
</layer-list>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="#000" />
|
||||
<padding android:left="10dp" android:top="3dp" android:right="10dp" android:bottom="3dp" />
|
||||
<corners android:radius="20dp" />
|
||||
</shape>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<shape
|
||||
android:shape="oval">
|
||||
<solid android:color="@color/translucent"/>
|
||||
</shape>
|
||||
</item>
|
||||
</layer-list>
|
||||
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item>
|
||||
<shape
|
||||
android:innerRadius="0dp"
|
||||
android:shape="oval"
|
||||
android:useLevel="false">
|
||||
<solid android:color="@android:color/transparent" />
|
||||
<stroke
|
||||
android:width="10dp"
|
||||
android:color="@color/toolbar_background" />
|
||||
</shape>
|
||||
</item>
|
||||
</layer-list>
|
||||
@@ -32,7 +32,7 @@
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:layout_height="?android:attr/actionBarSize"
|
||||
app:contentInsetStartWithNavigation="0dp"
|
||||
app:navigationIcon="@drawable/ic_back_grey"
|
||||
app:titleMarginStart="0dp"/>
|
||||
@@ -115,6 +115,20 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="10dp"
|
||||
android:text="@string/about_issues" />
|
||||
|
||||
<TextView
|
||||
style="?android:attr/listSeparatorTextViewStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/about_maps_title" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/about_maps"
|
||||
style="?android:attr/editTextPreferenceStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="10dp"
|
||||
android:text="@string/about_maps" />
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
@@ -2,29 +2,88 @@
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<data>
|
||||
<variable
|
||||
name="geofavorite"
|
||||
type="it.danieleverducci.nextcloudmaps.model.Geofavorite"/>
|
||||
</data>
|
||||
|
||||
<ScrollView
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/root">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/appbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="250dp"
|
||||
android:fitsSystemWindows="true">
|
||||
|
||||
<ImageView
|
||||
<com.google.android.material.appbar.CollapsingToolbarLayout
|
||||
android:id="@+id/collapsing_toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
app:contentScrim="@color/defaultBrand"
|
||||
app:layout_scrollFlags="scroll|snap|exitUntilCollapsed"
|
||||
app:title="@string/new_geobookmark"
|
||||
app:expandedTitleTextAppearance="@style/TextAppearance.GeofavoriteDetail.Header.Expanded"
|
||||
app:collapsedTitleTextAppearance="@style/TextAppearance.GeofavoriteDetail.Header.Collapsed">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_collapseMode="parallax">
|
||||
<org.osmdroid.views.MapView
|
||||
android:id="@+id/map"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="200dp"
|
||||
android:padding="50dp"
|
||||
app:srcCompat="@drawable/ic_app"
|
||||
android:background="@color/defaultBrand" />
|
||||
android:layout_height="match_parent"
|
||||
android:focusable="false"
|
||||
android:clickable="false"/>
|
||||
<View
|
||||
android:id="@+id/map_bt"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
</FrameLayout>
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?android:attr/actionBarSize"
|
||||
app:contentInsetStart="0dp"
|
||||
app:layout_collapseMode="pin"
|
||||
android:background="@android:color/transparent">
|
||||
|
||||
<!-- Back button -->
|
||||
<ImageView
|
||||
android:id="@+id/back_bt"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_gravity="start"
|
||||
android:layout_margin="8dp"
|
||||
android:padding="8dp"
|
||||
android:src="@drawable/ic_back_grey"
|
||||
app:tint="@color/white"
|
||||
android:background="@drawable/floating_semitransparent_button_background"/>
|
||||
|
||||
<!-- Save button -->
|
||||
<ImageView
|
||||
android:id="@+id/submit_bt"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_gravity="end"
|
||||
android:layout_margin="8dp"
|
||||
android:padding="8dp"
|
||||
android:src="@drawable/ic_ok"
|
||||
app:tint="@color/white"
|
||||
android:background="@drawable/round_button_background"/>
|
||||
|
||||
</androidx.appcompat.widget.Toolbar>
|
||||
|
||||
|
||||
|
||||
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
@@ -32,33 +91,152 @@
|
||||
android:orientation="vertical"
|
||||
android:padding="20dp">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/accuracy_progress_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="10dp"
|
||||
android:orientation="horizontal">
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0"
|
||||
android:src="@drawable/ic_accuracy_fail"
|
||||
app:tint="@color/disabled"/>
|
||||
<ProgressBar
|
||||
android:id="@+id/accuracy_progress"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="5dp"
|
||||
android:layout_marginRight="5dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:indeterminate="true"
|
||||
style="@style/Widget.AppCompat.ProgressBar.Horizontal" />
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0"
|
||||
android:src="@drawable/ic_accuracy_ok"/>
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/accuracy_tv"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:textAlignment="center"
|
||||
android:text="@string/accuracy_nosignal"
|
||||
android:textColor="@color/defaultBrand"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/action_icons"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/action_icon_share"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:padding="10dp"
|
||||
android:src="@drawable/ic_share"
|
||||
app:tint="@color/defaultBrand"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/action_icon_nav"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:padding="10dp"
|
||||
android:src="@drawable/ic_nav"
|
||||
app:tint="@color/defaultBrand"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/action_icon_delete"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:padding="10dp"
|
||||
android:src="@drawable/ic_delete_grey"
|
||||
app:tint="@color/defaultBrand"
|
||||
android:visibility="gone"/> <!-- TODO Implement delete -->
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/name_et"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="40dp"
|
||||
android:ems="10"
|
||||
android:hint="@string/name"
|
||||
android:lines="1"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="end"
|
||||
android:text="@{geofavorite.name}"/>
|
||||
android:textColor="@color/defaultBrand"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Title"
|
||||
android:background="@android:color/transparent"
|
||||
android:drawableLeft="@drawable/ic_edit"
|
||||
android:drawablePadding="5dp"
|
||||
android:drawableTint="@color/defaultBrand"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/description_et"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:ems="10"
|
||||
android:gravity="start|top"
|
||||
android:inputType="textMultiLine"
|
||||
android:hint="@string/description"
|
||||
android:ellipsize="end"
|
||||
android:text="@{geofavorite.comment}" />
|
||||
android:maxLines="10"
|
||||
android:background="@android:color/transparent"
|
||||
android:drawableLeft="@drawable/ic_edit"
|
||||
android:drawablePadding="5dp"
|
||||
android:drawableTint="@color/defaultBrand"
|
||||
android:hint="@string/description"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<AutoCompleteTextView
|
||||
android:id="@+id/category_at"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:maxLines="1"
|
||||
android:background="@android:color/transparent"
|
||||
android:drawableLeft="@drawable/ic_category_asc"
|
||||
android:drawablePadding="5dp"
|
||||
android:drawableTint="@color/defaultBrand"
|
||||
android:hint="@string/category"
|
||||
android:completionThreshold="0"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/category_at_clear"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="3dp"
|
||||
android:background="@color/transparent"
|
||||
android:src="@drawable/ic_clear"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:text="@string/category_hint"
|
||||
android:textSize="12sp"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginTop="40dp"
|
||||
android:textStyle="bold"
|
||||
android:text="@string/created" />
|
||||
|
||||
@@ -66,8 +244,20 @@
|
||||
android:id="@+id/created_tv"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAlignment="textEnd"
|
||||
app:formatDate="@{geofavorite.dateCreated}" />
|
||||
android:textAlignment="textEnd" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:textStyle="bold"
|
||||
android:text="@string/modified" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/modified_tv"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAlignment="textEnd" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
@@ -81,20 +271,19 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAlignment="textEnd"
|
||||
android:text="@{geofavorite.lat +` °N, ` + geofavorite.lng + ` °E`}" />
|
||||
app:drawableEndCompat="@drawable/ic_copy"
|
||||
android:drawablePadding="10sp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/submit_bt"
|
||||
style="@style/Widget.AppCompat.Button.Colored"
|
||||
<ProgressBar
|
||||
android:id="@+id/progress"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="60dp"
|
||||
android:layout_margin="50dp"
|
||||
android:text="@string/confirm"
|
||||
app:backgroundTint="@color/defaultBrand"
|
||||
android:onClick="onSubmit"/>
|
||||
android:layout_height="wrap_content"
|
||||
android:indeterminate="true"
|
||||
android:visibility="gone"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
</layout>
|
||||
@@ -15,152 +15,58 @@
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/activity_list_view"
|
||||
android:background="@color/primary"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/appBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:elevation="0dp">
|
||||
|
||||
<com.google.android.material.appbar.CollapsingToolbarLayout
|
||||
android:id="@+id/toolbar_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fitsSystemWindows="true"
|
||||
app:contentScrim="?attr/colorPrimary"
|
||||
app:layout_scrollFlags="scroll|enterAlways"
|
||||
app:toolbarId="@+id/toolbar" >
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/fragment_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="visible"
|
||||
app:contentInsetStartWithNavigation="0dp"
|
||||
app:titleMarginStart="0dp"
|
||||
tools:title="@string/app_name">
|
||||
android:name="it.danieleverducci.nextcloudmaps.fragments.GeofavoriteListFragment"
|
||||
tools:layout="@layout/fragment_geofavorite_list" />
|
||||
|
||||
<androidx.appcompat.widget.SearchView
|
||||
android:id="@+id/search_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
</androidx.appcompat.widget.SearchView>
|
||||
|
||||
</androidx.appcompat.widget.Toolbar>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/home_toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/spacer_2x"
|
||||
android:layout_marginTop="@dimen/spacer_1hx"
|
||||
android:layout_marginEnd="@dimen/spacer_2x"
|
||||
android:layout_marginBottom="@dimen/spacer_1hx"
|
||||
app:cardBackgroundColor="@color/appbar"
|
||||
app:cardCornerRadius="@dimen/spacer_1x"
|
||||
app:cardElevation="2dp"
|
||||
app:strokeWidth="0dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageButton
|
||||
android:id="@+id/menu_button"
|
||||
<!-- Add from map FAB -->
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/add_from_map"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:paddingStart="@dimen/spacer_1x"
|
||||
android:paddingTop="@dimen/spacer_2x"
|
||||
android:paddingEnd="@dimen/spacer_1x"
|
||||
android:paddingBottom="@dimen/spacer_2x"
|
||||
android:tint="?attr/colorAccent"
|
||||
android:src="@drawable/ic_menu_grey"/>
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/search_text"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="@dimen/spacer_1x"
|
||||
android:layout_marginEnd="@dimen/spacer_1x"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="end"
|
||||
android:gravity="start"
|
||||
android:lines="1"
|
||||
android:textSize="16sp"
|
||||
android:text="@string/search_in_all"/>
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/sort_mode"
|
||||
android:layout_width="?attr/actionBarSize"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:layout_gravity="center_vertical|end"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/list_mode"
|
||||
android:padding="@dimen/spacer_2x"
|
||||
android:tint="?attr/colorAccent"
|
||||
android:translationX="@dimen/spacer_1x"
|
||||
android:src="@drawable/ic_alphabetical_asc" />
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/view_mode"
|
||||
android:layout_width="?attr/actionBarSize"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:layout_gravity="center_vertical|end"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/list_mode"
|
||||
android:padding="@dimen/spacer_2x"
|
||||
android:tint="?attr/colorAccent"
|
||||
android:translationX="@dimen/spacer_1x"
|
||||
android:src="@drawable/ic_view_module" />
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/swipe_refresh"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="@dimen/spacer_1hx"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recycler_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layoutManager="androidx.recyclerview.widget.StaggeredGridLayoutManager"
|
||||
app:spanCount="2"
|
||||
tools:itemCount="3"
|
||||
tools:listitem="@layout/item_note">
|
||||
</androidx.recyclerview.widget.RecyclerView>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_margin="24dp"
|
||||
android:src="@drawable/ic_add_map"
|
||||
app:backgroundTint="@color/defaultBrand"
|
||||
app:fabSize="mini"
|
||||
app:tint="@color/white"
|
||||
tools:ignore="DuplicateClickableBoundsCheck"
|
||||
android:contentDescription="@string/add_from_map" />
|
||||
|
||||
<!-- Add from current position FAB -->
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/add"
|
||||
android:id="@+id/add_from_gps"
|
||||
android:layout_margin="24dp"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:fabSize="mini"
|
||||
android:src="@drawable/ic_add_gps"
|
||||
app:backgroundTint="@color/defaultBrand"
|
||||
app:tint="@color/white"
|
||||
android:contentDescription="@string/add_from_gps"/>
|
||||
|
||||
<!-- Main FAB -->
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/open_fab"
|
||||
android:layout_margin="16dp"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_add"
|
||||
app:backgroundTint="@color/defaultBrand"/>
|
||||
app:backgroundTint="@color/defaultBrand"
|
||||
app:tint="@color/white"
|
||||
android:contentDescription="@string/open_fab"/>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
</FrameLayout>
|
||||
@@ -46,8 +46,7 @@
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginBottom="48dp"
|
||||
android:textSize="24sp"
|
||||
android:gravity="center_horizontal"
|
||||
android:textColor="@color/accent"/>
|
||||
android:gravity="center_horizontal"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/chose_button"
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
android:id="@+id/scrollView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?attr/colorPrimary">
|
||||
android:background="?android:attr/colorPrimary">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
@@ -68,9 +68,11 @@
|
||||
android:id="@+id/app_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="20dp"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_toEndOf="@id/logo"
|
||||
android:ellipsize="end"
|
||||
android:lines="2"
|
||||
android:fontFamily="sans-serif-light"
|
||||
android:gravity="center_vertical"
|
||||
android:text="@string/app_name"
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto" >
|
||||
<FrameLayout
|
||||
android:id="@+id/root"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<org.osmdroid.views.MapView
|
||||
android:id="@+id/map"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:layout_gravity="center"
|
||||
android:src="@drawable/ic_map_pin"
|
||||
app:tint="@color/defaultBrand"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/back_bt"
|
||||
android:layout_width="?android:attr/actionBarSize"
|
||||
android:layout_height="?android:attr/actionBarSize"
|
||||
android:layout_margin="16dp"
|
||||
android:layout_gravity="start"
|
||||
android:padding="16dp"
|
||||
android:src="@drawable/ic_back_grey"
|
||||
app:tint="@color/text_color"
|
||||
android:background="@drawable/unselected_floating_semitransparent_button_background"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ok_bt"
|
||||
android:layout_width="?android:attr/actionBarSize"
|
||||
android:layout_height="?android:attr/actionBarSize"
|
||||
android:layout_margin="16dp"
|
||||
android:layout_gravity="end"
|
||||
android:padding="16dp"
|
||||
android:src="@drawable/ic_ok"
|
||||
app:tint="@color/white"
|
||||
android:background="@drawable/round_button_background"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
android:padding="10dp"
|
||||
android:orientation="horizontal"
|
||||
android:background="@color/defaultBrandAlpha">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/lat_et"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_weight="1"
|
||||
android:textAlignment="center"
|
||||
android:lines="1"
|
||||
android:maxLines="1"
|
||||
android:inputType="numberDecimal"
|
||||
android:textColor="@color/white"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:textStyle="bold"
|
||||
android:text="°N"
|
||||
android:textColor="@color/white"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/lon_et"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginStart="10dp"
|
||||
android:textAlignment="center"
|
||||
android:lines="1"
|
||||
android:maxLines="1"
|
||||
android:inputType="numberDecimal"
|
||||
android:textColor="@color/white"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:textStyle="bold"
|
||||
android:text="°E"
|
||||
android:textColor="@color/white"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/latlon_confirm_btn"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:padding="8dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:src="@drawable/ic_ok"
|
||||
app:tint="@color/white"
|
||||
android:background="@drawable/round_button_background"
|
||||
android:visibility="gone"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
</FrameLayout>
|
||||
</layout>
|
||||
@@ -0,0 +1,102 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout
|
||||
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="wrap_content"
|
||||
android:layout_marginTop="10dp">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="visible"
|
||||
app:contentInsetStartWithNavigation="0dp"
|
||||
app:titleMarginStart="0dp"
|
||||
tools:title="@string/app_name">
|
||||
|
||||
<androidx.appcompat.widget.SearchView
|
||||
android:id="@+id/search_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="30dp"
|
||||
android:layout_marginEnd="30dp"
|
||||
android:background="@color/translucent">
|
||||
</androidx.appcompat.widget.SearchView>
|
||||
|
||||
</androidx.appcompat.widget.Toolbar>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/home_toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/spacer_2x"
|
||||
android:layout_marginTop="@dimen/spacer_1hx"
|
||||
android:layout_marginEnd="@dimen/spacer_2x"
|
||||
android:layout_marginBottom="@dimen/spacer_1hx"
|
||||
app:cardBackgroundColor="@color/toolbar_background"
|
||||
app:cardCornerRadius="30dp"
|
||||
app:cardElevation="2dp"
|
||||
app:strokeWidth="0dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageButton
|
||||
android:id="@+id/menu_button"
|
||||
android:layout_width="?android:attr/actionBarSize"
|
||||
android:layout_height="?android:attr/actionBarSize"
|
||||
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:layout_gravity="center_vertical|start"
|
||||
android:paddingStart="@dimen/spacer_1x"
|
||||
android:paddingTop="@dimen/spacer_2x"
|
||||
android:paddingEnd="@dimen/spacer_1x"
|
||||
android:paddingBottom="@dimen/spacer_2x"
|
||||
android:tint="@color/text_color"
|
||||
android:src="@drawable/ic_menu_grey"
|
||||
android:contentDescription="@string/menu"/>
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/search_text"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="@dimen/spacer_1x"
|
||||
android:layout_marginEnd="@dimen/spacer_1x"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="end"
|
||||
android:gravity="start"
|
||||
android:lines="1"
|
||||
android:textSize="16sp"
|
||||
android:text="@string/search_in_all"/>
|
||||
|
||||
<!-- Filter button -->
|
||||
<ImageButton
|
||||
android:id="@+id/search_filter"
|
||||
android:layout_width="?android:attr/actionBarSize"
|
||||
android:layout_height="?android:attr/actionBarSize"
|
||||
android:src="@drawable/ic_filter_off"
|
||||
app:tint="@color/inactive"
|
||||
android:background="@color/transparent"/>
|
||||
|
||||
<!-- User badge -->
|
||||
<FrameLayout
|
||||
android:id="@+id/user_badge_container"
|
||||
android:layout_width="?android:attr/actionBarSize"
|
||||
android:layout_height="?android:attr/actionBarSize">
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/user_badge"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="10dp"/>
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/user_badge_mask"/>
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
</FrameLayout>
|
||||
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="20dp"
|
||||
android:paddingRight="20dp"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingBottom="10dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/category_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="30dp"
|
||||
android:gravity="center_vertical"
|
||||
android:lines="1"
|
||||
android:ellipsize="end"
|
||||
android:text="Lorem ipsum"
|
||||
app:drawableLeftCompat="@drawable/ic_category_asc"
|
||||
android:drawablePadding="8sp"
|
||||
android:drawableTint="@color/white"
|
||||
android:textColor="@color/white"
|
||||
android:textStyle="bold"
|
||||
android:background="@drawable/rounded_label_background"/>
|
||||
|
||||
</FrameLayout>
|
||||
@@ -0,0 +1,90 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/activity_list_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/appBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:elevation="0dp">
|
||||
|
||||
<com.google.android.material.appbar.CollapsingToolbarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fitsSystemWindows="true"
|
||||
app:contentScrim="?android:attr/colorPrimary"
|
||||
app:layout_scrollFlags="scroll|enterAlways"
|
||||
app:toolbarId="@+id/toolbar" >
|
||||
|
||||
<include layout="@layout/app_toolbar"/>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginTop="?android:attr/actionBarSize"
|
||||
android:paddingTop="20dp"
|
||||
android:paddingStart="5dp"
|
||||
android:paddingEnd="5dp">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/sort_mode"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/floating_bar_height"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/list_mode"
|
||||
android:paddingStart="@dimen/spacer_2x"
|
||||
android:paddingEnd="@dimen/spacer_2x"
|
||||
android:tint="@color/text_color"
|
||||
android:src="@drawable/ic_alphabetical_asc" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/view_mode_map"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/floating_bar_height"
|
||||
android:layout_alignParentRight="true"
|
||||
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/list_mode"
|
||||
android:paddingStart="@dimen/spacer_2x"
|
||||
android:paddingEnd="@dimen/spacer_2x"
|
||||
android:tint="@color/text_color"
|
||||
android:src="@drawable/ic_view_map" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/swipe_refresh"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recycler_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layoutManager="androidx.recyclerview.widget.StaggeredGridLayoutManager"
|
||||
app:spanCount="1"
|
||||
tools:itemCount="3"
|
||||
tools:listitem="@layout/item_geofav">
|
||||
</androidx.recyclerview.widget.RecyclerView>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
@@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/activity_list_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<org.osmdroid.views.MapView
|
||||
android:id="@+id/map"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/loading_wall"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/translucent"
|
||||
android:visibility="gone">
|
||||
|
||||
<ProgressBar
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:indeterminate="true"
|
||||
android:layout_gravity="center"/>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:id="@+id/appBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:elevation="0dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<include layout="@layout/app_toolbar"/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/view_mode_list"
|
||||
android:layout_width="@dimen/floating_bar_height"
|
||||
android:layout_height="@dimen/floating_bar_height"
|
||||
android:layout_marginTop="2dp"
|
||||
android:layout_marginEnd="@dimen/spacer_2x"
|
||||
android:layout_gravity="right"
|
||||
android:background="@drawable/unselected_floating_semitransparent_button_background"
|
||||
android:contentDescription="@string/list_mode"
|
||||
android:paddingStart="5dp"
|
||||
android:paddingEnd="5dp"
|
||||
android:tint="@color/text_color"
|
||||
android:src="@drawable/ic_view_list" />
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/center_position"
|
||||
android:layout_width="@dimen/floating_bar_height"
|
||||
android:layout_height="@dimen/floating_bar_height"
|
||||
android:layout_marginRight="100dp"
|
||||
android:layout_marginBottom="25dp"
|
||||
android:layout_gravity="bottom|right"
|
||||
android:background="@drawable/unselected_floating_semitransparent_button_background"
|
||||
android:padding="5dp"
|
||||
android:tint="@color/text_color"
|
||||
android:src="@drawable/ic_add_gps"
|
||||
android:visibility="gone"/>
|
||||
|
||||
</FrameLayout>
|
||||
@@ -0,0 +1,119 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:minWidth="250dp"
|
||||
android:background="@drawable/infowindow_geofav_background"
|
||||
android:padding="7dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<!-- Geofavorite title -->
|
||||
<TextView
|
||||
android:id="@+id/bubble_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/defaultBrand"
|
||||
android:padding="10dp"
|
||||
android:lines="1"
|
||||
android:ellipsize="end"
|
||||
style="@style/TextAppearance.GeofavoriteInfowindow"
|
||||
android:text="Lorem"/>
|
||||
|
||||
<!-- Geofavorite description -->
|
||||
<TextView
|
||||
android:id="@+id/bubble_description"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginRight="10dp"
|
||||
android:text="Lorem ipsum"/>
|
||||
|
||||
<!-- Geofavorite category -->
|
||||
<TextView
|
||||
android:id="@+id/bubble_subdescription"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="30dp"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:gravity="center_vertical"
|
||||
android:lines="1"
|
||||
android:ellipsize="end"
|
||||
android:text="Lorem ipsum"
|
||||
app:drawableLeftCompat="@drawable/ic_category_asc"
|
||||
android:drawablePadding="8sp"
|
||||
android:drawableTint="@color/white"
|
||||
android:textColor="@color/white"
|
||||
android:textStyle="bold"
|
||||
android:background="@drawable/rounded_label_background"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/action_icon_share"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:padding="10dp"
|
||||
android:src="@drawable/ic_share"
|
||||
app:tint="@color/defaultBrand"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/action_icon_nav"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:padding="10dp"
|
||||
android:src="@drawable/ic_nav"
|
||||
app:tint="@color/defaultBrand"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/action_icon_delete"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:padding="10dp"
|
||||
android:src="@drawable/ic_delete_grey"
|
||||
app:tint="@color/defaultBrand"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/action_icon_edit"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:padding="10dp"
|
||||
android:src="@drawable/ic_edit"
|
||||
app:tint="@color/defaultBrand"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/bubble_image"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="10dp"
|
||||
android:layout_marginTop="-1dp"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:src="@drawable/geofav_infowindow_pointer"
|
||||
app:tint="@color/defaultBrand" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,120 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Nextcloud Maps Geofavorites for Android
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="90dp"
|
||||
android:gravity="center_vertical"
|
||||
android:clickable="true"
|
||||
android:focusable="true">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_category"
|
||||
android:layout_width="64dp"
|
||||
android:layout_height="64dp"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_weight="0"
|
||||
android:background="@drawable/ic_list_pin"
|
||||
android:gravity="top|center"
|
||||
android:paddingTop="5dp"
|
||||
android:textSize="28dp"
|
||||
android:textAllCaps="true"
|
||||
android:textColor="@color/white"
|
||||
app:drawableTint="@color/defaultBrand" />
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_weight="1">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:maxLines="1"
|
||||
android:lines="1"
|
||||
android:ellipsize="middle"
|
||||
android:textColor="@color/text_color"
|
||||
android:textSize="@dimen/two_line_primary_text_size"
|
||||
tools:text="@tools:sample/lorem/random">
|
||||
</TextView>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/content"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:textColor="@color/secondary_text_color"
|
||||
android:textSize="@dimen/two_line_secondary_text_size"
|
||||
android:maxLines="1"
|
||||
android:lines="1"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center_vertical"
|
||||
tools:text="@tools:sample/lorem/random">
|
||||
</TextView>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/date"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0"
|
||||
android:textColor="@color/secondary_text_color"
|
||||
android:textSize="@dimen/two_line_secondary_text_size"
|
||||
android:maxLines="1"
|
||||
android:lines="1"
|
||||
android:gravity="center_vertical"
|
||||
tools:text="00/00/0000">
|
||||
</TextView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/geofav_nav_bt"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="0"
|
||||
android:padding="8dp"
|
||||
android:src="@drawable/ic_nav"
|
||||
app:tint="@color/secondary_text_color" /> <!-- TODO: app:tint is not working -->
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/geofav_context_menu_bt"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="0"
|
||||
android:padding="8dp"
|
||||
android:src="@drawable/ic_more"
|
||||
app:tint="@color/secondary_text_color" /> <!-- TODO: app:tint is not working -->
|
||||
|
||||
</LinearLayout>
|
||||
@@ -18,6 +18,7 @@
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
@@ -33,7 +34,8 @@
|
||||
android:contentDescription="@null"
|
||||
android:focusable="false"
|
||||
android:scaleType="center"
|
||||
android:src="@drawable/ic_time_grey"/>
|
||||
android:src="@drawable/ic_time_grey"
|
||||
app:tint="@color/text_color"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/navigationItemLabel"
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Nextcloud Maps Geofavorites for Android
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<LinearLayout
|
||||
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:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="12dp"
|
||||
android:clickable="true"
|
||||
android:focusable="true">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="0"
|
||||
android:layout_marginRight="10dp"
|
||||
android:src="@mipmap/ic_launcher"/>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:textSize="@dimen/note_font_size_item_title"
|
||||
android:textStyle="bold"
|
||||
android:singleLine="true"
|
||||
android:maxLines="1"
|
||||
android:lines="1"
|
||||
android:ellipsize="end"
|
||||
tools:text="@tools:sample/lorem/random">
|
||||
</TextView>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="5dp"
|
||||
android:textSize="@dimen/note_font_size_item_content"
|
||||
android:maxLines="2"
|
||||
android:lines="2"
|
||||
tools:text="@tools:sample/lorem/random">
|
||||
</TextView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
@@ -1,22 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Nextcloud Maps Geofavorites for Android
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
@@ -1,22 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Nextcloud Maps Geofavorites for Android
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
|
Before Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 7.9 KiB |
|
Before Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 18 KiB |
@@ -16,8 +16,9 @@
|
||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<LinearLayout
|
||||
android:id="@+id/root"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/root"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minWidth="300dp"
|
||||
@@ -57,7 +58,8 @@
|
||||
android:paddingEnd="@dimen/standard_half_padding"
|
||||
android:paddingBottom="@dimen/standard_half_padding"
|
||||
android:contentDescription="@string/menu_item_sort_by_title_a_z"
|
||||
android:src="@drawable/ic_alphabetical_asc" />
|
||||
android:src="@drawable/ic_alphabetical_asc"
|
||||
app:tint="@color/selector_item_unselected"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sortByTitleAscendingText"
|
||||
@@ -72,7 +74,8 @@
|
||||
android:paddingBottom="@dimen/standard_half_padding"
|
||||
android:singleLine="true"
|
||||
android:text="@string/menu_item_sort_by_title_a_z"
|
||||
android:textSize="@dimen/two_line_primary_text_size"/>
|
||||
android:textSize="@dimen/two_line_primary_text_size"
|
||||
android:textColor="@color/text_color"/>
|
||||
|
||||
</TableRow>
|
||||
|
||||
@@ -92,7 +95,8 @@
|
||||
android:paddingEnd="@dimen/standard_half_padding"
|
||||
android:paddingBottom="@dimen/standard_half_padding"
|
||||
android:contentDescription="@string/menu_item_sort_by_date_newest_first"
|
||||
android:src="@drawable/ic_modification_asc"/>
|
||||
android:src="@drawable/ic_modification_asc"
|
||||
app:tint="@color/selector_item_unselected"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sortByCreationDateDescendingText"
|
||||
@@ -107,7 +111,83 @@
|
||||
android:paddingBottom="@dimen/standard_half_padding"
|
||||
android:singleLine="true"
|
||||
android:text="@string/menu_item_sort_by_date_newest_first"
|
||||
android:textSize="@dimen/two_line_primary_text_size" />
|
||||
android:textSize="@dimen/two_line_primary_text_size"
|
||||
android:textColor="@color/selector_item_unselected" />
|
||||
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/standard_half_margin">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/sortByCategoryAscending"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:background="@color/transparent"
|
||||
android:paddingStart="@dimen/standard_padding"
|
||||
android:paddingTop="@dimen/standard_half_padding"
|
||||
android:paddingEnd="@dimen/standard_half_padding"
|
||||
android:paddingBottom="@dimen/standard_half_padding"
|
||||
android:contentDescription="@string/menu_item_sort_by_category_a_z"
|
||||
android:src="@drawable/ic_category_asc"
|
||||
app:tint="@color/selector_item_unselected"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sortByCategoryAscendingText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="middle"
|
||||
android:paddingStart="@dimen/zero"
|
||||
android:paddingTop="@dimen/standard_half_padding"
|
||||
android:paddingEnd="@dimen/standard_half_padding"
|
||||
android:paddingBottom="@dimen/standard_half_padding"
|
||||
android:singleLine="true"
|
||||
android:text="@string/menu_item_sort_by_category_a_z"
|
||||
android:textSize="@dimen/two_line_primary_text_size"
|
||||
android:textColor="@color/selector_item_unselected" />
|
||||
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/standard_half_margin"
|
||||
android:visibility="gone"> <!-- TODO: complete sorting by distance -->
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/sortByDistanceAscending"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:background="@color/transparent"
|
||||
android:paddingStart="@dimen/standard_padding"
|
||||
android:paddingTop="@dimen/standard_half_padding"
|
||||
android:paddingEnd="@dimen/standard_half_padding"
|
||||
android:paddingBottom="@dimen/standard_half_padding"
|
||||
android:contentDescription="@string/menu_item_sort_by_distance_nearest_first"
|
||||
android:src="@drawable/ic_distance_asc"
|
||||
app:tint="@color/selector_item_unselected"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sortByDistanceAscendingText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="middle"
|
||||
android:paddingStart="@dimen/zero"
|
||||
android:paddingTop="@dimen/standard_half_padding"
|
||||
android:paddingEnd="@dimen/standard_half_padding"
|
||||
android:paddingBottom="@dimen/standard_half_padding"
|
||||
android:singleLine="true"
|
||||
android:text="@string/menu_item_sort_by_distance_nearest_first"
|
||||
android:textSize="@dimen/two_line_primary_text_size"
|
||||
android:textColor="@color/selector_item_unselected" />
|
||||
|
||||
</TableRow>
|
||||
|
||||
@@ -125,7 +205,9 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/common_cancel"
|
||||
android:layout_marginTop="@dimen/standard_half_margin"/>
|
||||
android:textAllCaps="false"
|
||||
android:layout_marginTop="@dimen/standard_half_margin"
|
||||
style="@style/Widget.AppCompat.Button.ButtonBar.AlertDialog"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<!-- Share button -->
|
||||
<item
|
||||
android:id="@+id/list_context_menu_share"
|
||||
android:title="@string/list_context_menu_share"/>
|
||||
<!-- Delete button -->
|
||||
<item
|
||||
android:id="@+id/list_context_menu_delete"
|
||||
android:title="@string/list_context_menu_delete"/>
|
||||
</menu>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 1.2 KiB |