Add support to choose the ISO 8601 date format as the default track name.

Note using the "yyyy-MM-dd HH:mm" format so it is readable
for users. However, the colon, ":", is not a valid
FAT32 filename character. So when saving to the SD card,
the filename becomes "yyyy-MM-dd HH_mm".
This commit is contained in:
Jimmy Shih
2012-01-09 12:50:53 -08:00
parent 88c4e0e499
commit 43cf0659fd
10 changed files with 196 additions and 82 deletions
@@ -200,6 +200,18 @@ limitations under the License.
<item>@string/sensor_type_value_zephyr</item>
</string-array>
<string-array name="settings_recording_track_name_options">
<item>@string/settings_recording_track_name_date_local_option</item>
<item>@string/settings_recording_track_name_date_iso_8601_option</item>
<item>@string/settings_recording_track_name_number_option</item>
</string-array>
<string-array name="settings_recording_track_name_values">
<item>@string/settings_recording_track_name_date_local_value</item>
<item>@string/settings_recording_track_name_date_iso_8601_value</item>
<item>@string/settings_recording_track_name_number_value</item>
</string-array>
<string-array name="waypoint_types">
<item>@string/waypoint_type_atm</item>
<item>@string/waypoint_type_bank</item>
+3
View File
@@ -49,6 +49,9 @@ limitations under the License.
<string name="sensor_type_value_polar">POLAR</string>
<string name="sensor_type_value_srm_ant_bridge">SRM_ANT_BRIDGE</string>
<string name="sensor_type_value_zephyr">ZEPHYR</string>
<string name="settings_recording_track_name_date_iso_8601_value">DATE_ISO_8601</string>
<string name="settings_recording_track_name_date_local_value">DATE_LOCAL</string>
<string name="settings_recording_track_name_number_value">NUMBER</string>
<string name="track_action_end">com.google.android.apps.mytracks.END_CURRENT_TRACK</string>
<string name="track_action_save">android.intent.action.SAVE</string>
<string name="track_action_start">com.google.android.apps.mytracks.START_NEW_TRACK</string>
+1 -1
View File
@@ -36,9 +36,9 @@ limitations under the License.
<string name="send_to_fusion_tables_key">sendToFusionTables</string>
<string name="send_to_maps_key">sendToMaps</string>
<string name="sensor_type_key">sensorType</string>
<string name="settings_recording_track_name_key">settingsRecordingTrackName</string>
<string name="share_url_only_key">shareUrlOnly</string>
<string name="split_frequency_key">splitFrequency</string>
<string name="timestamp_track_name_key">timestampTrackName</string>
<string name="track_color_mode_dynamic_speed_variation_key">trackColorModeDynamicVariation
</string>
<string name="track_color_mode_fixed_speed_medium_display_key">trackColorModeFixedMediumDisplay
+33 -22
View File
@@ -1677,28 +1677,6 @@ limitations under the License.
Screenshot: settings_recording_top.png">
Time/distance after which a new marker will be inserted.
</string>
<string name="settings_recording_name"
translation_description="In the 'Recording' settings, the checkbox on whether the track should
be named based on the track's start time or based on a number.
Character limit: ~23 (1 line)
Screenshot: settings_recording_bottom.png">
Use timestamp
</string>
<string name="settings_recording_name_summary_off"
translation_description="In the 'Recording' settings, the summary for the 'Use timestamp'
checkbox when it is unchecked explaining using a number as the default track name.
Screenshot: settings_recording_bottom_unchecked.png">
Use a number as the default track name.
</string>
<string name="settings_recording_name_summary_on"
translation_description="In the 'Recording' settings, the summary for the 'Use timestamp'
checkbox when it is checked explaining using the track's start time as the default track name.
Screenshot: settings_recording_bottom.png">
Use the track\'s start time as the default track name.
</string>
<string name="settings_recording_track_distance"
translation_description="In the 'Recording' settings, the dropdown option to configure the
distance between two tracks.
@@ -1716,6 +1694,39 @@ limitations under the License.
Maximum distance between two recordings of your position. If two positions are further than
this, the track will be split.
</string>
<string name="settings_recording_track_name"
translation_description="In the 'Recording' settings, the title for the dropdown list to choose
the default track name.
Character limit: ~23 (1 line)">
Track name
</string>
<string name="settings_recording_track_name_date_iso_8601_option"
translation_description="In the 'Recording' settings, the option in the 'Track name' dropdown
list to use the track start time in ISO-8601 date format as the track name.
Character limit: ~21 (1 line)">
Date (ISO 8601)
</string>
<string name="settings_recording_track_name_date_local_option"
translation_description="In the 'Recording' settings, the option in the 'Track name' dropdown
list to use the track start time in local date format as the track name.
Character limit: ~21 (1 line)">
Date (local)
</string>
<string name="settings_recording_track_name_number_option"
translation_description="In the 'Recording' settings, the option in the 'Track name' dropdown
list to use a number as the track name.
Character limit: ~21 (1 line)">
Number
</string>
<string name="settings_recording_track_name_summary"
translation_description="In the 'Recording' settings, the summary for the 'Track name' dropdown
list explaining that it chooses the default track name format.">
Default track name
</string>
<string name="settings_recording_voice"
translation_description="In the 'Recording' settings, the dropdown option to configure the voice
announcement frequency.
+6 -6
View File
@@ -137,12 +137,12 @@ limitations under the License.
android:entryValues="@array/recording_auto_resume_timeout_values"
android:defaultValue="10" />
<CheckBoxPreference
android:key="@string/timestamp_track_name_key"
android:title="@string/settings_recording_name"
android:summaryOn="@string/settings_recording_name_summary_on"
android:summaryOff="@string/settings_recording_name_summary_off"
android:defaultValue="true" />
<ListPreference android:key="@string/settings_recording_track_name_key"
android:title="@string/settings_recording_track_name"
android:summary="@string/settings_recording_track_name_summary"
android:entries="@array/settings_recording_track_name_options"
android:entryValues="@array/settings_recording_track_name_values"
android:defaultValue="@string/settings_recording_track_name_date_local_value"/>
<com.google.android.apps.mytracks.AutoCompleteTextPreference
android:key="@string/default_activity_key"
@@ -18,43 +18,57 @@ package com.google.android.apps.mytracks.services;
import com.google.android.apps.mytracks.Constants;
import com.google.android.apps.mytracks.util.StringUtils;
import com.google.android.maps.mytracks.R;
import com.google.common.annotations.VisibleForTesting;
import android.content.Context;
import android.content.SharedPreferences;
import java.text.SimpleDateFormat;
/**
* Creates a default track name based on the current default track name policy.
* Creates a default track name based on the track name setting.
*
* @author Matthew Simmons
*/
class DefaultTrackNameFactory {
public class DefaultTrackNameFactory {
@VisibleForTesting
static final String ISO_8601_FORMAT = "yyyy-MM-dd HH:mm";
private final Context context;
DefaultTrackNameFactory(Context context) {
public DefaultTrackNameFactory(Context context) {
this.context = context;
}
/**
* Creates a new track name.
* Gets the default track name.
*
* @param trackId The ID for the current track.
* @param startTime The start time, in milliseconds since the epoch, of the
* current track.
* @return The new track name.
* @param trackId the track id
* @param startTime the track start time
*/
String newTrackName(long trackId, long startTime) {
if (useTimestampTrackName()) {
public String getDefaultTrackName(long trackId, long startTime) {
String trackNameSetting = getTrackNameSetting();
if (trackNameSetting.equals(
context.getString(R.string.settings_recording_track_name_date_local_value))) {
return StringUtils.formatDateTime(context, startTime);
} else if (trackNameSetting.equals(
context.getString(R.string.settings_recording_track_name_date_iso_8601_value))) {
SimpleDateFormat dateFormat = new SimpleDateFormat(ISO_8601_FORMAT);
return dateFormat.format(startTime);
} else {
return String.format(context.getString(R.string.track_name_format), trackId);
}
}
/** Determines whether the preferences allow a timestamp-based track name */
protected boolean useTimestampTrackName() {
SharedPreferences prefs = context.getSharedPreferences(
/**
* Gets the track name setting from the shared preferences.
*/
@VisibleForTesting
String getTrackNameSetting() {
SharedPreferences sharedPreferences = context.getSharedPreferences(
Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
return prefs.getBoolean(
context.getString(R.string.timestamp_track_name_key), true);
return sharedPreferences.getString(
context.getString(R.string.settings_recording_track_name_key),
context.getString(R.string.settings_recording_track_name_date_local_value));
}
}
@@ -586,7 +586,7 @@ public class TrackRecordingService extends Service {
Uri trackUri = providerUtils.insertTrack(track);
recordingTrackId = Long.parseLong(trackUri.getLastPathSegment());
track.setId(recordingTrackId);
track.setName(new DefaultTrackNameFactory(this).newTrackName(
track.setName(new DefaultTrackNameFactory(this).getDefaultTrackName(
recordingTrackId, startTime));
track.setCategory(getDefaultActivityType(this));
isRecording = true;
@@ -32,8 +32,8 @@ import java.util.TimeZone;
public class FileUtils {
/**
* The maximum Fat32 path length. See
* http://www.scribd.com/doc/2187273/FAT32-File-System-Specification
* 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;
@@ -148,7 +148,7 @@ public class FileUtils {
for (int i = 0; i < name.length(); i++) {
int codePoint = name.codePointAt(i);
char character = name.charAt(i);
if (Character.isLetterOrDigit(character) || codePoint > 127 || character == '-') {
if (Character.isLetterOrDigit(character) || codePoint > 127 || isSpecialFat32(character)) {
buffer.appendCodePoint(codePoint);
} else {
buffer.append("_");
@@ -157,6 +157,42 @@ public class FileUtils {
String result = buffer.toString();
return result.replaceAll("_+", "_");
}
/**
* Returns true if it is a special FAT32 character.
*
* @param character the character
*/
private 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 +
@@ -16,49 +16,55 @@
package com.google.android.apps.mytracks.services;
import com.google.android.apps.mytracks.util.StringUtils;
import com.google.android.maps.mytracks.R;
import android.content.Context;
import android.test.AndroidTestCase;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* Tests {@link DefaultTrackNameFactory}
*
*
* @author Matthew Simmons
*/
public class DefaultTrackNameFactoryTest extends AndroidTestCase {
/**
* A version of the factory which allows us to supply our own answer as to
* whether a timestamp-based track name should be used.
*/
private static class MockDefaultTrackNameFactory
extends DefaultTrackNameFactory {
private final boolean useTimestamp;
MockDefaultTrackNameFactory(Context context, boolean useTimestamp) {
super(context);
this.useTimestamp = useTimestamp;
}
@Override
protected boolean useTimestampTrackName() {
return useTimestamp;
}
private static final int TRACK_ID = 1;
private static final long START_TIME = 1288213406000L;
public void testDefaultTrackName_date_local() {
DefaultTrackNameFactory defaultTrackNameFactory = new DefaultTrackNameFactory(getContext()) {
@Override
String getTrackNameSetting() {
return getContext().getString(R.string.settings_recording_track_name_date_local_value);
}
};
assertEquals(StringUtils.formatDateTime(getContext(), START_TIME),
defaultTrackNameFactory.getDefaultTrackName(TRACK_ID, START_TIME));
}
private static final long TIMESTAMP = 1288213406000L;
public void testTimestampTrackName() {
DefaultTrackNameFactory factory =
new MockDefaultTrackNameFactory(getContext(), true);
public void testDefaultTrackName_date_iso_8601() {
DefaultTrackNameFactory defaultTrackNameFactory = new DefaultTrackNameFactory(getContext()) {
@Override
String getTrackNameSetting() {
return getContext().getString(R.string.settings_recording_track_name_date_iso_8601_value);
}
};
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(
DefaultTrackNameFactory.ISO_8601_FORMAT);
assertEquals(simpleDateFormat.format(new Date(START_TIME)),
defaultTrackNameFactory.getDefaultTrackName(TRACK_ID, START_TIME));
}
public void testDefaultTrackName_number() {
DefaultTrackNameFactory defaultTrackNameFactory = new DefaultTrackNameFactory(getContext()) {
@Override
String getTrackNameSetting() {
return getContext().getString(R.string.settings_recording_track_name_number_value);
}
};
assertEquals(
StringUtils.formatDateTime(getContext(), TIMESTAMP), factory.newTrackName(1, TIMESTAMP));
}
public void testIncrementingTrackName() {
DefaultTrackNameFactory factory =
new MockDefaultTrackNameFactory(getContext(), false);
assertEquals("Track 1", factory.newTrackName(1, TIMESTAMP));
"Track " + TRACK_ID, defaultTrackNameFactory.getDefaultTrackName(TRACK_ID, START_TIME));
}
}
@@ -62,13 +62,45 @@ public class FileUtilsTest extends TestCase {
assertEquals(expectedName, dirName);
}
/**
* Tests sanitize filename.
*/
public void testSanitizeFileName() {
String name = "Swim\10ming-^across: the/ pacific (ocean).您好-привет_";
String expected = "Swim_ming-_across_the_pacific_ocean_您好-привет_";
String name = "Swim\10ming-^across:/the/ pacific (ocean).";
String expected = "Swim_ming-^across_the_ pacific (ocean)_";
assertEquals(expected, fileUtils.sanitizeFileName(name));
}
/**
* Tests characters in other languages, like Chinese and Russian, are allowed.
*/
public void testSanitizeFileName_i18n() {
String name = "您好-привет";
String expected = "您好-привет";
assertEquals(expected, fileUtils.sanitizeFileName(name));
}
/**
* Tests special FAT32 characters are allowed.
*/
public void testSanitizeFileName_special_characters() {
String name = "$%'-_@~`!(){}^#&+,;=[] ";
String expected = "$%'-_@~`!(){}^#&+,;=[] ";
assertEquals(expected, fileUtils.sanitizeFileName(name));
}
/**
* Testing collapsing multiple underscores characters.
*/
public void testSanitizeFileName_collapse() {
String name = "hello//there";
String expected = "hello_there";
assertEquals(expected, fileUtils.sanitizeFileName(name));
}
public void testTruncateFileName() {
File directory = new File("/dir1/dir2/");
String suffix = ".gpx";