mirror of
https://github.com/oliexdev/openScale.git
synced 2025-08-28 18:49:56 +02:00
Add a ContentProvider so other apps can access (with permission) the openScale data.
This could be used for e.g. syncing weight logs to a third-party service such as Google Fit, without the openScale app having to do so; with this, a 'connector' app could be written that reads this data and does the push to a third-party service (or, indeed, an HTTP RPC connector, an MQTT connector, SMS notifications, or anything else) without openScale needing this functionality (or additional permissions).
This commit is contained in:
@@ -9,6 +9,15 @@
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
|
||||
|
||||
<!-- Permission to allow read of data from the database through a ContentProvider.
|
||||
Marked "dangerous" so that explicit user approval is required to read this data, not
|
||||
just the permission implied from installing the app from the Play Store. -->
|
||||
<permission
|
||||
android:name="com.health.openscale.READ_DATA"
|
||||
android:description="@string/permission_read_data_description"
|
||||
android:label="@string/permission_read_data_label"
|
||||
android:protectionLevel="dangerous" />
|
||||
|
||||
<uses-feature android:name="android.hardware.bluetooth_le" android:required="false"/>
|
||||
|
||||
<application
|
||||
@@ -59,6 +68,13 @@
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/file_provider_paths" />
|
||||
</provider>
|
||||
<provider
|
||||
android:name=".core.export.OpenScaleContentProvider"
|
||||
android:authorities="com.health.openscale.provider"
|
||||
android:enabled="true"
|
||||
android:exported="true"
|
||||
android:readPermission="com.health.openscale.READ_DATA">
|
||||
</provider>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
@@ -606,4 +606,19 @@ public class OpenScale {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// As getScaleUserList(), but as a Cursor for export via a Content Provider.
|
||||
public Cursor getScaleUserListCursor() {
|
||||
return userDAO.selectAll();
|
||||
}
|
||||
|
||||
// As getScaleUser(), but as a Cursor for export via a Content Provider.
|
||||
public Cursor getScaleUserCursor(int userId) {
|
||||
return userDAO.select(userId);
|
||||
}
|
||||
|
||||
// As getScaleMeasurementList(), but as a Cursor for export via a Content Provider.
|
||||
public Cursor getScaleMeasurementListCursor(int userId) {
|
||||
return measurementDAO.selectAll(userId);
|
||||
}
|
||||
}
|
||||
|
@@ -21,6 +21,7 @@ import android.arch.persistence.room.Insert;
|
||||
import android.arch.persistence.room.OnConflictStrategy;
|
||||
import android.arch.persistence.room.Query;
|
||||
import android.arch.persistence.room.Update;
|
||||
import android.database.Cursor;
|
||||
|
||||
import com.health.openscale.core.datatypes.ScaleMeasurement;
|
||||
|
||||
@@ -64,4 +65,8 @@ public interface ScaleMeasurementDAO {
|
||||
|
||||
@Query("DELETE FROM scaleMeasurements WHERE userId = :userId")
|
||||
void deleteAll(int userId);
|
||||
|
||||
// selectAll() is equivalent to getAll(), but returns a Cursor, for exposing via a ContentProvider.
|
||||
@Query("SELECT * FROM scaleMeasurements WHERE userId = :userId AND enabled = 1 ORDER BY datetime DESC")
|
||||
Cursor selectAll(int userId);
|
||||
}
|
||||
|
@@ -21,6 +21,7 @@ import android.arch.persistence.room.Delete;
|
||||
import android.arch.persistence.room.Insert;
|
||||
import android.arch.persistence.room.Query;
|
||||
import android.arch.persistence.room.Update;
|
||||
import android.database.Cursor;
|
||||
|
||||
import com.health.openscale.core.datatypes.ScaleUser;
|
||||
|
||||
@@ -45,4 +46,13 @@ public interface ScaleUserDAO {
|
||||
|
||||
@Delete
|
||||
void delete(ScaleUser user);
|
||||
|
||||
// selectAll() and select() are equivalent to getall() and get(), but return a Cursor,
|
||||
// for exposing via a ContentProvider.
|
||||
@Query("SELECT * FROM scaleUsers")
|
||||
Cursor selectAll();
|
||||
|
||||
@Query("SELECT * FROM scaleUsers WHERE id = :id")
|
||||
Cursor select(int id);
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,129 @@
|
||||
/* Copyright (C) 2018 Paul Cowan <paul@custardsource.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 com.health.openscale.core.export;
|
||||
|
||||
import android.content.ContentProvider;
|
||||
import android.content.ContentUris;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.UriMatcher;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.health.openscale.core.OpenScale;
|
||||
|
||||
/**
|
||||
* Exposes the user and measurement data from openScale via
|
||||
* <a href="https://developer.android.com/guide/topics/providers/content-providers">Android
|
||||
* Content Providers</a>. This allows other apps to access the openScale data for their own purposes
|
||||
* (e.g. syncing to third-party services like Google Fit, Fitbit API, etc) without openScale itself
|
||||
* needing to do so or request additional permissions. <br />
|
||||
*
|
||||
* This access is gated by the com.health.openscale.READ_DATA permission, which is defined in the
|
||||
* manifest; it is not accessible to any other app without user confirmation.<br />
|
||||
*
|
||||
* The following URIs are supported:
|
||||
* <ul>
|
||||
* <li><code>content://com.health.openscale.provider/user</code>: list all users.</li>
|
||||
* <li><code>content://com.health.openscale.provider/user/$ID</code>: retrieve single user
|
||||
* by ID.</li>
|
||||
* <li><code>content://com.health.openscale.provider/user/$ID/measurements</code>:
|
||||
* retrieve all measurements for the supplied user ID.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class OpenScaleContentProvider extends ContentProvider {
|
||||
private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
||||
|
||||
private static final String AUTHORITY = "com.health.openscale.provider";
|
||||
|
||||
private static final int MATCH_TYPE_USER_LIST = 1;
|
||||
private static final int MATCH_TYPE_USER_ENTRY = 2;
|
||||
private static final int MATCH_TYPE_USER_MEASUREMENTS = 3;
|
||||
|
||||
|
||||
static {
|
||||
uriMatcher.addURI(AUTHORITY, "user", MATCH_TYPE_USER_LIST);
|
||||
uriMatcher.addURI(AUTHORITY, "user/#", MATCH_TYPE_USER_ENTRY);
|
||||
uriMatcher.addURI(AUTHORITY, "user/#/measurements", MATCH_TYPE_USER_MEASUREMENTS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType(Uri uri) {
|
||||
switch (uriMatcher.match(uri)) {
|
||||
case MATCH_TYPE_USER_LIST:
|
||||
return "vnd.android.cursor.dir/vnd.com.health.openscale.provider.user";
|
||||
|
||||
case MATCH_TYPE_USER_ENTRY:
|
||||
return "vnd.android.cursor.item/vnd.com.health.openscale.provider.user";
|
||||
|
||||
case MATCH_TYPE_USER_MEASUREMENTS:
|
||||
return "vnd.android.cursor.item/vnd.com.health.openscale.provider.measurement";
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor query(Uri uri, String[] projection, String selection,
|
||||
String[] selectionArgs, String sortOrder) {
|
||||
final Context context = getContext();
|
||||
Cursor cursor;
|
||||
|
||||
switch (uriMatcher.match(uri)) {
|
||||
case MATCH_TYPE_USER_LIST:
|
||||
cursor = OpenScale.getInstance().getScaleUserListCursor();
|
||||
break;
|
||||
|
||||
case MATCH_TYPE_USER_ENTRY:
|
||||
cursor = OpenScale.getInstance().getScaleUserCursor(Integer.valueOf(uri.getPathSegments().get(1)));
|
||||
break;
|
||||
|
||||
case MATCH_TYPE_USER_MEASUREMENTS:
|
||||
cursor = OpenScale.getInstance().getScaleMeasurementListCursor(Integer.valueOf(uri.getPathSegments().get(1)));
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown URI: " + uri);
|
||||
}
|
||||
|
||||
cursor.setNotificationUri(context.getContentResolver(), uri);
|
||||
return cursor;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int delete(Uri uri, String selection, String[] selectionArgs) {
|
||||
throw new UnsupportedOperationException("Not yet implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri insert(Uri uri, ContentValues values) {
|
||||
throw new UnsupportedOperationException("Not yet implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int update(Uri uri, ContentValues values, String selection,
|
||||
String[] selectionArgs) {
|
||||
throw new UnsupportedOperationException("Not yet implemented");
|
||||
}
|
||||
}
|
@@ -190,6 +190,8 @@
|
||||
<string name="permission_not_granted">Permission not granted</string>
|
||||
<string name="permission_bluetooth_info">Coarse location permission needed to search for Bluetooth devices. It can be revoked after the device is found.</string>
|
||||
<string name="permission_bluetooth_info_title">Info</string>
|
||||
<string name="permission_read_data_description">read openScale data, including user information and all saved measurements</string>
|
||||
<string name="permission_read_data_label">Read openScale Data</string>
|
||||
<string name="label_next">Next</string>
|
||||
<string name="label_long_press_drag_reorder">Long press and drag measurement to reorder</string>
|
||||
<string name="label_click_measurement_configure">Click measurement to configure</string>
|
||||
|
Reference in New Issue
Block a user