forked from upstream-mirrors/OpenTracks
merge heads
This commit is contained in:
@@ -142,6 +142,33 @@ public class SettingsActivity extends PreferenceActivity {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
// Add a confirmation dialog for the "Allow access" preference.
|
||||
final CheckBoxPreference allowAccessPreference = (CheckBoxPreference) findPreference(
|
||||
getString(R.string.allow_access_key));
|
||||
allowAccessPreference.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
if ((Boolean) newValue) {
|
||||
AlertDialog dialog = new AlertDialog.Builder(SettingsActivity.this)
|
||||
.setCancelable(true)
|
||||
.setTitle(getString(R.string.settings_allow_access))
|
||||
.setMessage(getString(R.string.settings_allow_access_dialog_message))
|
||||
.setPositiveButton(android.R.string.ok, new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int button) {
|
||||
allowAccessPreference.setChecked(true);
|
||||
}
|
||||
})
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.create();
|
||||
dialog.show();
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean processIntent() {
|
||||
|
||||
@@ -17,11 +17,13 @@ package com.google.android.apps.mytracks.content;
|
||||
|
||||
import com.google.android.apps.mytracks.Constants;
|
||||
import com.google.android.apps.mytracks.util.ApiFeatures;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.content.ContentProvider;
|
||||
import android.content.ContentUris;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.UriMatcher;
|
||||
import android.database.Cursor;
|
||||
import android.database.SQLException;
|
||||
@@ -30,6 +32,8 @@ import android.database.sqlite.SQLiteException;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.database.sqlite.SQLiteQueryBuilder;
|
||||
import android.net.Uri;
|
||||
import android.os.Binder;
|
||||
import android.os.Process;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
@@ -183,8 +187,22 @@ public class MyTracksProvider extends ContentProvider {
|
||||
"waypoints/#", WAYPOINTS_ID);
|
||||
}
|
||||
|
||||
private boolean canAccess() {
|
||||
if (Binder.getCallingPid() == Process.myPid()) {
|
||||
return true;
|
||||
} else {
|
||||
Context context = getContext();
|
||||
SharedPreferences sharedPreferences = context.getSharedPreferences(
|
||||
Constants.SETTINGS_NAME, 0);
|
||||
return sharedPreferences.getBoolean(context.getString(R.string.allow_access_key), false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
if (!canAccess()) {
|
||||
return false;
|
||||
}
|
||||
DatabaseHelper dbHelper = new DatabaseHelper(getContext());
|
||||
try {
|
||||
db = dbHelper.getWritableDatabase();
|
||||
@@ -196,6 +214,9 @@ public class MyTracksProvider extends ContentProvider {
|
||||
|
||||
@Override
|
||||
public int delete(Uri url, String where, String[] selectionArgs) {
|
||||
if (!canAccess()) {
|
||||
return 0;
|
||||
}
|
||||
String table;
|
||||
boolean shouldVacuum = false;
|
||||
switch (urlMatcher.match(url)) {
|
||||
@@ -228,6 +249,9 @@ public class MyTracksProvider extends ContentProvider {
|
||||
|
||||
@Override
|
||||
public String getType(Uri url) {
|
||||
if (!canAccess()) {
|
||||
return null;
|
||||
}
|
||||
switch (urlMatcher.match(url)) {
|
||||
case TRACKPOINTS:
|
||||
return TrackPointsColumns.CONTENT_TYPE;
|
||||
@@ -248,6 +272,9 @@ public class MyTracksProvider extends ContentProvider {
|
||||
|
||||
@Override
|
||||
public Uri insert(Uri url, ContentValues initialValues) {
|
||||
if (!canAccess()) {
|
||||
return null;
|
||||
}
|
||||
Log.d(MyTracksProvider.TAG, "MyTracksProvider.insert");
|
||||
ContentValues values;
|
||||
if (initialValues != null) {
|
||||
@@ -276,6 +303,9 @@ public class MyTracksProvider extends ContentProvider {
|
||||
|
||||
@Override
|
||||
public int bulkInsert(Uri url, ContentValues[] valuesBulk) {
|
||||
if (!canAccess()) {
|
||||
return 0;
|
||||
}
|
||||
Log.d(MyTracksProvider.TAG, "MyTracksProvider.bulkInsert");
|
||||
int numInserted = 0;
|
||||
try {
|
||||
@@ -348,6 +378,9 @@ public class MyTracksProvider extends ContentProvider {
|
||||
public Cursor query(
|
||||
Uri url, String[] projection, String selection, String[] selectionArgs,
|
||||
String sort) {
|
||||
if (!canAccess()) {
|
||||
return null;
|
||||
}
|
||||
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
|
||||
int match = urlMatcher.match(url);
|
||||
String sortOrder = null;
|
||||
@@ -399,6 +432,9 @@ public class MyTracksProvider extends ContentProvider {
|
||||
@Override
|
||||
public int update(Uri url, ContentValues values, String where,
|
||||
String[] selectionArgs) {
|
||||
if (!canAccess()) {
|
||||
return 0;
|
||||
}
|
||||
int count;
|
||||
int match = urlMatcher.match(url);
|
||||
if (match == TRACKPOINTS) {
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package com.google.android.apps.mytracks.services;
|
||||
|
||||
import static com.google.android.apps.mytracks.Constants.TAG;
|
||||
|
||||
import com.google.android.apps.mytracks.widgets.TrackWidgetProvider;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.app.IntentService;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* A service to control starting and stopping of a recording. This service,
|
||||
* through the AndroidManifest.xml, is configured to only allow components of
|
||||
* the same application to invoke it. Thus this service can be used my MyTracks
|
||||
* app widget, {@link TrackWidgetProvider}, but not by other applications. This
|
||||
* application delegates starting and stopping a recording to
|
||||
* {@link TrackRecordingService} using RPC calls.
|
||||
*
|
||||
* @author jshih@google.com (Jimmy Shih)
|
||||
*/
|
||||
public class ControlRecordingService extends IntentService implements ServiceConnection {
|
||||
|
||||
private ITrackRecordingService trackRecordingService;
|
||||
private boolean connected = false;
|
||||
|
||||
public ControlRecordingService() {
|
||||
super(ControlRecordingService.class.getSimpleName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
Intent newIntent = new Intent(this, TrackRecordingService.class);
|
||||
startService(newIntent);
|
||||
bindService(newIntent, this, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||
trackRecordingService = ITrackRecordingService.Stub.asInterface(service);
|
||||
notifyConnected();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
connected = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies all threads that connection to {@link TrackRecordingService} is
|
||||
* available.
|
||||
*/
|
||||
private synchronized void notifyConnected() {
|
||||
connected = true;
|
||||
notifyAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits until the connection to {@link TrackRecordingService} is available.
|
||||
*/
|
||||
private synchronized void waitConnected() {
|
||||
while (!connected) {
|
||||
try {
|
||||
wait();
|
||||
} catch (InterruptedException e) {
|
||||
|
||||
// can safely ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onHandleIntent(Intent intent) {
|
||||
waitConnected();
|
||||
String action = intent.getAction();
|
||||
if (action != null) {
|
||||
try {
|
||||
if (action.equals(getString(R.string.start_new_track_action))) {
|
||||
trackRecordingService.startNewTrack();
|
||||
} else if (action.equals(getString(R.string.end_current_track_action))) {
|
||||
trackRecordingService.endCurrentTrack();
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Log.d(TAG, "ControlRecordingService onHandleIntent RemoteException", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (connected) {
|
||||
unbindService(this);
|
||||
connected = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,11 +56,13 @@ import android.location.Location;
|
||||
import android.location.LocationListener;
|
||||
import android.location.LocationManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.PowerManager;
|
||||
import android.os.PowerManager.WakeLock;
|
||||
import android.os.Process;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Timer;
|
||||
@@ -278,39 +280,24 @@ public class TrackRecordingService extends Service {
|
||||
handleStartCommand(intent, startId);
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handles onStart and onStartCommand. This service, through the
|
||||
* AndroidManifest.xml, is configured to allow both MyTracks and other
|
||||
* applications to invoke it. With only the intent, we cannot tell whether the
|
||||
* caller is MyTracks or another app. Thus when starting the service through
|
||||
* this method, we cannot read/write MyTracks data or start/stop a recording.
|
||||
*/
|
||||
private void handleStartCommand(Intent intent, int startId) {
|
||||
Log.d(TAG,
|
||||
"TrackRecordingService.handleStartCommand: " + startId);
|
||||
Log.d(TAG, "TrackRecordingService.handleStartCommand: " + startId);
|
||||
|
||||
if (intent == null)
|
||||
if (intent == null) {
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
// Check if called on phone reboot with resume intent.
|
||||
if (intent.getBooleanExtra(RESUME_TRACK_EXTRA_NAME, false)) {
|
||||
resumeTrack(startId);
|
||||
} else {
|
||||
// Process actions for controlling the service.
|
||||
processStartStopIntent(intent);
|
||||
}
|
||||
}
|
||||
|
||||
private void processStartStopIntent(Intent intent) {
|
||||
String action = intent.getAction();
|
||||
|
||||
if (isNewTrackAction(action)) {
|
||||
if (!isTrackInProgress()) {
|
||||
boolean selectNewTrack = intent.getBooleanExtra(getString(R.string.select_new_track_extra), false);
|
||||
|
||||
startNewTrack();
|
||||
if (selectNewTrack) {
|
||||
prefManager.setSelectedTrack(recordingTrackId);
|
||||
}
|
||||
}
|
||||
} else if (isEndTrackAction(action)) {
|
||||
if (isTrackInProgress()) {
|
||||
endCurrentTrack();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -318,14 +305,6 @@ public class TrackRecordingService extends Service {
|
||||
return recordingTrackId != -1 || isRecording;
|
||||
}
|
||||
|
||||
private boolean isNewTrackAction(String action) {
|
||||
return getString(R.string.start_new_track_action).equals(action);
|
||||
}
|
||||
|
||||
private boolean isEndTrackAction(String action) {
|
||||
return getString(R.string.end_current_track_action).equals(action);
|
||||
}
|
||||
|
||||
private void resumeTrack(int startId) {
|
||||
Log.d(TAG, "TrackRecordingService: requested resume");
|
||||
|
||||
@@ -574,7 +553,7 @@ public class TrackRecordingService extends Service {
|
||||
public long startNewTrack() {
|
||||
Log.d(TAG, "TrackRecordingService.startNewTrack");
|
||||
if (isTrackInProgress()) {
|
||||
throw new IllegalStateException("A track is already in progress!");
|
||||
return -1L;
|
||||
}
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
@@ -982,8 +961,7 @@ public class TrackRecordingService extends Service {
|
||||
* Build a statistics marker.
|
||||
* A statistics marker holds the stats for the* last segment up to this marker.
|
||||
*
|
||||
* @param Waypoint The waypoint which will be populated with stats data.
|
||||
* @return the unique id of the inserted marker
|
||||
* @param waypoint The waypoint which will be populated with stats data.
|
||||
*/
|
||||
private void buildStatisticsMarker(Waypoint waypoint) {
|
||||
StringUtils utils = new StringUtils(TrackRecordingService.this);
|
||||
@@ -1012,7 +990,7 @@ public class TrackRecordingService extends Service {
|
||||
private void endCurrentTrack() {
|
||||
Log.d(TAG, "TrackRecordingService.endCurrentTrack");
|
||||
if (!isTrackInProgress()) {
|
||||
throw new IllegalStateException("No recording track in progress!");
|
||||
return;
|
||||
}
|
||||
|
||||
announcementExecutor.shutdown();
|
||||
@@ -1053,12 +1031,14 @@ public class TrackRecordingService extends Service {
|
||||
}
|
||||
|
||||
private void sendTrackBroadcast(int actionResId, long trackId) {
|
||||
Intent broadcastIntent =
|
||||
new Intent()
|
||||
.setAction(getString(actionResId))
|
||||
.putExtra(getString(R.string.track_id_broadcast_extra), trackId);
|
||||
sendBroadcast(broadcastIntent,
|
||||
getString(R.string.broadcast_notifications_permission));
|
||||
Intent broadcastIntent = new Intent().setAction(getString(actionResId)).putExtra(
|
||||
getString(R.string.track_id_broadcast_extra), trackId);
|
||||
sendBroadcast(broadcastIntent, getString(R.string.mytracks_notifications_permission));
|
||||
|
||||
SharedPreferences sharedPreferences = getSharedPreferences(Constants.SETTINGS_NAME, 0);
|
||||
if (sharedPreferences.getBoolean(getString(R.string.allow_access_key), false)) {
|
||||
sendBroadcast(broadcastIntent, getString(R.string.broadcast_notifications_permission));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1190,29 +1170,58 @@ public class TrackRecordingService extends Service {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the service is available. If not, throws an
|
||||
* {@link IllegalStateException}.
|
||||
*/
|
||||
private void checkService() {
|
||||
if (service == null) {
|
||||
throw new IllegalStateException("The service has been already detached!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the RPC caller is from the same application or if the
|
||||
* "Allow access" setting indicates that another app can invoke this service's
|
||||
* RPCs.
|
||||
*/
|
||||
private boolean canAccess() {
|
||||
|
||||
// As a precondition for access, must check if the service is available.
|
||||
checkService();
|
||||
|
||||
if (Process.myPid() == Binder.getCallingPid()) {
|
||||
return true;
|
||||
} else {
|
||||
SharedPreferences sharedPreferences = service.getSharedPreferences(
|
||||
Constants.SETTINGS_NAME, 0);
|
||||
return sharedPreferences.getBoolean(service.getString(R.string.allow_access_key), false);
|
||||
}
|
||||
}
|
||||
|
||||
// Service method delegates.
|
||||
|
||||
@Override
|
||||
public boolean isRecording() {
|
||||
checkService();
|
||||
if (!canAccess()) {
|
||||
return false;
|
||||
}
|
||||
return service.isRecording();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getRecordingTrackId() {
|
||||
checkService();
|
||||
if (!canAccess()) {
|
||||
return -1L;
|
||||
}
|
||||
return service.recordingTrackId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long startNewTrack() {
|
||||
checkService();
|
||||
if (!canAccess()) {
|
||||
return -1L;
|
||||
}
|
||||
return service.startNewTrack();
|
||||
}
|
||||
|
||||
@@ -1223,25 +1232,33 @@ public class TrackRecordingService extends Service {
|
||||
* @return the unique ID of the inserted marker
|
||||
*/
|
||||
public long insertWaypoint(WaypointCreationRequest request) {
|
||||
checkService();
|
||||
if (!canAccess()) {
|
||||
return -1L;
|
||||
}
|
||||
return service.insertWaypoint(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endCurrentTrack() {
|
||||
checkService();
|
||||
if (!canAccess()) {
|
||||
return;
|
||||
}
|
||||
service.endCurrentTrack();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recordLocation(Location loc) {
|
||||
checkService();
|
||||
if (!canAccess()) {
|
||||
return;
|
||||
}
|
||||
service.locationListener.onLocationChanged(loc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getSensorData() {
|
||||
checkService();
|
||||
if (!canAccess()) {
|
||||
return null;
|
||||
}
|
||||
if (service.sensorManager == null) {
|
||||
Log.d(TAG, "No sensor manager for data.");
|
||||
return null;
|
||||
@@ -1255,7 +1272,9 @@ public class TrackRecordingService extends Service {
|
||||
|
||||
@Override
|
||||
public int getSensorState() {
|
||||
checkService();
|
||||
if (!canAccess()) {
|
||||
return Sensor.SensorState.NONE.getNumber();
|
||||
}
|
||||
if (service.sensorManager == null) {
|
||||
Log.d(TAG, "No sensor manager for data.");
|
||||
return Sensor.SensorState.NONE.getNumber();
|
||||
|
||||
@@ -19,6 +19,16 @@ package com.google.android.apps.mytracks.widgets;
|
||||
import static com.google.android.apps.mytracks.Constants.SETTINGS_NAME;
|
||||
import static com.google.android.apps.mytracks.Constants.TAG;
|
||||
|
||||
import com.google.android.apps.mytracks.MyTracks;
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.content.TracksColumns;
|
||||
import com.google.android.apps.mytracks.services.ControlRecordingService;
|
||||
import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||
import com.google.android.apps.mytracks.util.StringUtils;
|
||||
import com.google.android.apps.mytracks.util.UnitConversions;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.appwidget.AppWidgetProvider;
|
||||
@@ -32,16 +42,6 @@ import android.os.Handler;
|
||||
import android.util.Log;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
import com.google.android.apps.mytracks.MyTracks;
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.content.TracksColumns;
|
||||
import com.google.android.apps.mytracks.services.TrackRecordingService;
|
||||
import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||
import com.google.android.apps.mytracks.util.StringUtils;
|
||||
import com.google.android.apps.mytracks.util.UnitConversions;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
/**
|
||||
* An AppWidgetProvider for displaying key track statistics (distance, time,
|
||||
* speed) from the current or most recent track.
|
||||
@@ -175,14 +175,12 @@ public class TrackWidgetProvider
|
||||
// If a new track is started by this appwidget or elsewhere,
|
||||
// toggle the button to active and have it disable the track if pressed.
|
||||
setButtonIntent(
|
||||
views, context, R.string.end_current_track_action, R.drawable.appwidget_button_enabled,
|
||||
-1);
|
||||
views, context, R.string.end_current_track_action, R.drawable.appwidget_button_enabled);
|
||||
} else {
|
||||
// If a track is stopped by this appwidget or elsewhere,
|
||||
// toggle the button to inactive and have it start a new track if pressed.
|
||||
setButtonIntent(
|
||||
views, context, R.string.start_new_track_action, R.drawable.appwidget_button_disabled,
|
||||
R.string.select_new_track_extra);
|
||||
views, context, R.string.start_new_track_action, R.drawable.appwidget_button_disabled);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,15 +191,11 @@ public class TrackWidgetProvider
|
||||
* @param context The widget context
|
||||
* @param action The resource id of the action to fire when the button is pressed
|
||||
* @param icon The resource id of the icon to show for the button
|
||||
* @param extra Optional resource id of a boolean extra on the intent
|
||||
*/
|
||||
private void setButtonIntent(
|
||||
RemoteViews views, Context context, int action, int icon, int extra) {
|
||||
Intent intent = new Intent(context, TrackRecordingService.class);
|
||||
RemoteViews views, Context context, int action, int icon) {
|
||||
Intent intent = new Intent(context, ControlRecordingService.class);
|
||||
intent.setAction(context.getString(action));
|
||||
if (extra != -1) {
|
||||
intent.putExtra(context.getString(extra), true);
|
||||
}
|
||||
PendingIntent pendingIntent = PendingIntent.getService(context, 0,
|
||||
intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
views.setOnClickPendingIntent(R.id.appwidget_button, pendingIntent);
|
||||
|
||||
Reference in New Issue
Block a user