Commit decce445 by Kang Donghun

#73480 通信仕組み改善検証

parent ed09a6a5
...@@ -42,6 +42,7 @@ import jp.agentec.abook.abv.bl.acms.client.json.WorkerGroupJSON; ...@@ -42,6 +42,7 @@ import jp.agentec.abook.abv.bl.acms.client.json.WorkerGroupJSON;
import jp.agentec.abook.abv.bl.acms.client.parameters.AbstractAcmsLoginParameters; import jp.agentec.abook.abv.bl.acms.client.parameters.AbstractAcmsLoginParameters;
import jp.agentec.abook.abv.bl.acms.client.parameters.AcmsContentParameters; import jp.agentec.abook.abv.bl.acms.client.parameters.AcmsContentParameters;
import jp.agentec.abook.abv.bl.acms.client.parameters.AcmsParameters; import jp.agentec.abook.abv.bl.acms.client.parameters.AcmsParameters;
import jp.agentec.abook.abv.bl.acms.client.parameters.ShopSyncWatermarkParameters;
import jp.agentec.abook.abv.bl.acms.client.parameters.AddMemberGroupParameters; import jp.agentec.abook.abv.bl.acms.client.parameters.AddMemberGroupParameters;
import jp.agentec.abook.abv.bl.acms.client.parameters.AppStoreNewLoginParameters; import jp.agentec.abook.abv.bl.acms.client.parameters.AppStoreNewLoginParameters;
import jp.agentec.abook.abv.bl.acms.client.parameters.ContentReadingLogParameters; import jp.agentec.abook.abv.bl.acms.client.parameters.ContentReadingLogParameters;
...@@ -981,8 +982,7 @@ public class AcmsClient implements AcmsClientResponseListener { ...@@ -981,8 +982,7 @@ public class AcmsClient implements AcmsClientResponseListener {
AcmsApis.ApiSendPushMessage, AcmsApis.ApiSendPushMessage,
AcmsApis.ApiLockReport, AcmsApis.ApiLockReport,
AcmsApis.ApiUnlockReport, AcmsApis.ApiUnlockReport,
AcmsApis.ApiGetDashboardStatus, AcmsApis.ApiGetDashboardStatus
AcmsApis.ApiShopSyncWatermark
}; };
public HttpTaskWorker(String methodName, String apiUrl, T param) { public HttpTaskWorker(String methodName, String apiUrl, T param) {
...@@ -1060,7 +1060,7 @@ public class AcmsClient implements AcmsClientResponseListener { ...@@ -1060,7 +1060,7 @@ public class AcmsClient implements AcmsClientResponseListener {
/** /**
* DB同期の必要性を判定するウォーターマーク情報を取得する * DB同期の必要性を判定するウォーターマーク情報を取得する
*/ */
public ShopSyncWatermarkJSON checkShopSyncWatermark(AcmsParameters param) throws NetworkDisconnectedException, AcmsException { public ShopSyncWatermarkJSON checkShopSyncWatermark(ShopSyncWatermarkParameters param) throws NetworkDisconnectedException, AcmsException {
HttpResponse response = send(AcmsApis.ApiShopSyncWatermark, param); HttpResponse response = send(AcmsApis.ApiShopSyncWatermark, param);
return new ShopSyncWatermarkJSON(response.httpResponseBody); return new ShopSyncWatermarkJSON(response.httpResponseBody);
} }
......
...@@ -6,7 +6,8 @@ import jp.agentec.abook.abv.bl.common.exception.AcmsException; ...@@ -6,7 +6,8 @@ import jp.agentec.abook.abv.bl.common.exception.AcmsException;
import jp.agentec.abook.abv.bl.common.exception.JSONValidationException; import jp.agentec.abook.abv.bl.common.exception.JSONValidationException;
/** /**
* checkapi/shopSyncWatermark/ のレスポンスを変換するクラス * checkapi/shopSyncWatermark/ のレスポンスを変換するクラス。
* <p>リクエストは {@code screen=OPERATION_LIST}(作業一覧)/{@code REPORT_LIST}(報告・コンテンツ側)で区分する。</p>
*/ */
public class ShopSyncWatermarkJSON extends AcmsCommonJSON { public class ShopSyncWatermarkJSON extends AcmsCommonJSON {
public static final String NeedsFullSync = "needsFullSync"; public static final String NeedsFullSync = "needsFullSync";
......
package jp.agentec.abook.abv.bl.acms.client.parameters;
import java.net.URLEncoder;
import jp.agentec.abook.abv.bl.common.exception.ABVRuntimeException;
import jp.agentec.adf.util.StringUtil;
/**
* GET checkapi/shopSyncWatermark/?sid=&screen=&lastFetchDate=(任意)
*/
public class ShopSyncWatermarkParameters extends AcmsParameters {
public static final String SCREEN_OPERATION_LIST = "OPERATION_LIST";
public static final String SCREEN_REPORT_LIST = "REPORT_LIST";
private final String screen;
private final String lastFetchDate;
public ShopSyncWatermarkParameters(String sid, String screen, String lastFetchDate) {
super(sid);
this.screen = screen;
this.lastFetchDate = lastFetchDate;
}
@Override
public String toHttpParameterString(String encoding) {
try {
StringBuilder sb = new StringBuilder();
sb.append(URLEncoder.encode("sid", encoding)).append("=")
.append(URLEncoder.encode(getSid(), encoding).replace("+", "%20"));
sb.append("&");
sb.append(URLEncoder.encode("screen", encoding)).append("=")
.append(URLEncoder.encode(screen, encoding).replace("+", "%20"));
if (!StringUtil.isNullOrEmpty(lastFetchDate)) {
sb.append("&");
sb.append(URLEncoder.encode("lastFetchDate", encoding)).append("=")
.append(URLEncoder.encode(lastFetchDate, encoding).replace("+", "%20"));
}
return sb.toString();
} catch (Exception e) {
throw new ABVRuntimeException(e);
}
}
}
...@@ -163,5 +163,12 @@ public interface ServiceOption { ...@@ -163,5 +163,12 @@ public interface ServiceOption {
* 利用しない:N(通常)、利用する:Y * 利用しない:N(通常)、利用する:Y
*/ */
int UsableIOReport = 186; int UsableIOReport = 186;
/**
* 事業者オプション:自動同期(CMS側 ON/OFF)
* Y … アプリ設定に「自動同期」を表示し、ユーザーの ON/OFF に従って一覧復帰時の自動同期を行う
* N または未定義 … 設定に項目を出さず、自動同期は常にオフ扱い(新着更新の画面別API自体は事業者共通)
*/
int EnterpriseAutoSync = 187;
} }
} }
\ No newline at end of file
...@@ -246,7 +246,7 @@ public class ABVDataCache { ...@@ -246,7 +246,7 @@ public class ABVDataCache {
} }
} }
public class ServiceOption { public class ServiceOption {
public boolean isUndelivarableDelete() { public boolean isUndelivarableDelete() {
return isServiceOptionEnable(ServiceOptionId.UndeliverableDelete); return isServiceOptionEnable(ServiceOptionId.UndeliverableDelete);
} }
...@@ -367,6 +367,15 @@ public class ServiceOption { ...@@ -367,6 +367,15 @@ public class ServiceOption {
public boolean isUnableIOReport() { public boolean isUnableIOReport() {
return isServiceOptionEnable(ServiceOptionId.UsableIOReport); return isServiceOptionEnable(ServiceOptionId.UsableIOReport);
} }
/**
* {@link ServiceOptionId#EnterpriseAutoSync} が CMS で Y のとき true。
* <p>設定画面で自動同期チェックを表示し、一覧復帰時の自動同期をユーザ設定で許可する前提となる。</p>
*/
public boolean isEnterpriseAutoSyncOptionUsed() {
ServiceOptionDto dto = getServiceOption(ServiceOptionId.EnterpriseAutoSync);
return dto != null && ContractLogic.getBooleanValue(dto.val);
}
} }
public void setDiffMinFromUTC(String timeZone) { public void setDiffMinFromUTC(String timeZone) {
......
...@@ -2,8 +2,6 @@ package jp.agentec.abook.abv.bl.data.dao; ...@@ -2,8 +2,6 @@ package jp.agentec.abook.abv.bl.data.dao;
import jp.agentec.abook.abv.bl.common.db.Cursor; import jp.agentec.abook.abv.bl.common.db.Cursor;
import jp.agentec.abook.abv.bl.dto.AcmsDto; import jp.agentec.abook.abv.bl.dto.AcmsDto;
public class AcmsDao extends AbstractDao { public class AcmsDao extends AbstractDao {
/*package*/ AcmsDao() { /*package*/ AcmsDao() {
...@@ -41,9 +39,13 @@ public class AcmsDao extends AbstractDao { ...@@ -41,9 +39,13 @@ public class AcmsDao extends AbstractDao {
if (colnum != -1) { if (colnum != -1) {
dto.contentVersionLastFetchDate = cursor.getString(colnum); dto.contentVersionLastFetchDate = cursor.getString(colnum);
} }
colnum = cursor.getColumnIndex("shopsyncwatermark_last_fetch_date"); colnum = cursor.getColumnIndex("shopsyncwatermark_operation_list_last_fetch_date");
if (colnum != -1) { if (colnum != -1) {
dto.shopSyncWatermarkLastFetchDate = cursor.getString(colnum); dto.shopSyncWatermarkOperationListLastFetchDate = cursor.getString(colnum);
}
colnum = cursor.getColumnIndex("shopsyncwatermark_report_list_last_fetch_date");
if (colnum != -1) {
dto.shopSyncWatermarkReportListLastFetchDate = cursor.getString(colnum);
} }
return dto; return dto;
...@@ -58,7 +60,7 @@ public class AcmsDao extends AbstractDao { ...@@ -58,7 +60,7 @@ public class AcmsDao extends AbstractDao {
} }
public void insert(AcmsDto acmsDto) { public void insert(AcmsDto acmsDto) {
insert("insert into m_acms (url_path, acms_address,download_server_address, websocket_server_http_url, websocket_server_ws_url, schedulelist_last_fetch_date, contentversion_last_fetch_date, shopsyncwatermark_last_fetch_date) values (?,?,?,?,?,?,?,?)", acmsDto.getInsertValues()); insert("insert into m_acms (url_path, acms_address,download_server_address, websocket_server_http_url, websocket_server_ws_url, schedulelist_last_fetch_date, contentversion_last_fetch_date, shopsyncwatermark_operation_list_last_fetch_date, shopsyncwatermark_report_list_last_fetch_date) values (?,?,?,?,?,?,?,?,?)", acmsDto.getInsertValues());
} }
public boolean isTodayLastAnnounceChangePasswordDate() { public boolean isTodayLastAnnounceChangePasswordDate() {
...@@ -88,12 +90,23 @@ public class AcmsDao extends AbstractDao { ...@@ -88,12 +90,23 @@ public class AcmsDao extends AbstractDao {
return count > 0; return count > 0;
} }
public String selectShopSyncWatermarkLastFetchDate() { /** 作業一覧プロファイル用(m_acms.shopsyncwatermark_operation_list_last_fetch_date)。 */
return rawQueryGetString("select shopsyncwatermark_last_fetch_date from m_acms order by shopsyncwatermark_last_fetch_date desc limit 1", null); public String selectShopSyncWatermarkOperationListLastFetchDate() {
return rawQueryGetString("select shopsyncwatermark_operation_list_last_fetch_date from m_acms limit 1", null);
}
/** 報告・コンテンツ側プロファイル用(m_acms.shopsyncwatermark_report_list_last_fetch_date)。 */
public String selectShopSyncWatermarkReportListLastFetchDate() {
return rawQueryGetString("select shopsyncwatermark_report_list_last_fetch_date from m_acms limit 1", null);
}
public boolean updateShopSyncWatermarkOperationListLastFetchDate(String fetchDate) {
long count = update("update m_acms set shopsyncwatermark_operation_list_last_fetch_date=?", new Object[]{fetchDate});
return count > 0;
} }
public boolean updateShopSyncWatermarkLastFetchDate(String fetchDate) { public boolean updateShopSyncWatermarkReportListLastFetchDate(String fetchDate) {
long count = update("update m_acms set shopsyncwatermark_last_fetch_date=?", new Object[]{fetchDate}); long count = update("update m_acms set shopsyncwatermark_report_list_last_fetch_date=?", new Object[]{fetchDate});
return count > 0; return count > 0;
} }
......
...@@ -29,7 +29,8 @@ public class MAcms extends SQLiteTableScript { ...@@ -29,7 +29,8 @@ public class MAcms extends SQLiteTableScript {
sql.append(" , websocket_server_ws_url VARCHAR(256) "); sql.append(" , websocket_server_ws_url VARCHAR(256) ");
sql.append(" , schedulelist_last_fetch_date VARCHAR(256) "); sql.append(" , schedulelist_last_fetch_date VARCHAR(256) ");
sql.append(" , contentversion_last_fetch_date VARCHAR(256) "); sql.append(" , contentversion_last_fetch_date VARCHAR(256) ");
sql.append(" , shopsyncwatermark_last_fetch_date VARCHAR(256) "); sql.append(" , shopsyncwatermark_operation_list_last_fetch_date VARCHAR(256) ");
sql.append(" , shopsyncwatermark_report_list_last_fetch_date VARCHAR(256) ");
sql.append(" , PRIMARY KEY (url_path) "); sql.append(" , PRIMARY KEY (url_path) ");
sql.append(" ) "); sql.append(" ) ");
...@@ -47,7 +48,8 @@ public class MAcms extends SQLiteTableScript { ...@@ -47,7 +48,8 @@ public class MAcms extends SQLiteTableScript {
public List<String> getUpgradeScript(int oldVersion, int newVersion) { public List<String> getUpgradeScript(int oldVersion, int newVersion) {
List<String> ddl = new ArrayList<String>(); List<String> ddl = new ArrayList<String>();
if (oldVersion < DatabaseVersions.Ver1_0_613) { if (oldVersion < DatabaseVersions.Ver1_0_613) {
ddl.add("ALTER TABLE m_acms ADD COLUMN shopsyncwatermark_last_fetch_date VARCHAR(256)"); ddl.add("ALTER TABLE m_acms ADD COLUMN shopsyncwatermark_operation_list_last_fetch_date VARCHAR(256)");
ddl.add("ALTER TABLE m_acms ADD COLUMN shopsyncwatermark_report_list_last_fetch_date VARCHAR(256)");
} }
return ddl; return ddl;
} }
......
package jp.agentec.abook.abv.bl.download;
/**
* 新着更新のAPIセット(画面コンテキスト別)。
* <p>
* 共通(オンライン時・{@link ContentRefresher#refreshContentList} 入口):<br>
* ・checkSid(SID確認)<br>
* ・enqueteReply / contentDownloadLog / contentReadingLog(失敗分の再送・ログ送信)
* </p>
* <p>
* {@link #OPERATION_LIST_SCREEN} … 作業一覧に必要な最小セット(ウォーターマークFull時のワーカー内):<br>
* ・serviceOption({@code initializeContractServiceOption})<br>
* ・workingGroupList(operationList のグループ解釈と揃えるため operationList 取得前に実行)<br>
* ・category<br>
* ・operationGroupMaster<br>
* 完了後: operationList / getPushMessageList({@link jp.agentec.abook.abv.bl.logic.OperationLogic#syncOperationListFromServer} /
* {@link jp.agentec.abook.abv.bl.logic.PushMessageLogic#initializePushMessage})
* </p>
* <p>
* {@link #REPORT_LIST_SCREEN} … 報告・コンテンツ側に必要な最小セット(ウォーターマークFull時のワーカー内):<br>
* ・group<br>
* ・getMasterData<br>
* ・contentVersion({@code retrieveServerContent})<br>
* 完了後: 同上で operationList / プッシュ(一覧と通知をアプリで揃える)
* </p>
* <p>
* {@link #FULL} … 従来の一括(上記をワーカー内ですべて実行し、完了後は {@code initializeOperations})。
* </p>
*/
public enum ContentRefreshApiProfile {
/** ウォーターマークFull時はグループ〜コンテンツ版まで従来どおり一括。 */
FULL,
/** 作業一覧由来。 */
OPERATION_LIST_SCREEN,
/** 報告・閲覧コンテンツ由来。 */
REPORT_LIST_SCREEN
}
...@@ -10,6 +10,8 @@ import jp.agentec.abook.abv.bl.acms.client.json.ContentVersionsJSON; ...@@ -10,6 +10,8 @@ import jp.agentec.abook.abv.bl.acms.client.json.ContentVersionsJSON;
import jp.agentec.abook.abv.bl.acms.client.json.ShopSyncWatermarkJSON; import jp.agentec.abook.abv.bl.acms.client.json.ShopSyncWatermarkJSON;
import jp.agentec.abook.abv.bl.acms.client.parameters.ContentDownloadLogParameters; import jp.agentec.abook.abv.bl.acms.client.parameters.ContentDownloadLogParameters;
import jp.agentec.abook.abv.bl.acms.client.parameters.FetchDateParameters; import jp.agentec.abook.abv.bl.acms.client.parameters.FetchDateParameters;
import jp.agentec.abook.abv.bl.acms.client.parameters.ShopSyncWatermarkParameters;
import jp.agentec.abook.abv.bl.common.exception.AcmsException;
import jp.agentec.abook.abv.bl.common.ABVEnvironment; import jp.agentec.abook.abv.bl.common.ABVEnvironment;
import jp.agentec.abook.abv.bl.common.exception.ABVException; import jp.agentec.abook.abv.bl.common.exception.ABVException;
import jp.agentec.abook.abv.bl.common.exception.ABVExceptionCode; import jp.agentec.abook.abv.bl.common.exception.ABVExceptionCode;
...@@ -37,12 +39,15 @@ import jp.agentec.abook.abv.bl.logic.OperationLogic; ...@@ -37,12 +39,15 @@ import jp.agentec.abook.abv.bl.logic.OperationLogic;
import jp.agentec.abook.abv.bl.logic.PushMessageLogic; import jp.agentec.abook.abv.bl.logic.PushMessageLogic;
import jp.agentec.adf.util.CollectionUtil; import jp.agentec.adf.util.CollectionUtil;
import jp.agentec.adf.util.DateTimeUtil; import jp.agentec.adf.util.DateTimeUtil;
import jp.agentec.adf.util.StringUtil;
/** /**
* 新着更新を扱うクラス * 新着更新を扱うクラス。
* <p>
* プロファイル別のAPI対応は {@link ContentRefreshApiProfile} を参照。
* </p>
* *
* FIXME: DBのトランザクションができていない * FIXME: DBのトランザクションができていない
*
*/ */
public class ContentRefresher { public class ContentRefresher {
private static final String TAG = "ContentRefresher"; private static final String TAG = "ContentRefresher";
...@@ -66,8 +71,11 @@ import jp.agentec.adf.util.DateTimeUtil; ...@@ -66,8 +71,11 @@ import jp.agentec.adf.util.DateTimeUtil;
private ContentDownloadListener contentDownloadListener; private ContentDownloadListener contentDownloadListener;
private RefreshContentWorker refreshWorker = null; private RefreshContentWorker refreshWorker = null;
private String shopSyncWatermarkLastChangedAt; private boolean needsOperationWatermarkSync;
private boolean performedFullSyncByWatermark; private boolean needsReportWatermarkSync;
private String pendingOperationWatermarkLastChangedAt;
private String pendingReportWatermarkLastChangedAt;
private ContentRefreshApiProfile refreshApiProfile = ContentRefreshApiProfile.FULL;
private Map<Long, Boolean> refreshingContentMap = new ConcurrentHashMap<Long, Boolean>(); // TODO: 値は使っていないのでSetに変更する private Map<Long, Boolean> refreshingContentMap = new ConcurrentHashMap<Long, Boolean>(); // TODO: 値は使っていないのでSetに変更する
private boolean initializingRefreshing = false; private boolean initializingRefreshing = false;
...@@ -99,6 +107,13 @@ import jp.agentec.adf.util.DateTimeUtil; ...@@ -99,6 +107,13 @@ import jp.agentec.adf.util.DateTimeUtil;
* @since 1.0.0 * @since 1.0.0
*/ */
public void refreshContentList(ContentDownloadListener listener) throws Exception { public void refreshContentList(ContentDownloadListener listener) throws Exception {
refreshContentList(listener, ContentRefreshApiProfile.FULL);
}
/**
* @param apiProfile 作業一覧/報告一覧など、呼び出し元に応じたAPIセット
*/
public void refreshContentList(ContentDownloadListener listener, ContentRefreshApiProfile apiProfile) throws Exception {
if (contentDownloader.getActiveCount() > 0) { // コンテンツのダウンロード中は、自動更新を行わない。 if (contentDownloader.getActiveCount() > 0) { // コンテンツのダウンロード中は、自動更新を行わない。
contentDownloader.kickTask(); // waitingで止まらないように実行を促す contentDownloader.kickTask(); // waitingで止まらないように実行を促す
throw new ABVException(ABVExceptionCode.C_I_CONTENT_0002); throw new ABVException(ABVExceptionCode.C_I_CONTENT_0002);
...@@ -115,6 +130,8 @@ import jp.agentec.adf.util.DateTimeUtil; ...@@ -115,6 +130,8 @@ import jp.agentec.adf.util.DateTimeUtil;
throw new ABVException(ABVExceptionCode.C_I_CONTENT_0001); throw new ABVException(ABVExceptionCode.C_I_CONTENT_0001);
} }
refreshApiProfile = apiProfile != null ? apiProfile : ContentRefreshApiProfile.FULL;
if (listener != null) { if (listener != null) {
contentDownloadListener = listener; contentDownloadListener = listener;
} }
...@@ -147,6 +164,127 @@ import jp.agentec.adf.util.DateTimeUtil; ...@@ -147,6 +164,127 @@ import jp.agentec.adf.util.DateTimeUtil;
refreshingContentMap.clear(); refreshingContentMap.clear();
} }
private void initializeMasterDataWithRetry() throws Exception {
Logger.d(TAG, "before fetchDate : " + ABVDataCache.getInstance().tempMasterDataFetchDate);
String fetchDate = null;
for (int i = 0; i < 4; i++) {
try {
fetchDate = masterDataLogic.initializeMasterData(ABVDataCache.getInstance().tempMasterDataFetchDate, i);
break;
} catch (Exception e) {
Logger.e(TAG, "initializeMasterData Exception", e);
if (i == 3) {
throw new Exception("initializeMasterData");
} else {
Thread.sleep(500);
}
}
}
Logger.d(TAG, "after fetchDate : " + fetchDate);
if (fetchDate != null) {
ABVDataCache.getInstance().tempMasterDataFetchDate = fetchDate;
}
}
private boolean queryShopSyncWatermark(String screen, String storedLastFetch, boolean assignToOperationPending)
throws NetworkDisconnectedException, AcmsException {
ShopSyncWatermarkParameters p = new ShopSyncWatermarkParameters(cache.getMemberInfo().sid, screen,
StringUtil.isNullOrEmpty(storedLastFetch) ? null : storedLastFetch.trim());
ShopSyncWatermarkJSON json = AcmsClient.getInstance(cache.getUrlPath(), networkAdapter).checkShopSyncWatermark(p);
if (assignToOperationPending) {
pendingOperationWatermarkLastChangedAt = json.lastChangedAt;
} else {
pendingReportWatermarkLastChangedAt = json.lastChangedAt;
}
return json.needsFullSync;
}
private void evaluateShopSyncWatermarks() {
needsOperationWatermarkSync = false;
needsReportWatermarkSync = false;
pendingOperationWatermarkLastChangedAt = null;
pendingReportWatermarkLastChangedAt = null;
try {
AcmsDao dao = AbstractDao.getDao(AcmsDao.class);
switch (refreshApiProfile) {
case FULL:
needsOperationWatermarkSync = queryShopSyncWatermark(
ShopSyncWatermarkParameters.SCREEN_OPERATION_LIST,
dao.selectShopSyncWatermarkOperationListLastFetchDate(), true);
needsReportWatermarkSync = queryShopSyncWatermark(
ShopSyncWatermarkParameters.SCREEN_REPORT_LIST,
dao.selectShopSyncWatermarkReportListLastFetchDate(), false);
break;
case OPERATION_LIST_SCREEN:
needsOperationWatermarkSync = queryShopSyncWatermark(
ShopSyncWatermarkParameters.SCREEN_OPERATION_LIST,
dao.selectShopSyncWatermarkOperationListLastFetchDate(), true);
break;
case REPORT_LIST_SCREEN:
needsReportWatermarkSync = queryShopSyncWatermark(
ShopSyncWatermarkParameters.SCREEN_REPORT_LIST,
dao.selectShopSyncWatermarkReportListLastFetchDate(), false);
break;
}
} catch (Exception e) {
Logger.w(TAG, "shopSyncWatermark check failed. run fail-safe sync.", e);
switch (refreshApiProfile) {
case FULL:
needsOperationWatermarkSync = true;
needsReportWatermarkSync = true;
break;
case OPERATION_LIST_SCREEN:
needsOperationWatermarkSync = true;
break;
case REPORT_LIST_SCREEN:
needsReportWatermarkSync = true;
break;
}
}
}
private void saveShopSyncWatermarkOperationListAfterRefresh(boolean withLatestRefetch) {
String lastChangedAt = pendingOperationWatermarkLastChangedAt;
try {
if (withLatestRefetch) {
ShopSyncWatermarkParameters latestParam = new ShopSyncWatermarkParameters(cache.getMemberInfo().sid,
ShopSyncWatermarkParameters.SCREEN_OPERATION_LIST, null);
ShopSyncWatermarkJSON latestJson = AcmsClient.getInstance(cache.getUrlPath(), networkAdapter)
.checkShopSyncWatermark(latestParam);
if (latestJson != null && latestJson.lastChangedAt != null && latestJson.lastChangedAt.length() > 0) {
lastChangedAt = latestJson.lastChangedAt;
}
}
if (lastChangedAt == null || lastChangedAt.length() == 0) {
return;
}
AbstractDao.getDao(AcmsDao.class).updateShopSyncWatermarkOperationListLastFetchDate(lastChangedAt);
} catch (Exception e) {
Logger.w(TAG, "failed to save shopSyncWatermark OPERATION_LIST lastFetchDate.", e);
}
}
private void saveShopSyncWatermarkReportListAfterRefresh(boolean withLatestRefetch) {
String lastChangedAt = pendingReportWatermarkLastChangedAt;
try {
if (withLatestRefetch) {
ShopSyncWatermarkParameters latestParam = new ShopSyncWatermarkParameters(cache.getMemberInfo().sid,
ShopSyncWatermarkParameters.SCREEN_REPORT_LIST, null);
ShopSyncWatermarkJSON latestJson = AcmsClient.getInstance(cache.getUrlPath(), networkAdapter)
.checkShopSyncWatermark(latestParam);
if (latestJson != null && latestJson.lastChangedAt != null && latestJson.lastChangedAt.length() > 0) {
lastChangedAt = latestJson.lastChangedAt;
}
}
if (lastChangedAt == null || lastChangedAt.length() == 0) {
return;
}
AbstractDao.getDao(AcmsDao.class).updateShopSyncWatermarkReportListLastFetchDate(lastChangedAt);
} catch (Exception e) {
Logger.w(TAG, "failed to save shopSyncWatermark REPORT_LIST lastFetchDate.", e);
}
}
private class RefreshContentWorker extends Thread { private class RefreshContentWorker extends Thread {
private boolean interrupt = false; private boolean interrupt = false;
...@@ -161,45 +299,61 @@ import jp.agentec.adf.util.DateTimeUtil; ...@@ -161,45 +299,61 @@ import jp.agentec.adf.util.DateTimeUtil;
interrupt = false; interrupt = false;
try { try {
boolean isFinishedContentCheck = false; boolean isFinishedContentCheck = false;
boolean ranContentVersionPhase = false;
List<ContentDto> localContents = contentDao.getAllContents(); List<ContentDto> localContents = contentDao.getAllContents();
if (networkAdapter.isNetworkConnected()) { // オンライン時の処理 if (networkAdapter.isNetworkConnected()) { // オンライン時の処理
resendLog(); // ログ送信 // 共通: アンケート再送・DLログ・閲覧ログ送信
resendLog();
boolean needsFullSync = shouldRunFullSyncByShopSyncWatermark();
performedFullSyncByWatermark = needsFullSync; evaluateShopSyncWatermarks();
if (needsFullSync) { boolean needsAnyDbSync = needsOperationWatermarkSync || needsReportWatermarkSync;
contractLogic.initializeContractServiceOption(); // サービスオプション関連処理 boolean ranOperationPhase = false;
groupLogic.initializeGroups(); // グループ設定(グループ変更の場合、FetchDateをクリアする) boolean ranReportPhase = false;
categoryLogic.initializeCategories(); // カテゴリ設定
if (needsAnyDbSync) {
// マスタデータの最新更新された時のFetchDateを一時に保存する。 ContentRefreshApiProfile profile = refreshApiProfile;
Logger.d(TAG, "before fetchDate : " + ABVDataCache.getInstance().tempMasterDataFetchDate); Logger.d(TAG, "shopSyncWatermark needs sync op=%s report=%s apiProfile=%s",
needsOperationWatermarkSync, needsReportWatermarkSync, profile);
// CMSでメンテナンスされるHACCPマスタデータをアプリから取得できるようにJSONファイルを生成する。 switch (profile) {
String fetchDate = null; case FULL:
//失敗時、3回リトライ処理 if (needsOperationWatermarkSync && needsReportWatermarkSync) {
for (int i = 0; i < 4; i++) { // 両方更新時は従来 FULL と同じ依存順で一括実行
try { contractLogic.initializeContractServiceOption();
fetchDate = masterDataLogic.initializeMasterData(ABVDataCache.getInstance().tempMasterDataFetchDate, i); groupLogic.initializeGroups();
break; categoryLogic.initializeCategories();
} catch (Exception e) { initializeMasterDataWithRetry();
Logger.e(TAG, "initializeMasterData Exception", e); operationGroupMasterLogic.setOperationGroupMaster();
if (i == 3) { //3回目のリトライ失敗時、例外処理投げる。 ranOperationPhase = true;
throw new Exception("initializeMasterData"); ranReportPhase = true;
} else { } else if (needsOperationWatermarkSync) {
Thread.sleep(500); //0.5秒間隔でリトライ処理を実行 contractLogic.initializeContractServiceOption();
operationLogic.syncWorkingGroupsFromServer();
categoryLogic.initializeCategories();
operationGroupMasterLogic.setOperationGroupMaster();
ranOperationPhase = true;
} else if (needsReportWatermarkSync) {
groupLogic.initializeGroups();
initializeMasterDataWithRetry();
ranReportPhase = true;
} }
break;
case OPERATION_LIST_SCREEN:
if (needsOperationWatermarkSync) {
contractLogic.initializeContractServiceOption();
operationLogic.syncWorkingGroupsFromServer();
categoryLogic.initializeCategories();
operationGroupMasterLogic.setOperationGroupMaster();
ranOperationPhase = true;
} }
break;
case REPORT_LIST_SCREEN:
if (needsReportWatermarkSync) {
groupLogic.initializeGroups();
initializeMasterDataWithRetry();
ranReportPhase = true;
} }
break;
Logger.d(TAG, "after fetchDate : " + fetchDate);
if (fetchDate != null) {
// マスタデータの最新更新された時のFetchDateを一時に保存する。
ABVDataCache.getInstance().tempMasterDataFetchDate = fetchDate;
} }
// 作業種別情報を取得
operationGroupMasterLogic.setOperationGroupMaster();
} else { } else {
Logger.d(TAG, "skip full DB sync by shopSyncWatermark."); Logger.d(TAG, "skip full DB sync by shopSyncWatermark.");
} }
...@@ -210,10 +364,24 @@ import jp.agentec.adf.util.DateTimeUtil; ...@@ -210,10 +364,24 @@ import jp.agentec.adf.util.DateTimeUtil;
updateRefreshContentListState(-1L, null); updateRefreshContentListState(-1L, null);
return; return;
} }
if (needsFullSync) { // contentVersion:FULL は報告ウォーターマーク時のみ(従来)。作業一覧/報告一覧プロファイルは画面ごとに必ず実行し、
isFinishedContentCheck = retrieveServerContent(localContents); // ContentVersionAPIを呼出し新規と更新の場合ContentInfoをDLする // t_content が無いせいで operationList の新規行が落ちる問題と、報告側だけ最新化されない問題を避ける。
boolean runContentVersion =
(needsReportWatermarkSync && refreshApiProfile == ContentRefreshApiProfile.FULL)
|| refreshApiProfile == ContentRefreshApiProfile.OPERATION_LIST_SCREEN
|| refreshApiProfile == ContentRefreshApiProfile.REPORT_LIST_SCREEN;
if (runContentVersion) {
Logger.d(TAG, "contentVersion phase apiProfile=%s needsReportWm=%s", refreshApiProfile,
needsReportWatermarkSync);
isFinishedContentCheck = retrieveServerContent(localContents); // contentVersion(DL は非同期)
ranContentVersionPhase = true;
}
if (ranOperationPhase) {
saveShopSyncWatermarkOperationListAfterRefresh(ranContentVersionPhase);
}
if (ranReportPhase) {
saveShopSyncWatermarkReportListAfterRefresh(ranContentVersionPhase);
} }
saveShopSyncWatermarkLastFetchDateWithRefresh(performedFullSyncByWatermark);
} }
deleteLocalContent(localContents, isFinishedContentCheck); // コンテンツ削除処理 deleteLocalContent(localContents, isFinishedContentCheck); // コンテンツ削除処理
...@@ -396,41 +564,6 @@ import jp.agentec.adf.util.DateTimeUtil; ...@@ -396,41 +564,6 @@ import jp.agentec.adf.util.DateTimeUtil;
return true; return true;
} }
private boolean shouldRunFullSyncByShopSyncWatermark() {
try {
AcmsDao dao = AbstractDao.getDao(AcmsDao.class);
String lastFetchDate = dao.selectShopSyncWatermarkLastFetchDate();
FetchDateParameters param = new FetchDateParameters(cache.getMemberInfo().sid, lastFetchDate);
ShopSyncWatermarkJSON json = AcmsClient.getInstance(cache.getUrlPath(), networkAdapter).checkShopSyncWatermark(param);
shopSyncWatermarkLastChangedAt = json.lastChangedAt;
return json.needsFullSync;
} catch (Exception e) {
// フェイルセーフ: 判定できない場合は従来通りフル同期を実施
Logger.w(TAG, "shopSyncWatermark check failed. run full sync.", e);
shopSyncWatermarkLastChangedAt = null;
return true;
}
}
private void saveShopSyncWatermarkLastFetchDateWithRefresh(boolean shouldRefreshFromServer) {
String lastChangedAt = shopSyncWatermarkLastChangedAt;
try {
if (shouldRefreshFromServer) {
FetchDateParameters latestParam = new FetchDateParameters(cache.getMemberInfo().sid, null);
ShopSyncWatermarkJSON latestJson = AcmsClient.getInstance(cache.getUrlPath(), networkAdapter).checkShopSyncWatermark(latestParam);
if (latestJson != null && latestJson.lastChangedAt != null && latestJson.lastChangedAt.length() > 0) {
lastChangedAt = latestJson.lastChangedAt;
}
}
if (lastChangedAt == null || lastChangedAt.length() == 0) {
return;
}
AcmsDao dao = AbstractDao.getDao(AcmsDao.class);
dao.updateShopSyncWatermarkLastFetchDate(lastChangedAt);
} catch (Exception e) {
Logger.w(TAG, "failed to save shopSyncWatermark lastFetchDate.", e);
}
}
} }
public void updateRefreshContentListState(long contentId, Exception e) { public void updateRefreshContentListState(long contentId, Exception e) {
...@@ -441,13 +574,29 @@ import jp.agentec.adf.util.DateTimeUtil; ...@@ -441,13 +574,29 @@ import jp.agentec.adf.util.DateTimeUtil;
} }
if (contentDownloadListener != null) { if (contentDownloadListener != null) {
if (!isRefreshing() && e == null) { if (e == null) {
// 新着処理が終わったら以下の処理が実行
try { try {
// サーバー通信でプロジェクト取得 switch (refreshApiProfile) {
case FULL:
// コンテンツ ZIP 並行 DL 中は refreshingContentMap が残り得る。一覧反映は最終完了まで遅延。
if (!isRefreshing()) {
operationLogic.initializeOperations(); operationLogic.initializeOperations();
//プッシュメッセージ情報を取得
pushMessageLogic.initializePushMessage(); pushMessageLogic.initializePushMessage();
}
break;
case OPERATION_LIST_SCREEN:
case REPORT_LIST_SCREEN:
// ワーカー完了コールバックは contentId=-1。並行コンテンツ DL で isRefreshing が true のときも
// operationList を保存できない問題を避ける。REPORT 側は ZIP 完了ごとのコールバックが多いので
// contentId>=0 かつ refreshing 中はスキップし、最終完了時のみ同期する。
if (!isRefreshing() || contentId < 0) {
operationLogic.syncOperationListFromServer();
}
if (!isRefreshing()) {
pushMessageLogic.initializePushMessage();
}
break;
}
} catch (Exception e1) { } catch (Exception e1) {
Logger.e(TAG, e1); Logger.e(TAG, e1);
e = e1; e = e1;
......
...@@ -8,7 +8,8 @@ public class AcmsDto extends AbstractDto { ...@@ -8,7 +8,8 @@ public class AcmsDto extends AbstractDto {
public String websocketServerWsUrl; public String websocketServerWsUrl;
public String contentVersionLastFetchDate; // 保存のみ、内部で比較しない public String contentVersionLastFetchDate; // 保存のみ、内部で比較しない
public String scheduleListLastFetchDate; // 保存のみ、内部で比較しない public String scheduleListLastFetchDate; // 保存のみ、内部で比較しない
public String shopSyncWatermarkLastFetchDate; // DB同期判定用の最終取得日時 public String shopSyncWatermarkOperationListLastFetchDate;
public String shopSyncWatermarkReportListLastFetchDate;
public AcmsDto() { public AcmsDto() {
} }
...@@ -21,7 +22,8 @@ public class AcmsDto extends AbstractDto { ...@@ -21,7 +22,8 @@ public class AcmsDto extends AbstractDto {
this.websocketServerWsUrl = websocketServerWsUrl; this.websocketServerWsUrl = websocketServerWsUrl;
this.contentVersionLastFetchDate = ""; this.contentVersionLastFetchDate = "";
this.scheduleListLastFetchDate = ""; this.scheduleListLastFetchDate = "";
this.shopSyncWatermarkLastFetchDate = ""; this.shopSyncWatermarkOperationListLastFetchDate = "";
this.shopSyncWatermarkReportListLastFetchDate = "";
} }
@Override @Override
...@@ -31,7 +33,7 @@ public class AcmsDto extends AbstractDto { ...@@ -31,7 +33,7 @@ public class AcmsDto extends AbstractDto {
@Override @Override
public Object[] getInsertValues() { public Object[] getInsertValues() {
return new Object[]{urlPath, acmsAddress, downloadServerAddress, websocketServerHttpUrl, websocketServerWsUrl, scheduleListLastFetchDate, contentVersionLastFetchDate, shopSyncWatermarkLastFetchDate}; return new Object[]{urlPath, acmsAddress, downloadServerAddress, websocketServerHttpUrl, websocketServerWsUrl, scheduleListLastFetchDate, contentVersionLastFetchDate, shopSyncWatermarkOperationListLastFetchDate, shopSyncWatermarkReportListLastFetchDate};
} }
} }
...@@ -91,6 +91,32 @@ public class OperationLogic extends AbstractLogic { ...@@ -91,6 +91,32 @@ public class OperationLogic extends AbstractLogic {
private TaskWorkerGroupDao mTaskWorkerGroupDao = AbstractDao.getDao(TaskWorkerGroupDao.class); private TaskWorkerGroupDao mTaskWorkerGroupDao = AbstractDao.getDao(TaskWorkerGroupDao.class);
private static final int FINISHED_STATUS = 999; private static final int FINISHED_STATUS = 999;
/** 一覧バッチ同期時、直近同期からこの時間未満の作業は間隔条件により除外できる(ms)。 */
private static final int INTERVAL_LAST_EDIT_TIME_MS = 1800000;
private boolean isEnableTimeForAutoSync(Date lastEditDateTime) {
Date currentDateTime = DateTimeUtil.getCurrentDate();
if (lastEditDateTime == null) {
return true;
}
long diffInMillis = currentDateTime.getTime() - lastEditDateTime.getTime();
return diffInMillis >= INTERVAL_LAST_EDIT_TIME_MS;
}
/**
* operationLastSyncDate に基づき、自動同期の間隔条件を満たす needSync 作業だけを残す。
*/
public List<OperationDto> needSyncCheckArray(List<OperationDto> needSyncOperationInfoArray) {
List<OperationDto> needSyncCheckArray = new ArrayList<>();
for (OperationDto operationDto : needSyncOperationInfoArray) {
if (isEnableTimeForAutoSync(operationDto.operationLastSyncDate)) {
needSyncCheckArray.add(operationDto);
}
}
return needSyncCheckArray;
}
public void initializeOperations() throws AcmsException, NetworkDisconnectedException { public void initializeOperations() throws AcmsException, NetworkDisconnectedException {
// 作業グループリスト取得 // 作業グループリスト取得
setWorkingGroupList(); setWorkingGroupList();
...@@ -100,6 +126,20 @@ public class OperationLogic extends AbstractLogic { ...@@ -100,6 +126,20 @@ public class OperationLogic extends AbstractLogic {
} }
/** /**
* workingGroupList API のみ(作業一覧側・FULL 新着のワーカーで operationList より先に実行)。
*/
public void syncWorkingGroupsFromServer() throws NetworkDisconnectedException, AcmsException {
setWorkingGroupList();
}
/**
* operationList API のみ(新着完了後の一覧同期。workingGroupList は別途 {@link #syncWorkingGroupsFromServer})。
*/
public void syncOperationListFromServer() throws AcmsException, NetworkDisconnectedException {
retrieveServerOperation();
}
/**
* プロジェクト一覧取得し、登録・更新・削除する * プロジェクト一覧取得し、登録・更新・削除する
* *
* @throws AcmsException * @throws AcmsException
......
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" > <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:key="pref_screen_root" >
<PreferenceCategory android:title="@string/account" android:key="account_set"> <PreferenceCategory android:title="@string/account" android:key="account_set">
<PreferenceScreen <PreferenceScreen
...@@ -68,4 +69,13 @@ ...@@ -68,4 +69,13 @@
android:title="@string/spp_machine" > android:title="@string/spp_machine" >
</PreferenceScreen> </PreferenceScreen>
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory
android:key="operation_sync_category"
android:title="@string/auto_sync" >
<CheckBoxPreference
android:defaultValue="false"
android:key="operationAutoSync"
android:summary="@string/auto_sync_setting_subtitle"
android:title="@string/auto_sync_setting_title" />
</PreferenceCategory>
</PreferenceScreen> </PreferenceScreen>
\ No newline at end of file
...@@ -73,6 +73,7 @@ import jp.agentec.abook.abv.bl.data.dao.MemberInfoDao; ...@@ -73,6 +73,7 @@ import jp.agentec.abook.abv.bl.data.dao.MemberInfoDao;
import jp.agentec.abook.abv.bl.download.ContentDownloadListener; import jp.agentec.abook.abv.bl.download.ContentDownloadListener;
import jp.agentec.abook.abv.bl.download.ContentDownloader; import jp.agentec.abook.abv.bl.download.ContentDownloader;
import jp.agentec.abook.abv.bl.download.ContentFileExtractor; import jp.agentec.abook.abv.bl.download.ContentFileExtractor;
import jp.agentec.abook.abv.bl.download.ContentRefreshApiProfile;
import jp.agentec.abook.abv.bl.download.ContentRefresher; import jp.agentec.abook.abv.bl.download.ContentRefresher;
import jp.agentec.abook.abv.bl.download.ContentZipDownloadNotification; import jp.agentec.abook.abv.bl.download.ContentZipDownloadNotification;
import jp.agentec.abook.abv.bl.dto.ContentDto; import jp.agentec.abook.abv.bl.dto.ContentDto;
...@@ -128,6 +129,14 @@ public abstract class ABVAuthenticatedActivity extends ABVActivity implements Co ...@@ -128,6 +129,14 @@ public abstract class ABVAuthenticatedActivity extends ABVActivity implements Co
protected ContentDownloader contentDownloader = ContentDownloader.getInstance(); protected ContentDownloader contentDownloader = ContentDownloader.getInstance();
protected ContentRefresher contentRefresher = ContentRefresher.getInstance(); protected ContentRefresher contentRefresher = ContentRefresher.getInstance();
/**
* {@link ContentRefreshApiProfile}。デフォルトは従来一括(FULL)。
* @see jp.agentec.abook.abv.bl.download.ContentRefreshApiProfile
*/
protected ContentRefreshApiProfile getContentRefreshApiProfile() {
return ContentRefreshApiProfile.FULL;
}
private ExecutorService initilizeExecutor = Executors.newFixedThreadPool(2); // DL後の初期化専用 private ExecutorService initilizeExecutor = Executors.newFixedThreadPool(2); // DL後の初期化専用
protected ImageButton btnDownload; protected ImageButton btnDownload;
......
...@@ -50,6 +50,7 @@ import jp.agentec.abook.abv.bl.data.ABVDataCache; ...@@ -50,6 +50,7 @@ import jp.agentec.abook.abv.bl.data.ABVDataCache;
import jp.agentec.abook.abv.bl.data.dao.AbstractDao; import jp.agentec.abook.abv.bl.data.dao.AbstractDao;
import jp.agentec.abook.abv.bl.data.dao.TaskReportDao; import jp.agentec.abook.abv.bl.data.dao.TaskReportDao;
import jp.agentec.abook.abv.bl.download.ContentFileExtractor; import jp.agentec.abook.abv.bl.download.ContentFileExtractor;
import jp.agentec.abook.abv.bl.download.ContentRefreshApiProfile;
import jp.agentec.abook.abv.bl.dto.ContentDto; import jp.agentec.abook.abv.bl.dto.ContentDto;
import jp.agentec.abook.abv.bl.dto.MydataDto; import jp.agentec.abook.abv.bl.dto.MydataDto;
import jp.agentec.abook.abv.bl.dto.OperationDto; import jp.agentec.abook.abv.bl.dto.OperationDto;
...@@ -97,6 +98,12 @@ import static jp.agentec.abook.abv.bl.acms.client.json.OperationDataJSON.TaskRep ...@@ -97,6 +98,12 @@ import static jp.agentec.abook.abv.bl.acms.client.json.OperationDataJSON.TaskRep
public abstract class ABVContentViewActivity extends ABVAuthenticatedActivity { public abstract class ABVContentViewActivity extends ABVAuthenticatedActivity {
private static final String TAG ="ABVContentViewActivity"; private static final String TAG ="ABVContentViewActivity";
/** 報告・閲覧コンテンツ側の新着更新は画面別APIセット(事業者共通)。 */
@Override
protected ContentRefreshApiProfile getContentRefreshApiProfile() {
return ContentRefreshApiProfile.REPORT_LIST_SCREEN;
}
public final static int ABOOK_CHECK_TASK_IMAGE = 103; public final static int ABOOK_CHECK_TASK_IMAGE = 103;
public final static int ABOOK_CHECK_TASK_VIDEO = 104; public final static int ABOOK_CHECK_TASK_VIDEO = 104;
protected final static int ABOOK_CHECK_SELECT_SCENE = 105; protected final static int ABOOK_CHECK_SELECT_SCENE = 105;
......
...@@ -168,7 +168,7 @@ public abstract class ABVUIActivity extends ABVAuthenticatedActivity { ...@@ -168,7 +168,7 @@ public abstract class ABVUIActivity extends ABVAuthenticatedActivity {
// contentDownloadObserverがnullだと更新マークのアニメーションが完了しない TODO 2014/11/12 Jang // contentDownloadObserverがnullだと更新マークのアニメーションが完了しない TODO 2014/11/12 Jang
// onCreateでrefresh処理をcallしたときに起きる // onCreateでrefresh処理をcallしたときに起きる
contentRefresher.refreshContentList(this); contentRefresher.refreshContentList(this, getContentRefreshApiProfile());
} }
} catch (NetworkDisconnectedException e) { } catch (NetworkDisconnectedException e) {
refreshContentException(); refreshContentException();
......
...@@ -25,6 +25,8 @@ public interface AppDefType { ...@@ -25,6 +25,8 @@ public interface AppDefType {
String CURSOR_ENABLE = "cursorEnable"; String CURSOR_ENABLE = "cursorEnable";
String DISPLAY_MARKING = "displayMarking"; String DISPLAY_MARKING = "displayMarking";
String APP_VERSIONUP_PROCESSING = "appVersionProcessingFlag"; String APP_VERSIONUP_PROCESSING = "appVersionProcessingFlag";
/** 作業一覧の自動同期(DefaultSharedPreferences・事業者オプション187がYのときのみ設定に表示) */
String OPERATION_AUTO_SYNC = "operationAutoSync";
} }
interface UserPrefKey { interface UserPrefKey {
......
...@@ -17,9 +17,9 @@ import android.os.Environment; ...@@ -17,9 +17,9 @@ import android.os.Environment;
import android.os.Handler; import android.os.Handler;
import android.preference.Preference; import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener; import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceCategory;
import android.preference.PreferenceFragment; import android.preference.PreferenceFragment;
import android.preference.PreferenceGroup; import android.preference.PreferenceGroup;
import android.preference.PreferenceManager;
import android.preference.PreferenceScreen; import android.preference.PreferenceScreen;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
...@@ -83,24 +83,24 @@ public class ABookSettingFragment extends PreferenceFragment { ...@@ -83,24 +83,24 @@ public class ABookSettingFragment extends PreferenceFragment {
protected Handler handler = new Handler(); protected Handler handler = new Handler();
protected AlertDialog alertDialog = null; protected AlertDialog alertDialog = null;
private SharedPreferences pref;
// 機器連携(ペアリング) // 機器連携(ペアリング)
private static final String SET_CHINO_PAIRING = "setChinoPairing"; // CHINO機器 private static final String SET_CHINO_PAIRING = "setChinoPairing"; // CHINO機器
private static final String SET_SPP_PAIRING = "setSppPairing"; // SPP通信機器 private static final String SET_SPP_PAIRING = "setSppPairing"; // SPP通信機器
private static final String OPERATION_SYNC_CATEGORY = "operation_sync_category";
private PreferenceCategory operationSyncCategory;
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
handler = new Handler(); handler = new Handler();
pref = PreferenceManager.getDefaultSharedPreferences(getActivity());
// 設定XMLから削除した CheckBox(operationAutoSync)の残キーを掃除
if (pref.contains("operationAutoSync")) {
pref.edit().remove("operationAutoSync").apply();
}
addPreferencesFromResource(R.xml.pref); addPreferencesFromResource(R.xml.pref);
operationSyncCategory = (PreferenceCategory) findPreference(OPERATION_SYNC_CATEGORY);
applyOperationAutoSyncCategoryVisibility();
// アカウント // アカウント
setAccountSetting(); setAccountSetting();
// ログ情報 // ログ情報
...@@ -121,6 +121,27 @@ public class ABookSettingFragment extends PreferenceFragment { ...@@ -121,6 +121,27 @@ public class ABookSettingFragment extends PreferenceFragment {
@Override @Override
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
applyOperationAutoSyncCategoryVisibility();
}
/**
* 事業者オプション187(自動同期)が CMS で Y のときのみ「自動同期」設定を表示する。
*/
private void applyOperationAutoSyncCategoryVisibility() {
if (operationSyncCategory == null || getPreferenceScreen() == null) {
return;
}
PreferenceScreen root = getPreferenceScreen();
boolean show = ABVDataCache.getInstance().serviceOption.isEnterpriseAutoSyncOptionUsed();
if (show) {
if (operationSyncCategory.getParent() == null) {
root.addPreference(operationSyncCategory);
}
} else {
if (operationSyncCategory.getParent() != null) {
root.removePreference(operationSyncCategory);
}
}
} }
@Override @Override
......
...@@ -102,6 +102,9 @@ public class DashboardActivity extends OperationActivity { ...@@ -102,6 +102,9 @@ public class DashboardActivity extends OperationActivity {
public void onClick(View v) { public void onClick(View v) {
Logger.d(TAG, "ReloadUrl"); Logger.d(TAG, "ReloadUrl");
webView.loadUrl(DASHBOARD_URL); webView.loadUrl(DASHBOARD_URL);
if (ABVEnvironment.getInstance().networkAdapter.isNetworkConnected()) {
dataRefresh(true);
}
} }
}); });
......
...@@ -18,6 +18,7 @@ import jp.agentec.abook.abv.bl.common.ABVEnvironment; ...@@ -18,6 +18,7 @@ import jp.agentec.abook.abv.bl.common.ABVEnvironment;
import jp.agentec.abook.abv.bl.common.log.Logger; import jp.agentec.abook.abv.bl.common.log.Logger;
import jp.agentec.abook.abv.bl.data.ABVDataCache; import jp.agentec.abook.abv.bl.data.ABVDataCache;
import jp.agentec.abook.abv.bl.dto.PushMessageDto; import jp.agentec.abook.abv.bl.dto.PushMessageDto;
import jp.agentec.abook.abv.bl.download.ContentRefreshApiProfile;
import jp.agentec.abook.abv.bl.logic.AbstractLogic; import jp.agentec.abook.abv.bl.logic.AbstractLogic;
import jp.agentec.abook.abv.bl.logic.PushMessageLogic; import jp.agentec.abook.abv.bl.logic.PushMessageLogic;
import jp.agentec.abook.abv.launcher.android.R; import jp.agentec.abook.abv.launcher.android.R;
...@@ -36,6 +37,12 @@ import jp.agentec.adf.util.DateTimeUtil; ...@@ -36,6 +37,12 @@ import jp.agentec.adf.util.DateTimeUtil;
public class OperationActivity extends ABVUIActivity { public class OperationActivity extends ABVUIActivity {
@SuppressWarnings("unused") @SuppressWarnings("unused")
private static final String TAG = "OperationActivity"; private static final String TAG = "OperationActivity";
/** 作業一覧ツールバー由来の新着更新は画面別APIセット(事業者共通)。 */
@Override
protected ContentRefreshApiProfile getContentRefreshApiProfile() {
return ContentRefreshApiProfile.OPERATION_LIST_SCREEN;
}
protected ImageButton mOperationHomeButton; // ホームボタン protected ImageButton mOperationHomeButton; // ホームボタン
protected ImageButton mDashboardButton; // ダッシュボード protected ImageButton mDashboardButton; // ダッシュボード
protected ImageButton mCommonContentButton; // 関連資料 protected ImageButton mCommonContentButton; // 関連資料
...@@ -103,10 +110,16 @@ public class OperationActivity extends ABVUIActivity { ...@@ -103,10 +110,16 @@ public class OperationActivity extends ABVUIActivity {
} }
} }
/** 作業一覧へ戻る。SO187 で表示される「自動同期」がオンなら新着+一覧同期、オフまたは187無効なら新着のみ。 */
private void backHome() { private void backHome() {
Intent intent = new Intent(); Intent intent = new Intent();
intent.setClass(this, OperationListActivity.class); intent.setClass(this, OperationListActivity.class);
if (ABVEnvironment.getInstance().networkAdapter.isNetworkConnected()
&& OperationListActivity.isUnscopedAutoSyncAllowed(this)) {
intent.putExtra(OperationListActivity.EXTRA_RESUME_MODE, OperationListActivity.RESUME_REFRESH_AND_BATCH);
} else {
intent.putExtra(OperationListActivity.EXTRA_RESUME_MODE, OperationListActivity.RESUME_NEW_CONTENT_ONLY); intent.putExtra(OperationListActivity.EXTRA_RESUME_MODE, OperationListActivity.RESUME_NEW_CONTENT_ONLY);
}
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent); startActivity(intent);
} }
......
...@@ -2,6 +2,7 @@ package jp.agentec.abook.abv.ui.home.activity; ...@@ -2,6 +2,7 @@ package jp.agentec.abook.abv.ui.home.activity;
import android.app.DatePickerDialog; import android.app.DatePickerDialog;
import android.app.Dialog; import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.res.Configuration; import android.content.res.Configuration;
...@@ -98,6 +99,7 @@ import jp.agentec.abook.abv.bl.logic.OperationLogic; ...@@ -98,6 +99,7 @@ import jp.agentec.abook.abv.bl.logic.OperationLogic;
import jp.agentec.abook.abv.bl.logic.PushMessageLogic; import jp.agentec.abook.abv.bl.logic.PushMessageLogic;
import jp.agentec.abook.abv.cl.util.PreferenceUtil; import jp.agentec.abook.abv.cl.util.PreferenceUtil;
import jp.agentec.abook.abv.launcher.android.R; import jp.agentec.abook.abv.launcher.android.R;
import jp.agentec.abook.abv.ui.common.appinfo.AppDefType.DefPrefKey;
import jp.agentec.abook.abv.ui.common.appinfo.AppDefType.OperationLocationType; import jp.agentec.abook.abv.ui.common.appinfo.AppDefType.OperationLocationType;
import jp.agentec.abook.abv.ui.common.appinfo.AppDefType; import jp.agentec.abook.abv.ui.common.appinfo.AppDefType;
import jp.agentec.abook.abv.ui.common.constant.ErrorCode; import jp.agentec.abook.abv.ui.common.constant.ErrorCode;
...@@ -137,9 +139,22 @@ import static jp.agentec.abook.abv.ui.home.activity.CaptureQRCodeActivity.QRCODE ...@@ -137,9 +139,22 @@ import static jp.agentec.abook.abv.ui.home.activity.CaptureQRCodeActivity.QRCODE
public class OperationListActivity extends OperationActivity { public class OperationListActivity extends OperationActivity {
private static final String TAG = "OperationListActivity"; private static final String TAG = "OperationListActivity";
/** 下ツールバー「ホーム」から遷移したときは新着更新のみ(作業同期は行わない) */ /** 下ツールバー「ホーム」からの遷移モード({@link #EXTRA_RESUME_MODE}) */
public static final String EXTRA_RESUME_MODE = "OperationListActivity.EXTRA_RESUME_MODE"; public static final String EXTRA_RESUME_MODE = "OperationListActivity.EXTRA_RESUME_MODE";
/**
* CMS のサービスオプション187が無効、または設定に表示される「作業自動同期」がオフのとき。
* 仕様表の「自動同期OFF」列(ツールバーホーム等・一覧スコープで同期しない)。
*/
public static final String RESUME_NEW_CONTENT_ONLY = "resume_new_content_only"; public static final String RESUME_NEW_CONTENT_ONLY = "resume_new_content_only";
/**
* SO187 が Y で設定画面の自動同期チェックがオンのとき。
* 仕様表の「自動同期ON」列相当で新着+一覧スコープの作業同期も行う。
*/
public static final String RESUME_REFRESH_AND_BATCH = "resume_refresh_and_batch";
private static final int RESUME_HINT_NONE = 0;
private static final int RESUME_HINT_TOOLBAR_NEW_CONTENT_ONLY = 1;
private static final int RESUME_HINT_TOOLBAR_REFRESH_AND_BATCH = 2;
/** ダッシュボードから作業報告へ進む前の新着+単一作業同期 */ /** ダッシュボードから作業報告へ進む前の新着+単一作業同期 */
public static final String EXTRA_PENDING_DASH_SYNC_OPERATION_ID = "OperationListActivity.EXTRA_PENDING_DASH_SYNC_OPERATION_ID"; public static final String EXTRA_PENDING_DASH_SYNC_OPERATION_ID = "OperationListActivity.EXTRA_PENDING_DASH_SYNC_OPERATION_ID";
public static final String EXTRA_PENDING_DASH_TASK_KEY = "OperationListActivity.EXTRA_PENDING_DASH_TASK_KEY"; public static final String EXTRA_PENDING_DASH_TASK_KEY = "OperationListActivity.EXTRA_PENDING_DASH_TASK_KEY";
...@@ -512,25 +527,42 @@ public class OperationListActivity extends OperationActivity { ...@@ -512,25 +527,42 @@ public class OperationListActivity extends OperationActivity {
if (result) { if (result) {
// content update success // content update success
List<OperationDto> needSyncOperationList = new ArrayList<>(); List<OperationDto> needSyncOperationList = new ArrayList<>();
if (mAutoSyncScopeOperationId != null) { if (mAutoSyncScopeOperationId != null) {
// ⑦⑩⑪⑫・ダッシュボード/QR/プッシュ等:選択作業のみ
OperationDto operationDto = mOperationLogic.getOperation(mAutoSyncScopeOperationId); OperationDto operationDto = mOperationLogic.getOperation(mAutoSyncScopeOperationId);
if (operationDto != null && operationDto.needSyncFlg) { if (operationDto != null && operationDto.needSyncFlg) {
needSyncOperationList.add(operationDto); needSyncOperationList.add(operationDto);
} }
} else if (mSyncTargetOperationId != null && mSyncTargetOperationId != -1
&& !ABVDataCache.getInstance().serviceOption.isUnableIOReport()) {
// ③④ I/O帳票OFF:一覧復帰時は戻り元の作業のみ(needSync のときのみ)
OperationDto backFromReportOp = mOperationLogic.getOperation(mSyncTargetOperationId);
if (backFromReportOp != null && backFromReportOp.needSyncFlg) {
needSyncOperationList.add(backFromReportOp);
}
} else { } else {
// ①⑥ 等・I/O ON の一覧復帰:同期が必要な作業をすべて
needSyncOperationList.addAll(mOperationDao.getNeedSyncAllOperation()); needSyncOperationList.addAll(mOperationDao.getNeedSyncAllOperation());
} }
List<OperationDto> needSyncCheckArray = needSyncOperationList; // SO187+自動同期ONかつ一覧スコープのときのみ作業別間隔(30分)を適用(#73480 ed09a6a 以前仕様)。単一作業同期は除外。
List<OperationDto> needSyncCheckArray;
if (isUnscopedAutoSyncAllowed() && mAutoSyncScopeOperationId == null) {
needSyncCheckArray = mOperationLogic.needSyncCheckArray(needSyncOperationList);
} else {
needSyncCheckArray = new ArrayList<>(needSyncOperationList);
}
if (mSyncTargetOperationId != null && mSyncTargetOperationId != -1) { if (mSyncTargetOperationId != null && mSyncTargetOperationId != -1) {
// I/O ON:カテゴリ表示内 needSync と戻り元を追加(I/O OFF は上流で対象のみ済み)
if (ABVDataCache.getInstance().serviceOption.isUnableIOReport()) { if (ABVDataCache.getInstance().serviceOption.isUnableIOReport()) {
List<OperationDto> categorySyncOperationList = mListHelper.getNeedSyncOperationList(); List<OperationDto> categorySyncOperationList = mListHelper.getNeedSyncOperationList();
needSyncCheckArray.addAll(categorySyncOperationList); needSyncCheckArray.addAll(categorySyncOperationList);
}
OperationDto operationDto = mOperationLogic.getOperation(mSyncTargetOperationId); OperationDto operationDto = mOperationLogic.getOperation(mSyncTargetOperationId);
if (operationDto != null && operationDto.needSyncFlg) { if (operationDto != null && operationDto.needSyncFlg) {
needSyncCheckArray.add(operationDto); needSyncCheckArray.add(operationDto);
} }
}
mSyncTargetOperationId = null; mSyncTargetOperationId = null;
needSyncCheckArray = mOperationLogic.deduplicateOperationDto(needSyncCheckArray); needSyncCheckArray = mOperationLogic.deduplicateOperationDto(needSyncCheckArray);
} }
...@@ -578,18 +610,22 @@ public class OperationListActivity extends OperationActivity { ...@@ -578,18 +610,22 @@ public class OperationListActivity extends OperationActivity {
} }
/** /**
* ツールバー経由「新着のみ」の指定を Intent から取り出して消費する * 下ツールバーホーム由来の Intent を消費する。通常復帰は {@link #RESUME_HINT_NONE}
*/ */
private boolean consumeToolbarResume(Intent intent) { private int consumeToolbarResumeHint(Intent intent) {
if (intent == null) { if (intent == null) {
return false; return RESUME_HINT_NONE;
} }
String mode = intent.getStringExtra(EXTRA_RESUME_MODE); String mode = intent.getStringExtra(EXTRA_RESUME_MODE);
if (RESUME_NEW_CONTENT_ONLY.equals(mode)) { if (RESUME_NEW_CONTENT_ONLY.equals(mode)) {
intent.removeExtra(EXTRA_RESUME_MODE); intent.removeExtra(EXTRA_RESUME_MODE);
return true; return RESUME_HINT_TOOLBAR_NEW_CONTENT_ONLY;
} }
return false; if (RESUME_REFRESH_AND_BATCH.equals(mode)) {
intent.removeExtra(EXTRA_RESUME_MODE);
return RESUME_HINT_TOOLBAR_REFRESH_AND_BATCH;
}
return RESUME_HINT_NONE;
} }
@Override @Override
...@@ -619,11 +655,11 @@ public class OperationListActivity extends OperationActivity { ...@@ -619,11 +655,11 @@ public class OperationListActivity extends OperationActivity {
refreshOperationList(); refreshOperationList();
boolean toolbarResumeNewContentOnly = consumeToolbarResume(intent); int toolbarResumeHint = consumeToolbarResumeHint(intent);
final long operationId = getUserPref(AppDefType.UserPrefKey.SYNC_TARGET_OPERATION_ID, -1L); final long operationId = getUserPref(AppDefType.UserPrefKey.SYNC_TARGET_OPERATION_ID, -1L);
if (toolbarResumeNewContentOnly) { if (toolbarResumeHint == RESUME_HINT_TOOLBAR_NEW_CONTENT_ONLY) {
putUserPref(AppDefType.UserPrefKey.SYNC_TARGET_OPERATION_ID, -1L); putUserPref(AppDefType.UserPrefKey.SYNC_TARGET_OPERATION_ID, -1L);
activityResultFlg = false; activityResultFlg = false;
configurationToolbarIcon(); configurationToolbarIcon();
...@@ -638,6 +674,22 @@ public class OperationListActivity extends OperationActivity { ...@@ -638,6 +674,22 @@ public class OperationListActivity extends OperationActivity {
return; return;
} }
if (toolbarResumeHint == RESUME_HINT_TOOLBAR_REFRESH_AND_BATCH) {
putUserPref(AppDefType.UserPrefKey.SYNC_TARGET_OPERATION_ID, -1L);
activityResultFlg = false;
configurationToolbarIcon();
if (ABVEnvironment.getInstance().networkAdapter.isNetworkConnected()) {
mSyncTargetOperationId = null;
handler.postDelayed(new Runnable() {
@Override
public void run() {
autoSyncOperation();
}
}, 100);
}
return;
}
// 作業指示・報告からプロジェクト一覧へ戻った時の同期処理など // 作業指示・報告からプロジェクト一覧へ戻った時の同期処理など
if (ABVEnvironment.getInstance().networkAdapter.isNetworkConnected()) { if (ABVEnvironment.getInstance().networkAdapter.isNetworkConnected()) {
mSyncTargetOperationId = operationId; mSyncTargetOperationId = operationId;
...@@ -1853,6 +1905,9 @@ public class OperationListActivity extends OperationActivity { ...@@ -1853,6 +1905,9 @@ public class OperationListActivity extends OperationActivity {
@Override @Override
public void run() { public void run() {
if (!contentRefresher.isRefreshing()) { if (!contentRefresher.isRefreshing()) {
if (isUnscopedAutoSyncAllowed()) {
mAutoSyncingFlg = true;
}
dataRefresh(false); dataRefresh(false);
} }
} }
...@@ -1893,6 +1948,9 @@ public class OperationListActivity extends OperationActivity { ...@@ -1893,6 +1948,9 @@ public class OperationListActivity extends OperationActivity {
@Override @Override
public void run() { public void run() {
if (!contentRefresher.isRefreshing()) { if (!contentRefresher.isRefreshing()) {
if (isUnscopedAutoSyncAllowed()) {
mAutoSyncingFlg = true;
}
dataRefresh(false); dataRefresh(false);
} }
} }
...@@ -2388,6 +2446,24 @@ public class OperationListActivity extends OperationActivity { ...@@ -2388,6 +2446,24 @@ public class OperationListActivity extends OperationActivity {
} }
/** /**
* 仕様表の「自動同期」がオンかどうか。CMS の SO187(作業自動同期オプション)が Y のときだけ設定に出る
* チェックボックス({@link DefPrefKey#OPERATION_AUTO_SYNC})が参照される。187 が N のときは常に false
* (画面上に自動同期設定が無い=一覧ホーム/プルダウンでは新着のみ)。
* true のときだけツールバーホーム/プルダウン後に一覧スコープの一括作業同期を付ける。
* 一覧の通常復帰(①③④⑥など)はこのフラグに依らず新着+必要な作業同期を行う。
*/
public static boolean isUnscopedAutoSyncAllowed(Context context) {
if (!ABVDataCache.getInstance().serviceOption.isEnterpriseAutoSyncOptionUsed()) {
return false;
}
return PreferenceUtil.get(context, DefPrefKey.OPERATION_AUTO_SYNC, false);
}
private boolean isUnscopedAutoSyncAllowed() {
return isUnscopedAutoSyncAllowed(this);
}
/**
* Run refresh data and batch sync if need * Run refresh data and batch sync if need
*/ */
public void autoSyncOperation() { public void autoSyncOperation() {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment