forked from upstream-mirrors/OpenTracks
Add test cases about TripStatisticsUpdater.addLocation().
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2012 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.util;
|
||||
|
||||
import com.google.android.apps.analytics.GoogleAnalyticsTracker;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
/**
|
||||
* Utitlites for sending pageviews to Google Analytics.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class AnalyticsUtils {
|
||||
|
||||
private static final String UA = "UA-7222692-2";
|
||||
private static final String PRODUCT_NAME = "android-mytracks";
|
||||
private static GoogleAnalyticsTracker tracker;
|
||||
|
||||
private AnalyticsUtils() {}
|
||||
|
||||
/**
|
||||
* Sends a page view.
|
||||
*
|
||||
* @param context the context
|
||||
* @param page the page
|
||||
*/
|
||||
public static void sendPageViews(Context context, String page) {
|
||||
if (tracker == null) {
|
||||
tracker = GoogleAnalyticsTracker.getInstance();
|
||||
tracker.startNewSession(UA, context);
|
||||
tracker.setProductVersion(PRODUCT_NAME, SystemUtils.getMyTracksVersion(context));
|
||||
}
|
||||
tracker.trackPageView(page);
|
||||
}
|
||||
|
||||
public static void dispatch() {
|
||||
if (tracker != null) {
|
||||
tracker.dispatch();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2012 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.util;
|
||||
|
||||
import com.google.android.apps.mytracks.Constants;
|
||||
import com.google.android.apps.mytracks.services.sensors.BluetoothConnectionManager;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothSocket;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* API level 10 specific implementation of the {@link ApiAdapter}.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
@TargetApi(10)
|
||||
public class Api10Adapter extends Api9Adapter {
|
||||
|
||||
@Override
|
||||
public BluetoothSocket getBluetoothSocket(BluetoothDevice bluetoothDevice) throws IOException {
|
||||
try {
|
||||
return bluetoothDevice.createInsecureRfcommSocketToServiceRecord(
|
||||
BluetoothConnectionManager.MY_TRACKS_UUID);
|
||||
} catch (IOException e) {
|
||||
Log.d(Constants.TAG, "Unable to create insecure connection", e);
|
||||
}
|
||||
return bluetoothDevice.createRfcommSocketToServiceRecord(BluetoothConnectionManager.MY_TRACKS_UUID);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright 2012 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.util;
|
||||
|
||||
import com.google.android.apps.mytracks.ContextualActionModeCallback;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.app.SearchManager;
|
||||
import android.content.Context;
|
||||
import android.view.ActionMode;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.OnItemLongClickListener;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ListView;
|
||||
import android.widget.SearchView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* API level 11 specific implementation of the {@link ApiAdapter}.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
@TargetApi(11)
|
||||
public class Api11Adapter extends Api10Adapter {
|
||||
|
||||
@Override
|
||||
public void hideTitle(Activity activity) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureActionBarHomeAsUp(Activity activity) {
|
||||
activity.getActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureListViewContextualMenu(final Activity activity, ListView listView,
|
||||
final ContextualActionModeCallback contextualActionModeCallback) {
|
||||
listView.setOnItemLongClickListener(new OnItemLongClickListener() {
|
||||
ActionMode actionMode;
|
||||
|
||||
@Override
|
||||
public boolean onItemLongClick(
|
||||
AdapterView<?> parent, View view, final int position, final long id) {
|
||||
if (actionMode != null) {
|
||||
return false;
|
||||
}
|
||||
actionMode = activity.startActionMode(new ActionMode.Callback() {
|
||||
@Override
|
||||
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
|
||||
mode.getMenuInflater().inflate(R.menu.list_context_menu, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
|
||||
contextualActionModeCallback.onPrepare(menu, position, id);
|
||||
// Return true to indicate change
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyActionMode(ActionMode mode) {
|
||||
actionMode = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
|
||||
mode.finish();
|
||||
return contextualActionModeCallback.onClick(item.getItemId(), position, id);
|
||||
}
|
||||
});
|
||||
TextView textView = (TextView) view.findViewById(R.id.list_item_name);
|
||||
if (textView != null) {
|
||||
actionMode.setTitle(textView.getText());
|
||||
}
|
||||
view.setSelected(true);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@Override
|
||||
public void configureSearchWidget(Activity activity, final MenuItem menuItem) {
|
||||
SearchManager searchManager = (SearchManager) activity.getSystemService(Context.SEARCH_SERVICE);
|
||||
SearchView searchView = (SearchView) menuItem.getActionView();
|
||||
searchView.setSearchableInfo(searchManager.getSearchableInfo(activity.getComponentName()));
|
||||
searchView.setQueryRefinementEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleSearchMenuSelection(Activity activity) {
|
||||
// Returns false to allow the platform to expand the search widget.
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void addAllToArrayAdapter(ArrayAdapter<T> arrayAdapter, List<T> items) {
|
||||
arrayAdapter.addAll(items);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidMenu(Activity activity) {
|
||||
activity.invalidateOptionsMenu();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2012 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.util;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.ActionBar;
|
||||
import android.app.Activity;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.SearchView;
|
||||
|
||||
/**
|
||||
* API level 14 specific implementation of the {@link ApiAdapter}.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
@TargetApi(14)
|
||||
public class Api14Adapter extends Api11Adapter {
|
||||
|
||||
@Override
|
||||
public void configureActionBarHomeAsUp(Activity activity) {
|
||||
ActionBar actionBar = activity.getActionBar();
|
||||
actionBar.setHomeButtonEnabled(true);
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureSearchWidget(Activity activity, final MenuItem menuItem) {
|
||||
super.configureSearchWidget(activity, menuItem);
|
||||
SearchView searchView = (SearchView) menuItem.getActionView();
|
||||
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
|
||||
@Override
|
||||
public boolean onQueryTextSubmit(String query) {
|
||||
menuItem.collapseActionView();
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public boolean onQueryTextChange(String newText) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
searchView.setOnSuggestionListener(new SearchView.OnSuggestionListener() {
|
||||
@Override
|
||||
public boolean onSuggestionSelect(int position) {
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public boolean onSuggestionClick(int position) {
|
||||
menuItem.collapseActionView();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleSearchKey(MenuItem menuItem) {
|
||||
menuItem.expandActionView();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2013 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.util;
|
||||
|
||||
import com.google.android.apps.mytracks.widgets.TrackWidgetProvider;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.appwidget.AppWidgetProviderInfo;
|
||||
import android.os.Bundle;
|
||||
|
||||
/**
|
||||
* API level 16 specific implementation of the {@link ApiAdapter}.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
@TargetApi(16)
|
||||
public class Api16Adapter extends Api14Adapter {
|
||||
|
||||
private static final String APP_WIDGET_SIZE_KEY = "app_widget_size_key";
|
||||
|
||||
@Override
|
||||
public int getAppWidgetSize(AppWidgetManager appWidgetManager, int appWidgetId) {
|
||||
Bundle bundle = appWidgetManager.getAppWidgetOptions(appWidgetId);
|
||||
boolean isKeyguard = bundle.getInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY, -1)
|
||||
== AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD;
|
||||
return bundle.getInt(APP_WIDGET_SIZE_KEY, isKeyguard ? TrackWidgetProvider.KEYGUARD_DEFAULT_SIZE
|
||||
: TrackWidgetProvider.HOME_SCREEN_DEFAULT_SIZE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAppWidgetSize(AppWidgetManager appWidgetManager, int appWidgetId, int size) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putInt(APP_WIDGET_SIZE_KEY, size);
|
||||
appWidgetManager.updateAppWidgetOptions(appWidgetId, bundle);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright 2010 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.util;
|
||||
|
||||
import com.google.android.apps.mytracks.Constants;
|
||||
import com.google.android.apps.mytracks.ContextualActionModeCallback;
|
||||
import com.google.android.apps.mytracks.services.sensors.BluetoothConnectionManager;
|
||||
import com.google.android.apps.mytracks.widgets.TrackWidgetProvider;
|
||||
import com.google.api.client.http.HttpTransport;
|
||||
import com.google.api.client.http.apache.ApacheHttpTransport;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothSocket;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
import android.util.Log;
|
||||
import android.view.MenuItem;
|
||||
import android.view.Window;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ListView;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* API level 8 specific implementation of the {@link ApiAdapter}.
|
||||
*
|
||||
* @author Bartlomiej Niechwiej
|
||||
*/
|
||||
public class Api8Adapter implements ApiAdapter {
|
||||
|
||||
@Override
|
||||
public void applyPreferenceChanges(Editor editor) {
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enableStrictMode() {
|
||||
// Not supported
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] copyByteArray(byte[] input, int start, int end) {
|
||||
int length = end - start;
|
||||
byte[] output = new byte[length];
|
||||
System.arraycopy(input, start, output, 0, length);
|
||||
return output;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpTransport getHttpTransport() {
|
||||
return new ApacheHttpTransport();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGeoCoderPresent() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BluetoothSocket getBluetoothSocket(BluetoothDevice bluetoothDevice) throws IOException {
|
||||
try {
|
||||
Class<? extends BluetoothDevice> c = bluetoothDevice.getClass();
|
||||
Method insecure = c.getMethod("createInsecureRfcommSocket", Integer.class);
|
||||
insecure.setAccessible(true);
|
||||
return (BluetoothSocket) insecure.invoke(bluetoothDevice, 1);
|
||||
} catch (SecurityException e) {
|
||||
Log.d(Constants.TAG, "Unable to create insecure connection", e);
|
||||
} catch (NoSuchMethodException e) {
|
||||
Log.d(Constants.TAG, "Unable to create insecure connection", e);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.d(Constants.TAG, "Unable to create insecure connection", e);
|
||||
} catch (IllegalAccessException e) {
|
||||
Log.d(Constants.TAG, "Unable to create insecure connection", e);
|
||||
} catch (InvocationTargetException e) {
|
||||
Log.d(Constants.TAG, "Unable to create insecure connection", e);
|
||||
}
|
||||
return bluetoothDevice.createRfcommSocketToServiceRecord(BluetoothConnectionManager.MY_TRACKS_UUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hideTitle(Activity activity) {
|
||||
activity.requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureActionBarHomeAsUp(Activity activity) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureListViewContextualMenu(Activity activity, ListView listView,
|
||||
ContextualActionModeCallback contextualActionModeCallback) {
|
||||
activity.registerForContextMenu(listView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureSearchWidget(Activity activity, MenuItem menuItem) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleSearchMenuSelection(Activity activity) {
|
||||
activity.onSearchRequested();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void addAllToArrayAdapter(ArrayAdapter<T> arrayAdapter, List<T> items) {
|
||||
for (T item : items) {
|
||||
arrayAdapter.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidMenu(Activity activity) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleSearchKey(MenuItem menuItem) {
|
||||
// Return false and allow the framework to handle the search key.
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAppWidgetSize(AppWidgetManager appWidgetManager, int appWidgetId) {
|
||||
return TrackWidgetProvider.HOME_SCREEN_DEFAULT_SIZE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAppWidgetSize(AppWidgetManager appWidgetManager, int appWidgetId, int size) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2012 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.util;
|
||||
|
||||
import com.google.android.apps.mytracks.Constants;
|
||||
import com.google.api.client.http.HttpTransport;
|
||||
import com.google.api.client.http.javanet.NetHttpTransport;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
import android.location.Geocoder;
|
||||
import android.os.StrictMode;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* API level 9 specific implementation of the {@link ApiAdapter}.
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
@TargetApi(9)
|
||||
public class Api9Adapter extends Api8Adapter {
|
||||
|
||||
@Override
|
||||
public void applyPreferenceChanges(Editor editor) {
|
||||
// Apply asynchronously
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enableStrictMode() {
|
||||
Log.d(Constants.TAG, "Enabling strict mode");
|
||||
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
|
||||
.detectAll()
|
||||
.penaltyLog()
|
||||
.build());
|
||||
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
|
||||
.detectAll()
|
||||
.penaltyLog()
|
||||
.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] copyByteArray(byte[] input, int start, int end) {
|
||||
return Arrays.copyOfRange(input, start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpTransport getHttpTransport() {
|
||||
return new NetHttpTransport();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGeoCoderPresent() {
|
||||
return Geocoder.isPresent();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Copyright 2010 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.util;
|
||||
|
||||
import com.google.android.apps.mytracks.ContextualActionModeCallback;
|
||||
import com.google.api.client.http.HttpTransport;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothSocket;
|
||||
import android.content.SharedPreferences;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ListView;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A set of methods that may be implemented differently depending on the Android
|
||||
* API level.
|
||||
*
|
||||
* @author Bartlomiej Niechwiej
|
||||
*/
|
||||
public interface ApiAdapter {
|
||||
|
||||
/**
|
||||
* Applies all the changes done to a given preferences editor. Changes may or
|
||||
* may not be applied immediately.
|
||||
* <p>
|
||||
* Due to changes in API level 9.
|
||||
*
|
||||
* @param editor the editor
|
||||
*/
|
||||
public void applyPreferenceChanges(SharedPreferences.Editor editor);
|
||||
|
||||
/**
|
||||
* Enables strict mode where supported, only if this is a development build.
|
||||
* <p>
|
||||
* Due to changes in API level 9.
|
||||
*/
|
||||
public void enableStrictMode();
|
||||
|
||||
/**
|
||||
* Copies elements from an input byte array into a new byte array, from
|
||||
* indexes start (inclusive) to end (exclusive). The end index must be less
|
||||
* than or equal to the input length.
|
||||
* <p>
|
||||
* Due to changes in API level 9.
|
||||
*
|
||||
* @param input the input byte array
|
||||
* @param start the start index
|
||||
* @param end the end index
|
||||
* @return a new array containing elements from the input byte array.
|
||||
*/
|
||||
public byte[] copyByteArray(byte[] input, int start, int end);
|
||||
|
||||
/**
|
||||
* Gets a {@link HttpTransport}.
|
||||
* <p>
|
||||
* Due to changes in API level 9.
|
||||
*/
|
||||
public HttpTransport getHttpTransport();
|
||||
|
||||
/**
|
||||
* Returns true if GeoCoder is present.
|
||||
* <p>
|
||||
* Due to changes in API level 9.
|
||||
*/
|
||||
public boolean isGeoCoderPresent();
|
||||
|
||||
/**
|
||||
* Gets a {@link BluetoothSocket}.
|
||||
* <p>
|
||||
* Due to changes in API level 10.
|
||||
*
|
||||
* @param bluetoothDevice
|
||||
*/
|
||||
public BluetoothSocket getBluetoothSocket(BluetoothDevice bluetoothDevice) throws IOException;
|
||||
|
||||
/**
|
||||
* Hides the title. If the platform supports the action bar, do nothing.
|
||||
* Ideally, with the action bar, we would like to collapse the navigation tabs
|
||||
* into the action bar. However, collapsing is not supported by the
|
||||
* compatibility library.
|
||||
* <p>
|
||||
* Due to changes in API level 11.
|
||||
*
|
||||
* @param activity the activity
|
||||
*/
|
||||
public void hideTitle(Activity activity);
|
||||
|
||||
/**
|
||||
* Configures the action bar with the Home button as an Up button. If the
|
||||
* platform doesn't support the action bar, do nothing.
|
||||
* <p>
|
||||
* Due to changes in API level 11.
|
||||
*
|
||||
* @param activity the activity
|
||||
*/
|
||||
public void configureActionBarHomeAsUp(Activity activity);
|
||||
|
||||
/**
|
||||
* Configures the list view context menu.
|
||||
* <p>
|
||||
* Due to changes in API level 11.
|
||||
*
|
||||
* @param activity the activity
|
||||
* @param listView the list view
|
||||
* @param contextualActionModeCallback the callback when an item is selected
|
||||
* in the contextual action mode
|
||||
*/
|
||||
public void configureListViewContextualMenu(Activity activity, ListView listView,
|
||||
ContextualActionModeCallback contextualActionModeCallback);
|
||||
|
||||
/**
|
||||
* Configures the search widget.
|
||||
* <p>
|
||||
* Due to changes in API level 11.
|
||||
*
|
||||
* @param activity the activity
|
||||
* @param menuItem the search menu item
|
||||
*/
|
||||
public void configureSearchWidget(Activity activity, MenuItem menuItem);
|
||||
|
||||
/**
|
||||
* Handles the search menu selection. Returns true if handled.
|
||||
* <p>
|
||||
* Due to changes in API level 11.
|
||||
*
|
||||
* @param activity the activity
|
||||
*/
|
||||
public boolean handleSearchMenuSelection(Activity activity);
|
||||
|
||||
/**
|
||||
* Adds all items to an array adapter.
|
||||
* <p>
|
||||
* Due to changes in API level 11.
|
||||
*
|
||||
* @param arrayAdapter the array adapter
|
||||
* @param items list of items
|
||||
*/
|
||||
public <T> void addAllToArrayAdapter(ArrayAdapter<T> arrayAdapter, List<T> items);
|
||||
|
||||
/**
|
||||
* Invalidates the menu.
|
||||
* <p>
|
||||
* Due to changes in API level 11.
|
||||
*/
|
||||
public void invalidMenu(Activity activity);
|
||||
|
||||
/**
|
||||
* Handles the search key press. Returns true if handled.
|
||||
* <p>
|
||||
* Due to changes in API level 14.
|
||||
*
|
||||
* @param menu the search menu
|
||||
*/
|
||||
public boolean handleSearchKey(MenuItem menu);
|
||||
|
||||
/**
|
||||
* Gets the app widget size.
|
||||
* <p>
|
||||
* Due to changes in API level 16.
|
||||
*
|
||||
* @param appWidgetManager the app widget manager
|
||||
* @param appWidgetId the app widget id
|
||||
*/
|
||||
public int getAppWidgetSize(AppWidgetManager appWidgetManager, int appWidgetId);
|
||||
|
||||
/**
|
||||
* Sets the app widget size.
|
||||
*
|
||||
* @param appWidgetManager the app widget manager.
|
||||
* @param appWidgetId the app widgit id
|
||||
* @param size the size
|
||||
*/
|
||||
public void setAppWidgetSize(AppWidgetManager appWidgetManager, int appWidgetId, int size);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2010 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.util;
|
||||
|
||||
import android.os.Build;
|
||||
|
||||
/**
|
||||
* A factory to get the {@link ApiAdapter} for the current device.
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class ApiAdapterFactory {
|
||||
|
||||
private static ApiAdapter apiAdapter;
|
||||
|
||||
/**
|
||||
* Gets the {@link ApiAdapter} for the current device.
|
||||
*/
|
||||
public static ApiAdapter getApiAdapter() {
|
||||
if (apiAdapter == null) {
|
||||
if (Build.VERSION.SDK_INT >= 16) {
|
||||
apiAdapter = new Api16Adapter();
|
||||
} else if (Build.VERSION.SDK_INT >= 14) {
|
||||
apiAdapter = new Api14Adapter();
|
||||
} else if (Build.VERSION.SDK_INT >= 11) {
|
||||
apiAdapter = new Api11Adapter();
|
||||
} else if (Build.VERSION.SDK_INT >= 10) {
|
||||
apiAdapter = new Api10Adapter();
|
||||
} else if (Build.VERSION.SDK_INT >= 9) {
|
||||
apiAdapter = new Api9Adapter();
|
||||
} else {
|
||||
apiAdapter = new Api8Adapter();
|
||||
}
|
||||
}
|
||||
return apiAdapter;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2010 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.util;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothClass;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Utilities for dealing with bluetooth devices.
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class BluetoothDeviceUtils {
|
||||
|
||||
private BluetoothDeviceUtils() {}
|
||||
|
||||
/**
|
||||
* Populates the device names and the device addresses with all the suitable
|
||||
* bluetooth devices.
|
||||
*
|
||||
* @param bluetoothAdapter the bluetooth adapter
|
||||
* @param deviceNames list of device names
|
||||
* @param deviceAddresses list of device addresses
|
||||
*/
|
||||
public static void populateDeviceLists(
|
||||
BluetoothAdapter bluetoothAdapter, List<String> deviceNames, List<String> deviceAddresses) {
|
||||
// Ensure the bluetooth adapter is not in discovery mode.
|
||||
bluetoothAdapter.cancelDiscovery();
|
||||
|
||||
Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices();
|
||||
for (BluetoothDevice device : pairedDevices) {
|
||||
BluetoothClass bluetoothClass = device.getBluetoothClass();
|
||||
if (bluetoothClass != null) {
|
||||
// Not really sure what we want, but I know what we don't want.
|
||||
switch (bluetoothClass.getMajorDeviceClass()) {
|
||||
case BluetoothClass.Device.Major.COMPUTER:
|
||||
case BluetoothClass.Device.Major.PHONE:
|
||||
break;
|
||||
default:
|
||||
deviceAddresses.add(device.getAddress());
|
||||
deviceNames.add(device.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Copyright 2009 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.util;
|
||||
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* This class will generate google chart server url's.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class ChartURLGenerator {
|
||||
|
||||
private static final String CHARTS_BASE_URL = "http://chart.apis.google.com/chart?";
|
||||
|
||||
private ChartURLGenerator() {}
|
||||
|
||||
/**
|
||||
* Gets a chart of a track.
|
||||
*
|
||||
* @param distances An array of distance measurements
|
||||
* @param elevations A matching array of elevation measurements
|
||||
* @param track The track for this chart
|
||||
* @param context The current appplication context
|
||||
*/
|
||||
public static String getChartUrl(
|
||||
Vector<Double> distances, Vector<Double> elevations, Track track, Context context) {
|
||||
boolean metricUnits = PreferencesUtils.getBoolean(
|
||||
context, R.string.metric_units_key, PreferencesUtils.METRIC_UNITS_DEFAULT);
|
||||
return getChartUrl(
|
||||
distances, elevations, track, context.getString(R.string.stats_elevation), metricUnits);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a chart of a track. This form is for testing without contexts.
|
||||
*
|
||||
* @param distances An array of distance measurements
|
||||
* @param elevations A matching array of elevation measurements
|
||||
* @param track The track for this chart
|
||||
* @param title The title for the chart
|
||||
* @param metricUnits Should the data be displayed in metric units
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static String getChartUrl(Vector<Double> distances, Vector<Double> elevations, Track track,
|
||||
String title, boolean metricUnits) {
|
||||
if (distances == null || elevations == null || track == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (distances.size() != elevations.size()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Round it up.
|
||||
TripStatistics tripStatistics = track.getTripStatistics();
|
||||
double effectiveMaxY = tripStatistics.getMaxElevation();
|
||||
if (!metricUnits) {
|
||||
effectiveMaxY *= UnitConversions.M_TO_FT;
|
||||
}
|
||||
effectiveMaxY = ((int) (effectiveMaxY / 100)) * 100 + 100;
|
||||
|
||||
// Round it down.
|
||||
double effectiveMinY = tripStatistics.getMinElevation();
|
||||
if (!metricUnits) {
|
||||
effectiveMinY *= UnitConversions.M_TO_FT;
|
||||
}
|
||||
effectiveMinY = ((int) (effectiveMinY / 100)) * 100;
|
||||
if (tripStatistics.getMinElevation() < 0) {
|
||||
effectiveMinY -= 100;
|
||||
}
|
||||
|
||||
double ySpread = effectiveMaxY - effectiveMinY;
|
||||
|
||||
StringBuilder sb = new StringBuilder(CHARTS_BASE_URL);
|
||||
sb.append("&chs=600x350");
|
||||
sb.append("&cht=lxy");
|
||||
|
||||
// Title
|
||||
sb.append("&chtt=");
|
||||
sb.append(title);
|
||||
|
||||
// Labels
|
||||
sb.append("&chxt=x,y");
|
||||
double totalDistance = tripStatistics.getTotalDistance() * UnitConversions.M_TO_KM;
|
||||
if (!metricUnits) {
|
||||
totalDistance *= UnitConversions.KM_TO_MI;
|
||||
}
|
||||
int xInterval = ((int) (totalDistance / 6));
|
||||
int yInterval = ((int) (ySpread / 600)) * 100;
|
||||
if (yInterval < 100) {
|
||||
yInterval = 25;
|
||||
}
|
||||
// Range
|
||||
sb.append("&chxr=0,0,");
|
||||
sb.append((int) totalDistance);
|
||||
sb.append(',');
|
||||
sb.append(xInterval);
|
||||
|
||||
sb.append("|1,");
|
||||
sb.append(effectiveMinY);
|
||||
sb.append(',');
|
||||
sb.append(effectiveMaxY);
|
||||
sb.append(',');
|
||||
sb.append(yInterval);
|
||||
|
||||
// Line color
|
||||
sb.append("&chco=009A00");
|
||||
|
||||
// Fill
|
||||
sb.append("&chm=B,00AA00,0,0,0");
|
||||
|
||||
// Grid lines
|
||||
double desiredGrids = ySpread / yInterval;
|
||||
sb.append("&chg=100000,");
|
||||
sb.append(100.0 / desiredGrids);
|
||||
sb.append(",1,0");
|
||||
|
||||
// Data
|
||||
sb.append("&chd=e:");
|
||||
for (int i = 0; i < distances.size(); i++) {
|
||||
int normalized = (int) (getNormalizedDistance(distances.elementAt(i), track) * 4095);
|
||||
sb.append(ChartsExtendedEncoder.getEncodedValue(normalized));
|
||||
}
|
||||
|
||||
sb.append(ChartsExtendedEncoder.getSeparator());
|
||||
for (int i = 0; i < elevations.size(); i++) {
|
||||
double value = elevations.elementAt(i);
|
||||
if (!metricUnits) {
|
||||
value *= UnitConversions.M_TO_FT;
|
||||
}
|
||||
int normalized = (int) (getNormalizedElevation(value, effectiveMinY, ySpread) * 4095);
|
||||
sb.append(ChartsExtendedEncoder.getEncodedValue(normalized));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static double getNormalizedDistance(double value, Track track) {
|
||||
return value / track.getTripStatistics().getTotalDistance();
|
||||
}
|
||||
|
||||
private static double getNormalizedElevation(double value, double effectiveMinY, double ySpread) {
|
||||
return (value - effectiveMinY) / ySpread;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2009 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.util;
|
||||
|
||||
/**
|
||||
* The singleton class representing the extended encoding of chart data.
|
||||
*/
|
||||
public class ChartsExtendedEncoder {
|
||||
|
||||
// ChartServer data encoding in extended mode
|
||||
private static final String CHARTSERVER_EXTENDED_ENCODING_SEPARATOR = ",";
|
||||
private static final String CHARTSERVER_EXTENDED_ENCODING =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-.";
|
||||
private static final int CHARTSERVER_EXTENDED_SINGLE_CHARACTER_VALUES =
|
||||
CHARTSERVER_EXTENDED_ENCODING.length();
|
||||
private static final String MISSING_POINT_EXTENDED_ENCODING = "__";
|
||||
|
||||
private ChartsExtendedEncoder() { }
|
||||
|
||||
public static String getEncodedValue(int scaled) {
|
||||
int index1 = scaled / CHARTSERVER_EXTENDED_SINGLE_CHARACTER_VALUES;
|
||||
if (index1 < 0 || index1 >= CHARTSERVER_EXTENDED_SINGLE_CHARACTER_VALUES) {
|
||||
return MISSING_POINT_EXTENDED_ENCODING;
|
||||
}
|
||||
|
||||
int index2 = scaled % CHARTSERVER_EXTENDED_SINGLE_CHARACTER_VALUES;
|
||||
if (index2 < 0 || index2 >= CHARTSERVER_EXTENDED_SINGLE_CHARACTER_VALUES) {
|
||||
return MISSING_POINT_EXTENDED_ENCODING;
|
||||
}
|
||||
|
||||
return String.valueOf(CHARTSERVER_EXTENDED_ENCODING.charAt(index1))
|
||||
+ String.valueOf(CHARTSERVER_EXTENDED_ENCODING.charAt(index2));
|
||||
}
|
||||
|
||||
public static String getSeparator() {
|
||||
return CHARTSERVER_EXTENDED_ENCODING_SEPARATOR;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright 2012 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.util;
|
||||
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* Utilities for creating dialogs.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class DialogUtils {
|
||||
|
||||
private DialogUtils() {}
|
||||
|
||||
/**
|
||||
* Creates a confirmation dialog.
|
||||
*
|
||||
* @param context the context
|
||||
* @param messageId the message id
|
||||
* @param okListener the listener when OK is clicked
|
||||
*/
|
||||
public static Dialog createConfirmationDialog(
|
||||
Context context, int messageId, DialogInterface.OnClickListener okListener) {
|
||||
return createConfirmationDialog(context, messageId, null, okListener, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a confirmation dialog.
|
||||
*
|
||||
* @param context the context
|
||||
* @param messageId the messageId
|
||||
* @param view the view
|
||||
* @param okListener the listener when OK is clicked
|
||||
* @param cancelListener the listener when cancel is clicked
|
||||
*/
|
||||
public static Dialog createConfirmationDialog(
|
||||
Context context, int messageId, View view, DialogInterface.OnClickListener okListener,
|
||||
DialogInterface.OnClickListener cancelListener) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(context)
|
||||
.setCancelable(true)
|
||||
.setIcon(android.R.drawable.ic_dialog_alert)
|
||||
.setNegativeButton(android.R.string.cancel, cancelListener)
|
||||
.setPositiveButton(android.R.string.ok, okListener)
|
||||
.setTitle(R.string.generic_confirm_title);
|
||||
if (messageId != -1) {
|
||||
builder.setMessage(messageId);
|
||||
}
|
||||
if (view != null) {
|
||||
builder.setView(view);
|
||||
}
|
||||
|
||||
return builder.create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a spinner progress dialog.
|
||||
*
|
||||
* @param context the context
|
||||
* @param messageId the progress message id
|
||||
* @param onCancelListener the cancel listener
|
||||
*/
|
||||
public static ProgressDialog createSpinnerProgressDialog(
|
||||
Context context, int messageId, DialogInterface.OnCancelListener onCancelListener) {
|
||||
return createProgressDialog(true, context, messageId, onCancelListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a horizontal progress dialog.
|
||||
*
|
||||
* @param context the context
|
||||
* @param messageId the progress message id
|
||||
* @param onCancelListener the cancel listener
|
||||
* @param formatArgs the format arguments for the messageId
|
||||
*/
|
||||
public static ProgressDialog createHorizontalProgressDialog(Context context, int messageId,
|
||||
DialogInterface.OnCancelListener onCancelListener, Object... formatArgs) {
|
||||
return createProgressDialog(false, context, messageId, onCancelListener, formatArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a progress dialog.
|
||||
*
|
||||
* @param spinner true to use the spinner style
|
||||
* @param context the context
|
||||
* @param messageId the progress message id
|
||||
* @param onCancelListener the cancel listener
|
||||
* @param formatArgs the format arguments for the message id
|
||||
*/
|
||||
private static ProgressDialog createProgressDialog(boolean spinner, Context context,
|
||||
int messageId, DialogInterface.OnCancelListener onCancelListener, Object... formatArgs) {
|
||||
ProgressDialog progressDialog = new ProgressDialog(context);
|
||||
progressDialog.setCancelable(true);
|
||||
progressDialog.setCanceledOnTouchOutside(false);
|
||||
progressDialog.setIcon(android.R.drawable.ic_dialog_info);
|
||||
progressDialog.setIndeterminate(true);
|
||||
progressDialog.setMessage(context.getString(messageId, formatArgs));
|
||||
progressDialog.setOnCancelListener(onCancelListener);
|
||||
progressDialog.setProgressStyle(spinner ? ProgressDialog.STYLE_SPINNER
|
||||
: ProgressDialog.STYLE_HORIZONTAL);
|
||||
progressDialog.setTitle(R.string.generic_progress_title);
|
||||
return progressDialog;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2008 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.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
|
||||
/**
|
||||
* Utilities for EULA.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class EulaUtils {
|
||||
|
||||
private static final String EULA_PREFERENCE_FILE = "eula";
|
||||
|
||||
// Accepting Google mobile terms of service
|
||||
private static final String ACCEPT_EULA_PREFERENCE_KEY = "eula.google_mobile_tos_accepted";
|
||||
private static final String SHOW_WELCOME_PREFERENCE_KEY = "showWelcome";
|
||||
private static final String SHOW_ENABLE_SYNC_PREFERENCE_KEY = "showEnableSync";
|
||||
|
||||
private EulaUtils() {}
|
||||
|
||||
public static boolean getAcceptEula(Context context) {
|
||||
return getValue(context, ACCEPT_EULA_PREFERENCE_KEY, false);
|
||||
}
|
||||
|
||||
public static void setAcceptEula(Context context) {
|
||||
setValue(context, ACCEPT_EULA_PREFERENCE_KEY, true);
|
||||
}
|
||||
|
||||
public static boolean getShowWelcome(Context context) {
|
||||
return getValue(context, SHOW_WELCOME_PREFERENCE_KEY, true);
|
||||
}
|
||||
|
||||
public static void setShowWelcome(Context context) {
|
||||
setValue(context, SHOW_WELCOME_PREFERENCE_KEY, false);
|
||||
}
|
||||
|
||||
public static boolean getShowEnableSync(Context context) {
|
||||
return getValue(context, SHOW_ENABLE_SYNC_PREFERENCE_KEY, true);
|
||||
}
|
||||
|
||||
public static void setShowEnableSync(Context context) {
|
||||
setValue(context, SHOW_ENABLE_SYNC_PREFERENCE_KEY, false);
|
||||
}
|
||||
|
||||
private static boolean getValue(Context context, String key, boolean defaultValue) {
|
||||
SharedPreferences sharedPreferences = context.getSharedPreferences(
|
||||
EULA_PREFERENCE_FILE, Context.MODE_PRIVATE);
|
||||
return sharedPreferences.getBoolean(key, defaultValue);
|
||||
}
|
||||
|
||||
private static void setValue(Context context, String key, boolean value) {
|
||||
SharedPreferences sharedPreferences = context.getSharedPreferences(
|
||||
EULA_PREFERENCE_FILE, Context.MODE_PRIVATE);
|
||||
Editor editor = sharedPreferences.edit();
|
||||
editor.putBoolean(key, value);
|
||||
ApiAdapterFactory.getApiAdapter().applyPreferenceChanges(editor);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* Copyright 2010 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.util;
|
||||
|
||||
import com.google.android.apps.mytracks.Constants;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import android.os.Environment;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Utilities for dealing with files.
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class FileUtils {
|
||||
|
||||
private FileUtils() {}
|
||||
|
||||
/**
|
||||
* The maximum FAT32 path length. See the FAT32 spec at
|
||||
* http://msdn.microsoft.com/en-us/windows/hardware/gg463080
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static final int MAX_FAT32_PATH_LENGTH = 260;
|
||||
|
||||
/**
|
||||
* Returns true if the external storage is available.
|
||||
*/
|
||||
public static boolean isExternalStorageAvailable() {
|
||||
String state = Environment.getExternalStorageState();
|
||||
return Environment.MEDIA_MOUNTED.equals(state)
|
||||
|| Environment.MEDIA_MOUNTED_READ_ONLY.equals(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the external storage is writable.
|
||||
*/
|
||||
public static boolean isExternalStorageWriteable() {
|
||||
String state = Environment.getExternalStorageState();
|
||||
return Environment.MEDIA_MOUNTED.equals(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the directory exists.
|
||||
*
|
||||
* @param dir the directory
|
||||
*/
|
||||
public static boolean isDirectory(File dir) {
|
||||
return dir.exists() && dir.isDirectory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures the directory exists by creating it and its parents if
|
||||
* necessary.
|
||||
*
|
||||
* @return whether the directory exists (either already existed or was
|
||||
* successfully created)
|
||||
*/
|
||||
public static boolean ensureDirectoryExists(File dir) {
|
||||
if (isDirectory(dir)) {
|
||||
return true;
|
||||
}
|
||||
return dir.mkdirs();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a path inside the My Tracks directory in the SD card.
|
||||
*
|
||||
* @param components the path components inside the mytracks directory
|
||||
* @return the full path to the destination
|
||||
*/
|
||||
public static String buildExternalDirectoryPath(String... components) {
|
||||
StringBuilder dirNameBuilder = new StringBuilder();
|
||||
dirNameBuilder.append(Environment.getExternalStorageDirectory());
|
||||
dirNameBuilder.append(File.separatorChar);
|
||||
dirNameBuilder.append(Constants.SDCARD_TOP_DIR);
|
||||
for (String component : components) {
|
||||
dirNameBuilder.append(File.separatorChar);
|
||||
dirNameBuilder.append(component);
|
||||
}
|
||||
return dirNameBuilder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a filename with the given base name (prefix) and the given
|
||||
* extension, possibly adding a suffix to ensure the file doesn't exist.
|
||||
*
|
||||
* @param directory the directory the file will live in
|
||||
* @param fileBaseName the prefix for the file name
|
||||
* @param extension the file's extension
|
||||
* @return the complete file name, without the directory
|
||||
*/
|
||||
public static synchronized String buildUniqueFileName(
|
||||
File directory, String fileBaseName, String extension) {
|
||||
return buildUniqueFileName(directory, fileBaseName, extension, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a filename with the given base and the given extension, possibly
|
||||
* adding a suffix to ensure the file doesn't exist.
|
||||
*
|
||||
* @param directory the directory the filename will be located in
|
||||
* @param base the base for the filename
|
||||
* @param extension the extension for the filename
|
||||
* @param suffix the first numeric suffix to try to use, or 0 for none
|
||||
* @return the complete filename, without the directory
|
||||
*/
|
||||
private static String buildUniqueFileName(
|
||||
File directory, String base, String extension, int suffix) {
|
||||
String suffixName = "";
|
||||
if (suffix > 0) {
|
||||
suffixName += "(" + Integer.toString(suffix) + ")";
|
||||
}
|
||||
suffixName += "." + extension;
|
||||
|
||||
String baseName = sanitizeFileName(base);
|
||||
baseName = truncateFileName(directory, baseName, suffixName);
|
||||
String fullName = baseName + suffixName;
|
||||
|
||||
if (!new File(directory, fullName).exists()) {
|
||||
return fullName;
|
||||
}
|
||||
return buildUniqueFileName(directory, base, extension, suffix + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizes the name as a valid fat32 filename. For simplicity, fat32
|
||||
* filename characters may be any combination of letters, digits, or
|
||||
* characters with code point values greater than 127. Replaces the invalid
|
||||
* characters with "_" and collapses multiple "_" together.
|
||||
*
|
||||
* @param name name
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static String sanitizeFileName(String name) {
|
||||
StringBuffer buffer = new StringBuffer(name.length());
|
||||
for (int i = 0; i < name.length(); i++) {
|
||||
int codePoint = name.codePointAt(i);
|
||||
char character = name.charAt(i);
|
||||
if (Character.isLetterOrDigit(character) || codePoint > 127 || isSpecialFat32(character)) {
|
||||
buffer.appendCodePoint(codePoint);
|
||||
} else {
|
||||
buffer.append("_");
|
||||
}
|
||||
}
|
||||
String result = buffer.toString();
|
||||
return result.replaceAll("_+", "_");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if it is a special FAT32 character.
|
||||
*
|
||||
* @param character the character
|
||||
*/
|
||||
private static boolean isSpecialFat32(char character) {
|
||||
switch (character) {
|
||||
case '$':
|
||||
case '%':
|
||||
case '\'':
|
||||
case '-':
|
||||
case '_':
|
||||
case '@':
|
||||
case '~':
|
||||
case '`':
|
||||
case '!':
|
||||
case '(':
|
||||
case ')':
|
||||
case '{':
|
||||
case '}':
|
||||
case '^':
|
||||
case '#':
|
||||
case '&':
|
||||
case '+':
|
||||
case ',':
|
||||
case ';':
|
||||
case '=':
|
||||
case '[':
|
||||
case ']':
|
||||
case ' ':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncates the name if necessary so the filename path length (directory +
|
||||
* name + suffix) meets the Fat32 path limit.
|
||||
*
|
||||
* @param directory directory
|
||||
* @param name name
|
||||
* @param suffix suffix
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static String truncateFileName(File directory, String name, String suffix) {
|
||||
// 1 at the end accounts for the FAT32 filename trailing NUL character
|
||||
int requiredLength = directory.getPath().length() + suffix.length() + 1;
|
||||
if (name.length() + requiredLength > MAX_FAT32_PATH_LENGTH) {
|
||||
int limit = MAX_FAT32_PATH_LENGTH - requiredLength;
|
||||
return name.substring(0, limit);
|
||||
} else {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2013 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.util;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
import android.os.Parcel;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Utilities for Google Feedback.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class GoogleFeedbackUtils {
|
||||
|
||||
private static final String GOOGLE_FEEDBACK_PACKAGE_NAME = "com.google.android.feedback";
|
||||
private static final String TAG = GoogleFeedbackUtils.class.getSimpleName();
|
||||
|
||||
private static Boolean available = null;
|
||||
|
||||
/**
|
||||
* Returns true if the Google Feedback is available.
|
||||
*
|
||||
* @param context the context
|
||||
*/
|
||||
public static boolean isAvailable(Context context) {
|
||||
if (available == null) {
|
||||
List<ResolveInfo> infos = context.getPackageManager().queryIntentServices(
|
||||
new Intent(Intent.ACTION_BUG_REPORT), PackageManager.MATCH_DEFAULT_ONLY);
|
||||
for (ResolveInfo info : infos) {
|
||||
if (info.serviceInfo != null && info.serviceInfo.packageName != null
|
||||
&& info.serviceInfo.packageName.equals(GOOGLE_FEEDBACK_PACKAGE_NAME)) {
|
||||
available = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (available == null) {
|
||||
available = false;
|
||||
}
|
||||
}
|
||||
return available;
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds the Google Feedback service.
|
||||
*
|
||||
* @param context the context
|
||||
*/
|
||||
public static void bindFeedback(Context context) {
|
||||
Intent intent = new Intent(Intent.ACTION_BUG_REPORT);
|
||||
ServiceConnection serviceConnection = new ServiceConnection() {
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||
try {
|
||||
service.transact(Binder.FIRST_CALL_TRANSACTION, Parcel.obtain(), null, 0);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "RemoteException", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {}
|
||||
};
|
||||
// Bind to the service after creating it if necessary
|
||||
context.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2012 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.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
|
||||
/**
|
||||
* Utilities for Google location settings
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class GoogleLocationUtils {
|
||||
|
||||
public static final String
|
||||
ACTION_GOOGLE_LOCATION_SETTINGS = "com.google.android.gsf.GOOGLE_LOCATION_SETTINGS";
|
||||
private static final String
|
||||
ACTION_GOOGLE_APPS_LOCATION_SETTINGS = "com.google.android.gsf.GOOGLE_APPS_LOCATION_SETTINGS";
|
||||
|
||||
/**
|
||||
* Returns true if the Google location settings is available.
|
||||
*/
|
||||
public static boolean isAvailable(Context context) {
|
||||
Intent intent = new Intent(ACTION_GOOGLE_APPS_LOCATION_SETTINGS);
|
||||
ResolveInfo resolveInfo = context.getPackageManager()
|
||||
.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
|
||||
return resolveInfo != null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright 2012 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.util;
|
||||
|
||||
import com.google.android.apps.mytracks.content.DescriptionGeneratorImpl;
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.io.file.TrackFileFormat;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Utilities for creating intents.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class IntentUtils {
|
||||
|
||||
public static final String TEXT_PLAIN_TYPE = "text/plain";
|
||||
private static final String BLUETOOTH_PACKAGE_NAME = "com.android.bluetooth";
|
||||
private static final String TWITTER_PACKAGE_NAME = "com.twitter.android";
|
||||
|
||||
private IntentUtils() {}
|
||||
|
||||
/**
|
||||
* Creates an intent with {@link Intent#FLAG_ACTIVITY_CLEAR_TOP} and
|
||||
* {@link Intent#FLAG_ACTIVITY_NEW_TASK}.
|
||||
*
|
||||
* @param context the context
|
||||
* @param cls the class
|
||||
*/
|
||||
public static final Intent newIntent(Context context, Class<?> cls) {
|
||||
return new Intent(context, cls).addFlags(
|
||||
Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an intent to share a track url with an app.
|
||||
*
|
||||
* @param context the context
|
||||
* @param trackId the track id
|
||||
* @param trackUrl the track url
|
||||
* @param packageName the sharing app package name
|
||||
* @param className the sharing app class name
|
||||
*/
|
||||
public static final Intent newShareUrlIntent(
|
||||
Context context, long trackId, String trackUrl, String packageName, String className) {
|
||||
Track track = MyTracksProviderUtils.Factory.get(context).getTrack(trackId);
|
||||
String trackDescription = track == null ? ""
|
||||
: new DescriptionGeneratorImpl(context).generateTrackDescription(track, null, null, false);
|
||||
boolean urlOnly = TWITTER_PACKAGE_NAME.equals(packageName)
|
||||
|| BLUETOOTH_PACKAGE_NAME.equals(packageName);
|
||||
|
||||
return new Intent(Intent.ACTION_SEND)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT | Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP)
|
||||
.putExtra(Intent.EXTRA_SUBJECT, context.getString(R.string.share_track_subject))
|
||||
.putExtra(Intent.EXTRA_TEXT, urlOnly
|
||||
? trackUrl
|
||||
: context.getString(R.string.share_track_share_url_body, trackUrl, trackDescription))
|
||||
.setComponent(new ComponentName(packageName, className))
|
||||
.setType(TEXT_PLAIN_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an intent to share a track file with an app.
|
||||
*
|
||||
* @param context the context
|
||||
* @param trackId the track id
|
||||
* @param filePath the file path
|
||||
* @param trackFileFormat the track file format
|
||||
*/
|
||||
public static final Intent newShareFileIntent(
|
||||
Context context, long trackId, String filePath, TrackFileFormat trackFileFormat) {
|
||||
Track track = MyTracksProviderUtils.Factory.get(context).getTrack(trackId);
|
||||
String trackDescription = track == null ? ""
|
||||
: new DescriptionGeneratorImpl(context).generateTrackDescription(track, null, null, false);
|
||||
|
||||
return new Intent(Intent.ACTION_SEND)
|
||||
.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(new File(filePath)))
|
||||
.putExtra(Intent.EXTRA_SUBJECT, context.getString(R.string.share_track_subject))
|
||||
.putExtra(Intent.EXTRA_TEXT,
|
||||
context.getString(R.string.share_track_share_file_body, trackDescription))
|
||||
.putExtra(context.getString(R.string.track_id_broadcast_extra), trackId)
|
||||
.setType(trackFileFormat.getMimeType());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* Copyright 2012 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.util;
|
||||
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.format.DateUtils;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
||||
/**
|
||||
* Utilities to display a list item.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class ListItemUtils {
|
||||
|
||||
private ListItemUtils() {}
|
||||
|
||||
/**
|
||||
* Sets a list item.
|
||||
*
|
||||
* @param context the context
|
||||
* @param view the list item view
|
||||
* @param isRecording true if recording
|
||||
* @param isPaused true if paused
|
||||
* @param name the name value
|
||||
* @param iconId the icon id
|
||||
* @param iconContentDescriptionId the icon content description id
|
||||
* @param category the category value
|
||||
* @param totalTime the total time value
|
||||
* @param totalDistance the total distance value
|
||||
* @param startTime the start time value
|
||||
* @param description the description value
|
||||
*/
|
||||
public static void setListItem(Context context, View view, boolean isRecording, boolean isPaused,
|
||||
int iconId, int iconContentDescriptionId, String name, String category, String totalTime,
|
||||
String totalDistance, long startTime, String description, String sharedOwner) {
|
||||
|
||||
if (isRecording) {
|
||||
iconId = isPaused ? R.drawable.track_paused : R.drawable.track_recording;
|
||||
iconContentDescriptionId = isPaused ? R.string.icon_pause_recording
|
||||
: R.string.icon_record_track;
|
||||
}
|
||||
|
||||
ImageView iconImageView = (ImageView) view.findViewById(R.id.list_item_icon);
|
||||
iconImageView.setImageResource(iconId);
|
||||
iconImageView.setContentDescription(context.getString(iconContentDescriptionId));
|
||||
|
||||
TextView nameTextView = (TextView) view.findViewById(R.id.list_item_name);
|
||||
nameTextView.setText(name);
|
||||
|
||||
TextView timeDistanceTextView = (TextView) view.findViewById(R.id.list_item_time_distance);
|
||||
setTextView(timeDistanceTextView,
|
||||
getTimeDistance(isRecording, sharedOwner, totalTime, totalDistance), 0);
|
||||
|
||||
String[] startTimeDisplay = getStartTime(isRecording, context, startTime);
|
||||
TextView dateTextView = (TextView) view.findViewById(R.id.list_item_date);
|
||||
setTextView(dateTextView, startTimeDisplay[0], 0);
|
||||
|
||||
TextView timeTextView = (TextView) view.findViewById(R.id.list_item_time);
|
||||
setTextView(timeTextView, startTimeDisplay[1], 0);
|
||||
|
||||
TextView recordingTextView = (TextView) view.findViewById(R.id.list_item_recording);
|
||||
String value = isRecording ? context.getString(
|
||||
isPaused ? R.string.generic_paused : R.string.generic_recording)
|
||||
: null;
|
||||
int color = isRecording ? context.getResources()
|
||||
.getColor(isPaused ? android.R.color.white : R.color.red)
|
||||
: 0;
|
||||
setTextView(recordingTextView, value, color);
|
||||
|
||||
TextView descriptionTextView = (TextView) view.findViewById(R.id.list_item_description);
|
||||
setTextView(descriptionTextView, getDescription(isRecording, category, description), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the time/distance text.
|
||||
*
|
||||
* @param isRecording true if recording
|
||||
* @param sharedOwner the shared owner
|
||||
* @param totalTime the total time
|
||||
* @param totalDistance the total distance
|
||||
*/
|
||||
private static String getTimeDistance(
|
||||
boolean isRecording, String sharedOwner, String totalTime, String totalDistance) {
|
||||
if (isRecording) {
|
||||
return null;
|
||||
}
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
if (sharedOwner != null && sharedOwner.length() != 0) {
|
||||
buffer.append(sharedOwner);
|
||||
}
|
||||
if (totalTime != null && totalTime.length() != 0) {
|
||||
if (buffer.length() != 0) {
|
||||
buffer.append(" \u2027 ");
|
||||
}
|
||||
buffer.append(totalTime);
|
||||
}
|
||||
if (totalDistance != null && totalDistance.length() != 0) {
|
||||
if (buffer.length() != 0) {
|
||||
buffer.append(" ");
|
||||
}
|
||||
buffer.append(" / " + totalDistance);
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the description text.
|
||||
*
|
||||
* @param isRecording true if recording
|
||||
* @param category the category
|
||||
* @param description the description
|
||||
*/
|
||||
private static String getDescription(boolean isRecording, String category, String description) {
|
||||
if (isRecording) {
|
||||
return null;
|
||||
}
|
||||
if (category == null || category.length() == 0) {
|
||||
return description;
|
||||
}
|
||||
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
|
||||
buffer.append("[" + category + "]");
|
||||
if (description != null && description.length() != 0) {
|
||||
buffer.append(" " + description);
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the start time text.
|
||||
*
|
||||
* @param isRecording true if recording
|
||||
* @param context the context
|
||||
* @param startTime the start time
|
||||
* @return array of two strings.
|
||||
*/
|
||||
private static String[] getStartTime(boolean isRecording, Context context, long startTime) {
|
||||
if (isRecording || startTime == 0L) {
|
||||
return new String[] { null, null };
|
||||
}
|
||||
if (DateUtils.isToday(startTime)) {
|
||||
return new String[] { DateUtils.getRelativeTimeSpanString(
|
||||
startTime, System.currentTimeMillis(), DateUtils.MINUTE_IN_MILLIS,
|
||||
DateUtils.FORMAT_ABBREV_RELATIVE).toString(), null };
|
||||
}
|
||||
int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_ALL;
|
||||
if (!isThisYear(startTime)) {
|
||||
flags |= DateUtils.FORMAT_NUMERIC_DATE;
|
||||
}
|
||||
return new String[] { DateUtils.formatDateTime(context, startTime, flags),
|
||||
DateUtils.formatDateTime(
|
||||
context, startTime, DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_ABBREV_TIME) };
|
||||
}
|
||||
|
||||
/**
|
||||
* True if the time is this year.
|
||||
*
|
||||
* @param time the time
|
||||
*/
|
||||
private static boolean isThisYear(long time) {
|
||||
Calendar now = Calendar.getInstance();
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
now.setTimeInMillis(System.currentTimeMillis());
|
||||
calendar.setTimeInMillis(time);
|
||||
return now.get(Calendar.YEAR) == calendar.get(Calendar.YEAR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a text view.
|
||||
*
|
||||
* @param textView the text view
|
||||
* @param value the value for the text view
|
||||
*/
|
||||
private static void setTextView(TextView textView, String value, int color) {
|
||||
if (value == null || value.length() == 0) {
|
||||
textView.setVisibility(View.GONE);
|
||||
} else {
|
||||
textView.setVisibility(View.VISIBLE);
|
||||
textView.setText(value);
|
||||
if (color != 0) {
|
||||
textView.setTextColor(color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright 2008 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.util;
|
||||
|
||||
import com.google.android.apps.mytracks.Constants;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
|
||||
import android.location.Location;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Stack;
|
||||
|
||||
/**
|
||||
* Utility class for decimating tracks at a given level of precision.
|
||||
*
|
||||
* @author Leif Hendrik Wilden
|
||||
*/
|
||||
public class LocationUtils {
|
||||
|
||||
private LocationUtils() {}
|
||||
|
||||
/**
|
||||
* Computes the distance on the two sphere between the point c0 and the line
|
||||
* segment c1 to c2.
|
||||
*
|
||||
* @param c0 the first coordinate
|
||||
* @param c1 the beginning of the line segment
|
||||
* @param c2 the end of the lone segment
|
||||
* @return the distance in m (assuming spherical earth)
|
||||
*/
|
||||
private static double distance(final Location c0, final Location c1, final Location c2) {
|
||||
if (c1.equals(c2)) {
|
||||
return c2.distanceTo(c0);
|
||||
}
|
||||
|
||||
final double s0lat = c0.getLatitude() * UnitConversions.DEG_TO_RAD;
|
||||
final double s0lng = c0.getLongitude() * UnitConversions.DEG_TO_RAD;
|
||||
final double s1lat = c1.getLatitude() * UnitConversions.DEG_TO_RAD;
|
||||
final double s1lng = c1.getLongitude() * UnitConversions.DEG_TO_RAD;
|
||||
final double s2lat = c2.getLatitude() * UnitConversions.DEG_TO_RAD;
|
||||
final double s2lng = c2.getLongitude() * UnitConversions.DEG_TO_RAD;
|
||||
|
||||
double s2s1lat = s2lat - s1lat;
|
||||
double s2s1lng = s2lng - s1lng;
|
||||
final double u = ((s0lat - s1lat) * s2s1lat + (s0lng - s1lng) * s2s1lng)
|
||||
/ (s2s1lat * s2s1lat + s2s1lng * s2s1lng);
|
||||
if (u <= 0) {
|
||||
return c0.distanceTo(c1);
|
||||
}
|
||||
if (u >= 1) {
|
||||
return c0.distanceTo(c2);
|
||||
}
|
||||
Location sa = new Location("");
|
||||
sa.setLatitude(c0.getLatitude() - c1.getLatitude());
|
||||
sa.setLongitude(c0.getLongitude() - c1.getLongitude());
|
||||
Location sb = new Location("");
|
||||
sb.setLatitude(u * (c2.getLatitude() - c1.getLatitude()));
|
||||
sb.setLongitude(u * (c2.getLongitude() - c1.getLongitude()));
|
||||
return sa.distanceTo(sb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decimates the given locations for a given zoom level. This uses a
|
||||
* Douglas-Peucker decimation algorithm.
|
||||
*
|
||||
* @param tolerance in meters
|
||||
* @param locations input
|
||||
* @param decimated output
|
||||
*/
|
||||
private static void decimate(
|
||||
double tolerance, ArrayList<Location> locations, ArrayList<Location> decimated) {
|
||||
final int n = locations.size();
|
||||
if (n < 1) {
|
||||
return;
|
||||
}
|
||||
int idx;
|
||||
int maxIdx = 0;
|
||||
Stack<int[]> stack = new Stack<int[]>();
|
||||
double[] dists = new double[n];
|
||||
dists[0] = 1;
|
||||
dists[n - 1] = 1;
|
||||
double maxDist;
|
||||
double dist = 0.0;
|
||||
int[] current;
|
||||
|
||||
if (n > 2) {
|
||||
int[] stackVal = new int[] { 0, (n - 1) };
|
||||
stack.push(stackVal);
|
||||
while (stack.size() > 0) {
|
||||
current = stack.pop();
|
||||
maxDist = 0;
|
||||
for (idx = current[0] + 1; idx < current[1]; ++idx) {
|
||||
dist = LocationUtils.distance(
|
||||
locations.get(idx), locations.get(current[0]), locations.get(current[1]));
|
||||
if (dist > maxDist) {
|
||||
maxDist = dist;
|
||||
maxIdx = idx;
|
||||
}
|
||||
}
|
||||
if (maxDist > tolerance) {
|
||||
dists[maxIdx] = maxDist;
|
||||
int[] stackValCurMax = { current[0], maxIdx };
|
||||
stack.push(stackValCurMax);
|
||||
int[] stackValMaxCur = { maxIdx, current[1] };
|
||||
stack.push(stackValMaxCur);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
idx = 0;
|
||||
decimated.clear();
|
||||
for (Location l : locations) {
|
||||
if (dists[idx] != 0) {
|
||||
decimated.add(l);
|
||||
i++;
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
Log.d(Constants.TAG, "Decimating " + n + " points to " + i + " w/ tolerance = " + tolerance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decimates the given track for the given precision.
|
||||
*
|
||||
* @param track a track
|
||||
* @param precision desired precision in meters
|
||||
*/
|
||||
public static void decimate(Track track, double precision) {
|
||||
ArrayList<Location> decimated = new ArrayList<Location>();
|
||||
decimate(precision, track.getLocations(), decimated);
|
||||
track.setLocations(decimated);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given location is a valid (i.e. physically possible) location
|
||||
* on Earth. Note: The special separator locations (which have latitude = 100)
|
||||
* will not qualify as valid. Neither will locations with lat=0 and lng=0 as
|
||||
* these are most likely "bad" measurements which often cause trouble.
|
||||
*
|
||||
* @param location the location to test
|
||||
* @return true if the location is a valid location.
|
||||
*/
|
||||
public static boolean isValidLocation(Location location) {
|
||||
return location != null && Math.abs(location.getLatitude()) <= 90
|
||||
&& Math.abs(location.getLongitude()) <= 180;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* Copyright 2012 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.util;
|
||||
|
||||
import com.google.android.apps.mytracks.Constants;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
|
||||
/**
|
||||
* Utilities to access preferences stored in {@link SharedPreferences}.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class PreferencesUtils {
|
||||
|
||||
/*
|
||||
* Preferences values. The defaults need to match the defaults in the xml
|
||||
* files.
|
||||
*/
|
||||
public static final boolean ALLOW_ACCESS_DEFAULT = false;
|
||||
public static final int AUTO_RESUME_TRACK_CURRENT_RETRY_DEFAULT = 0;
|
||||
|
||||
// Values for auto_resume_track_timeout_key
|
||||
public static final int AUTO_RESUME_TRACK_TIMEOUT_ALWAYS = -1;
|
||||
public static final int AUTO_RESUME_TRACK_TIMEOUT_DEFAULT = 10;
|
||||
public static final int AUTO_RESUME_TRACK_TIMEOUT_NEVER = 0;
|
||||
|
||||
public static final String BLUETOOTH_SENSOR_DEFAULT = "";
|
||||
public static final boolean CHART_BY_DISTANCE_DEFAULT = true;
|
||||
public static final boolean CHART_SHOW_CADENCE_DEFAULT = true;
|
||||
public static final boolean CHART_SHOW_ELEVATION_DEFAULT = true;
|
||||
public static final boolean CHART_SHOW_HEART_RATE_DEFAULT = true;
|
||||
public static final boolean CHART_SHOW_POWER_DEFAULT = true;
|
||||
public static final boolean CHART_SHOW_SPEED_DEFAULT = true;
|
||||
|
||||
public static final boolean CONFIRM_PLAY_EARTH_DEFAULT = true;
|
||||
public static final boolean CONFIRM_SHARE_DRIVE_DEFAULT = true;
|
||||
public static final boolean CONFIRM_SHARE_MAPS_DEFAULT = true;
|
||||
|
||||
public static final String DEFAULT_ACTIVITY_DEFAULT = "";
|
||||
public static final boolean DEFAULT_MAP_PUBLIC_DEFAULT = false;
|
||||
public static final boolean DEFAULT_TABLE_PUBLIC_DEFAULT = false;
|
||||
public static final String DRIVE_DELETED_LIST_DEFAULT = "";
|
||||
public static final long DRIVE_LARGEST_CHANGE_ID_DEFAULT = -1L;
|
||||
public static final boolean DRIVE_SYNC_DEFAULT = false;
|
||||
|
||||
// Value for split_frequency_key and voice_frequency_key
|
||||
public static final int FREQUENCY_OFF = 0;
|
||||
|
||||
public static final String GOOGLE_ACCOUNT_DEFAULT = "";
|
||||
public static final int MAX_RECORDING_DISTANCE_DEFAULT = 200;
|
||||
public static final boolean METRIC_UNITS_DEFAULT = true;
|
||||
public static final int MIN_RECORDING_DISTANCE_DEFAULT = 5;
|
||||
|
||||
// Values for min_recording_interval_key
|
||||
public static final int MIN_RECORDING_INTERVAL_ADAPT_ACCURACY = -1;
|
||||
public static final int MIN_RECORDING_INTERVAL_ADAPT_BATTERY_LIFE = -2;
|
||||
public static final int MIN_RECORDING_INTERVAL_DEFAULT = 0;
|
||||
|
||||
// Values for min_required_accuracy
|
||||
public static final int MIN_REQUIRED_ACCURACY_DEFAULT = 200;
|
||||
public static final int MIN_REQUIRED_ACCURACY_EXCELLENT = 10;
|
||||
public static final int MIN_REQUIRED_ACCURACY_POOR = 5000;
|
||||
|
||||
public static final boolean PICK_EXISTING_MAP_DEFAULT = false;
|
||||
public static final long RECORDING_TRACK_ID_DEFAULT = -1L;
|
||||
public static final boolean RECORDING_TRACK_PAUSED_DEFAULT = true;
|
||||
public static final boolean REPORT_SPEED_DEFAULT = true;
|
||||
public static final long SELECTED_TRACK_ID_DEFAULT = -1L;
|
||||
|
||||
public static final boolean SEND_TO_DRIVE_DEFAULT = false;
|
||||
public static final boolean SEND_TO_FUSION_TABLES_DEFAULT = false;
|
||||
public static final boolean SEND_TO_MAPS_DEFAULT = false;
|
||||
public static final boolean SEND_TO_SPREADSHEETS_DEFAULT = false;
|
||||
|
||||
public static final String SENSOR_TYPE_DEFAULT = "NONE";
|
||||
public static final int SPLIT_FREQUENCY_DEFAULT = 0;
|
||||
public static final boolean STATS_SHOW_COORDINATE_DEFAULT = false;
|
||||
public static final boolean STATS_SHOW_ELEVATION_DEFAULT = false;
|
||||
public static final boolean STATS_SHOW_GRADE_DEFAULT = false;
|
||||
public static final boolean STATS_SHOW_MOVING_TIME_DEFAULT = false;
|
||||
public static final String TRACK_COLOR_MODE_DEFAULT = "SINGLE";
|
||||
public static final int TRACK_COLOR_MODE_MEDIUM_DEFAULT = 15;
|
||||
public static final int TRACK_COLOR_MODE_PERCENTAGE_DEFAULT = 25;
|
||||
public static final int TRACK_COLOR_MODE_SLOW_DEFAULT = 9;
|
||||
public static final String TRACK_NAME_DEFAULT = "LOCATION";
|
||||
public static final int TRACK_WIDGET_ITEM1_DEFAULT = 3; // moving time
|
||||
public static final int TRACK_WIDGET_ITEM2_DEFAULT = 0; // distance
|
||||
public static final int TRACK_WIDGET_ITEM3_DEFAULT = 1; // total time
|
||||
public static final int TRACK_WIDGET_ITEM4_DEFAULT = 2; // average speed
|
||||
public static final int VOICE_FREQUENCY_DEFAULT = 0;
|
||||
|
||||
private PreferencesUtils() {}
|
||||
|
||||
/**
|
||||
* Gets a preference key
|
||||
*
|
||||
* @param context the context
|
||||
* @param keyId the key id
|
||||
*/
|
||||
public static String getKey(Context context, int keyId) {
|
||||
return context.getString(keyId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a boolean preference value.
|
||||
*
|
||||
* @param context the context
|
||||
* @param keyId the key id
|
||||
* @param defaultValue the default value
|
||||
*/
|
||||
public static boolean getBoolean(Context context, int keyId, boolean defaultValue) {
|
||||
SharedPreferences sharedPreferences = context.getSharedPreferences(
|
||||
Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
return sharedPreferences.getBoolean(getKey(context, keyId), defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a boolean preference value.
|
||||
*
|
||||
* @param context the context
|
||||
* @param keyId the key id
|
||||
* @param value the value
|
||||
*/
|
||||
@SuppressLint("CommitPrefEdits")
|
||||
public static void setBoolean(Context context, int keyId, boolean value) {
|
||||
SharedPreferences sharedPreferences = context.getSharedPreferences(
|
||||
Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
Editor editor = sharedPreferences.edit();
|
||||
editor.putBoolean(getKey(context, keyId), value);
|
||||
ApiAdapterFactory.getApiAdapter().applyPreferenceChanges(editor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an integer preference value.
|
||||
*
|
||||
* @param context the context
|
||||
* @param keyId the key id
|
||||
* @param defaultValue the default value
|
||||
*/
|
||||
public static int getInt(Context context, int keyId, int defaultValue) {
|
||||
SharedPreferences sharedPreferences = context.getSharedPreferences(
|
||||
Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
return sharedPreferences.getInt(getKey(context, keyId), defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an integer preference value.
|
||||
*
|
||||
* @param context the context
|
||||
* @param keyId the key id
|
||||
* @param value the value
|
||||
*/
|
||||
@SuppressLint("CommitPrefEdits")
|
||||
public static void setInt(Context context, int keyId, int value) {
|
||||
SharedPreferences sharedPreferences = context.getSharedPreferences(
|
||||
Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
Editor editor = sharedPreferences.edit();
|
||||
editor.putInt(getKey(context, keyId), value);
|
||||
ApiAdapterFactory.getApiAdapter().applyPreferenceChanges(editor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a long preference value.
|
||||
*
|
||||
* @param context the context
|
||||
* @param keyId the key id
|
||||
*/
|
||||
public static long getLong(Context context, int keyId) {
|
||||
SharedPreferences sharedPreferences = context.getSharedPreferences(
|
||||
Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
return sharedPreferences.getLong(getKey(context, keyId), -1L);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a long preference value.
|
||||
*
|
||||
* @param context the context
|
||||
* @param keyId the key id
|
||||
* @param value the value
|
||||
*/
|
||||
@SuppressLint("CommitPrefEdits")
|
||||
public static void setLong(Context context, int keyId, long value) {
|
||||
SharedPreferences sharedPreferences = context.getSharedPreferences(
|
||||
Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
Editor editor = sharedPreferences.edit();
|
||||
editor.putLong(getKey(context, keyId), value);
|
||||
ApiAdapterFactory.getApiAdapter().applyPreferenceChanges(editor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string preference value.
|
||||
*
|
||||
* @param context the context
|
||||
* @param keyId the key id
|
||||
* @param defaultValue default value
|
||||
*/
|
||||
public static String getString(Context context, int keyId, String defaultValue) {
|
||||
SharedPreferences sharedPreferences = context.getSharedPreferences(
|
||||
Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
return sharedPreferences.getString(getKey(context, keyId), defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a string preference value.
|
||||
*
|
||||
* @param context the context
|
||||
* @param keyId the key id
|
||||
* @param value the value
|
||||
*/
|
||||
@SuppressLint("CommitPrefEdits")
|
||||
public static void setString(Context context, int keyId, String value) {
|
||||
SharedPreferences sharedPreferences = context.getSharedPreferences(
|
||||
Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
Editor editor = sharedPreferences.edit();
|
||||
editor.putString(getKey(context, keyId), value);
|
||||
ApiAdapterFactory.getApiAdapter().applyPreferenceChanges(editor);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,352 @@
|
||||
/*
|
||||
* Copyright 2012 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.util;
|
||||
|
||||
import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.location.Location;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
/**
|
||||
* Utilities for updating the statistics UI labels and values.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class StatsUtils {
|
||||
|
||||
private StatsUtils() {}
|
||||
|
||||
/**
|
||||
* Sets the location values.
|
||||
*
|
||||
* @param activity the activity
|
||||
* @param location the location
|
||||
* @param showAll true to show all the fields, false to show only the
|
||||
* elevation field
|
||||
*/
|
||||
public static void setLocationValues(Activity activity, Location location, boolean showAll) {
|
||||
boolean metricUnits = PreferencesUtils.getBoolean(
|
||||
activity, R.string.metric_units_key, PreferencesUtils.METRIC_UNITS_DEFAULT);
|
||||
boolean reportSpeed = PreferencesUtils.getBoolean(
|
||||
activity, R.string.report_speed_key, PreferencesUtils.REPORT_SPEED_DEFAULT);
|
||||
|
||||
// Set elevation
|
||||
boolean showElevation = PreferencesUtils.getBoolean(
|
||||
activity, R.string.stats_show_elevation_key, PreferencesUtils.STATS_SHOW_ELEVATION_DEFAULT);
|
||||
if (showElevation) {
|
||||
double altitude = location == null ? Double.NaN : location.getAltitude();
|
||||
setElevationValue(activity, R.id.stats_elevation_value, altitude, metricUnits);
|
||||
}
|
||||
|
||||
if (!showAll) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set speed/pace
|
||||
setSpeedLabel(
|
||||
activity, R.id.stats_speed_label, R.string.stats_speed, R.string.stats_pace, reportSpeed);
|
||||
double speed = location == null ? Double.NaN : location.getSpeed();
|
||||
setSpeedValue(activity, R.id.stats_speed_value, speed, metricUnits, reportSpeed);
|
||||
|
||||
// Set coordinate
|
||||
boolean showCoordinate = PreferencesUtils.getBoolean(activity,
|
||||
R.string.stats_show_coordinate_key, PreferencesUtils.STATS_SHOW_COORDINATE_DEFAULT);
|
||||
View coordinateLabelTableRow = activity.findViewById(R.id.stats_coordinate_label_table_row);
|
||||
View coordinateValueTableRow = activity.findViewById(R.id.stats_coordinate_value_table_row);
|
||||
if (coordinateLabelTableRow == null || coordinateValueTableRow == null) {
|
||||
return;
|
||||
}
|
||||
coordinateLabelTableRow.setVisibility(showCoordinate ? View.VISIBLE : View.GONE);
|
||||
coordinateValueTableRow.setVisibility(showCoordinate ? View.VISIBLE : View.GONE);
|
||||
if (showCoordinate) {
|
||||
double latitude = location == null ? Double.NaN : location.getLatitude();
|
||||
double longitude = location == null ? Double.NaN : location.getLongitude();
|
||||
setCoordinateValue(activity, R.id.stats_latitude_value, latitude);
|
||||
setCoordinateValue(activity, R.id.stats_longitude_value, longitude);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the total time value.
|
||||
*
|
||||
* @param activity the activity
|
||||
* @param totalTime the total time
|
||||
*/
|
||||
public static void setTotalTimeValue(Activity activity, long totalTime) {
|
||||
setTimeValue(activity, R.id.stats_total_time_value, totalTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the trip statistics values.
|
||||
*
|
||||
* @param activity the activity
|
||||
* @param tripStatistics the trip statistics
|
||||
*/
|
||||
public static void setTripStatisticsValues(Activity activity, TripStatistics tripStatistics) {
|
||||
boolean metricUnits = PreferencesUtils.getBoolean(
|
||||
activity, R.string.metric_units_key, PreferencesUtils.METRIC_UNITS_DEFAULT);
|
||||
boolean reportSpeed = PreferencesUtils.getBoolean(
|
||||
activity, R.string.report_speed_key, PreferencesUtils.REPORT_SPEED_DEFAULT);
|
||||
boolean showMovingTime = PreferencesUtils.getBoolean(activity,
|
||||
R.string.stats_show_moving_time_key, PreferencesUtils.STATS_SHOW_MOVING_TIME_DEFAULT);
|
||||
|
||||
// Set total distance
|
||||
double totalDistance = tripStatistics == null ? Double.NaN : tripStatistics.getTotalDistance();
|
||||
setDistanceValue(activity, R.id.stats_distance_value, totalDistance, metricUnits);
|
||||
|
||||
// Set total time
|
||||
setTimeValue(activity, R.id.stats_total_time_value,
|
||||
tripStatistics != null ? tripStatistics.getTotalTime() : -1L);
|
||||
|
||||
// Set moving time
|
||||
setItemVisibility(activity, R.id.stats_moving_time_label, R.id.stats_moving_time_spacer,
|
||||
R.id.stats_moving_time_value, showMovingTime);
|
||||
if (showMovingTime) {
|
||||
setTimeValue(activity, R.id.stats_moving_time_value,
|
||||
tripStatistics != null ? tripStatistics.getMovingTime() : -1L);
|
||||
}
|
||||
|
||||
// Set average speed
|
||||
setSpeedLabel(activity, R.id.stats_average_speed_label, R.string.stats_average_speed,
|
||||
R.string.stats_average_pace, reportSpeed);
|
||||
double averageSpeed = tripStatistics != null ? tripStatistics.getAverageSpeed() : Double.NaN;
|
||||
setSpeedValue(activity, R.id.stats_average_speed_value, averageSpeed, metricUnits, reportSpeed);
|
||||
|
||||
// Set average moving speed
|
||||
setItemVisibility(activity, R.id.stats_average_moving_speed_label,
|
||||
R.id.stats_average_moving_speed_spacer, R.id.stats_average_moving_speed_value,
|
||||
showMovingTime);
|
||||
if (showMovingTime) {
|
||||
setSpeedLabel(activity, R.id.stats_average_moving_speed_label,
|
||||
R.string.stats_average_moving_speed, R.string.stats_average_moving_pace, reportSpeed);
|
||||
double averageMovingSpeed = tripStatistics != null ? tripStatistics.getAverageMovingSpeed()
|
||||
: Double.NaN;
|
||||
setSpeedValue(activity, R.id.stats_average_moving_speed_value, averageMovingSpeed,
|
||||
metricUnits, reportSpeed);
|
||||
}
|
||||
|
||||
// Set max speed
|
||||
setSpeedLabel(activity, R.id.stats_max_speed_label, R.string.stats_max_speed,
|
||||
R.string.stats_fastest_pace, reportSpeed);
|
||||
double maxSpeed = tripStatistics == null ? Double.NaN : tripStatistics.getMaxSpeed();
|
||||
setSpeedValue(activity, R.id.stats_max_speed_value, maxSpeed, metricUnits, reportSpeed);
|
||||
|
||||
// Set elevation
|
||||
boolean showElevation = PreferencesUtils.getBoolean(
|
||||
activity, R.string.stats_show_elevation_key, PreferencesUtils.STATS_SHOW_ELEVATION_DEFAULT);
|
||||
View elevationLabelTableRow1 = activity.findViewById(R.id.stats_elevation_label_table_row1);
|
||||
View elevationValueTableRow1 = activity.findViewById(R.id.stats_elevation_value_table_row1);
|
||||
View elevationLabelTableRow2 = activity.findViewById(R.id.stats_elevation_label_table_row2);
|
||||
View elevationValueTableRow2 = activity.findViewById(R.id.stats_elevation_value_table_row2);
|
||||
if (elevationLabelTableRow1 == null
|
||||
|| elevationValueTableRow1 == null
|
||||
|| elevationLabelTableRow2 == null
|
||||
|| elevationValueTableRow2 == null) {
|
||||
return;
|
||||
}
|
||||
elevationLabelTableRow1.setVisibility(showElevation ? View.VISIBLE : View.GONE);
|
||||
elevationValueTableRow1.setVisibility(showElevation ? View.VISIBLE : View.GONE);
|
||||
elevationLabelTableRow2.setVisibility(showElevation ? View.VISIBLE : View.GONE);
|
||||
elevationValueTableRow2.setVisibility(showElevation ? View.VISIBLE : View.GONE);
|
||||
if (showElevation) {
|
||||
double elevationGain = tripStatistics == null ? Double.NaN
|
||||
: tripStatistics.getTotalElevationGain();
|
||||
double minElevation = tripStatistics == null ? Double.NaN : tripStatistics.getMinElevation();
|
||||
double maxElevation = tripStatistics == null ? Double.NaN : tripStatistics.getMaxElevation();
|
||||
setElevationValue(activity, R.id.stats_elevation_gain_value, elevationGain, metricUnits);
|
||||
setElevationValue(activity, R.id.stats_min_elevation_value, minElevation, metricUnits);
|
||||
setElevationValue(activity, R.id.stats_max_elevation_value, maxElevation, metricUnits);
|
||||
}
|
||||
|
||||
// Set grade
|
||||
boolean showGrade = PreferencesUtils.getBoolean(
|
||||
activity, R.string.stats_show_grade_key, PreferencesUtils.STATS_SHOW_GRADE_DEFAULT);
|
||||
View gradeLabelTableRow = activity.findViewById(R.id.stats_grade_label_table_row);
|
||||
View gradeValueTableRow = activity.findViewById(R.id.stats_grade_value_table_row);
|
||||
if (gradeLabelTableRow == null || gradeValueTableRow == null) {
|
||||
return;
|
||||
}
|
||||
gradeLabelTableRow.setVisibility(showGrade ? View.VISIBLE : View.GONE);
|
||||
gradeValueTableRow.setVisibility(showGrade ? View.VISIBLE : View.GONE);
|
||||
if (showGrade) {
|
||||
double minGrade = tripStatistics == null ? Double.NaN : tripStatistics.getMinGrade();
|
||||
double maxGrade = tripStatistics == null ? Double.NaN : tripStatistics.getMaxGrade();
|
||||
setGradeValue(activity, R.id.stats_min_grade_value, minGrade);
|
||||
setGradeValue(activity, R.id.stats_max_grade_value, maxGrade);
|
||||
}
|
||||
}
|
||||
|
||||
private static void setItemVisibility(
|
||||
Activity activity, int labelId, int spacerId, int valueId, boolean show) {
|
||||
View label = activity.findViewById(labelId);
|
||||
View spacer = activity.findViewById(spacerId);
|
||||
View value = activity.findViewById(valueId);
|
||||
if (label != null) {
|
||||
label.setVisibility(show ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
if (spacer != null) {
|
||||
spacer.setVisibility(show ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
if (value != null) {
|
||||
value.setVisibility(show ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a speed label.
|
||||
*
|
||||
* @param activity the activity
|
||||
* @param id the speed label resource id
|
||||
* @param speedId the speed string id
|
||||
* @param paceId the pace string id
|
||||
* @param reportSpeed true to report speed
|
||||
*/
|
||||
private static void setSpeedLabel(
|
||||
Activity activity, int id, int speedId, int paceId, boolean reportSpeed) {
|
||||
TextView textView = (TextView) activity.findViewById(id);
|
||||
if (textView == null) {
|
||||
return;
|
||||
}
|
||||
textView.setText(reportSpeed ? speedId : paceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a speed value.
|
||||
*
|
||||
* @param activity the activity
|
||||
* @param id the speed value resource id
|
||||
* @param speed the speed in meters per second
|
||||
* @param metricUnits true to display in metric units
|
||||
* @param reportSpeed true to report speed
|
||||
*/
|
||||
private static void setSpeedValue(
|
||||
Activity activity, int id, double speed, boolean metricUnits, boolean reportSpeed) {
|
||||
TextView textView = (TextView) activity.findViewById(id);
|
||||
if (textView == null) {
|
||||
return;
|
||||
}
|
||||
textView.setText(StringUtils.formatSpeed(activity, speed, metricUnits, reportSpeed));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a distance value.
|
||||
*
|
||||
* @param activity the activity
|
||||
* @param id the distance value resource id
|
||||
* @param distance the distance in meters
|
||||
* @param metricUnits true to display in metric units
|
||||
*/
|
||||
private static void setDistanceValue(
|
||||
Activity activity, int id, double distance, boolean metricUnits) {
|
||||
TextView textView = (TextView) activity.findViewById(id);
|
||||
if (textView == null) {
|
||||
return;
|
||||
}
|
||||
textView.setText(StringUtils.formatDistance(activity, distance, metricUnits));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a time value.
|
||||
*
|
||||
* @param activity the activity
|
||||
* @param id the time value resource id
|
||||
* @param time the time
|
||||
*/
|
||||
private static void setTimeValue(Activity activity, int id, long time) {
|
||||
TextView textView = (TextView) activity.findViewById(id);
|
||||
if (textView == null) {
|
||||
return;
|
||||
}
|
||||
String value = time == -1L ? activity.getString(R.string.value_unknown)
|
||||
: StringUtils.formatElapsedTime(time);
|
||||
textView.setText(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an elevation value.
|
||||
*
|
||||
* @param activity the activity
|
||||
* @param id the elevation value resource id
|
||||
* @param elevation the elevation in meters
|
||||
* @param metricUnits true to display in metric units
|
||||
*/
|
||||
private static void setElevationValue(
|
||||
Activity activity, int id, double elevation, boolean metricUnits) {
|
||||
TextView textView = (TextView) activity.findViewById(id);
|
||||
if (textView == null) {
|
||||
return;
|
||||
}
|
||||
String value;
|
||||
if (Double.isNaN(elevation) || Double.isInfinite(elevation)) {
|
||||
value = activity.getString(R.string.value_unknown);
|
||||
} else {
|
||||
if (metricUnits) {
|
||||
value = activity.getString(R.string.value_float_meter, elevation);
|
||||
} else {
|
||||
elevation *= UnitConversions.M_TO_FT;
|
||||
value = activity.getString(R.string.value_float_feet, elevation);
|
||||
}
|
||||
}
|
||||
textView.setText(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a grade value.
|
||||
*
|
||||
* @param activity the activity
|
||||
* @param id the grade value resource id
|
||||
* @param grade the grade in fraction between 0 and 1
|
||||
*/
|
||||
private static void setGradeValue(Activity activity, int id, double grade) {
|
||||
TextView textView = (TextView) activity.findViewById(id);
|
||||
if (textView == null) {
|
||||
return;
|
||||
}
|
||||
String value;
|
||||
if (Double.isNaN(grade) || Double.isInfinite(grade)) {
|
||||
value = activity.getString(R.string.value_unknown);
|
||||
} else {
|
||||
value = activity.getString(R.string.value_integer_percent, Math.round(grade * 100));
|
||||
}
|
||||
textView.setText(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a coordinate value.
|
||||
*
|
||||
* @param activity the activity
|
||||
* @param id the coordinate value resource id
|
||||
* @param coordinate the coordinate in degrees
|
||||
*/
|
||||
private static void setCoordinateValue(Activity activity, int id, double coordinate) {
|
||||
TextView textView = (TextView) activity.findViewById(id);
|
||||
if (textView == null) {
|
||||
return;
|
||||
}
|
||||
String value;
|
||||
if (Double.isNaN(coordinate) || Double.isInfinite(coordinate)) {
|
||||
value = activity.getString(R.string.value_unknown);
|
||||
} else {
|
||||
value = activity.getString(
|
||||
R.string.value_coordinate_degree, Location.convert(coordinate, Location.FORMAT_DEGREES));
|
||||
}
|
||||
textView.setText(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,398 @@
|
||||
/*
|
||||
* Copyright 2008 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.util;
|
||||
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.Html;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextUtils;
|
||||
import android.text.format.DateUtils;
|
||||
|
||||
import java.text.ParsePosition;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Various string manipulation methods.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class StringUtils {
|
||||
|
||||
private static final SimpleDateFormat ISO_8601_DATE_TIME_FORMAT = new SimpleDateFormat(
|
||||
"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US);
|
||||
private static final SimpleDateFormat ISO_8601_BASE = new SimpleDateFormat(
|
||||
"yyyy-MM-dd'T'HH:mm:ss", Locale.US);
|
||||
private static final Pattern ISO_8601_EXTRAS = Pattern.compile(
|
||||
"^(\\.\\d+)?(?:Z|([+-])(\\d{2}):(\\d{2}))?$");
|
||||
static {
|
||||
ISO_8601_DATE_TIME_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
ISO_8601_BASE.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
}
|
||||
|
||||
private StringUtils() {}
|
||||
|
||||
/**
|
||||
* Formats the date and time based on user's phone date/time preferences.
|
||||
*
|
||||
* @param context the context
|
||||
* @param time the time in milliseconds
|
||||
*/
|
||||
public static String formatDateTime(Context context, long time) {
|
||||
return DateUtils.formatDateTime(
|
||||
context, time, DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NUMERIC_DATE) + " "
|
||||
+ DateUtils.formatDateTime(context, time, DateUtils.FORMAT_SHOW_TIME).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the time using the ISO 8601 date time format with fractional
|
||||
* seconds in UTC time zone.
|
||||
*
|
||||
* @param time the time in milliseconds
|
||||
*/
|
||||
public static String formatDateTimeIso8601(long time) {
|
||||
return ISO_8601_DATE_TIME_FORMAT.format(time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the elapsed timed in the form "MM:SS" or "H:MM:SS".
|
||||
*
|
||||
* @param time the time in milliseconds
|
||||
*/
|
||||
public static String formatElapsedTime(long time) {
|
||||
/*
|
||||
* Temporary workaround for DateUtils.formatElapsedTime(time / 1000). In API
|
||||
* level 17, it returns strings like "1:0:00" instead of "1:00:00", which
|
||||
* breaks several unit tests.
|
||||
*/
|
||||
long elapsedSeconds = time / 1000;
|
||||
long hours = 0;
|
||||
long minutes = 0;
|
||||
long seconds = 0;
|
||||
|
||||
if (elapsedSeconds >= 3600) {
|
||||
hours = elapsedSeconds / 3600;
|
||||
elapsedSeconds -= hours * 3600;
|
||||
}
|
||||
if (elapsedSeconds >= 60) {
|
||||
minutes = elapsedSeconds / 60;
|
||||
elapsedSeconds -= minutes * 60;
|
||||
}
|
||||
seconds = elapsedSeconds;
|
||||
|
||||
if (hours > 0) {
|
||||
return String.format(Locale.US, "%d:%02d:%02d", hours, minutes, seconds);
|
||||
} else {
|
||||
return String.format(Locale.US, "%02d:%02d", minutes, seconds);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the elapsed time in the form "H:MM:SS".
|
||||
*
|
||||
* @param time the time in milliseconds
|
||||
*/
|
||||
public static String formatElapsedTimeWithHour(long time) {
|
||||
String value = formatElapsedTime(time);
|
||||
return TextUtils.split(value, ":").length == 2 ? "0:" + value : value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the distance.
|
||||
*
|
||||
* @param context the context
|
||||
* @param distance the distance in meters
|
||||
* @param metricUnits true to use metric units. False to use imperial units
|
||||
*/
|
||||
public static String formatDistance(Context context, double distance, boolean metricUnits) {
|
||||
if (Double.isNaN(distance) || Double.isInfinite(distance)) {
|
||||
return context.getString(R.string.value_unknown);
|
||||
}
|
||||
if (metricUnits) {
|
||||
if (distance > 500.0) {
|
||||
distance *= UnitConversions.M_TO_KM;
|
||||
return context.getString(R.string.value_float_kilometer, distance);
|
||||
} else {
|
||||
return context.getString(R.string.value_float_meter, distance);
|
||||
}
|
||||
} else {
|
||||
if (distance * UnitConversions.M_TO_MI > 0.5) {
|
||||
distance *= UnitConversions.M_TO_MI;
|
||||
return context.getString(R.string.value_float_mile, distance);
|
||||
} else {
|
||||
distance *= UnitConversions.M_TO_FT;
|
||||
return context.getString(R.string.value_float_feet, distance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the distance in an array of two strings. The first string is the
|
||||
* distance. The second string is the unit.
|
||||
*
|
||||
* @param context the context
|
||||
* @param distance the distance
|
||||
* @param metricUnits true to use metric unit
|
||||
*/
|
||||
public static String[] getDistanceParts(Context context, double distance, boolean metricUnits) {
|
||||
String[] result = new String[2];
|
||||
if (Double.isNaN(distance) || Double.isInfinite(distance)) {
|
||||
result[0] = context.getString(R.string.value_unknown);
|
||||
result[1] = context.getString(metricUnits ? R.string.unit_meter : R.string.unit_feet);
|
||||
return result;
|
||||
}
|
||||
|
||||
int unitId;
|
||||
if (metricUnits) {
|
||||
if (distance > 500.0) {
|
||||
distance *= UnitConversions.M_TO_KM;
|
||||
unitId = R.string.unit_kilometer;
|
||||
} else {
|
||||
unitId = R.string.unit_meter;
|
||||
}
|
||||
} else {
|
||||
if (distance * UnitConversions.M_TO_MI > 0.5) {
|
||||
distance *= UnitConversions.M_TO_MI;
|
||||
unitId = R.string.unit_mile;
|
||||
} else {
|
||||
distance *= UnitConversions.M_TO_FT;
|
||||
unitId = R.string.unit_feet;
|
||||
}
|
||||
}
|
||||
result[0] = String.format("%.2f", distance);
|
||||
result[1] = context.getString(unitId);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the speed.
|
||||
*
|
||||
* @param context the context
|
||||
* @param speed the speed in meters per second
|
||||
* @param metricUnits true to use metric units. False to use imperial units
|
||||
* @param reportSpeed true to report as speed. False to report as pace
|
||||
*/
|
||||
public static String formatSpeed(
|
||||
Context context, double speed, boolean metricUnits, boolean reportSpeed) {
|
||||
if (Double.isNaN(speed) || Double.isInfinite(speed)) {
|
||||
return context.getString(R.string.value_unknown);
|
||||
}
|
||||
speed *= UnitConversions.MS_TO_KMH;
|
||||
if (metricUnits) {
|
||||
if (reportSpeed) {
|
||||
return context.getString(R.string.value_float_kilometer_hour, speed);
|
||||
} else {
|
||||
// convert from hours to minutes
|
||||
double pace = speed == 0 ? 0.0 : 60.0 / speed;
|
||||
return context.getString(R.string.value_float_minute_kilometer, pace);
|
||||
}
|
||||
} else {
|
||||
speed *= UnitConversions.KM_TO_MI;
|
||||
if (reportSpeed) {
|
||||
return context.getString(R.string.value_float_mile_hour, speed);
|
||||
} else {
|
||||
// convert from hours to minutes
|
||||
double pace = speed == 0 ? 0.0 : 60.0 / speed;
|
||||
return context.getString(R.string.value_float_minute_mile, pace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the speed in an array of two strings. The first string is the
|
||||
* speed. The second string is the unit.
|
||||
*
|
||||
* @param context the context
|
||||
* @param speed the speed
|
||||
* @param metricUnits true to use metric unit
|
||||
* @param reportSpeed true to report speed
|
||||
*/
|
||||
public static String[] getSpeedParts(
|
||||
Context context, double speed, boolean metricUnits, boolean reportSpeed) {
|
||||
String[] result = new String[2];
|
||||
int unitId;
|
||||
if (metricUnits) {
|
||||
unitId = reportSpeed ? R.string.unit_kilometer_per_hour : R.string.unit_minute_per_kilometer;
|
||||
} else {
|
||||
unitId = reportSpeed ? R.string.unit_mile_per_hour : R.string.unit_minute_per_mile;
|
||||
}
|
||||
result[1] = context.getString(unitId);
|
||||
if (Double.isNaN(speed) || Double.isInfinite(speed)) {
|
||||
result[0] = context.getString(R.string.value_unknown);
|
||||
return result;
|
||||
}
|
||||
speed *= UnitConversions.MS_TO_KMH;
|
||||
if (metricUnits) {
|
||||
if (!reportSpeed) {
|
||||
// convert from hours to minutes
|
||||
speed = speed == 0 ? 0.0 : 60.0 / speed;
|
||||
}
|
||||
} else {
|
||||
speed *= UnitConversions.KM_TO_MI;
|
||||
if (!reportSpeed) {
|
||||
// convert from hours to minutes
|
||||
speed = speed == 0 ? 0.0 : 60.0 / speed;
|
||||
}
|
||||
}
|
||||
result[0] = String.format("%.2f", speed);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the given text as a XML CDATA element. This includes adding the
|
||||
* starting and ending CDATA tags. Please notice that this may result in
|
||||
* multiple consecutive CDATA tags.
|
||||
*
|
||||
* @param text the given text
|
||||
*/
|
||||
public static String formatCData(String text) {
|
||||
return "<![CDATA[" + text.replaceAll("]]>", "]]]]><![CDATA[>") + "]]>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the time, in milliseconds, from an XML date time string as defined at
|
||||
* http://www.w3.org/TR/xmlschema-2/#dateTime
|
||||
*
|
||||
* @param xmlDateTime the XML date time string
|
||||
*/
|
||||
public static long getTime(String xmlDateTime) {
|
||||
// Parse the date time base
|
||||
ParsePosition position = new ParsePosition(0);
|
||||
Date date = ISO_8601_BASE.parse(xmlDateTime, position);
|
||||
if (date == null) {
|
||||
throw new IllegalArgumentException("Invalid XML dateTime value: " + xmlDateTime
|
||||
+ " (at position " + position.getErrorIndex() + ")");
|
||||
}
|
||||
|
||||
// Parse the date time extras
|
||||
Matcher matcher = ISO_8601_EXTRAS.matcher(xmlDateTime.substring(position.getIndex()));
|
||||
if (!matcher.matches()) {
|
||||
// This will match even an empty string as all groups are optional. Thus a
|
||||
// non-match means invalid content.
|
||||
throw new IllegalArgumentException("Invalid XML dateTime value: " + xmlDateTime);
|
||||
}
|
||||
|
||||
long time = date.getTime();
|
||||
|
||||
// Account for fractional seconds
|
||||
String fractional = matcher.group(1);
|
||||
if (fractional != null) {
|
||||
// Regex ensures fractional part is in (0,1)
|
||||
float fractionalSeconds = Float.parseFloat(fractional);
|
||||
long fractionalMillis = (long) (fractionalSeconds * 1000.0f);
|
||||
time += fractionalMillis;
|
||||
}
|
||||
|
||||
// Account for timezones
|
||||
String sign = matcher.group(2);
|
||||
String offsetHoursStr = matcher.group(3);
|
||||
String offsetMinsStr = matcher.group(4);
|
||||
if (sign != null && offsetHoursStr != null && offsetMinsStr != null) {
|
||||
// Regex ensures sign is + or -
|
||||
boolean plusSign = sign.equals("+");
|
||||
int offsetHours = Integer.parseInt(offsetHoursStr);
|
||||
int offsetMins = Integer.parseInt(offsetMinsStr);
|
||||
|
||||
// Regex ensures values are >= 0
|
||||
if (offsetHours > 14 || offsetMins > 59) {
|
||||
throw new IllegalArgumentException("Bad timezone: " + xmlDateTime);
|
||||
}
|
||||
|
||||
long totalOffsetMillis = (offsetMins + offsetHours * 60L) * 60000L;
|
||||
|
||||
// Convert to UTC
|
||||
if (plusSign) {
|
||||
time -= totalOffsetMillis;
|
||||
} else {
|
||||
time += totalOffsetMillis;
|
||||
}
|
||||
}
|
||||
return time;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the time as an array of three integers. Index 0 contains the number of
|
||||
* seconds, index 1 contains the number of minutes, and index 2 contains the
|
||||
* number of hours.
|
||||
*
|
||||
* @param time the time in milliseconds
|
||||
* @return an array of 3 elements.
|
||||
*/
|
||||
public static int[] getTimeParts(long time) {
|
||||
if (time < 0) {
|
||||
int[] parts = getTimeParts(time * -1);
|
||||
parts[0] *= -1;
|
||||
parts[1] *= -1;
|
||||
parts[2] *= -1;
|
||||
return parts;
|
||||
}
|
||||
int[] parts = new int[3];
|
||||
|
||||
long seconds = time / 1000;
|
||||
parts[0] = (int) (seconds % 60);
|
||||
int minutes = (int) (seconds / 60);
|
||||
parts[1] = minutes % 60;
|
||||
parts[2] = minutes / 60;
|
||||
return parts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the html.
|
||||
*
|
||||
* @param context the context
|
||||
* @param resId the string resource id
|
||||
* @param formatArgs the string resource ids of the format arguments
|
||||
*/
|
||||
public static Spanned getHtml(Context context, int resId, Object... formatArgs) {
|
||||
Object[] args = new Object[formatArgs.length];
|
||||
for (int i = 0; i < formatArgs.length; i++) {
|
||||
String url = context.getString((Integer) formatArgs[i]);
|
||||
args[i] = " <a href='" + url + "'>" + url + "</a> ";
|
||||
}
|
||||
return Html.fromHtml(context.getString(resId, args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the frequency display options.
|
||||
*
|
||||
* @param context the context
|
||||
* @param metricUnits true to display in metric units
|
||||
*/
|
||||
public static String[] getFrequencyOptions(Context context, boolean metricUnits) {
|
||||
String[] values = context.getResources().getStringArray(R.array.frequency_values);
|
||||
String[] options = new String[values.length];
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
int value = Integer.parseInt(values[i]);
|
||||
if (value == PreferencesUtils.FREQUENCY_OFF) {
|
||||
options[i] = context.getString(R.string.value_off);
|
||||
} else if (value < 0) {
|
||||
options[i] = context.getString(
|
||||
metricUnits ? R.string.value_integer_kilometer : R.string.value_integer_mile,
|
||||
Math.abs(value));
|
||||
} else {
|
||||
options[i] = context.getString(R.string.value_integer_minute, value);
|
||||
}
|
||||
}
|
||||
return options;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import com.google.android.apps.mytracks.Constants;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.os.PowerManager;
|
||||
import android.os.PowerManager.WakeLock;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* Utility class for acessing basic Android functionality.
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class SystemUtils {
|
||||
|
||||
/**
|
||||
* Get the My Tracks version from the manifest.
|
||||
*
|
||||
* @return the version, or an empty string in case of failure.
|
||||
*/
|
||||
public static String getMyTracksVersion(Context context) {
|
||||
try {
|
||||
PackageInfo pi = context.getPackageManager().getPackageInfo(
|
||||
"com.google.android.maps.mytracks",
|
||||
PackageManager.GET_META_DATA);
|
||||
return pi.versionName;
|
||||
} catch (NameNotFoundException e) {
|
||||
Log.w(Constants.TAG, "Failed to get version info.", e);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to acquire a partial wake lock if not already acquired. Logs errors
|
||||
* and gives up trying in case the wake lock cannot be acquired.
|
||||
*/
|
||||
public static WakeLock acquireWakeLock(Activity activity, WakeLock wakeLock) {
|
||||
Log.i(Constants.TAG, "LocationUtils: Acquiring wake lock.");
|
||||
try {
|
||||
PowerManager pm = (PowerManager) activity
|
||||
.getSystemService(Context.POWER_SERVICE);
|
||||
if (pm == null) {
|
||||
Log.e(Constants.TAG, "LocationUtils: Power manager not found!");
|
||||
return wakeLock;
|
||||
}
|
||||
if (wakeLock == null) {
|
||||
wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
|
||||
Constants.TAG);
|
||||
if (wakeLock == null) {
|
||||
Log.e(Constants.TAG,
|
||||
"LocationUtils: Could not create wake lock (null).");
|
||||
}
|
||||
return wakeLock;
|
||||
}
|
||||
if (!wakeLock.isHeld()) {
|
||||
wakeLock.acquire();
|
||||
if (!wakeLock.isHeld()) {
|
||||
Log.e(Constants.TAG,
|
||||
"LocationUtils: Could not acquire wake lock.");
|
||||
}
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
Log.e(Constants.TAG,
|
||||
"LocationUtils: Caught unexpected exception: " + e.getMessage(), e);
|
||||
}
|
||||
return wakeLock;
|
||||
}
|
||||
|
||||
private SystemUtils() {}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright 2012 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.util;
|
||||
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Utilities for track icon.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class TrackIconUtils {
|
||||
|
||||
private static final String AIRPLANE = "AIRPLANE";
|
||||
private static final String BIKE = "BIKE";
|
||||
private static final String BOAT = "BOAT";
|
||||
private static final String DRIVE = "DRIVE";
|
||||
private static final String RUN = "RUN";
|
||||
private static final String SKI = "SKI";
|
||||
private static final String SNOW_BOARDING = "SNOW_BOARDING";
|
||||
private static final String WALK = "WALK";
|
||||
|
||||
private TrackIconUtils() {}
|
||||
|
||||
private static HashMap<String, Integer> map = new HashMap<String, Integer>();
|
||||
|
||||
static {
|
||||
map.put(AIRPLANE, R.drawable.track_airplane);
|
||||
map.put(BIKE, R.drawable.track_bike);
|
||||
map.put(BOAT, R.drawable.track_boat);
|
||||
map.put(DRIVE, R.drawable.track_drive);
|
||||
map.put(RUN, R.drawable.track_run);
|
||||
map.put(SKI, R.drawable.track_ski);
|
||||
map.put(SNOW_BOARDING, R.drawable.track_snow_boarding);
|
||||
map.put(WALK, R.drawable.track_walk);
|
||||
}
|
||||
|
||||
private static int[] airplane = new int[] {
|
||||
R.string.activity_type_airplane,
|
||||
R.string.activity_type_commercial_airplane,
|
||||
R.string.activity_type_rc_airplane
|
||||
};
|
||||
private static int[] bike = new int[] {
|
||||
R.string.activity_type_biking,
|
||||
R.string.activity_type_cycling,
|
||||
R.string.activity_type_dirt_bike,
|
||||
R.string.activity_type_motor_bike,
|
||||
R.string.activity_type_mountain_biking,
|
||||
R.string.activity_type_road_biking,
|
||||
R.string.activity_type_track_cycling };
|
||||
private static int[] boat = new int[] {
|
||||
R.string.activity_type_boat,
|
||||
R.string.activity_type_ferry,
|
||||
R.string.activity_type_motor_boating,
|
||||
R.string.activity_type_rc_boat
|
||||
};
|
||||
private static int[] drive = new int[] {
|
||||
R.string.activity_type_atv,
|
||||
R.string.activity_type_driving,
|
||||
R.string.activity_type_driving_bus,
|
||||
R.string.activity_type_driving_car };
|
||||
private static int[] run = new int[] {
|
||||
R.string.activity_type_running,
|
||||
R.string.activity_type_street_running,
|
||||
R.string.activity_type_track_running,
|
||||
R.string.activity_type_trail_running };
|
||||
private static int[] ski = new int[] {
|
||||
R.string.activity_type_cross_country_skiing,
|
||||
R.string.activity_type_skiing };
|
||||
private static int[] snowBoarding = new int[] {
|
||||
R.string.activity_type_snow_boarding
|
||||
};
|
||||
private static int[] walk = new int[] {
|
||||
R.string.activity_type_hiking,
|
||||
R.string.activity_type_off_trail_hiking,
|
||||
R.string.activity_type_speed_walking,
|
||||
R.string.activity_type_trail_hiking,
|
||||
R.string.activity_type_walking };
|
||||
|
||||
/**
|
||||
* Gets the icon drawable.
|
||||
*
|
||||
* @param iconValue the icon value
|
||||
*/
|
||||
public static int getIconDrawable(String iconValue) {
|
||||
if (iconValue == null || iconValue.equals("")) {
|
||||
return R.drawable.track_walk;
|
||||
}
|
||||
Integer drawable = map.get(iconValue);
|
||||
return drawable == null ? R.drawable.track_walk : drawable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the icon value.
|
||||
*
|
||||
* @param context the context
|
||||
* @param activityType the activity type
|
||||
*/
|
||||
public static String getIconValue(Context context, String activityType) {
|
||||
if (inList(context, activityType, airplane)) {
|
||||
return AIRPLANE;
|
||||
}
|
||||
if (inList(context, activityType, bike)) {
|
||||
return BIKE;
|
||||
}
|
||||
if (inList(context, activityType, boat)) {
|
||||
return BOAT;
|
||||
}
|
||||
if (inList(context, activityType, drive)) {
|
||||
return DRIVE;
|
||||
}
|
||||
if (inList(context, activityType, run)) {
|
||||
return RUN;
|
||||
}
|
||||
if (inList(context, activityType, ski)) {
|
||||
return SKI;
|
||||
}
|
||||
if (inList(context, activityType, snowBoarding)) {
|
||||
return SNOW_BOARDING;
|
||||
}
|
||||
if (inList(context, activityType, walk)) {
|
||||
return WALK;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the activity type is in the list.
|
||||
*
|
||||
* @param context the context
|
||||
* @param activityType the activity type
|
||||
* @param list the list
|
||||
*/
|
||||
private static boolean inList(Context context, String activityType, int[] list) {
|
||||
for (int i : list) {
|
||||
if (context.getString(i).equals(activityType)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright 2010 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.util;
|
||||
|
||||
import com.google.android.maps.mytracks.R;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import android.content.Context;
|
||||
import android.location.Address;
|
||||
import android.location.Geocoder;
|
||||
import android.location.Location;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Utilities for track name.
|
||||
*
|
||||
* @author Matthew Simmons
|
||||
*/
|
||||
public class TrackNameUtils {
|
||||
|
||||
@VisibleForTesting
|
||||
static final String ISO_8601_FORMAT = "yyyy-MM-dd HH:mm";
|
||||
|
||||
private TrackNameUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the track name.
|
||||
*
|
||||
* @param context the context
|
||||
* @param trackId the track id
|
||||
* @param startTime the track start time
|
||||
* @param location the track location
|
||||
*/
|
||||
public static String getTrackName(
|
||||
Context context, long trackId, long startTime, Location location) {
|
||||
String trackName = PreferencesUtils.getString(context, R.string.track_name_key,
|
||||
PreferencesUtils.TRACK_NAME_DEFAULT);
|
||||
|
||||
if (trackName.equals(
|
||||
context.getString(R.string.settings_recording_track_name_date_local_value))) {
|
||||
if (startTime == -1L) {
|
||||
return null;
|
||||
}
|
||||
return StringUtils.formatDateTime(context, startTime);
|
||||
} else if (trackName.equals(
|
||||
context.getString(R.string.settings_recording_track_name_date_iso_8601_value))) {
|
||||
if (startTime == -1L) {
|
||||
return null;
|
||||
}
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat(ISO_8601_FORMAT, Locale.US);
|
||||
return dateFormat.format(startTime);
|
||||
} else if (trackName.equals(
|
||||
context.getString(R.string.settings_recording_track_name_number_value))) {
|
||||
if (trackId == -1L) {
|
||||
return null;
|
||||
}
|
||||
return context.getString(R.string.track_name_format, trackId);
|
||||
} else {
|
||||
// trackName equals settings_recording_track_name_location_value or any
|
||||
// value
|
||||
if (location != null) {
|
||||
return getReverseGeoCoding(context, location);
|
||||
} else {
|
||||
// Use the startTime if available
|
||||
if (startTime != -1L) {
|
||||
return StringUtils.formatDateTime(context, startTime);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the reverse geo coding string for a location.
|
||||
*
|
||||
* @param context the context
|
||||
* @param location the location
|
||||
*/
|
||||
private static String getReverseGeoCoding(Context context, Location location) {
|
||||
if (location == null || !ApiAdapterFactory.getApiAdapter().isGeoCoderPresent()) {
|
||||
return null;
|
||||
}
|
||||
Geocoder geocoder = new Geocoder(context);
|
||||
try {
|
||||
List<Address> addresses = geocoder.getFromLocation(
|
||||
location.getLatitude(), location.getLongitude(), 1);
|
||||
if (addresses.size() > 0) {
|
||||
Address address = addresses.get(0);
|
||||
int lines = address.getMaxAddressLineIndex();
|
||||
if (lines > 0) {
|
||||
return address.getAddressLine(0);
|
||||
}
|
||||
String featureName = address.getFeatureName();
|
||||
if (featureName != null) {
|
||||
return featureName;
|
||||
}
|
||||
String thoroughfare = address.getThoroughfare();
|
||||
if (thoroughfare != null) {
|
||||
return thoroughfare;
|
||||
}
|
||||
String locality = address.getLocality();
|
||||
if (locality != null) {
|
||||
return locality;
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// Can safely ignore
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+194
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import com.google.android.apps.mytracks.TrackEditActivity;
|
||||
import com.google.android.apps.mytracks.content.WaypointCreationRequest;
|
||||
import com.google.android.apps.mytracks.services.ITrackRecordingService;
|
||||
import com.google.android.apps.mytracks.services.TrackRecordingService;
|
||||
import com.google.android.apps.mytracks.services.TrackRecordingServiceConnection;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityManager.RunningServiceInfo;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Utilities for {@link TrackRecordingServiceConnection}.
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class TrackRecordingServiceConnectionUtils {
|
||||
|
||||
private static final String TAG = TrackRecordingServiceConnectionUtils.class.getSimpleName();
|
||||
|
||||
private TrackRecordingServiceConnectionUtils() {}
|
||||
|
||||
/**
|
||||
* Returns true if the recording service is running.
|
||||
*
|
||||
* @param context the current context
|
||||
*/
|
||||
public static boolean isRecordingServiceRunning(Context context) {
|
||||
ActivityManager activityManager = (ActivityManager) context.getSystemService(
|
||||
Context.ACTIVITY_SERVICE);
|
||||
List<RunningServiceInfo> services = activityManager.getRunningServices(Integer.MAX_VALUE);
|
||||
|
||||
for (RunningServiceInfo serviceInfo : services) {
|
||||
ComponentName componentName = serviceInfo.service;
|
||||
String serviceName = componentName.getClassName();
|
||||
if (TrackRecordingService.class.getName().equals(serviceName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resumes the recording track.
|
||||
*
|
||||
* @param trackRecordingServiceConnection the track recording service
|
||||
*/
|
||||
public static void resumeTrack(TrackRecordingServiceConnection trackRecordingServiceConnection) {
|
||||
try {
|
||||
ITrackRecordingService service = trackRecordingServiceConnection.getServiceIfBound();
|
||||
if (service != null) {
|
||||
service.resumeCurrentTrack();
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Unable to resume track.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pauses the recording track.
|
||||
*
|
||||
* @param trackRecordingServiceConnection the track recording service
|
||||
* connection
|
||||
*/
|
||||
public static void pauseTrack(TrackRecordingServiceConnection trackRecordingServiceConnection) {
|
||||
try {
|
||||
ITrackRecordingService service = trackRecordingServiceConnection.getServiceIfBound();
|
||||
if (service != null) {
|
||||
service.pauseCurrentTrack();
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Unable to resume track.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the recording.
|
||||
*
|
||||
* @param context the context
|
||||
* @param trackRecordingServiceConnection the track recording service
|
||||
* connection
|
||||
* @param showEditor true to show the editor
|
||||
*/
|
||||
public static void stopRecording(Context context,
|
||||
TrackRecordingServiceConnection trackRecordingServiceConnection, boolean showEditor) {
|
||||
ITrackRecordingService trackRecordingService = trackRecordingServiceConnection
|
||||
.getServiceIfBound();
|
||||
if (trackRecordingService != null) {
|
||||
try {
|
||||
if (showEditor) {
|
||||
/*
|
||||
* Need to remember the recordingTrackId before calling
|
||||
* endCurrentTrack. endCurrentTrack sets the value to -1L.
|
||||
*/
|
||||
long recordingTrackId = PreferencesUtils.getLong(
|
||||
context, R.string.recording_track_id_key);
|
||||
trackRecordingService.endCurrentTrack();
|
||||
if (recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT) {
|
||||
Intent intent = IntentUtils.newIntent(context, TrackEditActivity.class)
|
||||
.putExtra(TrackEditActivity.EXTRA_TRACK_ID, recordingTrackId)
|
||||
.putExtra(TrackEditActivity.EXTRA_NEW_TRACK, true);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
} else {
|
||||
trackRecordingService.endCurrentTrack();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Unable to stop recording.", e);
|
||||
}
|
||||
} else {
|
||||
resetRecordingState(context);
|
||||
}
|
||||
trackRecordingServiceConnection.unbindAndStop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resumes the track recording service connection.
|
||||
*
|
||||
* @param context the context
|
||||
* @param trackRecordingServiceConnection the track recording service
|
||||
* connection
|
||||
*/
|
||||
public static void startConnection(
|
||||
Context context, TrackRecordingServiceConnection trackRecordingServiceConnection) {
|
||||
trackRecordingServiceConnection.bindIfStarted();
|
||||
if (!isRecordingServiceRunning(context)) {
|
||||
resetRecordingState(context);
|
||||
}
|
||||
}
|
||||
|
||||
private static void resetRecordingState(Context context) {
|
||||
long recordingTrackId = PreferencesUtils.getLong(context, R.string.recording_track_id_key);
|
||||
if (recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT) {
|
||||
PreferencesUtils.setLong(
|
||||
context, R.string.recording_track_id_key, PreferencesUtils.RECORDING_TRACK_ID_DEFAULT);
|
||||
}
|
||||
boolean recordingTrackPaused = PreferencesUtils.getBoolean(context,
|
||||
R.string.recording_track_paused_key, PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT);
|
||||
if (!recordingTrackPaused) {
|
||||
PreferencesUtils.setBoolean(context, R.string.recording_track_paused_key,
|
||||
PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a marker.
|
||||
*/
|
||||
public static void addMarker(Context context,
|
||||
TrackRecordingServiceConnection trackRecordingServiceConnection,
|
||||
WaypointCreationRequest waypointCreationRequest) {
|
||||
ITrackRecordingService trackRecordingService = trackRecordingServiceConnection
|
||||
.getServiceIfBound();
|
||||
if (trackRecordingService == null) {
|
||||
Log.d(TAG, "Unable to add marker, no track recording service");
|
||||
} else {
|
||||
try {
|
||||
if (trackRecordingService.insertWaypoint(waypointCreationRequest) != -1L) {
|
||||
Toast.makeText(context, R.string.marker_add_success, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Unable to add marker", e);
|
||||
} catch (IllegalStateException e) {
|
||||
Log.e(TAG, "Unable to add marker.", e);
|
||||
}
|
||||
}
|
||||
Toast.makeText(context, R.string.marker_add_error, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2008 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.util;
|
||||
|
||||
/**
|
||||
* Unit conversion constants.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class UnitConversions {
|
||||
|
||||
private UnitConversions() {}
|
||||
|
||||
// multiplication factor to convert kilometers to miles
|
||||
public static final double KM_TO_MI = 0.621371192;
|
||||
|
||||
// multiplication factor to convert miles to kilometers
|
||||
public static final double MI_TO_KM = 1 / KM_TO_MI;
|
||||
|
||||
// multiplication factor to convert miles to feet
|
||||
public static final double MI_TO_FT = 5280.0;
|
||||
|
||||
// multiplication factor to convert feet to miles
|
||||
public static final double FT_TO_MI = 1 / MI_TO_FT;
|
||||
|
||||
// multiplication factor to convert meters to kilometers
|
||||
public static final double M_TO_KM = 1 / 1000.0;
|
||||
|
||||
// multiplication factor to convert meters per second to kilometers per hour
|
||||
public static final double MS_TO_KMH = 3.6;
|
||||
|
||||
// multiplication factor to convert meters to miles
|
||||
public static final double M_TO_MI = M_TO_KM * KM_TO_MI;
|
||||
|
||||
// multiplication factor to convert meters to feet
|
||||
public static final double M_TO_FT = M_TO_MI * MI_TO_FT;
|
||||
|
||||
// multiplication factor to convert degrees to radians
|
||||
public static final double DEG_TO_RAD = Math.PI / 180.0;
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Utilities for dealing with content and other types of URIs.
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class UriUtils {
|
||||
|
||||
public static boolean matchesContentUri(Uri uri, Uri baseContentUri) {
|
||||
if (uri == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that scheme and authority are the same.
|
||||
if (!uri.getScheme().equals(baseContentUri.getScheme()) ||
|
||||
!uri.getAuthority().equals(baseContentUri.getAuthority())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Checks that all the base path components are in the URI.
|
||||
List<String> uriPathSegments = uri.getPathSegments();
|
||||
List<String> basePathSegments = baseContentUri.getPathSegments();
|
||||
if (basePathSegments.size() > uriPathSegments.size()) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < basePathSegments.size(); i++) {
|
||||
if (!uriPathSegments.get(i).equals(basePathSegments.get(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean isFileUri(Uri uri) {
|
||||
return "file".equals(uri.getScheme());
|
||||
}
|
||||
|
||||
private UriUtils() {}
|
||||
}
|
||||
Reference in New Issue
Block a user