merge heads

This commit is contained in:
Jimmy Shih
2012-03-23 12:37:44 -07:00
89 changed files with 308 additions and 411 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Before

Width:  |  Height:  |  Size: 671 B

After

Width:  |  Height:  |  Size: 671 B

Before

Width:  |  Height:  |  Size: 680 B

After

Width:  |  Height:  |  Size: 680 B

Before

Width:  |  Height:  |  Size: 738 B

After

Width:  |  Height:  |  Size: 738 B

Before

Width:  |  Height:  |  Size: 783 B

After

Width:  |  Height:  |  Size: 783 B

Before

Width:  |  Height:  |  Size: 746 B

After

Width:  |  Height:  |  Size: 746 B

Before

Width:  |  Height:  |  Size: 720 B

After

Width:  |  Height:  |  Size: 720 B

Before

Width:  |  Height:  |  Size: 703 B

After

Width:  |  Height:  |  Size: 703 B

Before

Width:  |  Height:  |  Size: 671 B

After

Width:  |  Height:  |  Size: 671 B

Before

Width:  |  Height:  |  Size: 810 B

After

Width:  |  Height:  |  Size: 810 B

Before

Width:  |  Height:  |  Size: 698 B

After

Width:  |  Height:  |  Size: 698 B

Before

Width:  |  Height:  |  Size: 694 B

After

Width:  |  Height:  |  Size: 694 B

Before

Width:  |  Height:  |  Size: 725 B

After

Width:  |  Height:  |  Size: 725 B

Before

Width:  |  Height:  |  Size: 805 B

After

Width:  |  Height:  |  Size: 805 B

Before

Width:  |  Height:  |  Size: 812 B

After

Width:  |  Height:  |  Size: 812 B

Before

Width:  |  Height:  |  Size: 689 B

After

Width:  |  Height:  |  Size: 689 B

Before

Width:  |  Height:  |  Size: 760 B

After

Width:  |  Height:  |  Size: 760 B

Before

Width:  |  Height:  |  Size: 707 B

After

Width:  |  Height:  |  Size: 707 B

Before

Width:  |  Height:  |  Size: 743 B

After

Width:  |  Height:  |  Size: 743 B

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Before

Width:  |  Height:  |  Size: 384 B

After

Width:  |  Height:  |  Size: 384 B

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

Before

Width:  |  Height:  |  Size: 913 B

After

Width:  |  Height:  |  Size: 913 B

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Before

Width:  |  Height:  |  Size: 286 B

After

Width:  |  Height:  |  Size: 286 B

Before

Width:  |  Height:  |  Size: 275 B

After

Width:  |  Height:  |  Size: 275 B

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 974 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 722 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 220 B

+71 -86
View File
@@ -14,93 +14,78 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="fill_parent" android:layout_height="fill_parent"
android:layout_width="fill_parent" android:layout_width="fill_parent" >
android:orientation="vertical"> <TableLayout
<TextView
android:background="@drawable/list_bar"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="8dp"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:paddingLeft="10dp" android:stretchColumns="1" >
android:text="@string/send_google_title" <TableRow android:id="@+id/send_google_maps_row" >
android:textColor="@color/white" <ImageView
android:textSize="20sp"/> android:contentDescription="@string/send_google_maps"
<ScrollView android:layout_height="wrap_content"
android:layout_height="0dp" android:layout_width="wrap_content"
android:layout_margin="5dp" android:src="@drawable/maps_icon" />
android:layout_weight="1" <CheckBox
android:layout_width="fill_parent"> android:id="@+id/send_google_maps"
<TableLayout android:layout_gravity="center_vertical"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_width="fill_parent" android:layout_marginLeft="8dp"
android:stretchColumns="1"> android:layout_width="0dp"
android:text="@string/send_google_maps"
<!-- Google Maps --> style="@android:style/TextAppearance.DeviceDefault.Medium" />
<TableRow android:id="@+id/send_google_maps_row"> </TableRow>
<ImageView <TableRow android:id="@+id/send_google_maps_option_row" >
android:contentDescription="@string/send_google_maps" <RadioGroup
android:src="@drawable/maps_icon" android:layout_column="1"
style="@style/SendGoogleImageView"/> android:layout_height="wrap_content"
<CheckBox android:layout_marginLeft="8dp"
android:id="@+id/send_google_maps" android:layout_width="0dp" >
android:text="@string/send_google_maps" <RadioButton
style="@style/SendGoogleCheckBox"/> android:id="@+id/send_google_new_map"
</TableRow>
<TableRow android:id="@+id/send_google_maps_option_row">
<RadioGroup
android:layout_column="1"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="10dp" android:layout_width="wrap_content"
android:layout_width="0dp"> android:text="@string/send_google_new_map"
<RadioButton style="@android:style/TextAppearance.DeviceDefault.Medium" />
android:id="@+id/send_google_new_map" <RadioButton
android:text="@string/send_google_new_map" android:id="@+id/send_google_existing_map"
style="@style/SendGoogleRadioButton"/> android:layout_height="wrap_content"
<RadioButton android:layout_width="wrap_content"
android:id="@+id/send_google_existing_map" android:text="@string/send_google_existing_map"
android:text="@string/send_google_existing_map" style="@android:style/TextAppearance.DeviceDefault.Medium" />
style="@style/SendGoogleRadioButton"/> </RadioGroup>
</RadioGroup> </TableRow>
</TableRow> <TableRow android:id="@+id/send_google_fusion_tables_row" >
<ImageView
<!-- Google Fusion Tables --> android:contentDescription="@string/send_google_fusion_tables"
<TableRow android:id="@+id/send_google_fusion_tables_row"> android:layout_height="wrap_content"
<ImageView android:layout_width="wrap_content"
android:contentDescription="@string/send_google_fusion_tables" android:src="@drawable/googledocs_icon" />
android:src="@drawable/googledocs_icon" <CheckBox
style="@style/SendGoogleImageView"/> android:id="@+id/send_google_fusion_tables"
<CheckBox android:layout_gravity="center_vertical"
android:id="@+id/send_google_fusion_tables" android:layout_height="wrap_content"
android:text="@string/send_google_fusion_tables" android:layout_marginLeft="8dp"
style="@style/SendGoogleCheckBox"/> android:layout_width="0dp"
</TableRow> android:text="@string/send_google_fusion_tables"
style="@android:style/TextAppearance.DeviceDefault.Medium" />
<!-- Google Docs --> </TableRow>
<TableRow android:id="@+id/send_google_docs_row"> <TableRow android:id="@+id/send_google_docs_row" >
<ImageView <ImageView
android:contentDescription="@string/send_google_docs" android:contentDescription="@string/send_google_docs"
android:src="@drawable/googledocs_icon" android:layout_height="wrap_content"
style="@style/SendGoogleImageView"/> android:layout_width="wrap_content"
<CheckBox android:src="@drawable/googledocs_icon" />
android:id="@+id/send_google_docs" <CheckBox
android:text="@string/send_google_docs" android:id="@+id/send_google_docs"
style="@style/SendGoogleCheckBox"/> android:layout_gravity="center_vertical"
</TableRow> android:layout_height="fill_parent"
</TableLayout> android:layout_marginLeft="8dp"
</ScrollView> android:layout_width="0dp"
<LinearLayout style="@style/ButtonLayout"> android:text="@string/send_google_docs"
<LinearLayout style="@style/ButtonSpacer"/> style="@android:style/TextAppearance.DeviceDefault.Medium" />
<Button </TableRow>
android:id="@+id/send_google_send_now" </TableLayout>
android:layout_gravity="left" </ScrollView>
android:text="@string/send_google_send_now"
style="@style/Button"/>
<Button
android:id="@+id/send_google_cancel"
android:layout_gravity="right"
android:text="@string/generic_cancel"
style="@style/Button"/>
<LinearLayout style="@style/ButtonSpacer"/>
</LinearLayout>
</LinearLayout>
+1 -1
View File
@@ -41,7 +41,7 @@ limitations under the License.
android:title="@string/menu_markers"/> android:title="@string/menu_markers"/>
<item <item
android:icon="@drawable/chart_settings" android:icon="@drawable/ic_menu_chartsettings"
android:id="@+id/menu_chart_settings" android:id="@+id/menu_chart_settings"
android:showAsAction="ifRoom" android:showAsAction="ifRoom"
android:title="@string/menu_chart_view_chart_settings" android:title="@string/menu_chart_view_chart_settings"
-3
View File
@@ -15,12 +15,9 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<menu xmlns:android="http://schemas.android.com/apk/res/android" > <menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item <item
android:icon="@android:drawable/ic_menu_search" android:icon="@android:drawable/ic_menu_search"
android:id="@+id/menu_search" android:id="@+id/menu_search"
android:onClick="onSearch"
android:showAsAction="always" android:showAsAction="always"
android:title="@string/menu_search"/> android:title="@string/menu_search"/>
</menu> </menu>
+5
View File
@@ -1343,6 +1343,11 @@ limitations under the License.
found."> found.">
No account found No account found
</string> </string>
<string name="send_google_no_service_selected"
translation_description="When sending a track to Google, a message indicating that the user has
not selected any Google service and no data is sent.">
You did not select any service. The track was not sent.
</string>
<string name="send_google_progress_title" <string name="send_google_progress_title"
translation_description="When sending a track to a Google service, the title on the progress translation_description="When sending a track to a Google service, the title on the progress
dialog. dialog.
-22
View File
@@ -74,28 +74,6 @@ limitations under the License.
<item name="android:visibility">gone</item> <item name="android:visibility">gone</item>
</style> </style>
<!-- Send Google CheckBox -->
<style name="SendGoogleCheckBox">
<item name="android:checked">true</item>
<item name="android:layout_height">fill_parent</item>
<item name="android:layout_marginLeft">10dp</item>
<item name="android:layout_width">0dp</item>
<item name="android:textSize">18sp</item>
</style>
<!-- Send Google ImageView -->
<style name="SendGoogleImageView">
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_width">wrap_content</item>
</style>
<!-- Send Google RadioButton -->
<style name="SendGoogleRadioButton">
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_width">wrap_content</item>
<item name="android:textSize">18sp</item>
</style>
<style name="SensorStateLabel"> <style name="SensorStateLabel">
<item name="android:layout_height">wrap_content</item> <item name="android:layout_height">wrap_content</item>
<item name="android:layout_width">fill_parent</item> <item name="android:layout_width">fill_parent</item>
@@ -72,7 +72,7 @@ public class ImportActivity extends Activity {
Intent intent = getIntent(); Intent intent = getIntent();
importAll = intent.getBooleanExtra(EXTRA_IMPORT_ALL, false); importAll = intent.getBooleanExtra(EXTRA_IMPORT_ALL, false);
if (importAll) { if (importAll) {
path = new FileUtils().buildExternalDirectoryPath("gpx"); path = FileUtils.buildExternalDirectoryPath("gpx");
} else { } else {
String action = intent.getAction(); String action = intent.getAction();
if (!(Intent.ACTION_ATTACH_DATA.equals(action) || Intent.ACTION_VIEW.equals(action))) { if (!(Intent.ACTION_ATTACH_DATA.equals(action) || Intent.ACTION_VIEW.equals(action))) {
@@ -121,8 +121,7 @@ public class ImportAsyncTask extends AsyncTask<Void, Integer, Boolean> {
@Override @Override
protected Boolean doInBackground(Void... params) { protected Boolean doInBackground(Void... params) {
try { try {
FileUtils fileUtils = new FileUtils(); if (!FileUtils.isSdCardAvailable()) {
if (!fileUtils.isSdCardAvailable()) {
return false; return false;
} }
@@ -32,8 +32,8 @@ import com.google.android.apps.mytracks.services.TrackRecordingServiceConnection
import com.google.android.apps.mytracks.util.AnalyticsUtils; import com.google.android.apps.mytracks.util.AnalyticsUtils;
import com.google.android.apps.mytracks.util.ApiAdapterFactory; import com.google.android.apps.mytracks.util.ApiAdapterFactory;
import com.google.android.apps.mytracks.util.EulaUtils; import com.google.android.apps.mytracks.util.EulaUtils;
import com.google.android.apps.mytracks.util.SystemUtils;
import com.google.android.apps.mytracks.util.UriUtils; import com.google.android.apps.mytracks.util.UriUtils;
import com.google.android.maps.mytracks.BuildConfig;
import com.google.android.maps.mytracks.R; import com.google.android.maps.mytracks.R;
import android.app.Activity; import android.app.Activity;
@@ -73,7 +73,7 @@ public class MyTracks extends TabActivity implements OnTouchListener {
private static final int DIALOG_CHECK_UNITS_ID = 1; private static final int DIALOG_CHECK_UNITS_ID = 1;
private static final String CHECK_UNITS_PREFERENCE_FILE = "checkunits"; private static final String CHECK_UNITS_PREFERENCE_FILE = "checkunits";
private static final String CHECK_UNITS_PREFERENCE_KEY = "checkunits.checked"; private static final String CHECK_UNITS_PREFERENCE_KEY = "checkunits.checked";
private TrackDataHub dataHub; private TrackDataHub dataHub;
/** /**
@@ -140,7 +140,7 @@ public class MyTracks extends TabActivity implements OnTouchListener {
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "MyTracks.onCreate"); Log.d(TAG, "MyTracks.onCreate");
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
if (!SystemUtils.isRelease(this)) { if (BuildConfig.DEBUG) {
ApiAdapterFactory.getApiAdapter().enableStrictMode(); ApiAdapterFactory.getApiAdapter().enableStrictMode();
} }
@@ -153,7 +153,7 @@ public class MyTracks extends TabActivity implements OnTouchListener {
serviceConnection = new TrackRecordingServiceConnection(this, serviceBindCallback); serviceConnection = new TrackRecordingServiceConnection(this, serviceBindCallback);
setVolumeControlStream(TextToSpeech.Engine.DEFAULT_STREAM); setVolumeControlStream(TextToSpeech.Engine.DEFAULT_STREAM);
// Show the action bar (or nothing at all). // Show the action bar (or nothing at all).
ApiAdapterFactory.getApiAdapter().showActionBar(this); ApiAdapterFactory.getApiAdapter().showActionBar(this);
@@ -167,7 +167,7 @@ public class MyTracks extends TabActivity implements OnTouchListener {
android.R.drawable.ic_menu_mapmode)) android.R.drawable.ic_menu_mapmode))
.setContent(new Intent(this, MapActivity.class))); .setContent(new Intent(this, MapActivity.class)));
tabHost.addTab(tabHost.newTabSpec(STATS_TAB_TAG) tabHost.addTab(tabHost.newTabSpec(STATS_TAB_TAG)
.setIndicator("Stats", res.getDrawable(R.drawable.menu_stats)) .setIndicator("Stats", res.getDrawable(R.drawable.ic_menu_statistics))
.setContent(new Intent(this, StatsActivity.class))); .setContent(new Intent(this, StatsActivity.class)));
tabHost.addTab(tabHost.newTabSpec(CHART_TAB_TAG) tabHost.addTab(tabHost.newTabSpec(CHART_TAB_TAG)
.setIndicator("Chart", res.getDrawable(R.drawable.menu_elevation)) .setIndicator("Chart", res.getDrawable(R.drawable.menu_elevation))
@@ -324,6 +324,17 @@ public class TrackList extends ListActivity
getMenuInflater().inflate(R.menu.search_only, menu); getMenuInflater().inflate(R.menu.search_only, menu);
return true; return true;
} }
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_search:
onSearchRequested();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override @Override
protected Dialog onCreateDialog(int id) { protected Dialog onCreateDialog(int id) {
@@ -387,11 +398,6 @@ public class TrackList extends ListActivity
} }
} }
/* Callback from menu/search_only.xml */
public void onSearch(@SuppressWarnings("unused") MenuItem i) {
onSearchRequested();
}
private void updateButtonsEnabled() { private void updateButtonsEnabled() {
View deleteAll = findViewById(R.id.tracklist_btn_delete_all); View deleteAll = findViewById(R.id.tracklist_btn_delete_all);
View exportAll = findViewById(R.id.tracklist_btn_export_all); View exportAll = findViewById(R.id.tracklist_btn_export_all);
@@ -224,10 +224,16 @@ public class WaypointsList extends ListActivity implements View.OnClickListener
getMenuInflater().inflate(R.menu.search_only, menu); getMenuInflater().inflate(R.menu.search_only, menu);
return true; return true;
} }
/* Callback from menu/search_only.xml */ @Override
public void onSearch(@SuppressWarnings("unused") MenuItem i) { public boolean onOptionsItemSelected(MenuItem item) {
onSearchRequested(); switch (item.getItemId()) {
case R.id.menu_search:
onSearchRequested();
return true;
default:
return super.onOptionsItemSelected(item);
}
} }
@Override @Override
@@ -53,7 +53,7 @@ public class BackupAsyncTask extends AsyncTask<Void, Integer, Boolean> {
*/ */
public BackupAsyncTask(BackupActivity backupActivity) { public BackupAsyncTask(BackupActivity backupActivity) {
this.backupActivity = backupActivity; this.backupActivity = backupActivity;
this.externalFileBackup = new ExternalFileBackup(backupActivity, new FileUtils()); this.externalFileBackup = new ExternalFileBackup(backupActivity);
success = false; success = false;
completed = false; completed = false;
messageId = R.string.sd_card_error_write_file; messageId = R.string.sd_card_error_write_file;
@@ -80,9 +80,7 @@ public class BackupAsyncTask extends AsyncTask<Void, Integer, Boolean> {
@Override @Override
protected Boolean doInBackground(Void... params) { protected Boolean doInBackground(Void... params) {
FileUtils fileUtils = new FileUtils(); if (!FileUtils.isSdCardAvailable()) {
if (!fileUtils.isSdCardAvailable()) {
messageId = R.string.sd_card_error_no_storage; messageId = R.string.sd_card_error_no_storage;
return false; return false;
} }
@@ -65,11 +65,9 @@ class ExternalFileBackup {
private static final int COMPRESSION_LEVEL = 8; private static final int COMPRESSION_LEVEL = 8;
private final Context context; private final Context context;
private final FileUtils fileUtils;
public ExternalFileBackup(Context context, FileUtils fileUtils) { public ExternalFileBackup(Context context) {
this.context = context; this.context = context;
this.fileUtils = fileUtils;
} }
/** /**
@@ -87,12 +85,12 @@ class ExternalFileBackup {
* @param create whether to try creating the directory if it doesn't exist * @param create whether to try creating the directory if it doesn't exist
*/ */
private File getBackupsDirectory(boolean create) { private File getBackupsDirectory(boolean create) {
String dirName = fileUtils.buildExternalDirectoryPath(BACKUPS_SUBDIR); String dirName = FileUtils.buildExternalDirectoryPath(BACKUPS_SUBDIR);
final File dir = new File(dirName); final File dir = new File(dirName);
Log.d(Constants.TAG, "Dir: " + dir.getAbsolutePath()); Log.d(Constants.TAG, "Dir: " + dir.getAbsolutePath());
if (create) { if (create) {
// Try to create - if that fails, return null // Try to create - if that fails, return null
return fileUtils.ensureDirectoryExists(dir) ? dir : null; return FileUtils.ensureDirectoryExists(dir) ? dir : null;
} else { } else {
// Return it if it already exists, otherwise return null // Return it if it already exists, otherwise return null
return dir.isDirectory() ? dir : null; return dir.isDirectory() ? dir : null;
@@ -16,7 +16,6 @@
package com.google.android.apps.mytracks.io.backup; package com.google.android.apps.mytracks.io.backup;
import com.google.android.apps.mytracks.util.FileUtils;
import com.google.android.maps.mytracks.R; import com.google.android.maps.mytracks.R;
import android.os.AsyncTask; import android.os.AsyncTask;
@@ -57,7 +56,7 @@ public class RestoreAsyncTask extends AsyncTask<Void, Integer, Boolean> {
public RestoreAsyncTask(RestoreActivity restoreActivity, Date date) { public RestoreAsyncTask(RestoreActivity restoreActivity, Date date) {
this.restoreActivity = restoreActivity; this.restoreActivity = restoreActivity;
this.date = date; this.date = date;
this.externalFileBackup = new ExternalFileBackup(restoreActivity, new FileUtils()); this.externalFileBackup = new ExternalFileBackup(restoreActivity);
success = false; success = false;
completed = false; completed = false;
messageId = R.string.sd_card_error_read_file; messageId = R.string.sd_card_error_read_file;
@@ -55,11 +55,10 @@ public class RestoreChooserActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
FileUtils fileUtils = new FileUtils(); ExternalFileBackup externalFileBackup = new ExternalFileBackup(this);
ExternalFileBackup externalFileBackup = new ExternalFileBackup(this, fileUtils);
// Get the list of existing backups // Get the list of existing backups
if (!fileUtils.isSdCardAvailable()) { if (!FileUtils.isSdCardAvailable()) {
Toast.makeText(this, R.string.sd_card_error_no_storage, Toast.LENGTH_LONG).show(); Toast.makeText(this, R.string.sd_card_error_no_storage, Toast.LENGTH_LONG).show();
finish(); finish();
return; return;
@@ -101,7 +101,7 @@ public class SaveActivity extends Activity {
} }
if (shareTrack || playTrack) { if (shareTrack || playTrack) {
// Save to the temp directory // Save to the temp directory
String dirName = new FileUtils().buildExternalDirectoryPath( String dirName = FileUtils.buildExternalDirectoryPath(
trackFileFormat.getExtension(), "tmp"); trackFileFormat.getExtension(), "tmp");
trackWriter.setDirectory(new File(dirName)); trackWriter.setDirectory(new File(dirName));
} }
@@ -52,7 +52,6 @@ class TrackWriterImpl implements TrackWriter {
private final MyTracksProviderUtils providerUtils; private final MyTracksProviderUtils providerUtils;
private final Track track; private final Track track;
private final TrackFormatWriter writer; private final TrackFormatWriter writer;
private final FileUtils fileUtils;
private boolean success = false; private boolean success = false;
private int errorMessage = -1; private int errorMessage = -1;
private File directory = null; private File directory = null;
@@ -66,7 +65,6 @@ class TrackWriterImpl implements TrackWriter {
this.providerUtils = providerUtils; this.providerUtils = providerUtils;
this.track = track; this.track = track;
this.writer = writer; this.writer = writer;
this.fileUtils = new FileUtils();
} }
@Override @Override
@@ -175,7 +173,7 @@ class TrackWriterImpl implements TrackWriter {
} }
// Make sure the file doesn't exist yet (possibly by changing the filename) // Make sure the file doesn't exist yet (possibly by changing the filename)
String fileName = fileUtils.buildUniqueFileName( String fileName = FileUtils.buildUniqueFileName(
directory, track.getName(), writer.getExtension()); directory, track.getName(), writer.getExtension());
if (fileName == null) { if (fileName == null) {
Log.e(Constants.TAG, Log.e(Constants.TAG,
@@ -200,16 +198,16 @@ class TrackWriterImpl implements TrackWriter {
protected boolean canWriteFile() { protected boolean canWriteFile() {
if (directory == null) { if (directory == null) {
String dirName = String dirName =
fileUtils.buildExternalDirectoryPath(writer.getExtension()); FileUtils.buildExternalDirectoryPath(writer.getExtension());
directory = newFile(dirName); directory = newFile(dirName);
} }
if (!fileUtils.isSdCardAvailable()) { if (!FileUtils.isSdCardAvailable()) {
Log.i(Constants.TAG, "Could not find SD card."); Log.i(Constants.TAG, "Could not find SD card.");
errorMessage = R.string.sd_card_error_no_storage; errorMessage = R.string.sd_card_error_no_storage;
return false; return false;
} }
if (!fileUtils.ensureDirectoryExists(directory)) { if (!FileUtils.ensureDirectoryExists(directory)) {
Log.i(Constants.TAG, "Could not create export directory."); Log.i(Constants.TAG, "Could not create export directory.");
errorMessage = R.string.sd_card_error_create_dir; errorMessage = R.string.sd_card_error_create_dir;
return false; return false;
@@ -22,6 +22,7 @@ import com.google.android.maps.mytracks.R;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog; import android.app.Dialog;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
@@ -30,27 +31,26 @@ import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor; import android.content.SharedPreferences.Editor;
import android.os.Bundle; import android.os.Bundle;
import android.view.View; import android.view.View;
import android.view.Window;
import android.widget.Button;
import android.widget.CheckBox; import android.widget.CheckBox;
import android.widget.CompoundButton; import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.RadioButton; import android.widget.RadioButton;
import android.widget.TableRow; import android.widget.TableRow;
import android.widget.Toast;
import java.util.ArrayList; import java.util.ArrayList;
/** /**
* A chooser to select the Google services to upload a track to. * A chooser to select the Google services to upload a track to.
* *
* @author Jimmy Shih * @author Jimmy Shih
*/ */
public class UploadServiceChooserActivity extends Activity { public class UploadServiceChooserActivity extends Activity {
private static final int SERVICE_PICKER_DIALOG = 1; private static final int DIALOG_CHOOSER_ID = 0;
private SendRequest sendRequest; private SendRequest sendRequest;
private Dialog dialog; private AlertDialog alertDialog;
private TableRow mapsTableRow; private TableRow mapsTableRow;
private TableRow fusionTablesTableRow; private TableRow fusionTablesTableRow;
@@ -64,9 +64,6 @@ public class UploadServiceChooserActivity extends Activity {
private RadioButton newMapRadioButton; private RadioButton newMapRadioButton;
private RadioButton existingMapRadioButton; private RadioButton existingMapRadioButton;
private Button cancel;
private Button send;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@@ -76,74 +73,78 @@ public class UploadServiceChooserActivity extends Activity {
@Override @Override
protected void onResume() { protected void onResume() {
super.onResume(); super.onResume();
showDialog(SERVICE_PICKER_DIALOG); showDialog(DIALOG_CHOOSER_ID);
} }
@Override @Override
protected Dialog onCreateDialog(int id) { protected Dialog onCreateDialog(int id) {
switch (id) { if (id != DIALOG_CHOOSER_ID) {
case SERVICE_PICKER_DIALOG: return null;
dialog = new Dialog(this); }
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); View view = getLayoutInflater().inflate(R.layout.upload_service_chooser, null);
dialog.setContentView(R.layout.upload_service_chooser);
dialog.setCancelable(true); mapsTableRow = (TableRow) view.findViewById(R.id.send_google_maps_row);
dialog.setOnCancelListener(new DialogInterface.OnCancelListener() { fusionTablesTableRow = (TableRow) view.findViewById(R.id.send_google_fusion_tables_row);
docsTableRow = (TableRow) view.findViewById(R.id.send_google_docs_row);
mapsCheckBox = (CheckBox) view.findViewById(R.id.send_google_maps);
fusionTablesCheckBox = (CheckBox) view.findViewById(R.id.send_google_fusion_tables);
docsCheckBox = (CheckBox) view.findViewById(R.id.send_google_docs);
mapsOptionTableRow = (TableRow) view.findViewById(R.id.send_google_maps_option_row);
newMapRadioButton = (RadioButton) view.findViewById(R.id.send_google_new_map);
existingMapRadioButton = (RadioButton) view.findViewById(R.id.send_google_existing_map);
// Setup checkboxes
OnCheckedChangeListener checkBoxListener = new OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton button, boolean checked) {
updateStateBySelection();
}
};
mapsCheckBox.setOnCheckedChangeListener(checkBoxListener);
fusionTablesCheckBox.setOnCheckedChangeListener(checkBoxListener);
docsCheckBox.setOnCheckedChangeListener(checkBoxListener);
// Setup initial state
initState();
// Update state based on sendRequest
updateStateBySendRequest();
// Update state based on current selection
updateStateBySelection();
alertDialog = new AlertDialog.Builder(this)
.setCancelable(true)
.setNegativeButton(R.string.generic_cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
finish();
}
})
.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override @Override
public void onCancel(DialogInterface d) { public void onCancel(DialogInterface d) {
finish(); finish();
} }
}); })
.setPositiveButton(R.string.send_google_send_now, new DialogInterface.OnClickListener() {
mapsTableRow = (TableRow) dialog.findViewById(R.id.send_google_maps_row); @Override
fusionTablesTableRow = (TableRow) dialog.findViewById(R.id.send_google_fusion_tables_row); public void onClick(DialogInterface dialog, int which) {
docsTableRow = (TableRow) dialog.findViewById(R.id.send_google_docs_row);
mapsCheckBox = (CheckBox) dialog.findViewById(R.id.send_google_maps);
fusionTablesCheckBox = (CheckBox) dialog.findViewById(R.id.send_google_fusion_tables);
docsCheckBox = (CheckBox) dialog.findViewById(R.id.send_google_docs);
mapsOptionTableRow = (TableRow) dialog.findViewById(R.id.send_google_maps_option_row);
newMapRadioButton = (RadioButton) dialog.findViewById(R.id.send_google_new_map);
existingMapRadioButton = (RadioButton) dialog.findViewById(R.id.send_google_existing_map);
// Setup checkboxes
OnCheckedChangeListener checkBoxListener = new OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton button, boolean checked) {
updateStateBySelection();
}
};
mapsCheckBox.setOnCheckedChangeListener(checkBoxListener);
fusionTablesCheckBox.setOnCheckedChangeListener(checkBoxListener);
docsCheckBox.setOnCheckedChangeListener(checkBoxListener);
// Setup buttons
cancel = (Button) dialog.findViewById(R.id.send_google_cancel);
cancel.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
finish();
}
});
send = (Button) dialog.findViewById(R.id.send_google_send_now);
send.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
saveState(); saveState();
startNextActivity(); if (sendMaps() || sendFusionTables() || sendDocs()) {
startNextActivity();
} else {
Toast.makeText(UploadServiceChooserActivity.this,
R.string.send_google_no_service_selected, Toast.LENGTH_LONG).show();
finish();
}
} }
}); })
.setTitle(R.string.send_google_title)
// Setup initial state .setView(view)
initState(); .create();
return alertDialog;
// Update state based on sendRequest
updateStateBySendRequest();
// Update state based on current user selection
updateStateBySelection();
return dialog;
default:
return null;
}
} }
/** /**
@@ -186,7 +187,6 @@ public class UploadServiceChooserActivity extends Activity {
*/ */
private void updateStateBySelection() { private void updateStateBySelection() {
mapsOptionTableRow.setVisibility(sendMaps() ? View.VISIBLE : View.GONE); mapsOptionTableRow.setVisibility(sendMaps() ? View.VISIBLE : View.GONE);
send.setEnabled(sendMaps() || sendFusionTables() || sendDocs());
} }
/** /**
@@ -242,7 +242,7 @@ public class UploadServiceChooserActivity extends Activity {
startActivity(intent); startActivity(intent);
finish(); finish();
} }
/** /**
* Sends stats to Google Analytics. * Sends stats to Google Analytics.
*/ */
@@ -259,12 +259,12 @@ public class UploadServiceChooserActivity extends Activity {
} }
AnalyticsUtils.sendPageViews(this, pages.toArray(new String[pages.size()])); AnalyticsUtils.sendPageViews(this, pages.toArray(new String[pages.size()]));
} }
@VisibleForTesting @VisibleForTesting
Dialog getDialog() { AlertDialog getAlertDialog() {
return dialog; return alertDialog;
} }
@VisibleForTesting @VisibleForTesting
SendRequest getSendRequest() { SendRequest getSendRequest() {
return sendRequest; return sendRequest;
@@ -91,10 +91,7 @@ public class RemoveTempFilesService extends Service {
} }
private void cleanTempDirectory(String name) { private void cleanTempDirectory(String name) {
FileUtils fileUtils = new FileUtils(); cleanTempDirectory(new File(FileUtils.buildExternalDirectoryPath(name, "tmp")));
String dirName = fileUtils.buildExternalDirectoryPath(name, "tmp");
File dir = new File(dirName);
cleanTempDirectory(dir);
} }
/** /**
@@ -18,6 +18,7 @@ package com.google.android.apps.mytracks.services;
import static com.google.android.apps.mytracks.Constants.TAG; import static com.google.android.apps.mytracks.Constants.TAG;
import com.google.android.apps.mytracks.util.SystemUtils; import com.google.android.apps.mytracks.util.SystemUtils;
import com.google.android.maps.mytracks.BuildConfig;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
@@ -157,7 +158,7 @@ public class TrackRecordingServiceConnection {
Log.i(TAG, "Binding to the service"); Log.i(TAG, "Binding to the service");
Intent intent = new Intent(context, TrackRecordingService.class); Intent intent = new Intent(context, TrackRecordingService.class);
int flags = SystemUtils.isRelease(context) ? 0 : Context.BIND_DEBUG_UNBIND; int flags = BuildConfig.DEBUG ? Context.BIND_DEBUG_UNBIND : 0;
context.bindService(intent, serviceConnection, flags); context.bindService(intent, serviceConnection, flags);
} }
@@ -23,7 +23,7 @@ import com.dsi.ant.exception.AntInterfaceException;
import com.google.android.apps.mytracks.Constants; import com.google.android.apps.mytracks.Constants;
import com.google.android.apps.mytracks.content.Sensor; import com.google.android.apps.mytracks.content.Sensor;
import com.google.android.apps.mytracks.util.ApiAdapterFactory; import com.google.android.apps.mytracks.util.ApiAdapterFactory;
import com.google.android.apps.mytracks.util.SystemUtils; import com.google.android.maps.mytracks.BuildConfig;
import com.google.android.maps.mytracks.R; import com.google.android.maps.mytracks.R;
import android.content.Context; import android.content.Context;
@@ -32,7 +32,7 @@ import android.util.Log;
/** /**
* A sensor manager to the PC7 SRM ANT+ bridge. * A sensor manager to the PC7 SRM ANT+ bridge.
* *
* @author Sandor Dornbush * @author Sandor Dornbush
* @author Umran Abdulla * @author Umran Abdulla
*/ */
@@ -54,15 +54,15 @@ public class AntSrmBridgeSensorManager extends AntSensorManager {
private static final int INDEX_MESSAGE_SPEED = 5; private static final int INDEX_MESSAGE_SPEED = 5;
private static final int INDEX_MESSAGE_CADENCE = 7; private static final int INDEX_MESSAGE_CADENCE = 7;
private static final int INDEX_MESSAGE_BPM = 8; private static final int INDEX_MESSAGE_BPM = 8;
private static final int MSG_INITIAL = 5; private static final int MSG_INITIAL = 5;
private static final int MSG_DATA = 6; private static final int MSG_DATA = 6;
private short deviceNumber; private short deviceNumber;
public AntSrmBridgeSensorManager(Context context) { public AntSrmBridgeSensorManager(Context context) {
super(context); super(context);
Log.i(TAG, "new ANT SRM Bridge Sensor Manager created"); Log.i(TAG, "new ANT SRM Bridge Sensor Manager created");
deviceNumber = WILDCARD; deviceNumber = WILDCARD;
@@ -77,15 +77,15 @@ public class AntSrmBridgeSensorManager extends AntSensorManager {
} }
Log.i(TAG, "Will pair with device: " + deviceNumber); Log.i(TAG, "Will pair with device: " + deviceNumber);
} }
@Override @Override
protected boolean handleMessage(byte messageId, byte[] messageData) { protected boolean handleMessage(byte messageId, byte[] messageData) {
if (super.handleMessage(messageId, messageData)) { if (super.handleMessage(messageId, messageData)) {
return true; return true;
} }
if (!SystemUtils.isRelease(context)) { if (BuildConfig.DEBUG) {
Log.d(TAG, "Received ANT msg: " + AntUtils.antMessageToString(messageId) + "(" + messageId + ")"); Log.d(TAG, "Received ANT msg: " + AntUtils.antMessageToString(messageId) + "(" + messageId + ")");
} }
@@ -100,7 +100,7 @@ public class AntSrmBridgeSensorManager extends AntSensorManager {
return true; return true;
} }
/** /**
* Decode an ant device message. * Decode an ant device message.
* @param messageData The byte array received from the device. * @param messageData The byte array received from the device.
@@ -132,10 +132,10 @@ public class AntSrmBridgeSensorManager extends AntSensorManager {
} }
setSensorState(Sensor.SensorState.CONNECTED); setSensorState(Sensor.SensorState.CONNECTED);
int messageType = antMessage[INDEX_MESSAGE_TYPE] & 0xFF; int messageType = antMessage[INDEX_MESSAGE_TYPE] & 0xFF;
Log.d(TAG, "Received message-type=" + messageType); Log.d(TAG, "Received message-type=" + messageType);
switch (messageType) { switch (messageType) {
case MSG_INITIAL: case MSG_INITIAL:
break; break;
@@ -149,7 +149,7 @@ public class AntSrmBridgeSensorManager extends AntSensorManager {
{ {
int messageId = msg[INDEX_MESSAGE_ID] & 0xFF; int messageId = msg[INDEX_MESSAGE_ID] & 0xFF;
Log.d(TAG, "Received message-id=" + messageId); Log.d(TAG, "Received message-id=" + messageId);
int powerVal = (((msg[INDEX_MESSAGE_POWER] & 0xFF) << 8) | int powerVal = (((msg[INDEX_MESSAGE_POWER] & 0xFF) << 8) |
(msg[INDEX_MESSAGE_POWER+1] & 0xFF)); (msg[INDEX_MESSAGE_POWER+1] & 0xFF));
@SuppressWarnings("unused") @SuppressWarnings("unused")
@@ -158,12 +158,12 @@ public class AntSrmBridgeSensorManager extends AntSensorManager {
int cadenceVal = (msg[INDEX_MESSAGE_CADENCE] & 0xFF); int cadenceVal = (msg[INDEX_MESSAGE_CADENCE] & 0xFF);
int bpmVal = (msg[INDEX_MESSAGE_BPM] & 0xFF); int bpmVal = (msg[INDEX_MESSAGE_BPM] & 0xFF);
long time = System.currentTimeMillis(); long time = System.currentTimeMillis();
Sensor.SensorData.Builder power = Sensor.SensorData.Builder power =
Sensor.SensorData.newBuilder() Sensor.SensorData.newBuilder()
.setValue(powerVal) .setValue(powerVal)
.setState(Sensor.SensorState.SENDING); .setState(Sensor.SensorState.SENDING);
/* /*
* Although speed is available from the SRM Bridge, MyTracks doesn't use the value, and * Although speed is available from the SRM Bridge, MyTracks doesn't use the value, and
* computes speed from the GPS location data. * computes speed from the GPS location data.
@@ -171,12 +171,12 @@ public class AntSrmBridgeSensorManager extends AntSensorManager {
// Sensor.SensorData.Builder speed = Sensor.SensorData.newBuilder().setValue(speedVal).setState( // Sensor.SensorData.Builder speed = Sensor.SensorData.newBuilder().setValue(speedVal).setState(
// Sensor.SensorState.SENDING); // Sensor.SensorState.SENDING);
Sensor.SensorData.Builder cadence = Sensor.SensorData.Builder cadence =
Sensor.SensorData.newBuilder() Sensor.SensorData.newBuilder()
.setValue(cadenceVal) .setValue(cadenceVal)
.setState(Sensor.SensorState.SENDING); .setState(Sensor.SensorState.SENDING);
Sensor.SensorData.Builder bpm = Sensor.SensorData.Builder bpm =
Sensor.SensorData.newBuilder() Sensor.SensorData.newBuilder()
.setValue(bpmVal) .setValue(bpmVal)
.setState(Sensor.SensorState.SENDING); .setState(Sensor.SensorState.SENDING);
@@ -189,7 +189,7 @@ public class AntSrmBridgeSensorManager extends AntSensorManager {
.setHeartRate(bpm) .setHeartRate(bpm)
.build(); .build();
} }
void handleChannelId(byte[] rawMessage) { void handleChannelId(byte[] rawMessage) {
AntChannelIdMessage message = new AntChannelIdMessage(rawMessage); AntChannelIdMessage message = new AntChannelIdMessage(rawMessage);
deviceNumber = message.getDeviceNumber(); deviceNumber = message.getDeviceNumber();
@@ -203,12 +203,12 @@ public class AntSrmBridgeSensorManager extends AntSensorManager {
} }
private void handleMessageResponse(byte[] rawMessage) { private void handleMessageResponse(byte[] rawMessage) {
AntChannelResponseMessage message = AntChannelResponseMessage message =
new AntChannelResponseMessage(rawMessage); new AntChannelResponseMessage(rawMessage);
if (!SystemUtils.isRelease(context)) { if (BuildConfig.DEBUG) {
Log.d(TAG, "Received ANT Response: " + AntUtils.antMessageToString(message.getMessageId()) + Log.d(TAG, "Received ANT Response: " + AntUtils.antMessageToString(message.getMessageId()) +
"(" + message.getMessageId() + ")" + "(" + message.getMessageId() + ")" +
", Code: " + AntUtils.antEventToStr(message.getMessageCode()) + ", Code: " + AntUtils.antEventToStr(message.getMessageCode()) +
"(" + message.getMessageCode() + ")"); "(" + message.getMessageCode() + ")");
} }
switch (message.getMessageId()) { switch (message.getMessageId()) {
@@ -29,20 +29,46 @@ import java.io.File;
*/ */
public class FileUtils { public class FileUtils {
private FileUtils() {}
/** /**
* The maximum FAT32 path length. See the FAT32 spec at * The maximum FAT32 path length. See the FAT32 spec at
* http://msdn.microsoft.com/en-us/windows/hardware/gg463080 * http://msdn.microsoft.com/en-us/windows/hardware/gg463080
*/ */
@VisibleForTesting @VisibleForTesting
static final int MAX_FAT32_PATH_LENGTH = 260; static final int MAX_FAT32_PATH_LENGTH = 260;
/**
* Returns whether the SD card is available.
*/
public static boolean isSdCardAvailable() {
return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState());
}
/**
* Ensures the given 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 (dir.exists() && dir.isDirectory()) {
return true;
}
if (dir.mkdirs()) {
return true;
}
return false;
}
/** /**
* Builds a path inside the My Tracks directory in the SD card. * Builds a path inside the My Tracks directory in the SD card.
* *
* @param components the path components inside the mytracks directory * @param components the path components inside the mytracks directory
* @return the full path to the destination * @return the full path to the destination
*/ */
public String buildExternalDirectoryPath(String... components) { public static String buildExternalDirectoryPath(String... components) {
StringBuilder dirNameBuilder = new StringBuilder(); StringBuilder dirNameBuilder = new StringBuilder();
dirNameBuilder.append(Environment.getExternalStorageDirectory()); dirNameBuilder.append(Environment.getExternalStorageDirectory());
dirNameBuilder.append(File.separatorChar); dirNameBuilder.append(File.separatorChar);
@@ -54,33 +80,6 @@ public class FileUtils {
return dirNameBuilder.toString(); return dirNameBuilder.toString();
} }
/**
* Returns whether the SD card is available.
*/
public boolean isSdCardAvailable() {
return Environment.MEDIA_MOUNTED.equals(
Environment.getExternalStorageState());
}
/**
* Ensures the given directory exists by creating it and its parents if
* necessary.
*
* @return whether the directory exists (either already existed or was
* successfully created)
*/
public boolean ensureDirectoryExists(File dir) {
if (dir.exists() && dir.isDirectory()) {
return true;
}
if (dir.mkdirs()) {
return true;
}
return false;
}
/** /**
* Builds a filename with the given base name (prefix) and the given * Builds a filename with the given base name (prefix) and the given
* extension, possibly adding a suffix to ensure the file doesn't exist. * extension, possibly adding a suffix to ensure the file doesn't exist.
@@ -90,8 +89,8 @@ public class FileUtils {
* @param extension the file's extension * @param extension the file's extension
* @return the complete file name, without the directory * @return the complete file name, without the directory
*/ */
public synchronized String buildUniqueFileName(File directory, public static synchronized String buildUniqueFileName(
String fileBaseName, String extension) { File directory, String fileBaseName, String extension) {
return buildUniqueFileName(directory, fileBaseName, extension, 0); return buildUniqueFileName(directory, fileBaseName, extension, 0);
} }
@@ -105,7 +104,8 @@ public class FileUtils {
* @param suffix the first numeric suffix to try to use, or 0 for none * @param suffix the first numeric suffix to try to use, or 0 for none
* @return the complete filename, without the directory * @return the complete filename, without the directory
*/ */
private String buildUniqueFileName(File directory, String base, String extension, int suffix) { private static String buildUniqueFileName(
File directory, String base, String extension, int suffix) {
String suffixName = ""; String suffixName = "";
if (suffix > 0) { if (suffix > 0) {
suffixName += "(" + Integer.toString(suffix) + ")"; suffixName += "(" + Integer.toString(suffix) + ")";
@@ -115,8 +115,8 @@ public class FileUtils {
String baseName = sanitizeFileName(base); String baseName = sanitizeFileName(base);
baseName = truncateFileName(directory, baseName, suffixName); baseName = truncateFileName(directory, baseName, suffixName);
String fullName = baseName + suffixName; String fullName = baseName + suffixName;
if (!fileExists(directory, fullName)) { if (!new File(directory, fullName).exists()) {
return fullName; return fullName;
} }
return buildUniqueFileName(directory, base, extension, suffix + 1); return buildUniqueFileName(directory, base, extension, suffix + 1);
@@ -131,7 +131,7 @@ public class FileUtils {
* @param name name * @param name name
*/ */
@VisibleForTesting @VisibleForTesting
String sanitizeFileName(String name) { static String sanitizeFileName(String name) {
StringBuffer buffer = new StringBuffer(name.length()); StringBuffer buffer = new StringBuffer(name.length());
for (int i = 0; i < name.length(); i++) { for (int i = 0; i < name.length(); i++) {
int codePoint = name.codePointAt(i); int codePoint = name.codePointAt(i);
@@ -145,13 +145,13 @@ public class FileUtils {
String result = buffer.toString(); String result = buffer.toString();
return result.replaceAll("_+", "_"); return result.replaceAll("_+", "_");
} }
/** /**
* Returns true if it is a special FAT32 character. * Returns true if it is a special FAT32 character.
* *
* @param character the character * @param character the character
*/ */
private boolean isSpecialFat32(char character) { private static boolean isSpecialFat32(char character) {
switch (character) { switch (character) {
case '$': case '$':
case '%': case '%':
@@ -191,8 +191,8 @@ public class FileUtils {
* @param suffix suffix * @param suffix suffix
*/ */
@VisibleForTesting @VisibleForTesting
String truncateFileName(File directory, String name, String suffix) { static String truncateFileName(File directory, String name, String suffix) {
// 1 at the end accounts for the FAT32 filename trailing NUL character // 1 at the end accounts for the FAT32 filename trailing NUL character
int requiredLength = directory.getPath().length() + suffix.length() + 1; int requiredLength = directory.getPath().length() + suffix.length() + 1;
if (name.length() + requiredLength > MAX_FAT32_PATH_LENGTH) { if (name.length() + requiredLength > MAX_FAT32_PATH_LENGTH) {
int limit = MAX_FAT32_PATH_LENGTH - requiredLength; int limit = MAX_FAT32_PATH_LENGTH - requiredLength;
@@ -201,13 +201,4 @@ public class FileUtils {
return name; return name;
} }
} }
/**
* Checks whether a file with the given name exists in the given directory.
* This is isolated so it can be overridden in tests.
*/
protected boolean fileExists(File directory, String fullName) {
File file = new File(directory, fullName);
return file.exists();
}
} }
@@ -1,12 +1,12 @@
/* /*
* Copyright 2011 Google Inc. * Copyright 2011 Google Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not * 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 * use this file except in compliance with the License. You may obtain a copy of
* the License at * the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -22,7 +22,6 @@ import android.content.Context;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
import android.os.PowerManager; import android.os.PowerManager;
import android.os.PowerManager.WakeLock; import android.os.PowerManager.WakeLock;
import android.util.Log; import android.util.Log;
@@ -34,26 +33,6 @@ import android.util.Log;
*/ */
public class SystemUtils { public class SystemUtils {
private static final int RELEASE_SIGNATURE_HASHCODE = -1855564782;
/**
* Returns whether or not this is a release build.
*/
public static boolean isRelease(Context context) {
try {
Signature [] sigs = context.getPackageManager().getPackageInfo(
context.getPackageName(), PackageManager.GET_SIGNATURES).signatures;
for (Signature sig : sigs) {
if (sig.hashCode() == RELEASE_SIGNATURE_HASHCODE) {
return true;
}
}
} catch (NameNotFoundException e) {
Log.e(Constants.TAG, "Unable to get signatures", e);
}
return false;
}
/** /**
* Get the My Tracks version from the manifest. * Get the My Tracks version from the manifest.
* *
@@ -24,7 +24,6 @@ import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor; import android.content.SharedPreferences.Editor;
import android.test.ActivityInstrumentationTestCase2; import android.test.ActivityInstrumentationTestCase2;
import android.widget.Button;
import android.widget.CheckBox; import android.widget.CheckBox;
import android.widget.RadioButton; import android.widget.RadioButton;
@@ -59,7 +58,6 @@ public class UploadServiceChooserActivityTest extends
assertTrue(getMapsCheckBox().isShown()); assertTrue(getMapsCheckBox().isShown());
assertTrue(getFusionTablesCheckBox().isShown()); assertTrue(getFusionTablesCheckBox().isShown());
assertTrue(getDocsCheckBox().isShown()); assertTrue(getDocsCheckBox().isShown());
assertTrue(getCancelButton().isEnabled());
// Clicks to disable all send items. // Clicks to disable all send items.
uploadServiceChooserActivity.runOnUiThread(new Runnable() { uploadServiceChooserActivity.runOnUiThread(new Runnable() {
@@ -79,11 +77,9 @@ public class UploadServiceChooserActivityTest extends
assertTrue(getMapsCheckBox().isShown()); assertTrue(getMapsCheckBox().isShown());
assertTrue(getFusionTablesCheckBox().isShown()); assertTrue(getFusionTablesCheckBox().isShown());
assertTrue(getDocsCheckBox().isShown()); assertTrue(getDocsCheckBox().isShown());
assertTrue(getCancelButton().isEnabled());
assertFalse(getNewMapRadioButton().isShown()); assertFalse(getNewMapRadioButton().isShown());
assertFalse(getExistingMapRadioButton().isShown()); assertFalse(getExistingMapRadioButton().isShown());
assertFalse(getSendButton().isEnabled());
} }
/** /**
@@ -94,7 +90,6 @@ public class UploadServiceChooserActivityTest extends
initialActivity(true, false, false); initialActivity(true, false, false);
instrumentation.waitForIdleSync(); instrumentation.waitForIdleSync();
assertTrue(getMapsCheckBox().isShown()); assertTrue(getMapsCheckBox().isShown());
assertTrue(getCancelButton().isEnabled());
// Clicks to enable this items. // Clicks to enable this items.
uploadServiceChooserActivity.runOnUiThread(new Runnable() { uploadServiceChooserActivity.runOnUiThread(new Runnable() {
@@ -108,8 +103,6 @@ public class UploadServiceChooserActivityTest extends
assertTrue(getMapsCheckBox().isShown()); assertTrue(getMapsCheckBox().isShown());
assertTrue(getNewMapRadioButton().isShown()); assertTrue(getNewMapRadioButton().isShown());
assertTrue(getExistingMapRadioButton().isShown()); assertTrue(getExistingMapRadioButton().isShown());
assertTrue(getSendButton().isEnabled());
assertTrue(getCancelButton().isEnabled());
} }
/** /**
@@ -120,8 +113,6 @@ public class UploadServiceChooserActivityTest extends
assertFalse(getMapsCheckBox().isShown()); assertFalse(getMapsCheckBox().isShown());
assertFalse(getFusionTablesCheckBox().isShown()); assertFalse(getFusionTablesCheckBox().isShown());
assertFalse(getDocsCheckBox().isShown()); assertFalse(getDocsCheckBox().isShown());
assertFalse(getSendButton().isEnabled());
assertTrue(getCancelButton().isEnabled());
} }
/** /**
@@ -259,35 +250,26 @@ public class UploadServiceChooserActivityTest extends
uploadServiceChooserActivity = this.getActivity(); uploadServiceChooserActivity = this.getActivity();
} }
private Button getSendButton() {
return (Button) uploadServiceChooserActivity.getDialog()
.findViewById(R.id.send_google_send_now);
}
Button getCancelButton() {
return (Button) uploadServiceChooserActivity.getDialog().findViewById(R.id.send_google_cancel);
}
private CheckBox getMapsCheckBox() { private CheckBox getMapsCheckBox() {
return (CheckBox) uploadServiceChooserActivity.getDialog().findViewById(R.id.send_google_maps); return (CheckBox) uploadServiceChooserActivity.getAlertDialog().findViewById(R.id.send_google_maps);
} }
private CheckBox getFusionTablesCheckBox() { private CheckBox getFusionTablesCheckBox() {
return (CheckBox) uploadServiceChooserActivity.getDialog().findViewById( return (CheckBox) uploadServiceChooserActivity.getAlertDialog().findViewById(
R.id.send_google_fusion_tables); R.id.send_google_fusion_tables);
} }
private CheckBox getDocsCheckBox() { private CheckBox getDocsCheckBox() {
return (CheckBox) uploadServiceChooserActivity.getDialog().findViewById(R.id.send_google_docs); return (CheckBox) uploadServiceChooserActivity.getAlertDialog().findViewById(R.id.send_google_docs);
} }
private RadioButton getNewMapRadioButton() { private RadioButton getNewMapRadioButton() {
return (RadioButton) uploadServiceChooserActivity.getDialog().findViewById( return (RadioButton) uploadServiceChooserActivity.getAlertDialog().findViewById(
R.id.send_google_new_map); R.id.send_google_new_map);
} }
private RadioButton getExistingMapRadioButton() { private RadioButton getExistingMapRadioButton() {
return (RadioButton) uploadServiceChooserActivity.getDialog().findViewById( return (RadioButton) uploadServiceChooserActivity.getAlertDialog().findViewById(
R.id.send_google_existing_map); R.id.send_google_existing_map);
} }
} }
@@ -20,8 +20,6 @@ import com.google.android.apps.mytracks.Constants;
import android.os.Environment; import android.os.Environment;
import java.io.File; import java.io.File;
import java.util.HashSet;
import java.util.Set;
import junit.framework.TestCase; import junit.framework.TestCase;
@@ -31,76 +29,81 @@ import junit.framework.TestCase;
* @author Rodrigo Damazio * @author Rodrigo Damazio
*/ */
public class FileUtilsTest extends TestCase { public class FileUtilsTest extends TestCase {
private FileUtils fileUtils;
private Set<String> existingFiles;
@Override
protected void setUp() throws Exception {
super.setUp();
existingFiles = new HashSet<String>();
fileUtils = new FileUtils() {
@Override
protected boolean fileExists(File directory, String fullName) {
return existingFiles.contains(fullName);
}
};
}
/**
* Tests {@link FileUtils#buildExternalDirectoryPath(String...)}.
*/
public void testBuildExternalDirectoryPath() { public void testBuildExternalDirectoryPath() {
String expectedName = Environment.getExternalStorageDirectory() String expectedName = Environment.getExternalStorageDirectory() + File.separator
+ File.separator + Constants.SDCARD_TOP_DIR + File.separator + "a" + File.separator + "b" + File.separator
+ Constants.SDCARD_TOP_DIR
+ File.separator
+ "a"
+ File.separator
+ "b"
+ File.separator
+ "c"; + "c";
String dirName = FileUtils.buildExternalDirectoryPath("a", "b", "c");
String dirName = fileUtils.buildExternalDirectoryPath("a", "b", "c");
assertEquals(expectedName, dirName); assertEquals(expectedName, dirName);
} }
/** /**
* Tests sanitize filename. * Tests {@link FileUtils#buildUniqueFileName(File, String, String)} when the
* file is new.
*/
public void testBuildUniqueFileName_new() {
String filename = FileUtils.buildUniqueFileName(new File("/dir"), "Filename", "ext");
assertEquals("Filename.ext", filename);
}
/**
* Tests {@link FileUtils#buildUniqueFileName(File, String, String)} when the
* file exists already.
*/
public void testBuildUniqueFileName_exist() {
// Expect "/default.prop" to exist on the phone/emulator
String filename = FileUtils.buildUniqueFileName(new File("/"), "default", "prop");
assertEquals("default(1).prop", filename);
}
/**
* Tests {@link FileUtils#sanitizeFileName(String)} with special characters.
* Verifies that they are sanitized.
*/ */
public void testSanitizeFileName() { public void testSanitizeFileName() {
String name = "Swim\10ming-^across:/the/ pacific (ocean)."; String name = "Swim\10ming-^across:/the/ pacific (ocean).";
String expected = "Swim_ming-^across_the_ pacific (ocean)_"; String expected = "Swim_ming-^across_the_ pacific (ocean)_";
assertEquals(expected, fileUtils.sanitizeFileName(name)); assertEquals(expected, FileUtils.sanitizeFileName(name));
} }
/** /**
* Tests characters in other languages, like Chinese and Russian, are allowed. * Tests {@link FileUtils#sanitizeFileName(String)} with i18n characters (in
* Chinese and Russian). Verifies that they are allowed.
*/ */
public void testSanitizeFileName_i18n() { public void testSanitizeFileName_i18n() {
String name = "您好-привет"; String name = "您好-привет";
String expected = "您好-привет"; String expected = "您好-привет";
assertEquals(expected, FileUtils.sanitizeFileName(name));
assertEquals(expected, fileUtils.sanitizeFileName(name));
} }
/** /**
* Tests special FAT32 characters are allowed. * Tests {@link FileUtils#sanitizeFileName(String)} with special FAT32
* characters. Verifies that they are allowed.
*/ */
public void testSanitizeFileName_special_characters() { public void testSanitizeFileName_special_characters() {
String name = "$%'-_@~`!(){}^#&+,;=[] "; String name = "$%'-_@~`!(){}^#&+,;=[] ";
String expected = "$%'-_@~`!(){}^#&+,;=[] "; String expected = "$%'-_@~`!(){}^#&+,;=[] ";
assertEquals(expected, FileUtils.sanitizeFileName(name));
assertEquals(expected, fileUtils.sanitizeFileName(name));
} }
/** /**
* Testing collapsing multiple underscores characters. * Tests {@link FileUtils#sanitizeFileName(String)} with multiple escaped
* characters in a row. Verifies that they are collapsed into one underscore.
*/ */
public void testSanitizeFileName_collapse() { public void testSanitizeFileName_collapse() {
String name = "hello//there"; String name = "hello//there";
String expected = "hello_there"; String expected = "hello_there";
assertEquals(expected, FileUtils.sanitizeFileName(name));
assertEquals(expected, fileUtils.sanitizeFileName(name));
} }
/**
* Tests {@link FileUtils#truncateFileName(File, String, String)}. Verifies
* the a long file name is truncated.
*/
public void testTruncateFileName() { public void testTruncateFileName() {
File directory = new File("/dir1/dir2/"); File directory = new File("/dir1/dir2/");
String suffix = ".gpx"; String suffix = ".gpx";
@@ -109,36 +112,12 @@ public class FileUtilsTest extends TestCase {
name[i] = 'a'; name[i] = 'a';
} }
String nameString = new String(name); String nameString = new String(name);
String truncated = FileUtils.truncateFileName(directory, nameString, suffix);
String truncated = fileUtils.truncateFileName(directory, nameString, suffix);
for (int i = 0; i < truncated.length(); i++) { for (int i = 0; i < truncated.length(); i++) {
assertEquals('a', truncated.charAt(i)); assertEquals('a', truncated.charAt(i));
} }
assertEquals(FileUtils.MAX_FAT32_PATH_LENGTH, assertEquals(FileUtils.MAX_FAT32_PATH_LENGTH,
new File(directory, truncated + suffix).getPath().length()); new File(directory, truncated + suffix).getPath().length());
} }
public void testBuildUniqueFileName_someExist() {
existingFiles = new HashSet<String>();
existingFiles.add("Filename.ext");
existingFiles.add("Filename(1).ext");
existingFiles.add("Filename(2).ext");
existingFiles.add("Filename(3).ext");
existingFiles.add("Filename(4).ext");
String filename = fileUtils.buildUniqueFileName(new File("/dir/"), "Filename", "ext");
assertEquals("Filename(5).ext", filename);
}
public void testBuildUniqueFileName_oneExists() {
existingFiles.add("Filename.ext");
String filename = fileUtils.buildUniqueFileName(new File("/dir/"), "Filename", "ext");
assertEquals("Filename(1).ext", filename);
}
public void testBuildUniqueFileName_noneExists() {
String filename = fileUtils.buildUniqueFileName(new File("/dir/"), "Filename", "ext");
assertEquals("Filename.ext", filename);
}
} }