/*
* Copyright (C) 2013 uPhyca Inc. http://www.uphyca.com/
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.uphyca.kitkat.storage.internal.impl;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import android.annotation.TargetApi;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Build;
import com.uphyca.kitkat.storage.internal.SkyDriveClient;
import com.uphyca.kitkat.storage.skydrive.SkyDriveObject;
/**
* android.provider.DocumentsProvider のメソッドの中で別スレッドで実行したネットワークアクセスの結果を同期するための実装。
* クライアントはUIスレッドからプロバイダにアクセスした際に、同じスレッドでネットワークアクセスすると android.os.NetworkOnMainThreadException が発生する。
* デベロッパードキュメントでは android.os.ParcelFileDescriptorのpipeを使うのがベター的なことが書いてあった。
* pipeの動作を確認するテストは com.uphyca.kitkat.storage.ParcelFileDescriptorTest なのだが、こんな感じでやるとロックしてしまう。
*
* @author masui@uphyca.com
*/
public class StrictSkyDriveClient implements SkyDriveClient {
private final SkyDriveClient mDelegate;
public StrictSkyDriveClient(SkyDriveClient delegate) {
mDelegate = delegate;
}
@Override
public void initializeIfNecessary() {
mDelegate.initializeIfNecessary();
}
@Override
public void login(Activity activity, SkyDriveAuthListener listener) {
mDelegate.login(activity, listener);
}
@Override
public SkyDriveObject[] get(final String documentId) {
try {
return sync(new NetworkOperation<SkyDriveObject[]>() {
@Override
public SkyDriveObject[] execute() throws IOException {
return mDelegate.get(documentId);
}
});
} catch (IOException e) {
return new SkyDriveObject[0];
}
}
@Override
public File download(final String documentId) throws IOException {
return sync(new NetworkOperation<File>() {
@Override
public File execute() throws IOException {
return mDelegate.download(documentId);
}
});
}
@Override
public String upload(final String path, final String name, final File file) throws IOException {
return sync(new NetworkOperation<String>() {
@Override
public String execute() throws IOException {
return mDelegate.upload(path, name, file);
}
});
}
@Override
public String mkdir(final String path, final String name) throws IOException {
return sync(new NetworkOperation<String>() {
@Override
public String execute() throws IOException {
return mDelegate.mkdir(path, name);
}
});
}
@Override
public String touch(final String path, final String name) throws IOException {
return sync(new NetworkOperation<String>() {
@Override
public String execute() throws IOException {
return mDelegate.touch(path, name);
}
});
}
@Override
public void delete(final String path) throws IOException {
sync(new NetworkOperation<Void>() {
@Override
public Void execute() throws IOException {
mDelegate.delete(path);
return null;
}
});
}
private static <T> T sync(final NetworkOperation<T> operation) throws IOException {
return new NetworkOperationTask<T>().sync(operation);
}
private interface NetworkOperation<T> {
T execute() throws IOException;
}
private static class NetworkOperationTask<T> extends AsyncTask<NetworkOperation<T>, Void, T> {
private final CountDownLatch mLock = new CountDownLatch(1);
private T mResult;
private IOException mError;
@Override
protected void onPostExecute(T t) {
mResult = t;
mLock.countDown();
}
@Override
protected T doInBackground(NetworkOperation<T>... params) {
try {
return params[0].execute();
} catch (IOException e) {
mError = e;
return null;
}
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
T sync(NetworkOperation<T> operation) throws IOException {
executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, operation);
try {
mLock.await();
} catch (InterruptedException ignore) {
}
if (mError != null) {
throw mError;
}
return mResult;
}
}
}