在我的Android应用程序的一个部分中,用户可以将CSV文件导入应用程序。为此,用户必须允许该应用访问其Google云端硬盘。我通过使用Google Drive API登录来完成此操作,并允许用户只能选择CSV文件。在调试模式下,以下代码可以正常运行。但是,在发布应用程序后,用户从未登录。
在调试模式:https://www.youtube.com/watch?v=aFj_fn13x2c&feature=youtu.be
发布版本:https://www.youtube.com/watch?v=pq2POP43waM
权限:
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentSender;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AlertDialog;
import android.util.Log;
import android.util.Patterns;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AutoCompleteTextView;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.google.android.gms.auth.api.signin.GoogleSignIn;
import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
import com.google.android.gms.auth.api.signin.GoogleSignInClient;
import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
import com.google.android.gms.common.api.GoogleApiActivity;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.Scope;
import com.google.android.gms.drive.Drive;
import com.google.android.gms.drive.DriveClient;
import com.google.android.gms.drive.DriveContents;
import com.google.android.gms.drive.DriveFile;
import com.google.android.gms.drive.DriveId;
import com.google.android.gms.drive.DriveResourceClient;
import com.google.android.gms.drive.Metadata;
import com.google.android.gms.drive.OpenFileActivityBuilder;
import com.google.android.gms.drive.OpenFileActivityOptions;
import com.google.android.gms.drive.query.Filters;
import com.google.android.gms.drive.query.SearchableField;
import com.google.android.gms.tasks.Continuation;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.android.gms.tasks.Task;
import com.google.android.gms.tasks.TaskCompletionSource;
import com.google.android.gms.tasks.Tasks;
import com.google.gson.Gson;
import com.project.danielo.eventer.Custom_Classes.AddAndEditMethods;
import com.project.danielo.eventer.Custom_Classes.CSVExporter;
import com.project.danielo.eventer.Custom_Classes.CustomDateParser;
import com.project.danielo.eventer.Custom_Classes.CustomRegex;
import com.project.danielo.eventer.StaticVariables;
import com.project.danielo.eventer.adapter.CustomEventObject;
import com.project.danielo.eventer.dialog_fragments.NotificationSettings;
import com.project.danielo.eventer.notification_package.CustomNotification;
import com.project.danielo.eventer.sqllite.DBHandler;
import com.project.danielo.eventer.R;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.io.StringWriter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import static android.app.Activity.RESULT_OK;
public class SettingsFragments extends Fragment {
public SettingsFragments(){
}
private View settingsView;
Button
btnImportEvents, btnDriveSettings;
ProgressBar progressBar;
GoogleApiClient apiClient;
private static final String TAG = "Google drive activity";
private static final int REQUEST_CODE_OPENER = 15;
private static final int REQUEST_CODE_SIGN_IN = 16;
private static final int REQUEST_CODE_OPEN_ITEM = 1;
private DriveId driveId;
private DriveClient driveClient;
private OpenFileActivityOptions openFileActivityOptions;
private DriveResourceClient resourceClient;
private TaskCompletionSource<DriveId> mOpenItemTaskSource;
private DriveContents driveContents;
private Metadata metadata;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
signIn();
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
settingsView = inflater.inflate(R.layout.layout_for_settings_fragment, null, false);
btnImportEvents = (Button)settingsView.findViewById(R.id.btn_import_events);
btnDriveSettings = (Button)settingsView.findViewById(R.id.btn_google_drive_settings);
progressBar = (ProgressBar)settingsView.findViewById(R.id.settings_progress_bar);
/*Upon this button click, the app checks if user is logged
* before giving user access to Google Drive account
*/
btnImportEvents.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!isConnectedToTheInternet()) {
openPleaseConnectToInternet();
}else{
if(!isUserSignedInToGoogleDriveAccount()){
openSignInGoogleDriveAccountDialog();
}else{
openFileChooser();
}
}
}
});
/*Upon this button click, the app logs user of current Google Drive account
* and opens choose account dialog
*/
btnDriveSettings.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!isConnectedToTheInternet()) {
openPleaseConnectToInternet();
}else{
progressBar.setVisibility(View.VISIBLE);
/*
/user is signed in, so we must initialize sign in client and sign out to reopen Google Drive Account chooser
*/
GoogleSignInOptions signInOptions =
new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestScopes(Drive.SCOPE_FILE)
.requestScopes(Drive.SCOPE_APPFOLDER)
.build();
GoogleSignInClient signInClient = GoogleSignIn.getClient(getContext(),signInOptions);
signInClient.signOut();
signIn();
}
}
});
return settingsView;
}
/***********************START OF IMPORT EVENTS METHODS**************************/
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
progressBar.setVisibility(View.INVISIBLE);
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case REQUEST_CODE_SIGN_IN:
if (resultCode != RESULT_OK) {
// Sign-in may fail or be cancelled by the user. For this sample, sign-in is
// required and is fatal. For apps where sign-in is optional, handle
// appropriately
return;
}
Task<GoogleSignInAccount> getAccountTask =
GoogleSignIn.getSignedInAccountFromIntent(data);
if (getAccountTask.isSuccessful()) {
initializeDriveClient(getAccountTask.getResult());
}
break;
case REQUEST_CODE_OPENER:
if (resultCode == RESULT_OK) {
driveId = (DriveId) data.getParcelableExtra(
OpenFileActivityBuilder.EXTRA_RESPONSE_DRIVE_ID);
loadCurrentFile();
}
break;
}
super.onActivityResult(requestCode, resultCode, data);
}
//Google drive sign in
private void signIn(){
Set<Scope> requiredScopes = new HashSet<>(2);
requiredScopes.add(Drive.SCOPE_FILE);
requiredScopes.add(Drive.SCOPE_APPFOLDER);
GoogleSignInAccount signInAccount = GoogleSignIn.getLastSignedInAccount(getContext());
if (signInAccount != null && signInAccount.getGrantedScopes().containsAll(requiredScopes)) {
initializeDriveClient(signInAccount);
} else {
GoogleSignInOptions signInOptions =
new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestScopes(Drive.SCOPE_FILE)
.requestScopes(Drive.SCOPE_APPFOLDER)
.build();
GoogleSignInClient signInClient = GoogleSignIn.getClient(getContext(), signInOptions);
startActivityForResult(signInClient.getSignInIntent(), REQUEST_CODE_SIGN_IN);
}
}
//list files in drive
private void openFileChooser(){
progressBar.setVisibility(View.VISIBLE);
OpenFileActivityOptions openOptions =
new OpenFileActivityOptions.Builder()
.setSelectionFilter(Filters.eq(SearchableField.MIME_TYPE, "text/csv"))
// .setMimeType(mimeTypes)
.setActivityTitle("Choose a CSV file")
.build();
driveClient.newOpenFileActivityIntentSender(openOptions)
.addOnSuccessListener(new OnSuccessListener<IntentSender>() {
@Override
public void onSuccess(IntentSender intentSender) {
try {
startIntentSenderForResult(
intentSender,
REQUEST_CODE_OPENER,
/* fillInIntent= */ null,
/* flagsMask= */ 0,
/* flagsValues= */ 0,
/* extraFlags= */ 0,
null);
;
} catch (IntentSender.SendIntentException e) {
Log.w(TAG, "Unable to send intent.", e);
}
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
Log.e(TAG, "Unable to create OpenFileActivityIntent.", e);
}
});
}
private void initializeDriveClient(GoogleSignInAccount signInAccount) {
driveClient = Drive.getDriveClient(getContext(), signInAccount);
resourceClient = Drive.getDriveResourceClient(getContext(), signInAccount);
if(progressBar != null) {
progressBar.setVisibility(View.INVISIBLE);
}
}
/**
* Retrieves the currently selected Drive file's metadata and contents.
*/
private void loadCurrentFile() {
progressBar.setVisibility(View.VISIBLE);
Log.d(TAG, "Retrieving...");
final DriveFile file = driveId.asDriveFile();
// Retrieve and store the file metadata and contents.
resourceClient.getMetadata(file)
.continueWithTask(new Continuation<Metadata, Task<DriveContents>>() {
@Override
public Task<DriveContents> then(@NonNull Task<Metadata> task) {
if (task.isSuccessful()) {
metadata = task.getResult();
return resourceClient.openFile(file, DriveFile.MODE_READ_ONLY);
} else {
return Tasks.forException(task.getException());
}
}
}).addOnSuccessListener(new OnSuccessListener<DriveContents>() {
@Override
public void onSuccess(DriveContents contents) {
driveContents = contents;
refreshUiFromCurrentFile();
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
Log.e(TAG, "Unable to retrieve file metadata and contents.", e);
}
});
}
//converting inputstream to string
private void refreshUiFromCurrentFile() {
Log.d(TAG, "Refreshing...");
String contents = "";
try {
StringWriter writer = new StringWriter();
IOUtils.copy(driveContents.getInputStream(), writer);
contents = writer.toString();
} catch (IOException e) {
e.printStackTrace();
}
if(contents.trim().isEmpty()){
return;
}
}
private boolean isConnectedToTheInternet(){
ConnectivityManager cm =
(ConnectivityManager)getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
boolean isConnected = false;
try{
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
isConnected = activeNetwork != null &&
activeNetwork.isConnectedOrConnecting();
}catch (SecurityException e){
e.printStackTrace();
}
return isConnected;
}
private boolean isUserSignedInToGoogleDriveAccount(){
GoogleSignInAccount signInAccount = GoogleSignIn.getLastSignedInAccount(getContext());
if(signInAccount == null){
return false;
}
return true;
}
private void openSignInGoogleDriveAccountDialog(){
AlertDialog alertDialog = new AlertDialog.Builder(getContext()).create();
alertDialog.setTitle("No google account selected");
alertDialog.setMessage("Please sign in to Google Drive Account by pressing Google Drive Settings button");
alertDialog.setButton(AlertDialog.BUTTON_NEUTRAL, "OK",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
alertDialog.show();
}
private void openPleaseConnectToInternet(){
AlertDialog alertDialog = new AlertDialog.Builder(getContext()).create();
alertDialog.setTitle("!Internet Connection needed");
alertDialog.setMessage("Please Connect to the internet");
alertDialog.setButton(AlertDialog.BUTTON_NEUTRAL, "OK",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
alertDialog.show();
}
}
好的,我想出了问题。要使用google drive API,必须设置OAuth 2.0凭据。我为我的应用程序的调试版本设置了凭据,因此API可以根据需要运行。当我尝试使用相同的凭据发布版本时,会出现问题。这是一个问题,因为您需要SHA-1密钥才能设置O-Auth 2.0凭据。调试版和发行版具有不同的SHA-1密钥。