implement java side of OneDriveStorage2

This commit is contained in:
Philipp Crocoll
2019-09-10 05:26:38 +02:00
parent 56891d6183
commit f99843737f
3 changed files with 248 additions and 122 deletions

View File

@@ -37,7 +37,7 @@ dependencies {
}
compile 'com.pcloud.sdk:java-core:1.1.0'
compile 'com.pcloud.sdk:android:1.1.0'
compile('com.microsoft.graph:msgraph-sdk-android:1.2.+')
compile('com.microsoft.graph:msgraph-sdk-android:1.7.0')
compile ('com.microsoft.identity.client:msal:0.1.+') {
exclude group: 'com.android.support', module: 'appcompat-v7'
}

View File

@@ -4,6 +4,7 @@ import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Base64;
import android.util.Log;
@@ -11,12 +12,16 @@ import com.microsoft.graph.core.ClientException;
import com.microsoft.graph.core.DefaultClientConfig;
import com.microsoft.graph.core.GraphErrorCodes;
import com.microsoft.graph.extensions.DriveItem;
import com.microsoft.graph.extensions.Folder;
import com.microsoft.graph.extensions.GraphServiceClient;
import com.microsoft.graph.extensions.IDriveItemCollectionPage;
import com.microsoft.graph.extensions.IDriveItemCollectionRequestBuilder;
import com.microsoft.graph.extensions.IDriveItemRequest;
import com.microsoft.graph.extensions.IDriveItemRequestBuilder;
import com.microsoft.graph.extensions.IDriveSharedWithMeCollectionPage;
import com.microsoft.graph.extensions.IDriveSharedWithMeCollectionRequestBuilder;
import com.microsoft.graph.extensions.IGraphServiceClient;
import com.microsoft.graph.http.GraphServiceException;
import com.microsoft.identity.client.AuthenticationCallback;
import com.microsoft.identity.client.AuthenticationResult;
import com.microsoft.identity.client.MsalException;
@@ -48,8 +53,6 @@ public class OneDriveStorage2 extends JavaFileStorageBase
public OneDriveStorage2(final Activity context, final String clientId) {
mPublicClientApp = new PublicClientApplication(context, clientId);
initAuthenticator(context);
}
@@ -57,76 +60,27 @@ public class OneDriveStorage2 extends JavaFileStorageBase
@Override
public boolean requiresSetup(String path)
{
return !isConnected(null);
return false;
}
@Override
public void startSelectFile(FileStorageSetupInitiatorActivity activity, boolean isForSave, int requestCode) {
initAuthenticator((Activity)activity.getActivity());
String path = getProtocolId()+":///";
Log.d("KP2AJ", "startSelectFile "+path+", connected: "+path);
if (isConnected(null))
{
Intent intent = new Intent();
intent.putExtra(EXTRA_IS_FOR_SAVE, isForSave);
intent.putExtra(EXTRA_PATH, path);
activity.onImmediateResult(requestCode, RESULT_FILECHOOSER_PREPARED, intent);
}
else
{
activity.startSelectFileProcess(path, isForSave, requestCode);
}
Log.d("KP2AJ", "startSelectFile "+path);
activity.startSelectFileProcess(path, isForSave, requestCode);
}
private boolean isConnected(String path) {
try {
if (tryGetMsGraphClient(path) == null)
try {
final CountDownLatch latch = new CountDownLatch(1);
logDebug("isConnected? " + path);
Log.d("KP2AJ", "trying silent login");
String userId = extractUserId(path);
final MsalException[] _exception = {null};
final AuthenticationResult[] _result = {null};
User user = mPublicClientApp.getUser(userId);
mPublicClientApp.acquireTokenSilentAsync(scopes, user,
new AuthenticationCallback() {
@Override
public void onSuccess(AuthenticationResult authenticationResult) {
_result[0] = authenticationResult;
latch.countDown();
}
@Override
public void onError(MsalException exception) {
_exception[0] = exception;
latch.countDown();
}
@Override
public void onCancel() {
latch.countDown();
}
});
latch.await();
if (_result[0] != null) {
buildClient(_result[0]);
} else if (_exception[0] != null){
_exception[0].printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
return tryGetMsGraphClient(path) != null;
}
catch (Exception e)
{
logDebug("exception in isConnected: " + e.toString());
return false;
}
@@ -142,23 +96,21 @@ public class OneDriveStorage2 extends JavaFileStorageBase
private String extractUserId(String path) throws Exception {
String pathWithoutProtocol = removeProtocol(path);
String[] parts = pathWithoutProtocol.split("/",1);
if (parts.length != 2 || ("".equals(parts[0])))
String[] parts = pathWithoutProtocol.split("/",2);
logDebug("extractUserId for path " + path);
logDebug("# parts: " + parts.length);
if (parts.length < 1 || ("".equals(parts[0])))
{
throw new Exception("path does not contain user");
}
logDebug("parts[0]: " + parts[0]);
return parts[0];
}
private void initAuthenticator(Activity activity) {
}
@Override
public void prepareFileUsage(FileStorageSetupInitiatorActivity activity, String path, int requestCode, boolean alwaysReturnSuccess) {
initAuthenticator((Activity)activity.getActivity());
if (isConnected(path))
{
Intent intent = new Intent();
@@ -174,12 +126,12 @@ public class OneDriveStorage2 extends JavaFileStorageBase
@Override
public String getProtocolId() {
return "onedrive";
return "onedrive2";
}
@Override
public void prepareFileUsage(Context appContext, String path) throws UserInteractionRequiredException {
if (!isConnected(null))
if (!isConnected(path))
{
throw new UserInteractionRequiredException();
}
@@ -203,11 +155,15 @@ public class OneDriveStorage2 extends JavaFileStorageBase
private IGraphServiceClient buildClient(AuthenticationResult authenticationResult) throws InterruptedException {
private IGraphServiceClient buildClient(AuthenticationResult authenticationResult) throws Exception {
logDebug("buildClient...");
IGraphServiceClient newClient = new GraphServiceClient.Builder()
.fromConfig(DefaultClientConfig.createWithAuthenticationProvider(new GraphServiceClientManager(authenticationResult.getAccessToken())))
.buildClient();
logDebug("authToken = " + authenticationResult.getAccessToken());
if (authenticationResult.getUser() == null)
throw new Exception("authenticationResult.getUser() == null!");
mClientByUser.put(authenticationResult.getUser().getUserIdentifier(), newClient);
return newClient;
@@ -249,14 +205,30 @@ public class OneDriveStorage2 extends JavaFileStorageBase
{
public IGraphServiceClient client;
public String oneDrivePath;
public IDriveItemRequestBuilder getPathItem()
{
IDriveItemRequestBuilder pathItem = client.getDrive().getRoot();
public String share;
public IDriveItemRequestBuilder getPathItem() throws Exception {
IDriveItemRequestBuilder pathItem;
if (!hasShare()) {
logDebug("p: " + oneDrivePath);
for (StackTraceElement ste : Thread.currentThread().getStackTrace()) {
logDebug(ste.toString());
}
throw new Exception("Cannot get path item without share");
}
if ("me".equals(share))
pathItem = client.getMe().getDrive().getRoot();
else
pathItem = client.getShares(share).getRoot();
if ("".equals(oneDrivePath) == false) {
pathItem = pathItem.getItemWithPath(oneDrivePath);
}
return pathItem;
}
public boolean hasShare() {
return !(share == null || "".equals(share));
}
}
@Override
@@ -264,9 +236,8 @@ public class OneDriveStorage2 extends JavaFileStorageBase
try {
ClientAndPath clientAndpath = getOneDriveClientAndPath(path);
logDebug("openFileForRead. Path="+path);
InputStream result = clientAndpath.client.getDrive()
.getRoot()
.getItemWithPath(clientAndpath.oneDrivePath)
InputStream result = clientAndpath
.getPathItem()
.getContent()
.buildRequest()
.get();
@@ -284,13 +255,24 @@ public class OneDriveStorage2 extends JavaFileStorageBase
ClientAndPath result = new ClientAndPath();
String pathWithoutProtocol = removeProtocol(path);
String[] parts = pathWithoutProtocol.split("/",2);
if (parts.length != 2 || ("".equals(parts[0])))
String[] parts = pathWithoutProtocol.split("/",3);
if (parts.length < 2 || ("".equals(parts[0])))
{
throw new Exception("path does not contain user");
}
result.client = mClientByUser.get(parts[0]);
result.oneDrivePath = parts[1];
if (result.client == null)
throw new Exception("failed to get client for " + parts[0]);
logDebug("building client for " + path + " results in " + parts.length + " segments");
logDebug("share is " + parts[1]);
result.share = parts[1];
if (parts.length > 2) {
result.oneDrivePath = parts[2];
}
return result;
}
@@ -299,6 +281,8 @@ public class OneDriveStorage2 extends JavaFileStorageBase
private Exception convertException(ClientException e) {
if (e.isError(GraphErrorCodes.ItemNotFound))
return new FileNotFoundException(e.getMessage());
if (e.getMessage().contains("\n\n404 : ")) //hacky solution to check for not found. errorCode was null in my tests so I had to find a workaround.
return new FileNotFoundException(e.getMessage());
return e;
}
@@ -306,9 +290,8 @@ public class OneDriveStorage2 extends JavaFileStorageBase
public void uploadFile(String path, byte[] data, boolean writeTransactional) throws Exception {
try {
ClientAndPath clientAndPath = getOneDriveClientAndPath(path);
clientAndPath.client.getDrive()
.getRoot()
.getItemWithPath(clientAndPath.oneDrivePath)
clientAndPath
.getPathItem()
.getContent()
.buildRequest()
.put(data);
@@ -319,7 +302,24 @@ public class OneDriveStorage2 extends JavaFileStorageBase
@Override
public String createFolder(String parentPath, String newDirName) throws Exception {
throw new Exception("not implemented.");
try {
DriveItem driveItem = new DriveItem();
driveItem.name = newDirName;
driveItem.folder = new Folder();
ClientAndPath clientAndPath = getOneDriveClientAndPath(parentPath);
logDebug("building request for " + clientAndPath.oneDrivePath);
DriveItem res = clientAndPath.getPathItem()
.getChildren()
.buildRequest()
.post(driveItem);
return createFilePath(parentPath, newDirName);
} catch (ClientException e) {
throw convertException(e);
}
}
@Override
@@ -332,12 +332,79 @@ public class OneDriveStorage2 extends JavaFileStorageBase
return path;
}
private List<FileEntry> listShares(String parentPath, IGraphServiceClient client) throws Exception {
ArrayList<FileEntry> result = new ArrayList<FileEntry>();
logDebug("listShares: " + (client == null));
if (!parentPath.endsWith("/"))
parentPath += "/";
logDebug("listShares");
FileEntry myEntry = getFileEntry(parentPath+"me/", client.getMe().getDrive().getRoot().buildRequest().get());
//if ((myEntry.displayName == null) || "".equals(myEntry.displayName))
myEntry.displayName = "My OneDrive";
logDebug("myEntry.path = " + myEntry.path + ", isDir = " + myEntry.isDirectory);
result.add(myEntry);
IDriveSharedWithMeCollectionPage sharedWithMeCollectionPage = client.getMe().getDrive().getSharedWithMe().buildRequest().get();
while (true) {
List<DriveItem> sharedWithMeItems = sharedWithMeCollectionPage.getCurrentPage();
if (sharedWithMeItems.isEmpty())
break;
for (DriveItem i : sharedWithMeItems) {
Log.d("kp2aSHARE",i.name + " " + i.description + " " + i.id + " " + i.webUrl);
String urlToEncode = i.webUrl;
//calculate shareid according to https://docs.microsoft.com/en-us/graph/api/shares-get?view=graph-rest-1.0&tabs=java
String shareId = "u!"+android.util.Base64.encodeToString(urlToEncode.getBytes(), Base64.NO_PADDING).replace('/','_').replace('+','_')
.replace("\n",""); //encodeToString adds a newline character add the end - remove
Log.d("kp2aSHARE","shareId: " +shareId);
FileEntry sharedFileEntry = getFileEntry(parentPath + shareId +"/", i);
result.add(sharedFileEntry);
/*
try {
DriveItem x2 = client.shares(shareId).root().buildRequest().get();
Log.d("kp2aSHARE","x2: " + x2.name + " " + x2.description + " " + x2.id + " ");
}
catch (ClientException e)
{
if (e.getCause() != null)
Log.d("kp2aSHARE","cause: " + e.getCause().toString());
Log.d("kp2aSHARE","exception: " + e.toString());
}
catch (Exception e)
{
Log.d("kp2aSHARE","share item exc: " + e.toString());
}
*/
}
IDriveSharedWithMeCollectionRequestBuilder b = sharedWithMeCollectionPage.getNextPage();
if (b == null) break;
sharedWithMeCollectionPage =b.buildRequest().get();
}
return result;
}
@Override
public List<FileEntry> listFiles(String parentPath) throws Exception {
try {
ArrayList<FileEntry> result = new ArrayList<FileEntry>();
ClientAndPath clientAndPath = getOneDriveClientAndPath(parentPath);
parentPath = clientAndPath.oneDrivePath;
logDebug("listing files for " + parentPath +", " + clientAndPath.share + clientAndPath.hasShare());
if (!clientAndPath.hasShare())
{
logDebug("listing shares.");
return listShares(parentPath, clientAndPath.client);
}
logDebug("listing regular children.");
ArrayList<FileEntry> result = new ArrayList<FileEntry>();
/*logDebug("parent before:" + parentPath);
parentPath = parentPath.substring(getProtocolPrefix().length());
logDebug("parent after: " + parentPath);*/
IDriveItemCollectionPage itemsPage = clientAndPath.getPathItem()
.getChildren()
@@ -377,7 +444,7 @@ public class OneDriveStorage2 extends JavaFileStorageBase
e.displayName = i.name;
e.canRead = e.canWrite = true;
e.path = getProtocolId() +"://"+path;
e.path = path;
if (i.lastModifiedDateTime != null)
e.lastModifiedTime = i.lastModifiedDateTime.getTimeInMillis();
else if ((i.remoteItem != null)&&(i.remoteItem.lastModifiedDateTime != null))
@@ -391,6 +458,16 @@ public class OneDriveStorage2 extends JavaFileStorageBase
try {
ClientAndPath clientAndPath = getOneDriveClientAndPath(filename);
if (((clientAndPath.oneDrivePath == null) || "".equals(clientAndPath.oneDrivePath))
&& !clientAndPath.hasShare())
{
FileEntry rootEntry = new FileEntry();
rootEntry.displayName = "";
rootEntry.isDirectory = true;
return rootEntry;
}
IDriveItemRequestBuilder pathItem = clientAndPath.getPathItem();
IDriveItemRequest request = pathItem.buildRequest();
@@ -405,9 +482,7 @@ public class OneDriveStorage2 extends JavaFileStorageBase
public void delete(String path) throws Exception {
try {
ClientAndPath clientAndPath = getOneDriveClientAndPath(path);
clientAndPath.client.getDrive()
.getRoot()
.getItemWithPath(clientAndPath.oneDrivePath)
clientAndPath.getPathItem()
.buildRequest()
.delete();
} catch (ClientException e) {
@@ -422,63 +497,114 @@ public class OneDriveStorage2 extends JavaFileStorageBase
if (activity.getProcessName().equals(PROCESS_NAME_SELECTFILE))
activity.getState().putString(EXTRA_PATH, activity.getPath());
String userId = activity.getState().getString("OneDriveUser");
if (mClientByUser.containsKey(userId)) {
finishActivityWithSuccess(activity);
User user = null;
try {
String userId = extractUserId(activity.getPath());
if (mClientByUser.containsKey(userId)) {
finishActivityWithSuccess(activity);
return;
}
logDebug("needs acquire token");
Log.d("KP2AJ", "trying silent login " + activity.getPath());
final MsalException[] _exception = {null};
final AuthenticationResult[] _result = {null};
logDebug("getting user for " + userId);
user = mPublicClientApp.getUser(userId);
logDebug("getting user ok.");
} catch (Exception e) {
logDebug(e.toString());
e.printStackTrace();
}
if (user != null)
{
mPublicClientApp.acquireTokenSilentAsync(scopes, user,
new AuthenticationCallback() {
@Override
public void onSuccess(AuthenticationResult authenticationResult) {
successAuthCallback(authenticationResult, activity);
}
@Override
public void onError(MsalException exception) {
startInteractiveAcquireToken(activity);
}
@Override
public void onCancel() {
cancelAuthCallback((Activity) activity);
}
});
return;
}
JavaFileStorage.FileStorageSetupActivity storageSetupAct = activity;
final CountDownLatch latch = new CountDownLatch(1);
final AuthenticationResult[] _authenticationResult = {null};
MsalException _exception[] = {null};
startInteractiveAcquireToken(activity);
}
private void startInteractiveAcquireToken(FileStorageSetupActivity activity) {
if (!acquireTokenRunning) {
acquireTokenRunning = true;
mPublicClientApp.acquireToken((Activity) activity, scopes, new AuthenticationCallback() {
@Override
public void onSuccess(AuthenticationResult authenticationResult) {
Log.i(TAG, "authenticating successful");
try {
buildClient(authenticationResult);
} catch (InterruptedException e) {
e.printStackTrace();
}
activity.getState().putString(EXTRA_PATH, getProtocolPrefix() + authenticationResult.getUser().getUserIdentifier() + "/");
finishActivityWithSuccess(activity);
acquireTokenRunning = false;
return;
successAuthCallback(authenticationResult, activity);
}
@Override
public void onError(MsalException exception) {
Log.i(TAG, "authenticating not successful");
Intent data = new Intent();
data.putExtra(EXTRA_ERROR_MESSAGE, "authenticating not successful");
((Activity) activity).setResult(Activity.RESULT_CANCELED, data);
((Activity) activity).finish();
acquireTokenRunning = false;
errorAuthCallback((Activity) activity);
}
@Override
public void onCancel() {
Log.i(TAG, "authenticating cancelled");
Intent data = new Intent();
data.putExtra(EXTRA_ERROR_MESSAGE, "authenticating not cancelled");
((Activity) activity).setResult(Activity.RESULT_CANCELED, data);
((Activity) activity).finish();
acquireTokenRunning = false;
cancelAuthCallback((Activity) activity);
}
});
}
}
private void successAuthCallback(AuthenticationResult authenticationResult, FileStorageSetupActivity activity) {
Log.i(TAG, "authenticating successful");
try {
buildClient(authenticationResult);
} catch (Exception e) {
logDebug(e.toString());
e.printStackTrace();
}
activity.getState().putString(EXTRA_PATH, getProtocolPrefix() + authenticationResult.getUser().getUserIdentifier() + "/");
finishActivityWithSuccess(activity);
acquireTokenRunning = false;
}
private void errorAuthCallback(Activity activity) {
Log.i(TAG, "authenticating not successful");
Intent data = new Intent();
data.putExtra(EXTRA_ERROR_MESSAGE, "authenticating not successful");
activity.setResult(Activity.RESULT_CANCELED, data);
activity.finish();
acquireTokenRunning = false;
}
private void cancelAuthCallback(Activity activity) {
Log.i(TAG, "authenticating cancelled");
Intent data = new Intent();
data.putExtra(EXTRA_ERROR_MESSAGE, "authenticating not cancelled");
activity.setResult(Activity.RESULT_CANCELED, data);
activity.finish();
acquireTokenRunning = false;
}
@Override
public void onActivityResult(FileStorageSetupActivity activity, int requestCode, int resultCode, Intent data) {
mPublicClientApp.handleInteractiveRequestRedirect(requestCode, resultCode, data);

View File

@@ -430,11 +430,11 @@ public class MainActivity extends Activity implements JavaFileStorage.FileStorag
&& (e.canWrite == file.canWrite)
&& (e.isDirectory == file.isDirectory)
&& (e.displayName.equals(file.displayName))
&& (e.sizeInBytes == file.sizeInBytes ))
&& (file.isDirectory || (e.sizeInBytes == file.sizeInBytes )))
return;
}
throw new Exception("didn't find file " + file.path + " in file list!");
throw new Exception("didn't find file " + file.path + " (" + file.displayName + ") in file list!");
}
}