Compare commits

..

No commits in common. "master" and "v0.3" have entirely different histories.
master ... v0.3

95 changed files with 641 additions and 2106 deletions

View File

@ -7,11 +7,11 @@
<deviceKey>
<Key>
<type value="VIRTUAL_DEVICE_PATH" />
<value value="$USER_HOME$/.android/avd/Pixel_5_API_29.avd" />
<value value="$USER_HOME$/.android/avd/Pixel_XL_API_30.avd" />
</Key>
</deviceKey>
</Target>
</targetSelectedWithDropDown>
<timeTargetWasSelectedWithDropDown value="2023-02-15T07:23:10.782369Z" />
<timeTargetWasSelectedWithDropDown value="2021-09-14T16:48:49.750165Z" />
</component>
</project>

14
.idea/misc.xml generated
View File

@ -4,23 +4,11 @@
<option name="filePathToZoomLevelMap">
<map>
<entry key="../../../../layout/custom_preview.xml" value="0.5661458333333333" />
<entry key="app/src/main/res/drawable/category_listitem_background.xml" value="0.35104166666666664" />
<entry key="app/src/main/res/drawable/coordinates_label_background.xml" value="0.3614583333333333" />
<entry key="app/src/main/res/drawable/floating_semitransparent_button_background.xml" value="0.512962962962963" />
<entry key="app/src/main/res/drawable/ic_list_pin.xml" value="0.3614583333333333" />
<entry key="app/src/main/res/drawable/ic_map_pin.xml" value="0.6425925925925926" />
<entry key="app/src/main/res/drawable/ic_more.xml" value="0.6166666666666667" />
<entry key="app/src/main/res/drawable/ic_nav.xml" value="0.6083333333333333" />
<entry key="app/src/main/res/drawable/ic_share.xml" value="0.8828125" />
<entry key="app/src/main/res/drawable/round_button_background.xml" value="0.3614583333333333" />
<entry key="app/src/main/res/layout/activity_geofavorite_detail.xml" value="0.4" />
<entry key="app/src/main/res/layout/activity_list_view.xml" value="0.4" />
<entry key="app/src/main/res/layout/activity_login.xml" value="0.2630208333333333" />
<entry key="app/src/main/res/layout/activity_list_view.xml" value="0.5307291666666667" />
<entry key="app/src/main/res/layout/activity_main.xml" value="0.5307291666666667" />
<entry key="app/src/main/res/layout/activity_map_picker.xml" value="0.33016304347826086" />
<entry key="app/src/main/res/layout/item_geofav.xml" value="0.5307291666666667" />
<entry key="app/src/main/res/layout/item_navigation.xml" value="0.8" />
<entry key="app/src/main/res/layout/sorting_order_fragment.xml" value="0.4740740740740741" />
<entry key="app/src/main/res/menu/list_context_menu.xml" value="0.41944444444444445" />
</map>
</option>

View File

@ -1,20 +1,12 @@
![Nextcloud Maps Geofavorites Logo](/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png)
![Nextcloud Maps Geobookmarks Logo](/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png)
# Nextcloud Maps Geofavorites Android app
# Nextcloud Maps Geobookmarks 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 list.
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.
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.
**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).
![Screenshot 1](screenshots/1.png) ![Screenshot 1](screenshots/2.png)
![Screenshot 1](screenshots/1.png) ![Screenshot 1](screenshots/2.png)

View File

@ -18,14 +18,14 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 31
compileSdkVersion 30
defaultConfig {
applicationId "it.danieleverducci.nextcloudmaps"
minSdkVersion 23
targetSdkVersion 31
versionCode 8
versionName "0.3.6"
targetSdkVersion 30
versionCode 2
versionName "0.2"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
@ -54,7 +54,7 @@ repositories {
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation 'com.android.support:design:31.0.0'
implementation 'com.android.support:design:30.0.1'
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'androidx.recyclerview:recyclerview:1.2.1'
implementation "androidx.cardview:cardview:1.0.0"
@ -70,12 +70,9 @@ dependencies {
implementation 'com.squareup.retrofit2:converter-gson:2.6.1'
// Nextcloud SSO
implementation "com.github.nextcloud:Android-SingleSignOn:0.6.1"
implementation "com.github.nextcloud:Android-SingleSignOn:0.5.6"
// OSMDroid
implementation 'org.osmdroid:osmdroid-android:6.1.10'
//Threeten-Backport (ports Java 8 Date API on Java 6+)
implementation 'org.threeten:threetenbp:1.5.1'
compile 'org.osmdroid:osmdroid-android:6.1.10'
}

View File

@ -0,0 +1,20 @@
{
"version": 3,
"artifactType": {
"type": "APK",
"kind": "Directory"
},
"applicationId": "it.danieleverducci.nextcloudmaps",
"variantName": "release",
"elements": [
{
"type": "SINGLE",
"filters": [],
"attributes": [],
"versionCode": 2,
"versionName": "0.2",
"outputFile": "app-release.apk"
}
],
"elementType": "File"
}

View File

@ -21,13 +21,10 @@
package="it.danieleverducci.nextcloudmaps">
<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
@ -38,16 +35,14 @@
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".activity.login.LoginActivity"
android:exported="true">
<activity android:name=".activity.login.LoginActivity">
<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"
android:exported="true">
<activity android:name=".activity.main.MainActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
</intent-filter>
@ -55,62 +50,7 @@
<activity
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>
android:theme="@style/AppTheme"/>
<activity
android:name=".activity.about.AboutActivity"

View File

@ -1,26 +0,0 @@
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);
}
}

View File

@ -30,9 +30,8 @@ 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 NextcloudMapsStyledActivity {
public class AboutActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -63,10 +62,6 @@ public class AboutActivity extends NextcloudMapsStyledActivity {
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

View File

@ -1,24 +0,0 @@
package it.danieleverducci.nextcloudmaps.activity.detail;
import android.content.Context;
import android.widget.ArrayAdapter;
import androidx.annotation.NonNull;
import java.util.HashSet;
public class CategoriesSpinnerAdapter extends ArrayAdapter<String> {
public CategoriesSpinnerAdapter(@NonNull Context context) {
super(context, android.R.layout.simple_dropdown_item_1line);
}
// TODO: implement colors
public void setCategoriesList(HashSet<String> categories) {
clear();
addAll(categories);
notifyDataSetChanged();
}
}

View File

@ -19,7 +19,6 @@ package it.danieleverducci.nextcloudmaps.activity.detail;
import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.location.Location;
@ -37,46 +36,42 @@ import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
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.config.IConfigurationProvider;
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 java.util.Date;
import it.danieleverducci.nextcloudmaps.BuildConfig;
import it.danieleverducci.nextcloudmaps.R;
import it.danieleverducci.nextcloudmaps.activity.NextcloudMapsStyledActivity;
import it.danieleverducci.nextcloudmaps.activity.mappicker.MapPickerActivity;
import it.danieleverducci.nextcloudmaps.api.ApiProvider;
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;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class GeofavoriteDetailActivity extends NextcloudMapsStyledActivity implements LocationListener, ActivityCompat.OnRequestPermissionsResultCallback {
public class GeofavoriteDetailActivity extends AppCompatActivity implements LocationListener, ActivityCompat.OnRequestPermissionsResultCallback {
public static final String TAG = "GeofavDetail";
public static final String DEFAULT_CATEGORY = "Personal";
public static final int MINIMUM_ACCEPTABLE_ACCURACY = 50; // In meters
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);
// OSMDroid config
Configuration.getInstance().load(getApplicationContext(),
PreferenceManager.getDefaultSharedPreferences(getApplicationContext()));
mViewHolder = new ViewHolder(getLayoutInflater());
setContentView(mViewHolder.getRootView());
@ -86,26 +81,6 @@ public class GeofavoriteDetailActivity extends NextcloudMapsStyledActivity imple
finish();
}
@Override
public void onMapEditPressed() {
//TODO
}
@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();
@ -113,67 +88,22 @@ public class GeofavoriteDetailActivity extends NextcloudMapsStyledActivity imple
@Override
public void onMapClicked() {
// TODO: Open map activity with pin
startActivity(IntentGenerator.newGeoUriIntent(GeofavoriteDetailActivity.this, mGeofavorite));
Toast.makeText(GeofavoriteDetailActivity.this, "TODO: Open map activity with pin", Toast.LENGTH_SHORT).show();
}
});
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) {
if (getIntent().hasExtra(ARG_GEOFAVORITE)) {
// Opening geofavorite from list
mGeofavorite = mViewModel.getGeofavorite(
getIntent().getIntExtra(ARG_GEOFAVORITE, 0)
);
mGeofavorite = (Geofavorite) getIntent().getSerializableExtra(ARG_GEOFAVORITE);
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();
}
mGeofavorite.setCategory(DEFAULT_CATEGORY);
mGeofavorite.setDateCreated(System.currentTimeMillis());
mGeofavorite.setDateModified(System.currentTimeMillis());
// Precompile location
getLocation();
}
mViewHolder.updateView(mGeofavorite);
@ -217,7 +147,35 @@ public class GeofavoriteDetailActivity extends NextcloudMapsStyledActivity imple
return;
}
mViewModel.saveGeofavorite(mGeofavorite);
Call<Geofavorite> call;
if (mGeofavorite.getId() == 0) {
// New geofavorite
call = ApiProvider.getAPI().createGeofavorite(mGeofavorite);
} else {
// Update existing geofavorite
call = ApiProvider.getAPI().updateGeofavorite(mGeofavorite.getId(), mGeofavorite);
}
call.enqueue(new Callback<Geofavorite>() {
@Override
public void onResponse(Call<Geofavorite> call, Response<Geofavorite> response) {
if (response.isSuccessful())
finish();
else
onGeofavoriteSaveFailed();
}
@Override
public void onFailure(Call<Geofavorite> call, Throwable t) {
onGeofavoriteSaveFailed();
Log.e(TAG, "Unable to update geofavorite: " + t.getMessage());
}
});
}
private void onGeofavoriteSaveFailed() {
runOnUiThread(() ->
Toast.makeText(GeofavoriteDetailActivity.this, R.string.error_saving_geofavorite, Toast.LENGTH_SHORT).show()
);
}
/**
@ -297,31 +255,18 @@ public class GeofavoriteDetailActivity extends NextcloudMapsStyledActivity imple
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
CategoriesSpinnerAdapter categoriesAdapter = new CategoriesSpinnerAdapter(binding.root.getContext());
this.binding.categoryAt.setAdapter(categoriesAdapter);
this.binding.categoryAt.setText(Geofavorite.DEFAULT_CATEGORY);
// 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);
@ -335,12 +280,11 @@ public class GeofavoriteDetailActivity extends NextcloudMapsStyledActivity imple
}
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);
binding.nameEt.setText(item.getName());
binding.descriptionEt.setText(item.getComment());
binding.createdTv.setText(new Date(item.getDateCreated() * 1000).toString());
binding.modifiedTv.setText(new Date(item.getDateModified() * 1000).toString());
binding.categoryTv.setText(item.getCategory()); // TODO: Category spinner from existing categories
updateViewCoords(item);
}
@ -360,34 +304,21 @@ public class GeofavoriteDetailActivity extends NextcloudMapsStyledActivity imple
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) {
((CategoriesSpinnerAdapter)binding.categoryAt.getAdapter()).setCategoriesList(categories);
// Color the accuracy background with a scale from red (MINIMUM_ACCEPTABLE_ACCURACY) to green (0 meters)
float red = accuracy / MINIMUM_ACCEPTABLE_ACCURACY;
if (red > 1.0f) red = 1.0f;
float green = 1.0f - red;
if (Build.VERSION.SDK_INT >= 26)
binding.accuracyTv.setBackgroundColor(Color.rgb(red, green, 0.0f));
}
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) {
@ -413,20 +344,6 @@ public class GeofavoriteDetailActivity extends NextcloudMapsStyledActivity imple
if (v.getId() == R.id.back_bt && this.listener != null) {
this.listener.onBackPressed();
}
if (v.getId() == R.id.manual_pos_bt && this.listener != null) {
this.listener.onMapEditPressed();
}
// 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();
}
}
}
@ -434,9 +351,5 @@ public class GeofavoriteDetailActivity extends NextcloudMapsStyledActivity imple
void onSubmit();
void onMapClicked();
void onBackPressed();
void onMapEditPressed();
void onActionIconShareClicked();
void onActionIconNavClicked();
void onActionIconDeleteClicked();
}
}

View File

@ -1,43 +0,0 @@
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();
}
}

View File

@ -39,13 +39,12 @@ 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 NextcloudMapsStyledActivity {
public class LoginActivity extends AppCompatActivity {
protected ApiProvider mApi;
protected ProgressBar progress;
protected Button button;
@ -115,6 +114,9 @@ public class LoginActivity extends NextcloudMapsStyledActivity {
}
private void accountAccessDone() {
Context l_context = getApplicationContext();
mApi = new ApiProvider(l_context);
Intent intent = new Intent(LoginActivity.this, MainActivity.class);
startActivity(intent);

View File

@ -21,10 +21,13 @@
package it.danieleverducci.nextcloudmaps.activity.main;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.content.Intent;
import android.text.Html;
import android.util.Log;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
@ -35,12 +38,8 @@ import android.widget.PopupMenu;
import android.widget.TextView;
import androidx.annotation.NonNull;
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;
@ -55,12 +54,9 @@ public class GeofavoriteAdapter extends RecyclerView.Adapter<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 DateTimeFormatter dateFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT);
private List<Geofavorite> geofavoriteList = new ArrayList<>();
private List<Geofavorite> geofavoriteListFiltered = new ArrayList<>();
@ -86,6 +82,17 @@ public class GeofavoriteAdapter extends RecyclerView.Adapter<GeofavoriteAdapter.
return geofavoriteListFiltered.get(position);
}
public void removeById(int id) {
for (Geofavorite g : geofavoriteList) {
if (g.getId() == id) {
geofavoriteList.remove(g);
geofavoriteListFiltered.remove(g);
break;
}
}
notifyDataSetChanged();
}
public int getSortRule() {
return sortRule;
}
@ -108,12 +115,8 @@ public class GeofavoriteAdapter extends RecyclerView.Adapter<GeofavoriteAdapter.
public void onBindViewHolder(@NonNull GeofavoriteViewHolder holder, int position) {
Geofavorite geofavorite = geofavoriteListFiltered.get(position);
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));
holder.tv_title.setText(Html.fromHtml(geofavorite.getName()));
holder.tv_content.setText(geofavorite.getComment());
}
@Override
@ -161,9 +164,9 @@ public class GeofavoriteAdapter extends RecyclerView.Adapter<GeofavoriteAdapter.
};
class GeofavoriteViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
TextView tv_category, tv_title, tv_content, tv_date;
TextView tv_title, tv_content;
ImageView bt_context_menu;
ImageView bt_nav;
ImageView bt_share;
ItemClickListener itemClickListener;
@ -171,53 +174,40 @@ public class GeofavoriteAdapter extends RecyclerView.Adapter<GeofavoriteAdapter.
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);
bt_share = itemView.findViewById(R.id.geofav_share_bt);
this.itemClickListener = itemClickListener;
itemView.setOnClickListener(this);
bt_context_menu.setOnClickListener(this);
bt_nav.setOnClickListener(this);
bt_share.setOnClickListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.geofav_context_menu_bt:
onOverflowIconClicked(view, getBindingAdapterPosition());
onOverflowIconClicked(view, getAdapterPosition());
break;
case R.id.geofav_nav_bt:
case R.id.geofav_share_bt:
if (itemClickListener != null)
itemClickListener.onItemNavClick(get(getBindingAdapterPosition()));
itemClickListener.onItemShareClick(get(getAdapterPosition()));
break;
default:
if (itemClickListener != null)
itemClickListener.onItemClick(get(getBindingAdapterPosition()));
itemClickListener.onItemClick(get(getAdapterPosition()));
}
}
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);
} else if (sortRule == SORT_BY_CREATED) {
Collections.sort(geofavoriteListFiltered, Geofavorite.ByLastCreated);
} else if (sortRule == SORT_BY_CATEGORY) {
Collections.sort(geofavoriteListFiltered, Geofavorite.ByCategory);
} else if (sortRule == SORT_BY_DISTANCE) {
Collections.sort(geofavoriteListFiltered, Geofavorite.ByDistance);
}
}
@ -239,9 +229,9 @@ public class GeofavoriteAdapter extends RecyclerView.Adapter<GeofavoriteAdapter.
}
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)
if (item.getItemId() == R.id.list_context_menu_detail && itemClickListener != null)
itemClickListener.onItemDetailsClick(gf);
if (item.getItemId() == R.id.list_context_menu_delete)
itemClickListener.onItemDeleteClick(gf);
return true;
}
@ -249,7 +239,7 @@ public class GeofavoriteAdapter extends RecyclerView.Adapter<GeofavoriteAdapter.
public interface ItemClickListener {
void onItemClick(Geofavorite item);
void onItemShareClick(Geofavorite item);
void onItemNavClick(Geofavorite item);
void onItemDetailsClick(Geofavorite item);
void onItemDeleteClick(Geofavorite item);
}

View File

@ -23,10 +23,8 @@ import android.content.SharedPreferences;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.AppCompatImageButton;
@ -37,7 +35,6 @@ import androidx.core.view.GravityCompat;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.ViewModelProvider;
import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
@ -51,31 +48,27 @@ 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.utils.GeoUriParser;
import it.danieleverducci.nextcloudmaps.utils.IntentGenerator;
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;
import androidx.lifecycle.Observer;
public class MainActivity extends NextcloudMapsStyledActivity implements OnSortingOrderListener {
public class MainActivity extends AppCompatActivity implements MainView, OnSortingOrderListener {
private static final int INTENT_ADD = 100;
private static final int INTENT_EDIT = 200;
private static final String TAG = "MainActivity";
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_ADD_GEOFAVORITE = "add";
private static final String NAVIGATION_KEY_SHOW_ABOUT = "about";
private static final String NAVIGATION_KEY_SWITCH_ACCOUNT = "switch_account";
@ -90,17 +83,14 @@ public class MainActivity extends NextcloudMapsStyledActivity implements OnSorti
private StaggeredGridLayoutManager layoutManager;
private FloatingActionButton fab;
private MainPresenter presenter;
private GeofavoriteAdapter geofavoriteAdapter;
private ItemClickListener rvItemClickListener;
private MainActivityViewModel mMainActivityViewModel;
private boolean isFabOpen = false;
NavigationAdapter navigationCommonAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
@ -113,20 +103,32 @@ public class MainActivity extends NextcloudMapsStyledActivity implements OnSorti
layoutManager = new StaggeredGridLayoutManager(gridViewEnabled ? 2 : 1, StaggeredGridLayoutManager.VERTICAL);
recyclerView.setLayoutManager(layoutManager);
presenter = new MainPresenter(this);
rvItemClickListener = new ItemClickListener() {
@Override
public void onItemClick(Geofavorite item) {
showGeofavoriteDetailActivity(item);
public void onItemClick(Geofavorite geofavorite) {
Intent i = new Intent();
i.setAction(Intent.ACTION_VIEW);
i.setData(geofavorite.getGeoUri());
startActivity(i);
}
@Override
public void onItemShareClick(Geofavorite item) {
startActivity(Intent.createChooser(IntentGenerator.newShareIntent(MainActivity.this, item), getString(R.string.share_via)));
Intent i = new Intent();
i.setAction(Intent.ACTION_SEND);
i.setType("text/plain");
String shareMessage = getString(R.string.share_message)
.replace("{lat}", ""+item.getLat())
.replace("{lng}", ""+item.getLng());
i.putExtra(Intent.EXTRA_TEXT, shareMessage );
startActivity(Intent.createChooser(i, getString(R.string.share_via)));
}
@Override
public void onItemNavClick(Geofavorite item) {
startActivity(IntentGenerator.newGeoUriIntent(MainActivity.this, item));
public void onItemDetailsClick(Geofavorite item) {
showGeofavoriteDetailActivity(item);
}
@Override
@ -135,52 +137,15 @@ public class MainActivity extends NextcloudMapsStyledActivity implements OnSorti
}
};
geofavoriteAdapter = new GeofavoriteAdapter(this, rvItemClickListener);
geofavoriteAdapter = new GeofavoriteAdapter(getApplicationContext(), rvItemClickListener);
recyclerView.setAdapter(geofavoriteAdapter);
geofavoriteAdapter.setSortRule(sortRule);
mMainActivityViewModel = new ViewModelProvider(this).get(MainActivityViewModel.class);
mMainActivityViewModel.init(getApplicationContext());
mMainActivityViewModel.getIsUpdating().observe(this, new Observer<Boolean>() {
@Override
public void onChanged(@Nullable Boolean aBoolean) {
if(aBoolean){
swipeRefresh.setRefreshing(true);
}
else{
swipeRefresh.setRefreshing(false);
}
}
});
mMainActivityViewModel.getOnFinished().observe(this, new Observer<Boolean>() {
@Override
public void onChanged(@Nullable Boolean success) {
if(success == null || !success){
Toast.makeText(MainActivity.this, R.string.list_geofavorite_connection_error, Toast.LENGTH_LONG).show();
}
}
});
mMainActivityViewModel.getGeofavorites().observe(this, new Observer<List<Geofavorite>>() {
@Override
public void onChanged(List<Geofavorite> geofavorites) {
geofavoriteAdapter.setGeofavoriteList(geofavorites);
}
});
mMainActivityViewModel.updateGeofavorites();
swipeRefresh = findViewById(R.id.swipe_refresh);
swipeRefresh.setOnRefreshListener(() ->
mMainActivityViewModel.updateGeofavorites());
swipeRefresh.setOnRefreshListener(() -> presenter.getGeofavorites());
fab = findViewById(R.id.open_fab);
fab.setOnClickListener(view -> openFab(!this.isFabOpen));
fab = findViewById(R.id.add_from_gps);
fab.setOnClickListener(view -> addGeofavoriteFromGps());
fab = findViewById(R.id.add_from_map);
fab.setOnClickListener(view -> addGeofavoriteFromMap());
fab = findViewById(R.id.add);
fab.setOnClickListener(view -> addGeofavorite());
toolbar = findViewById(R.id.toolbar);
homeToolbar = findViewById(R.id.home_toolbar);
@ -230,9 +195,11 @@ public class MainActivity extends NextcloudMapsStyledActivity implements OnSorti
}
@Override
protected void onPause() {
openFab(false);
super.onPause();
protected void onStart() {
super.onStart();
// Update list
presenter.getGeofavorites();
}
private void setupNavigationMenu() {
@ -240,11 +207,8 @@ public class MainActivity extends NextcloudMapsStyledActivity implements OnSorti
navigationCommonAdapter = new NavigationAdapter(this, item -> {
switch (item.id) {
case NAVIGATION_KEY_ADD_GEOFAVORITE_FROM_GPS:
addGeofavoriteFromGps();
break;
case NAVIGATION_KEY_ADD_GEOFAVORITE_FROM_MAP:
addGeofavoriteFromMap();
case NAVIGATION_KEY_ADD_GEOFAVORITE:
addGeofavorite();
break;
case NAVIGATION_KEY_SHOW_ABOUT:
show_about();
@ -255,8 +219,7 @@ public class MainActivity extends NextcloudMapsStyledActivity implements OnSorti
}
});
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_ADD_GEOFAVORITE, getString(R.string.new_geobookmark), R.drawable.ic_add));
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);
@ -281,16 +244,19 @@ public class MainActivity extends NextcloudMapsStyledActivity implements OnSorti
SortingOrderDialogFragment.newInstance(sortOrder).show(fragmentTransaction, SortingOrderDialogFragment.SORTING_ORDER_FRAGMENT);
}
private void addGeofavoriteFromGps() {
startActivity(
new Intent(this, GeofavoriteDetailActivity.class)
);
@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 addGeofavoriteFromMap() {
startActivity(
new Intent(this, MapPickerActivity.class)
);
private void addGeofavorite() {
startActivityForResult(
new Intent(this, GeofavoriteDetailActivity.class), INTENT_ADD);
}
private void show_about() {
@ -298,11 +264,39 @@ public class MainActivity extends NextcloudMapsStyledActivity implements OnSorti
}
private void switch_account() {
ApiProvider.logout();
SingleAccountHelper.setCurrentAccount(this, null);
Intent intent = new Intent(MainActivity.this, LoginActivity.class);
startActivity(intent);
finish();
}
@Override
public void showLoading() {
swipeRefresh.setRefreshing(true);
}
@Override
public void hideLoading() {
swipeRefresh.setRefreshing(false);
}
@Override
public void onGetResult(List<Geofavorite> geofavorite_list) {
geofavoriteAdapter.setGeofavoriteList(geofavorite_list);
}
@Override
public void onGeofavoriteDeleted(int id) {
// Update list
runOnUiThread(() -> {
geofavoriteAdapter.removeById(id);
Toast.makeText(MainActivity.this, R.string.list_geofavorite_deleted, Toast.LENGTH_LONG).show();
});
}
@Override
public void onErrorLoading(String message) {
Toast.makeText(MainActivity.this, R.string.list_geofavorite_connection_error, Toast.LENGTH_LONG).show();
Log.e(TAG, "Unable to obtain geofavorites list: " + message);
}
@Override
@ -322,12 +316,6 @@ public class MainActivity extends NextcloudMapsStyledActivity implements OnSorti
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;
}
}
@ -345,12 +333,13 @@ public class MainActivity extends NextcloudMapsStyledActivity implements OnSorti
private void showGeofavoriteDeteleDialog(Geofavorite item) {
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setMessage(getString(R.string.dialog_delete_message).replace("{name}", item.getName() != null ? item.getName() : ""))
builder.setMessage(getString(R.string.dialog_delete_message).replace("{name}", item.getName()))
.setTitle(R.string.dialog_delete_title)
.setPositiveButton(R.string.dialog_delete_delete, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
mMainActivityViewModel.deleteGeofavorite(item);
presenter.deleteGeofavorite(item.getId());
dialog.dismiss();
// Callback is onGeofavoriteDeleted
}
})
.setNegativeButton(R.string.dialog_delete_cancel, new DialogInterface.OnClickListener() {
@ -364,27 +353,8 @@ public class MainActivity extends NextcloudMapsStyledActivity implements OnSorti
private void showGeofavoriteDetailActivity(Geofavorite item) {
Intent i = new Intent(this, GeofavoriteDetailActivity.class);
i.putExtra(GeofavoriteDetailActivity.ARG_GEOFAVORITE, item.getId());
i.putExtra(GeofavoriteDetailActivity.ARG_GEOFAVORITE, item);
startActivity(i);
}
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);
}
}
}

View File

@ -1,42 +0,0 @@
package it.danieleverducci.nextcloudmaps.activity.main;
import android.content.Context;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import java.util.List;
import it.danieleverducci.nextcloudmaps.model.Geofavorite;
import it.danieleverducci.nextcloudmaps.repository.GeofavoriteRepository;
public class MainActivityViewModel 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 void deleteGeofavorite(Geofavorite geofav) {
mRepo.deleteGeofavorite(geofav);
}
public LiveData<Boolean> getIsUpdating(){
return mRepo.isUpdating();
}
public LiveData<Boolean> getOnFinished(){
return mRepo.onFinished();
}
}

View File

@ -0,0 +1,89 @@
/*
* 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());
} else {
((AppCompatActivity) view).runOnUiThread(() -> {
view.hideLoading();
view.onErrorLoading(response.raw().message());
});
}
});
}
@Override
public void onFailure(@NonNull Call<List<Geofavorite>> call, @NonNull Throwable t) {
((AppCompatActivity) view).runOnUiThread(() -> {
view.hideLoading();
view.onErrorLoading(t.getLocalizedMessage());
});
}
});
}
public void deleteGeofavorite(int id) {
view.showLoading();
Call<Geofavorite> call = ApiProvider.getAPI().deleteGeofavorite(id);
call.enqueue(new Callback<Geofavorite>() {
@Override
public void onResponse(Call<Geofavorite> call, Response<Geofavorite> response) {
view.hideLoading();
view.onGeofavoriteDeleted(id);
}
@Override
public void onFailure(Call<Geofavorite> call, Throwable t) {
view.hideLoading();
view.onErrorLoading(t.getLocalizedMessage());
}
});
}
}

View File

@ -0,0 +1,33 @@
/*
* 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 onGeofavoriteDeleted(int id);
void onErrorLoading(String message);
}

View File

@ -80,12 +80,15 @@ 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);
}

View File

@ -32,7 +32,6 @@ 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;
@ -92,14 +91,13 @@ public class SortingOrderDialogFragment extends DialogFragment {
/**
* 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[8];
mTaggedViews = new View[4];
mTaggedViews[0] = view.findViewById(R.id.sortByTitleAscending);
mTaggedViews[0].setTag(GeofavoriteAdapter.SORT_BY_TITLE);
mTaggedViews[1] = view.findViewById(R.id.sortByTitleAscendingText);
@ -108,14 +106,6 @@ 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();
}
@ -131,10 +121,10 @@ public class SortingOrderDialogFragment extends DialogFragment {
if (view instanceof ImageButton) {
Drawable normalDrawable = ((ImageButton) view).getDrawable();
Drawable wrapDrawable = DrawableCompat.wrap(normalDrawable);
DrawableCompat.setTint(wrapDrawable, ContextCompat.getColor(getContext(), R.color.selector_item_selected));
DrawableCompat.setTint(wrapDrawable, this.getResources().getColor(R.color.defaultTint));
}
if (view instanceof TextView) {
((TextView)view).setTextColor(ContextCompat.getColor(getContext(), R.color.selector_item_selected));
((TextView)view).setTextColor(this.getResources().getColor(R.color.defaultTint));
((TextView)view).setTypeface(Typeface.DEFAULT_BOLD);
}
}

View File

@ -1,223 +0,0 @@
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();
}
}

View File

@ -24,7 +24,6 @@ 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;
@ -36,27 +35,47 @@ import com.nextcloud.android.sso.model.SingleSignOnAccount;
import retrofit2.NextcloudRetrofitApiBuilder;
public class ApiProvider {
private static final String TAG = ApiProvider.class.getCanonicalName();
private final String TAG = ApiProvider.class.getCanonicalName();
@NonNull
protected Context context;
protected static API mApi;
@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());
protected static String ssoAccountName;
mApi = new NextcloudRetrofitApiBuilder(nextcloudAPI, API.mApiEndpoint).create(API.class);
} catch (NextcloudFilesAppAccountNotFoundException | NoCurrentAccountSelectedException e) {
Log.d(TAG, "setAccout() called with: ex = [" + e + "]");
public ApiProvider(Context context) {
this.context = context;
initSsoApi(new NextcloudAPI.ApiConnectedListener() {
@Override
public void onConnected() {
Log.d(TAG, "Connected to Nextcloud instance");
}
}
@Override
public void onError(Exception ex) {
Log.d(TAG, "Unable to connect to Nextcloud instance: " + ex.toString());
}
});
}
public void initSsoApi(final NextcloudAPI.ApiConnectedListener callback) {
try {
SingleSignOnAccount ssoAccount = SingleAccountHelper.getCurrentSingleSignOnAccount(context);
NextcloudAPI nextcloudAPI = new NextcloudAPI(context, ssoAccount, new GsonBuilder().create(), callback);
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 void logout() {
mApi = null;
public static String getAccountName() {
return ssoAccountName;
}
}

View File

@ -20,28 +20,23 @@
package it.danieleverducci.nextcloudmaps.model;
import android.graphics.Color;
import android.net.Uri;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.BaseObservable;
import androidx.databinding.Bindable;
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.Comparator;
import it.danieleverducci.nextcloudmaps.utils.GeoUriParser;
import java.util.Date;
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:
* {
@ -107,17 +102,10 @@ public class Geofavorite implements Serializable {
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;
}
@ -151,73 +139,18 @@ public class Geofavorite implements Serializable {
this.comment = comment;
}
/**
* 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> ByTitleAZ = (note, t1) -> note.name.compareTo(t1.name);
public static Comparator<Geofavorite> ByLastCreated = (note, t1) -> t1.id - note.id;
public String getCoordinatesString() {
return this.lat + " N, " + this.lng + " E";
}
public Uri getGeoUri() {
return GeoUriParser.createGeoUri(this.lat, this.lng, this.name);
}
public Uri getGmapsUri() {
return GeoUriParser.createGmapsUri(this.lat, this.lng);
return Uri.parse("geo:" + this.lat + "," + this.lng + "(" + this.name + ")");
}
public boolean valid() {
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() {
// If category is default, return null: will be used Nextcloud's accent
if (this.category == null || this.category.equals(DEFAULT_CATEGORY) || this.category.length() == 0)
return 0;
float letter1Index = this.category.toLowerCase().charAt(0);
float letter2Index = this.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) });
}
public String categoryLetter() {
if (category == null || category.length() == 0 || category.equals(DEFAULT_CATEGORY))
return "\u2022";
return category.substring(0,1);
return getLat() != 0 && getLng() != 0 && getName() != null && getName().length() > 0;
}
@NonNull
@ -225,5 +158,4 @@ public class Geofavorite implements Serializable {
public String toString() {
return "[" + getName() + " (" + getLat() + "," + getLng() + ")]";
}
}

View File

@ -1,172 +0,0 @@
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 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);
}
}

View File

@ -1,88 +0,0 @@
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;
}
}

View File

@ -1,38 +0,0 @@
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("{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;
}
}
}

View File

@ -1,36 +0,0 @@
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;
}
}
}

View File

@ -1,44 +0,0 @@
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);
}
}

View File

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:left="8dp"
android:right="8dp"
android:top="8dp"
android:bottom="8dp">
<shape
android:shape="oval">
<solid android:color="@color/defaultBrandAlpha"/>
</shape>
</item>
</layer-list>

View File

@ -1,10 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M20.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>

View File

@ -1,10 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="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>

View File

@ -1,5 +0,0 @@
<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>

View File

@ -1,5 +0,0 @@
<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>

View File

@ -1,16 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="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>

View File

@ -1,10 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M21,3L3,10.53v0.98l6.84,2.65L12.48,21h0.98L21,3z"/>
</vector>

View File

@ -1,5 +0,0 @@
<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>

View File

@ -1,5 +0,0 @@
<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>

View File

@ -1,16 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M13.95,13H9V8.05l5.61,-5.61C13.78,2.16 12.9,2 12,2c-4.2,0 -8,3.22 -8,8.2c0,3.32 2.67,7.25 8,11.8c5.33,-4.55 8,-8.48 8,-11.8c0,-1.01 -0.16,-1.94 -0.45,-2.8L13.95,13z"/>
<path
android:fillColor="@android:color/white"
android:pathData="M11,11l2.12,0l6.16,-6.16l-2.12,-2.12l-6.16,6.16z"/>
<path
android:fillColor="@android:color/white"
android:pathData="M20.71,2L20,1.29C19.8,1.1 19.55,1 19.29,1c-0.13,0 -0.48,0.07 -0.71,0.29l-0.72,0.72l2.12,2.12l0.72,-0.72C21.1,3.02 21.1,2.39 20.71,2z"/>
</vector>

View File

@ -1,5 +0,0 @@
<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>

View File

@ -1,10 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>
</vector>

View File

@ -1,9 +0,0 @@
<?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>

View File

@ -115,20 +115,6 @@
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>

View File

@ -2,32 +2,23 @@
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.coordinatorlayout.widget.CoordinatorLayout
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/root">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
<ScrollView
android:layout_width="match_parent"
android:layout_height="250dp"
android:fitsSystemWindows="true">
android:layout_height="match_parent">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@+id/collapsing_toolbar"
<LinearLayout
android:orientation="vertical"
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">
android:layout_height="wrap_content">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_collapseMode="parallax">
android:layout_height="200dp">
<org.osmdroid.views.MapView
android:id="@+id/map"
android:layout_width="match_parent"
@ -40,237 +31,121 @@
android:layout_height="match_parent"/>
</FrameLayout>
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?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="?attr/actionBarSize"
android:layout_height="?attr/actionBarSize"
android:layout_gravity="start"
android:padding="16dp"
android:src="@drawable/ic_back_grey"
app:tint="@color/white"
android:background="@drawable/floating_semitransparent_button_background"/>
<!-- Manual position button -->
<ImageView
android:id="@+id/manual_pos_bt"
android:layout_width="?attr/actionBarSize"
android:layout_height="?attr/actionBarSize"
android:layout_gravity="end"
android:padding="16dp"
android:src="@drawable/ic_manual_pos"
app:tint="@color/white"
android:background="@drawable/floating_semitransparent_button_background"
android:visibility="gone"/> <!-- TODO: Implement edit -->
</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"
android:layout_height="match_parent"
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"/>
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="20dp">
<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"
<EditText
android:id="@+id/name_et"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:padding="10dp"
android:src="@drawable/ic_share"
app:tint="@color/defaultBrand"/>
android:ems="10"
android:hint="@string/name"
android:lines="1"
android:maxLines="1"
android:singleLine="true"
android:ellipsize="end"/>
<ImageView
android:id="@+id/action_icon_nav"
android:layout_width="0dp"
<EditText
android:id="@+id/description_et"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:padding="10dp"
android:src="@drawable/ic_nav"
app:tint="@color/defaultBrand"/>
android:ems="10"
android:gravity="start|top"
android:inputType="textMultiLine"
android:lines="5"
android:maxLines="10"
android:hint="@string/description"
android:ellipsize="end" />
<ImageView
android:id="@+id/action_icon_delete"
android:layout_width="0dp"
<TextView
android:layout_width="match_parent"
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 -->
android:layout_marginTop="20dp"
android:textStyle="bold"
android:text="@string/created" />
<TextView
android:id="@+id/created_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
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"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:textStyle="bold"
android:text="@string/category" />
<TextView
android:id="@+id/category_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAlignment="textEnd" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="@string/coords"
android:textStyle="bold" />
<TextView
android:id="@+id/coords_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAlignment="textEnd" />
<TextView
android:id="@+id/accuracy_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:textAlignment="center"
android:text="@string/accuracy"
android:textColor="@android:color/white"
android:background="@android:color/darker_gray"/>
<Button
android:id="@+id/submit_bt"
style="@style/Widget.AppCompat.Button.Colored"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_margin="50dp"
android:text="@string/confirm"
app:backgroundTint="@color/defaultBrand"
android:onClick="onSubmit"/>
</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: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:maxLines="10"
android:background="@android:color/transparent"
android:drawableLeft="@drawable/ic_edit"
android:drawablePadding="5dp"
android:drawableTint="@color/defaultBrand"
android:hint="@string/description"/>
<AutoCompleteTextView
android:id="@+id/category_at"
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:maxLines="10"
android:background="@android:color/transparent"
android:drawableLeft="@drawable/ic_category_asc"
android:drawablePadding="5dp"
android:drawableTint="@color/defaultBrand"
android:hint="@string/category"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="40dp"
android:textStyle="bold"
android:text="@string/created" />
<TextView
android:id="@+id/created_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
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"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="@string/coords"
android:textStyle="bold" />
<TextView
android:id="@+id/coords_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAlignment="textEnd" />
<ProgressBar
android:id="@+id/progress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:indeterminate="true"
android:visibility="gone"/>
<Button
android:id="@+id/submit_bt"
style="@style/Widget.AppCompat.Button.Colored"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_margin="50dp"
android:text="@string/confirm"
app:backgroundTint="@color/defaultBrand"
android:onClick="onSubmit"/>
</LinearLayout>
</ScrollView>
</androidx.core.widget.NestedScrollView>
<!-- Back button -->
<ImageView
android:id="@+id/back_bt"
android:layout_width="48dp"
android:layout_height="48dp"
android:padding="12dp"
android:src="@drawable/ic_back_grey"
app:tint="@color/white"
android:background="@color/defaultBrandAlpha"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</FrameLayout>
</layout>

View File

@ -21,6 +21,7 @@
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">
@ -64,6 +65,7 @@
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">
@ -75,15 +77,14 @@
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/menu_button"
android:layout_width="?attr/actionBarSize"
android:layout_height="?attr/actionBarSize"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?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:tint="?attr/colorAccent"
android:src="@drawable/ic_menu_grey"/>
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/search_text"
@ -105,11 +106,8 @@
android:layout_gravity="center_vertical|end"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/list_mode"
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:padding="@dimen/spacer_2x"
android:tint="?attr/colorAccent"
android:translationX="@dimen/spacer_1x"
android:src="@drawable/ic_alphabetical_asc" />
<androidx.appcompat.widget.AppCompatImageView
@ -119,13 +117,10 @@
android:layout_gravity="center_vertical|end"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/list_mode"
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:padding="@dimen/spacer_2x"
android:tint="?attr/colorAccent"
android:translationX="@dimen/spacer_1x"
android:src="@drawable/ic_view_module"
android:visibility="gone"/> <!-- TODO: Replace with Map View icon -->
android:src="@drawable/ic_view_module" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
@ -159,31 +154,8 @@
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<!-- Add from map FAB -->
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/add_from_map"
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_map"
app:backgroundTint="@color/defaultBrand"/>
<!-- Add from current position FAB -->
<com.google.android.material.floatingactionbutton.FloatingActionButton
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"/>
<!-- Main FAB -->
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/open_fab"
android:id="@+id/add"
android:layout_margin="16dp"
android:layout_gravity="bottom|end"
android:layout_width="wrap_content"

View File

@ -46,7 +46,8 @@
android:layout_centerVertical="true"
android:layout_marginBottom="48dp"
android:textSize="24sp"
android:gravity="center_horizontal"/>
android:gravity="center_horizontal"
android:textColor="@color/accent"/>
<Button
android:id="@+id/chose_button"

View File

@ -1,104 +0,0 @@
<?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"/>
<ImageView
android:id="@+id/back_bt"
android:layout_width="?attr/actionBarSize"
android:layout_height="?attr/actionBarSize"
android:layout_gravity="start"
android:padding="16dp"
android:src="@drawable/ic_back_grey"
app:tint="@color/white"
android:background="@drawable/floating_semitransparent_button_background"/>
<ImageView
android:id="@+id/ok_bt"
android:layout_width="?attr/actionBarSize"
android:layout_height="?attr/actionBarSize"
android:layout_gravity="end"
android:padding="16dp"
android:src="@drawable/ic_ok"
app:tint="@color/white"
android:background="@drawable/floating_semitransparent_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>

View File

@ -19,102 +19,69 @@
<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:padding="12dp"
android:clickable="true"
android:focusable="true">
<TextView
android:id="@+id/tv_category"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_marginStart="4dp"
<ImageView
android:layout_width="wrap_content"
android:layout_height="match_parent"
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" />
android:src="@mipmap/ic_launcher"/>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="10dp"
android:layout_weight="1">
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="@dimen/note_font_size_item_title"
android:textStyle="bold"
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"
android:ellipsize="end"
tools:text="@tools:sample/lorem/random">
</TextView>
<LinearLayout
<TextView
android:id="@+id/content"
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>
android:layout_marginTop="5dp"
android:textSize="@dimen/note_font_size_item_content"
android:maxLines="2"
android:gravity="center_vertical"
tools:text="@tools:sample/lorem/random">
</TextView>
</LinearLayout>
<ImageView
android:id="@+id/geofav_nav_bt"
android:id="@+id/geofav_share_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 -->
android:padding="10dp"
android:src="@drawable/ic_share"
android:tint="@color/list_text" /> <!-- 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:padding="10dp"
android:src="@drawable/ic_more"
app:tint="@color/secondary_text_color" /> <!-- TODO: app:tint is not working -->
android:tint="@color/list_text" /> <!-- TODO: app:tint is not working -->
</LinearLayout>

View File

@ -18,7 +18,6 @@
<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"
@ -34,8 +33,7 @@
android:contentDescription="@null"
android:focusable="false"
android:scaleType="center"
android:src="@drawable/ic_time_grey"
app:tint="@color/text_color"/>
android:src="@drawable/ic_time_grey"/>
<TextView
android:id="@+id/navigationItemLabel"

View File

@ -0,0 +1,22 @@
<?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>

View File

@ -0,0 +1,22 @@
<?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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -16,9 +16,8 @@
~ 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"
android:id="@+id/root"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minWidth="300dp"
@ -58,8 +57,7 @@
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"
app:tint="@color/selector_item_unselected"/>
android:src="@drawable/ic_alphabetical_asc" />
<TextView
android:id="@+id/sortByTitleAscendingText"
@ -74,8 +72,7 @@
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:textColor="@color/text_color"/>
android:textSize="@dimen/two_line_primary_text_size"/>
</TableRow>
@ -95,8 +92,7 @@
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"
app:tint="@color/selector_item_unselected"/>
android:src="@drawable/ic_modification_asc"/>
<TextView
android:id="@+id/sortByCreationDateDescendingText"
@ -111,83 +107,7 @@
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: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" />
android:textSize="@dimen/two_line_primary_text_size" />
</TableRow>
@ -205,9 +125,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/common_cancel"
android:textAllCaps="false"
android:layout_marginTop="@dimen/standard_half_margin"
style="@style/Widget.AppCompat.Button.ButtonBar.AlertDialog"/>
android:layout_marginTop="@dimen/standard_half_margin"/>
</LinearLayout>

View File

@ -1,10 +1,10 @@
<?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 -->
<!-- Detail button -->
<item
android:id="@+id/list_context_menu_share"
android:title="@string/list_context_menu_share"/>
android:id="@+id/list_context_menu_detail"
android:title="@string/list_context_menu_detail"/>
<!-- Delete button -->
<item
android:id="@+id/list_context_menu_delete"

View File

@ -1,104 +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/>.
-->
<resources>
<!-- App strings -->
<string name="app_name">Maps Geofavorites</string>
<string name="welcome">Benvenuto su Nextcloud Maps Geofavorites</string>
<!-- Login Activity -->
<string name="choose_account">Scegli account</string>
<!-- Geofavorites list -->
<string name="new_geobookmark">Nuovo geosegnalibro</string>
<string name="about">Informazioni</string>
<string name="switch_account">Cambia account</string>
<string name="list_mode">Lista</string>
<string name="search_in_all">Cerca per nome</string>
<string name="share_via">Condividi via</string>
<string name="share_message">Posizione condivisa: {lat}°N, {lng}°E https://www.openstreetmap.org/#map=17/{lat}/{lng}</string>
<string name="list_context_menu_share">Condividi</string>
<string name="list_context_menu_delete">Elimina</string>
<string name="dialog_delete_title">Elimina geosegnalibro</string>
<string name="dialog_delete_message">Stai per eliminare il geosegnalibro {name}. Procedere?</string>
<string name="dialog_delete_delete">Elimina</string>
<string name="dialog_delete_cancel">Mantieni</string>
<string name="list_geofavorite_deleted">Geosegnalibro eliminato</string>
<string name="list_geofavorite_connection_error">Impossibile ottenere la lista dei geosegnalibri</string>
<!-- Sort dialog -->
<string name="sort_by">Ordina per</string>
<string name="menu_item_sort_by_title_a_z">A - Z</string>
<string name="menu_item_sort_by_date_newest_first">Più recenti</string>
<string name="menu_item_sort_by_category_a_z">Categoria</string>
<string name="menu_item_sort_by_distance_nearest_first">Distanza</string>
<!-- Geofavorites detail -->
<string name="name">Nome</string>
<string name="description">Descrizione</string>
<string name="created">Creato</string>
<string name="modified">Modificato</string>
<string name="category">Categoria</string>
<string name="coords">Coordinate</string>
<string name="accuracy">Accuratezza: {accuracy} m</string>
<string name="accuracy_nosignal">Nessun segnale GPS!</string>
<string name="location_permission_required">Per creare un geosegnalibro è necessario consentire l\'accesso alla posizione.</string>
<string name="confirm">Salva</string>
<string name="error_saving_geofavorite">Impossibile salvare il geosegnalibro</string>
<string name="error_unsupported_uri">Impossibile ottenere le coordinate dai dati ricevuti</string>
<string name="geofavorite_saved">Geosegnalibro salvato</string>
<string name="incomplete_geofavorite">Geosegnalibro incompleto: nome e categoria sono obbligatori</string>
<!-- Map picker activity -->
<string name="coordinates_parse_error">Le coordinate dovrebbero essere nel formato xx.xxxxxx</string>
<string name="coordinates_invalid_error">Coordinate non valide</string>
<string name="coordinates_north">°N</string>
<string name="coordinates_east">°E</string>
<!-- About -->
<string name="about_version_title">Versione</string>
<string name="about_version">Stai usando la versione &lt;strong>%1$s&lt;/strong></string>
<string name="about_source_title">Codice sorgente</string>
<string name="about_source">Questo progetto è disponibile su GitHub: &lt;a href="%1$s">%1$s&lt;/a></string>
<string name="about_issues_title">Segnalazioni</string>
<string name="about_issues">Puoi segnalare bugs, proporre migliorie o richiedere nuove funzionalità sul GitHub issue tracker: &lt;a href="%1$s">%1$s&lt;/a></string>
<string name="about_maps_title">Mappe</string>
<string name="about_maps">Questa app usa i server e le tiles di OpenStreetMap. Non accetto donazioni per questa app, ma raccomando piuttosto di donare a &lt;a href="%1$s">OpenStreetMap&lt;/a>, poiché questa applicazione non potrebbe esistere senza di loro.</string>
<string name="about_app_license_title">Licenza</string>
<string name="about_app_license">Questa applicazione è rilasciata sotto licenza GNU GENERAL PUBLIC LICENSE v3+.</string>
<string name="about_app_license_button">Mostra licenza</string>
<!-- Common strings -->
<string name="common_yes">Si</string>
<string name="common_cancel">Annulla</string>
<!-- URLs -->
<string name="url_source" translatable="false">https://github.com/penguin86/nextcloud-maps-client</string>
<string name="url_issues" translatable="false">https://github.com/penguin86/nextcloud-maps-client/issues</string>
<string name="url_license" translatable="false">https://raw.githubusercontent.com/penguin86/nextcloud-maps-client/master/LICENSE</string>
<string name="url_maps" translatable="false">https://donate.openstreetmap.org</string>
<!-- Settings -->
<string name="setting_sort_by">SETTING_SORT_BY</string>
<string name="setting_grid_view_enabled">SETTING_GRID_VIEW_ENABLED</string>
<!-- Menu -->
<string name="new_geobookmark_gps">Crea dalla posizione corrente</string>
<string name="new_geobookmark_map">Crea dalla mappa</string>
</resources>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="text_color">#eee</color>
<color name="disabled">#888</color>
<color name="defaultBackground">#000</color>
</resources>

View File

@ -19,20 +19,15 @@
<resources>
<!-- Generic Colors -->
<color name="primary">#ffffff</color>
<color name="accent">#121212</color>
<color name="transparent">#00000000</color>
<color name="defaultBrand">#0082C9</color>
<color name="defaultBrandAlpha">#550082C9</color>
<color name="disabled">#666</color>
<color name="systemBar">@color/defaultBackground</color>
<color name="defaultBackground">#fff</color>
<color name="appbar">@android:color/white</color>
<color name="defaultTint">#202124</color>
<!-- List Colors -->
<color name="text_color">#333</color>
<color name="secondary_text_color">@color/disabled</color>
<!-- Selectors text/tint colors -->
<color name="selector_item_selected">@color/defaultBrand</color>
<color name="selector_item_unselected">@color/disabled</color>
<color name="list_text">#aaa</color>
<!-- Generic Colors -->
<color name="white">#fff</color>

View File

@ -28,15 +28,14 @@
<dimen name="drawer_header_logo_size">42dp</dimen>
<!-- Font Sizes -->
<dimen name="note_font_size_item_title">18sp</dimen>
<dimen name="note_font_size_item_content">14sp</dimen>
<dimen name="zero">0dp</dimen>
<dimen name="standard_margin">16dp</dimen>
<dimen name="standard_half_margin">8dp</dimen>
<dimen name="standard_padding">16dp</dimen>
<dimen name="standard_half_padding">8dp</dimen>
<dimen name="two_line_primary_text_size">16sp</dimen>
<dimen name="two_line_secondary_text_size">12sp</dimen>
<!-- FAB dimensions -->
<dimen name="fab_vertical_offset">75dp</dimen>
</resources>

View File

@ -17,24 +17,24 @@
<resources>
<!-- App strings -->
<string name="app_name">Maps Geofavorites</string>
<string name="welcome">Welcome to Nextcloud Maps Geofavorites</string>
<string name="app_name">Maps Geobookmarks</string>
<string name="welcome">Welcome to Nextcloud Maps Geobookmarks</string>
<!-- Login Activity -->
<string name="choose_account">Choose account</string>
<!-- Geofavorites list -->
<string name="new_geobookmark">New geofavorite</string>
<!-- Geobookmarks list -->
<string name="new_geobookmark">New geobookmark</string>
<string name="about">About</string>
<string name="switch_account">Switch account</string>
<string name="list_mode">List</string>
<string name="search_in_all">Search by name</string>
<string name="share_via">Share via</string>
<string name="share_message">Check out this place: {lat}°N, {lng}°E https://www.openstreetmap.org/#map=17/{lat}/{lng}</string>
<string name="list_context_menu_share">Share</string>
<string name="list_context_menu_detail">Details</string>
<string name="list_context_menu_delete">Delete</string>
<string name="dialog_delete_title">Delete geofavorite</string>
<string name="dialog_delete_message">You are about to delete geofavorite {name}. Proceed?</string>
<string name="dialog_delete_title">Delete geobookmark</string>
<string name="dialog_delete_message">You are about to delete geobookmark {name}. Proceed?</string>
<string name="dialog_delete_delete">Delete</string>
<string name="dialog_delete_cancel">Maintain</string>
<string name="list_geofavorite_deleted">Geofavorite deleted</string>
@ -44,10 +44,9 @@
<string name="sort_by">Sort by</string>
<string name="menu_item_sort_by_title_a_z">A - Z</string>
<string name="menu_item_sort_by_date_newest_first">Newest first</string>
<string name="menu_item_sort_by_category_a_z">Category</string>
<string name="menu_item_sort_by_distance_nearest_first">Distance</string>
<string name="menu_item_sort_by_distance">Distance</string>
<!-- Geofavorites detail -->
<!-- Geobookmarks detail -->
<string name="name">Name</string>
<string name="description">Description</string>
<string name="created">Created</string>
@ -55,19 +54,10 @@
<string name="category">Category</string>
<string name="coords">Coordinates</string>
<string name="accuracy">Accuracy: {accuracy} m</string>
<string name="accuracy_nosignal">No GPS signal!</string>
<string name="location_permission_required">Location permission is required to create a geofavorite.</string>
<string name="confirm">Save</string>
<string name="error_saving_geofavorite">Unable to save geofavorite</string>
<string name="error_unsupported_uri">Unable to obtain coordinates from shared data</string>
<string name="geofavorite_saved">Geofavorite saved</string>
<string name="incomplete_geofavorite">Incomplete geofavorite: Name and category are mandatory</string>
<!-- Map picker activity -->
<string name="coordinates_parse_error">Coordinates should be in format xx.xxxxxx</string>
<string name="coordinates_invalid_error">Invalid coordinates</string>
<string name="coordinates_north">°N</string>
<string name="coordinates_east">°E</string>
<string name="incomplete_geofavorite">Incomplete geofavorite: Name and GPS coordinates are mandatory</string>
<!-- About -->
<string name="about_version_title">Version</string>
@ -76,8 +66,6 @@
<string name="about_source">This project is hosted on GitHub: &lt;a href="%1$s">%1$s&lt;/a></string>
<string name="about_issues_title">Issues</string>
<string name="about_issues">You can report bugs, enhancement proposals and feature requests at the GitHub issue tracker: &lt;a href="%1$s">%1$s&lt;/a></string>
<string name="about_maps_title">Maps</string>
<string name="about_maps">This app uses Open Street Maps tiles and servers to display the map. I do not accept any donation for this app, but strongly encourage to make any donation to &lt;a href="%1$s">OpenStreetMap&lt;/a>, as this application could not exist without them.</string>
<string name="about_app_license_title">App license</string>
<string name="about_app_license">This application is licensed under the GNU GENERAL PUBLIC LICENSE v3+.</string>
<string name="about_app_license_button">View license</string>
@ -88,16 +76,11 @@
<!-- URLs -->
<string name="url_source" translatable="false">https://github.com/penguin86/nextcloud-maps-client</string>
<string name="url_issues" translatable="false">https://github.com/penguin86/nextcloud-maps-client/issues</string>
<string name="url_issues" translatable="false">https://github.com/penguin86/nextcloud-maps-client/issues/new/choose</string>
<string name="url_license" translatable="false">https://raw.githubusercontent.com/penguin86/nextcloud-maps-client/master/LICENSE</string>
<string name="url_maps" translatable="false">https://donate.openstreetmap.org</string>
<!-- Settings -->
<string name="setting_sort_by">SETTING_SORT_BY</string>
<string name="setting_grid_view_enabled">SETTING_GRID_VIEW_ENABLED</string>
<!-- Menu -->
<string name="new_geobookmark_gps">New from current position</string>
<string name="new_geobookmark_map">New from map</string>
</resources>

View File

@ -18,34 +18,34 @@
<resources>
<!-- Base application theme. -->
<style name="BaseTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
<item name="colorAccent">@color/defaultBrand</item>
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/primary</item>
<item name="colorPrimaryDark">@color/primary</item>
<item name="colorAccent">@color/accent</item>
<item name="android:actionModeBackground">?attr/colorPrimary</item>
<item name="colorControlNormal">?attr/colorAccent</item>
<item name="windowActionModeOverlay">true</item>
<!-- App bar -->
<item name="colorPrimary">@color/systemBar</item>
<!-- Generic background -->
<item name="android:windowBackground">@color/defaultBackground</item>
<item name="toolbarStyle">@style/toolbarStyle</item>
</style>
<style name="AppTheme" parent="BaseTheme"/>
<style name="SplashTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
<style name="EditorTheme" parent="Theme.AppCompat.DayNight.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@android:color/transparent</item>
<item name="colorPrimaryDark">@color/accent</item>
<item name="colorAccent">@color/accent</item>
</style>
<style name="SplashTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="android:windowFullscreen">true</item>
</style>
<!-- Geofavorite detail text appearances -->
<style name="TextAppearance.GeofavoriteDetail.Header.Expanded" parent="TextAppearance.Design.CollapsingToolbar.Expanded">
<item name="android:textSize">36sp</item>
<item name="android:textColor">@color/defaultBrand</item>
</style>
<style name="TextAppearance.GeofavoriteDetail.Header.Collapsed" parent="TextAppearance.AppCompat.Title">
<item name="android:textColor">@color/white</item>
<style name="toolbarStyle" parent="@style/Widget.AppCompat.Toolbar">
<item name="android:background">?attr/colorPrimary</item>
</style>
</resources>

View File

@ -25,7 +25,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.1.1'
classpath 'com.android.tools.build:gradle:4.2.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files

View File

@ -1 +1 @@
First alpha release: working geobookmarks list, open geobookmarks in other apps via geo link, add geofavorite on current location.
First alpha release: working geobookmarks list, open geobookmarks in other apps via geo link, add geobookmark on current location.

View File

@ -1 +0,0 @@
Added geofavorite detail and deletion

View File

@ -1 +0,0 @@
Added map in geofavorite detail

View File

@ -1 +0,0 @@
First categories implementation

View File

@ -1 +0,0 @@
Fixed bug when opening newly-created element

View File

@ -1,5 +0,0 @@
Add geofavorite from map or coordinates
Night mode (dark theme)
New, less ambiguous, category icons (the old circle with letter inside is conventionally associated with user accounts)
Graphic fixes
Italian translation

View File

@ -1,4 +0,0 @@
Fix crash when using corrupted dataset with null category (It may happen on imports from Google Maps)
Reimplemented API Provider to try to fix NullPointerException on mApi reported in Play Store
Open Google Maps on Google devices
Support share from: google maps urls, OpenStreetMap urls (only if containing coordinates)

View File

@ -1,9 +1,3 @@
UNOFFICIAL and FOSS Nextcloud Maps client at its earliest stages of developement. Shows your Nextcloud Maps geofavorites list.
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.
Shows 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.
Requires Maps app to be installed on the Nextcloud instance.
As per Nextcloud's guidelines, the login is implemented using Nextcloud's Single Sign On module and thus requires Nextcloud app installed.
Promo banner by Gasteaud, Public domain, via Wikimedia Commons.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 118 KiB

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

View File

@ -1 +1 @@
Manage Nextcloud Maps Geofavorites on your phone
Manage Nextcloud Maps Geobookmarks on your Android phone

View File

@ -1 +1 @@
Nextcloud Maps Geofavorites
Nextcloud Maps Geobookmarks

View File

@ -1,3 +0,0 @@
Client per Nextcloud Maps non ufficiale e FOSS al suo stato iniziale di sviluppo.
Mostra la lista dei geosegnalibri di Nextcloud Maps e permette di aprirli in qualunque app supporti i "geo:" urls (i.e. Google Maps, Organic Maps etc...).
È possibile creare nuovi geosegnalibri nella posizione GPS attuale, scegliendo la posizione dalla mappa o condividendola da altre app sotto forma di "geo:" uri.

View File

@ -1 +0,0 @@
Un semplice client per Nextcloud Maps

View File

@ -1 +0,0 @@
Nextcloud Maps Geofavorites

View File

@ -1,6 +1,23 @@
#Sun Feb 20 08:50:37 CET 2022
#
# Nextcloud 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/>.
#
#Mon Oct 05 23:27:18 ART 2020
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB