Skip to content

Commit 47b4337

Browse files
committed
Support full log viewing.
1 parent 55d9cd3 commit 47b4337

10 files changed

+333
-88
lines changed

AndroidManifest.xml

+3
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@
3636
<activity android:name=".OptusPlayerListActivity"
3737
android:label="@string/app_name">
3838
</activity>
39+
<activity android:name=".OptusGameLogListActivity"
40+
android:label="@string/app_name">
41+
</activity>
3942
<activity android:name=".ShogiPreferenceActivity"
4043
android:label="@string/app_name">
4144
</activity>

src/com/ysaito/shogi/ExternalCacheManager.java

+50-13
Original file line numberDiff line numberDiff line change
@@ -15,29 +15,46 @@
1515

1616
/**
1717
* Class for managing a file system directory (Context.getCacheDir) as an LRU cache.
18-
* This class is generally MT safe, except that the constructor must be called by the application's main
19-
* event loop thread, since it calls Context.getCacheDir, which doesn't look MT safe.
18+
* This class is generally MT safe, except for getInstance(), which must be called by the application's main
19+
* event loop thread; it calls Context.getCacheDir, which doesn't look MT safe.
2020
*/
2121
public class ExternalCacheManager {
2222
private static final String TAG = "ExtenalCacheManager";
2323
private final File mDir;
2424
private final Context mContext;
25+
private static final int MAX_CACHE_STALENESS_MS = 7200 * 1000; // 2h
2526

2627
// Cache key -> last access time
2728
private HashMap<String, Long> mLastAccessTimes;
2829

2930
// The path where a serialized mLastAccessTimes is saved serialized.
3031
private static final String SUMMARY_PATH = "external_cache_summary";
31-
32-
// TODO: add background purging
33-
32+
33+
private static final HashMap<String, ExternalCacheManager> mInstances =
34+
new HashMap<String, ExternalCacheManager>();
35+
3436
/**
37+
* Create and return a process-wide cache instance. This method must be called by the
38+
* main application event loop thread.
39+
*
40+
* @param context It should be the value of getApplicationContext().
3541
*
3642
* @param id The ID for this cache. It can be any string (it should be usable as a filename).
3743
* It should be unique within the application, so that
3844
* multiple instances of this class can partition the per-application cache space.
3945
*/
40-
public ExternalCacheManager(Context context, String id) {
46+
public static ExternalCacheManager getInstance(Context context, String id) {
47+
ExternalCacheManager c = mInstances.get(id);
48+
if (c == null) {
49+
c = new ExternalCacheManager(context, id);
50+
mInstances.put(id, c);
51+
}
52+
return c;
53+
}
54+
55+
// TODO: add background purging
56+
57+
private ExternalCacheManager(Context context, String id) {
4158
mContext = context;
4259
mDir = new File(context.getCacheDir(), id);
4360
mDir.mkdirs();
@@ -75,6 +92,11 @@ public synchronized void clearAll() {
7592
Log.d(TAG, SUMMARY_PATH + ": Failed to clear cache: " + e.toString());
7693
}
7794
}
95+
96+
private static class CacheEntry implements Serializable {
97+
long createMs; // The time the entry was created. Millisec since the epoch
98+
Serializable obj;
99+
}
78100

79101
/**
80102
* Write the mapping key -> obj to the cache. Errors (e.g., IOError) are simply ignored.
@@ -83,12 +105,16 @@ public synchronized void clearAll() {
83105
* chars such as '/'.
84106
*/
85107
public synchronized void write(String key, Serializable obj) {
108+
CacheEntry ent = new CacheEntry();
109+
ent.createMs = System.currentTimeMillis();
110+
ent.obj = obj;
111+
86112
FileOutputStream data_out = null;
87113
try {
88114
try {
89115
File path = new File(mDir, key);
90116
data_out = new FileOutputStream(path);
91-
new ObjectOutputStream(data_out).writeObject(obj);
117+
new ObjectOutputStream(data_out).writeObject(ent);
92118
mLastAccessTimes.put(key, System.currentTimeMillis());
93119
saveSummary();
94120
} finally {
@@ -103,25 +129,36 @@ public synchronized void write(String key, Serializable obj) {
103129
* If object for "key" is in the cache, return it. Else, or in case of an error (e.g., IOError)
104130
* return null.
105131
*/
106-
public synchronized Object read(String key) {
132+
public static class ReadResult {
133+
public Object obj;
134+
public boolean needRefresh;
135+
}
136+
137+
public synchronized ReadResult read(String key) {
138+
final long now = System.currentTimeMillis();
139+
ReadResult r = new ReadResult();
140+
r.obj = null;
141+
r.needRefresh = true;
107142
FileInputStream data_in = null;
108-
Object obj = null;
109143
try {
110144
try {
111145
File path = new File(mDir, key);
112146
data_in = new FileInputStream(path);
113-
obj = new ObjectInputStream(data_in).readObject();
114-
mLastAccessTimes.put(key, System.currentTimeMillis());
147+
CacheEntry ent = (CacheEntry)(new ObjectInputStream(data_in).readObject());
148+
if (ent != null) {
149+
r.obj = ent.obj;
150+
r.needRefresh = (now - ent.createMs >= MAX_CACHE_STALENESS_MS);
151+
}
152+
mLastAccessTimes.put(key, now);
115153
saveSummary();
116154
} finally {
117155
if (data_in != null) data_in.close();
118156
}
119157
} catch (FileNotFoundException e) {
120-
return null;
121158
} catch (Throwable e) {
122159
Log.d(TAG, SUMMARY_PATH + ": failed to write: " + e.toString());
123160
}
124-
return obj;
161+
return r;
125162
}
126163

127164
private void saveSummary() throws IOException {

src/com/ysaito/shogi/GameLog.java

+12-7
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ public final String attr(String key) {
8282
return mAttrs.get(key);
8383
}
8484

85+
/**
86+
* Return the local sdcard path in which the log is saved. Returns null if
87+
* the log is not in sdcard.
88+
*/
8589
public final File path() { return mPath; }
8690

8791
/**
@@ -105,12 +109,7 @@ public String digest() {
105109
for (int i = 0; i < mPlays.size(); ++i) {
106110
digest.update(mPlays.get(i).toString().getBytes());
107111
}
108-
byte b[] = digest.digest();
109-
StringBuffer hex = new StringBuffer();
110-
for (int i = 0;i < b.length; i++) {
111-
hex.append(Integer.toHexString(b[i] & 0xff));
112-
}
113-
mDigest = hex.toString();
112+
mDigest = Util.bytesToHexText(digest.digest());
114113
} catch (NoSuchAlgorithmException e) {
115114
throw new AssertionError("MessageDigest.NoSuchAlgorithmException: " + e.getMessage());
116115
}
@@ -232,6 +231,8 @@ public static GameLog newLog(
232231
/**
233232
* Parse an embedded KIF file downloaded from http://wiki.optus.nu/.
234233
* Such a file can be created by saving a "テキスト表示" link directly to a file.
234+
*
235+
* @param path The path in which the file is stored. Can be null.
235236
*/
236237
public static GameLog parseHtml(File path, InputStream stream) throws ParseException, IOException {
237238
BufferedReader reader = new BufferedReader(Util.inputStreamToReader(stream, "EUC-JP"));
@@ -321,7 +322,7 @@ public void toKif(OutputStream out, String format) throws IOException {
321322
stream.close();
322323
}
323324

324-
/**
325+
/**
325326
* Given a KIF file encoded in UTF-8, parse it. If this method doesn't throw
326327
* an exception, it always return a non-null GameLog object.
327328
*/
@@ -330,6 +331,10 @@ public static GameLog parseKif(File path, InputStream in) throws ParseException,
330331
return doParseKif(path, stream);
331332
}
332333

334+
/**
335+
* @param path The local sdcard path in which the kif data is stored. Should
336+
* be null if the data is not on sdcard.
337+
*/
333338
private static GameLog doParseKif(File path, Reader stream) throws ParseException, IOException {
334339
GameLog l = new GameLog();
335340
l.mPath = path;

src/com/ysaito/shogi/GameLogListActivity.java

+10-10
Original file line numberDiff line numberDiff line change
@@ -45,19 +45,19 @@ public MyAdapter(Context context) {
4545
mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
4646
}
4747

48-
public int getCount() {
48+
@Override public int getCount() {
4949
return mLogs.size();
5050
}
5151

52-
public Object getItem(int position) {
52+
@Override public Object getItem(int position) {
5353
return null;
5454
}
5555

56-
public long getItemId(int position) {
56+
@Override public long getItemId(int position) {
5757
return position;
5858
}
5959

60-
public View getView(int position, View convertView, ViewGroup parent) {
60+
@Override public View getView(int position, View convertView, ViewGroup parent) {
6161
TextView text;
6262

6363
if (convertView == null) {
@@ -146,13 +146,13 @@ private void startListLogs(LogListManager.Mode mode) {
146146

147147
LogListManager.getSingletonInstance().listLogs(
148148
new LogListManager.ListLogsListener() {
149-
public void onNewGameLogs(Collection<GameLog> logs) {
149+
@Override public void onNewGameLogs(Collection<GameLog> logs) {
150150
if (mLogs.addAll(logs)) {
151151
mLogsSorted = false;
152152
mAdapter.notifyDataSetChanged();
153153
}
154154
}
155-
public void onFinish() { mAdapter.notifyDataSetChanged(); }
155+
@Override public void onFinish() { mAdapter.notifyDataSetChanged(); }
156156
},
157157
this,
158158
mode);
@@ -237,7 +237,7 @@ public boolean onContextItemSelected(MenuItem item) {
237237
if (log != null) {
238238
LogListManager.getSingletonInstance().deleteLog(
239239
new LogListManager.DeleteLogListener() {
240-
public void onFinish(LogListManager.UndoToken undoToken) {
240+
@Override public void onFinish(LogListManager.UndoToken undoToken) {
241241
if (undoToken != null) { // no error happened
242242
addUndoToken(undoToken);
243243
startListLogs(LogListManager.Mode.READ_SDCARD_SUMMARY);
@@ -254,7 +254,7 @@ public void onFinish(LogListManager.UndoToken undoToken) {
254254
if (log != null) {
255255
LogListManager.getSingletonInstance().saveLogInSdcard(
256256
new LogListManager.TrivialListener() {
257-
public void onFinish() {
257+
@Override public void onFinish() {
258258
startListLogs(LogListManager.Mode.READ_SDCARD_SUMMARY);
259259
}
260260
},
@@ -272,7 +272,7 @@ public void onFinish() {
272272
.setTitle(R.string.game_log_properties)
273273
.setView(view)
274274
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
275-
public void onClick(DialogInterface dialog, int whichButton) { }
275+
@Override public void onClick(DialogInterface dialog, int whichButton) { }
276276
}).create().show();
277277
}
278278
return true;
@@ -300,7 +300,7 @@ private void updateUndoMenu() {
300300
final LogListManager.UndoToken undo = mUndoTokens.remove(lastIndex);
301301
LogListManager.getSingletonInstance().undo(
302302
new LogListManager.TrivialListener() {
303-
public void onFinish() {
303+
@Override public void onFinish() {
304304
startListLogs(LogListManager.Mode.READ_SDCARD_SUMMARY);
305305
}
306306
},

0 commit comments

Comments
 (0)