android - 用户选择帐户后,Google Drive API 不会让用户保持登录状态
问题描述
在我的 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 密钥。
推荐阅读
- java - 如何使用 Java 代码调用 GraphQL 查询/突变
- android - 如何在android中为标记设置动画
- reactjs - 使用 React Native 为 Zomato API 创建搜索功能
- wordpress - 有人可以解释这段代码// wordpress
- python - 深度 Q 网络给出相同的 Q 值并且没有改善
- react-native - 从 React Native 中的参数获取数据并将其传递给属性
- javascript - 将 firestore 对象存储在数组中,然后根据数字键进行排序
- ansible - 剧本中的get_url错误,从网络服务器下载文件
- actframework - 有没有办法等待一个不会阻塞 Act Framework 中执行线程的 Future 完成?
- python - 美丽的汤返回“无”