0

I made one activity and one service. Activity for just getting authorised once which is working fine but when i try to list files using same account in service it doesnt works.

Here is the mainactivity

public class MainActivity extends Activity {
GoogleAccountCredential mCredential;
private TextView mOutputText;
ProgressDialog mProgress;

static final int REQUEST_ACCOUNT_PICKER = 1000;
static final int REQUEST_AUTHORIZATION = 1001;
static final int REQUEST_GOOGLE_PLAY_SERVICES = 1002;
private static final String PREF_ACCOUNT_NAME = "accountName";
private static final String[] SCOPES = { DriveScopes.DRIVE_METADATA_READONLY };

/**
 * Create the main activity.
 * @param savedInstanceState previously saved instance data.
 */
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    LinearLayout activityLayout = new LinearLayout(this);
    LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
            LinearLayout.LayoutParams.MATCH_PARENT,
            LinearLayout.LayoutParams.MATCH_PARENT);
    activityLayout.setLayoutParams(lp);
    activityLayout.setOrientation(LinearLayout.VERTICAL);
    activityLayout.setPadding(16, 16, 16, 16);

    ViewGroup.LayoutParams tlp = new ViewGroup.LayoutParams(
            ViewGroup.LayoutParams.WRAP_CONTENT,
            ViewGroup.LayoutParams.WRAP_CONTENT);

    mOutputText = new TextView(this);
    mOutputText.setLayoutParams(tlp);
    mOutputText.setPadding(16, 16, 16, 16);
    mOutputText.setVerticalScrollBarEnabled(true);
    mOutputText.setMovementMethod(new ScrollingMovementMethod());
    activityLayout.addView(mOutputText);

    mProgress = new ProgressDialog(this);
    mProgress.setMessage("Calling Drive API ...");
    mOutputText.setText("hi");
    setContentView(activityLayout);

    // Initialize credentials and service object.
    mCredential = GoogleAccountCredential.usingOAuth2(
            getApplicationContext(), Arrays.asList(SCOPES))
            .setBackOff(new ExponentialBackOff())
            .setSelectedAccountName((null));
}


/**
 * Called whenever this activity is pushed to the foreground, such as after
 * a call to onCreate().
 */
@Override
protected void onResume() {
    super.onResume();
    if (isGooglePlayServicesAvailable()) {
        refreshResults();
    } else {
        mOutputText.setText("Google Play Services required: " +
                "after installing, close and relaunch this app.");
    }
}

/**
 * Called when an activity launched here (specifically, AccountPicker
 * and authorization) exits, giving you the requestCode you started it with,
 * the resultCode it returned, and any additional data from it.
 * @param requestCode code indicating which activity result is incoming.
 * @param resultCode code indicating the result of the incoming
 *     activity result.
 * @param data Intent (containing result data) returned by incoming
 *     activity result.
 */
@Override
protected void onActivityResult(
        int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    switch(requestCode) {
        case REQUEST_GOOGLE_PLAY_SERVICES:
            if (resultCode != RESULT_OK) {
                isGooglePlayServicesAvailable();
            }
            break;
        case REQUEST_ACCOUNT_PICKER:
            if (resultCode == RESULT_OK && data != null &&
                    data.getExtras() != null) {
                String accountName =
                        data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
                if (accountName != null) {
                    mCredential.setSelectedAccountName(accountName);
                    SharedPreferences settings =
                            PreferenceManager.getDefaultSharedPreferences(this);
                    SharedPreferences.Editor editor = settings.edit();
                    editor.putString(PREF_ACCOUNT_NAME, accountName);
                    editor.apply();
                }
            } else if (resultCode == RESULT_CANCELED) {
                mOutputText.setText("Account unspecified.");
            }
            break;
        case REQUEST_AUTHORIZATION:
            if (resultCode != RESULT_OK) {
                chooseAccount();
            }
            break;
    }

    super.onActivityResult(requestCode, resultCode, data);
}

/**
 * Attempt to get a set of data from the Drive API to display. If the
 * email address isn't known yet, then call chooseAccount() method so the
 * user can pick an account.
 */
private void refreshResults() {
    if (mCredential.getSelectedAccountName() == null) {
        chooseAccount();
    } else {
        if (isDeviceOnline()) {
            new MakeRequestTask(mCredential).execute();
        } else {
            mOutputText.setText("No network connection available.");
        }
    }
}

/**
 * Starts an activity in Google Play Services so the user can pick an
 * account.
 */
private void chooseAccount() {
    startActivityForResult(
            mCredential.newChooseAccountIntent(), REQUEST_ACCOUNT_PICKER);
}

/**
 * Checks whether the device currently has a network connection.
 * @return true if the device has a network connection, false otherwise.
 */
private boolean isDeviceOnline() {
    ConnectivityManager connMgr =
            (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
    return (networkInfo != null && networkInfo.isConnected());
}

/**
 * Check that Google Play services APK is installed and up to date. Will
 * launch an error dialog for the user to update Google Play Services if
 * possible.
 * @return true if Google Play Services is available and up to
 *     date on this device; false otherwise.
 */
private boolean isGooglePlayServicesAvailable() {
    final int connectionStatusCode =
            GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
    if (GooglePlayServicesUtil.isUserRecoverableError(connectionStatusCode)) {
        showGooglePlayServicesAvailabilityErrorDialog(connectionStatusCode);
        return false;
    } else if (connectionStatusCode != ConnectionResult.SUCCESS ) {
        return false;
    }
    return true;
}

/**
 * Display an error dialog showing that Google Play Services is missing
 * or out of date.
 * @param connectionStatusCode code describing the presence (or lack of)
 *     Google Play Services on this device.
 */
void showGooglePlayServicesAvailabilityErrorDialog(
        final int connectionStatusCode) {
    Dialog dialog = GooglePlayServicesUtil.getErrorDialog(
            connectionStatusCode,
            MainActivity.this,
            REQUEST_GOOGLE_PLAY_SERVICES);
    dialog.show();
}

/**
 * An asynchronous task that handles the Drive API call.
 * Placing the API calls in their own task ensures the UI stays responsive.
 */
private class MakeRequestTask extends AsyncTask<Void, Void, List<String>> {
    private com.google.api.services.drive.Drive mService = null;
    private Exception mLastError = null;

    public MakeRequestTask(GoogleAccountCredential credential) {
        HttpTransport transport = AndroidHttp.newCompatibleTransport();
        JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
        mService = new com.google.api.services.drive.Drive.Builder(
                transport, jsonFactory, credential)
                .setApplicationName("Drive API Android Quickstart")
                .build();
    }

    /**
     * Background task to call Drive API.
     * @param params no parameters needed for this task.
     */
    @Override
    protected List<String> doInBackground(Void... params) {
        try {
            return getDataFromApi();
        } catch (Exception e) {
            mLastError = e;
            cancel(true);
            return null;
        }
    }

    /**
     * Fetch a list of up to 10 file names and IDs.
     * @return List of Strings describing files, or an empty list if no files
     *         found.
     * @throws IOException
     */
        private List<String> getDataFromApi() throws IOException {
            // Get a list of up to 10 files.
            List<String> fileInfo = new ArrayList<String>();
            FileList result = mService.files().list()
                    .setMaxResults(10)
                    .execute();
            List<File> files = result.getItems();
            if (files != null) {
                for (File file : files) {
                    fileInfo.add(String.format("%s (%s)\n",
                            file.getTitle(), file.getId()));
                }
            }
            return fileInfo;
        }


    @Override
    protected void onPreExecute() {
        mOutputText.setText("");
        mProgress.show();
    }

    @Override
    protected void onPostExecute(List<String> output) {
        mProgress.hide();
        if(mCredential.getSelectedAccountName()!=null){
           sendBroadcast(new Intent("account").putExtra("account",mCredential.getSelectedAccountName()));
            finish();}
    }

    @Override
    protected void onCancelled() {
        mProgress.hide();
        if (mLastError != null) {
            if (mLastError instanceof GooglePlayServicesAvailabilityIOException) {
                showGooglePlayServicesAvailabilityErrorDialog(
                        ((GooglePlayServicesAvailabilityIOException) mLastError)
                                .getConnectionStatusCode());
            } else if (mLastError instanceof UserRecoverableAuthIOException) {
                startActivityForResult(
                        ((UserRecoverableAuthIOException) mLastError).getIntent(),
                        MainActivity.REQUEST_AUTHORIZATION);
            } else {
                mOutputText.setText("The following error occurred:\n"
                        + mLastError.getMessage());
            }
        } else {
            mOutputText.setText("Request cancelled.");
        }
    }
}
}

Here is the required

service code

    void initialize(String id){
        account=id;
        mCredential = GoogleAccountCredential.usingOAuth2(
                this, Arrays.asList(SCOPES))
                .setBackOff(new ExponentialBackOff())
                .setSelectedAccountName(id);
        HttpTransport transport = AndroidHttp.newCompatibleTransport();
        JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
        mService = new com.google.api.services.drive.Drive.Builder(
                transport, jsonFactory, mCredential)
                .setApplicationName("Drive API Android Quickstart")
                .build();
    }
public void listRoot(final com.google.api.services.drive.Drive mService,  final listReturn listReturn) {
        final ArrayList<com.google.api.services.drive.model.File> files = new ArrayList<>();
        if (asyncTask != null && asyncTask.getStatus() == AsyncTask.Status.RUNNING)
            asyncTask.cancel(true);
        asyncTask = new AsyncTask<Void, Integer, Void>() {
            Exception e=null;
            @Override
            public void onProgressUpdate(Integer... v){
                if(v!=null && v[0]==1 && e!=null)listReturn.throwError(e);
                else listReturn.list(files);
            }
            @Override
            protected Void doInBackground(Void... voids) {
                try {
                    String pg = "";
                    for (; ; ) {
                        Drive.Files.List lst = mService
                                .files()
                                .list()
                                .setFields(mFolderFields)
                                .setMaxResults(100);

                        if (!pg.equals(""))
                            lst.setPageToken(pg);
                         FileList fl;
                        try {
                            fl = lst.execute();
                        } catch (Exception e1) {
                            fl=null;
                            e1.printStackTrace();
                        }


                        if (fl.size() == 0)
                            break;
                        for (com.google.api.services.drive.model.File file : fl.getItems()) {
                            if (files.contains(file))
                                continue;
                            else if (isRoot(file)) {
                                files.add(file);
                            }
                        }
                        if (pg.equals(fl.getNextPageToken()))
                            break;
                        pg = fl.getNextPageToken();
                        if (pg == null || pg.equals(""))
                            break;
                    }
                    publishProgress(0);
                } catch (Exception e) {
                    this.e=e;
                    publishProgress(1);

                }
                return null;
            }
        }.execute();
    }

Error comes in line

fl=lst.execute();

edit- Error log

com.google.api.client.googleapis.extensions.android.gms.auth.UserRecoverableAuthIOException
12-18 23:35:18.677 12181-12707/com.amaze.filemanager.driveplugin W/System.err:     at com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential$RequestHandler.intercept(GoogleAccountCredential.java:284)
12-18 23:35:18.677 12181-12707/com.amaze.filemanager.driveplugin W/System.err:     at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:859)
12-18 23:35:18.677 12181-12707/com.amaze.filemanager.driveplugin W/System.err:     at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:419)
12-18 23:35:18.677 12181-12707/com.amaze.filemanager.driveplugin W/System.err:     at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:352)
12-18 23:35:18.677 12181-12707/com.amaze.filemanager.driveplugin W/System.err:     at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.execute(AbstractGoogleClientRequest.java:469)
12-18 23:35:18.677 12181-12707/com.amaze.filemanager.driveplugin W/System.err:     at com.amaze.filemanager.driveplugin.DriveUtil$2.doInBackground(DriveUtil.java:119)
12-18 23:35:18.677 12181-12707/com.amaze.filemanager.driveplugin W/System.err:     at com.amaze.filemanager.driveplugin.DriveUtil$2.doInBackground(DriveUtil.java:97)
12-18 23:35:18.677 12181-12707/com.amaze.filemanager.driveplugin W/System.err:     at android.os.AsyncTask$2.call(AsyncTask.java:292)
12-18 23:35:18.677 12181-12707/com.amaze.filemanager.driveplugin W/System.err:     at java.util.concurrent.FutureTask.run(FutureTask.java:237)
12-18 23:35:18.677 12181-12707/com.amaze.filemanager.driveplugin W/System.err:     at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)

12-18 23:35:18.677 12181-12707/com.amaze.filemanager.driveplugin W/System.err: Caused by: com.google.android.gms.auth.UserRecoverableAuthException: NeedPermission
12-18 23:35:18.687 12181-12707/com.amaze.filemanager.driveplugin W/System.err:     at com.google.android.gms.auth.GoogleAuthUtil.zza(Unknown Source)
12-18 23:35:18.687 12181-12707/com.amaze.filemanager.driveplugin W/System.err:     at com.google.android.gms.auth.GoogleAuthUtil.getToken(Unknown Source)
Arpit Khurana
  • 144
  • 2
  • 14
  • So you mean your code executing without any error, but your request is returning empty list. Right? – Dhaval Patel Dec 18 '15 at 06:57
  • Code works fine in activity but not in service – Arpit Khurana Dec 18 '15 at 07:00
  • Are you getting any exception? – Dhaval Patel Dec 18 '15 at 07:01
  • i explained in the question.The line which gives error. lst.execute() – Arpit Khurana Dec 18 '15 at 07:02
  • Can you specify the error? – Dhaval Patel Dec 18 '15 at 07:02
  • logs just say this i am afraid com.amaze.filemanager.driveplugin.DriveUtil$2.doInBackground(DriveUtil.java:110) 12-18 12:06:23.610 16372-16372/? W/System.err: at com.amaze.filemanager.driveplugin.DriveUtil$2.doInBackground(DriveUtil.java:97) – Arpit Khurana Dec 18 '15 at 07:03
  • and that line i told you is lst.execute() – Arpit Khurana Dec 18 '15 at 07:04
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/98311/discussion-between-arpit-khurana-and-dhaval-patel). – Arpit Khurana Dec 18 '15 at 07:06
  • Take a look at this [SO question - answer](http://stackoverflow.com/questions/34282761/google-drive-integration-using-current-activity/34380599#34380599), It is for GDAA, but the very same applies to the REST Api. See the bottom of the answer. – seanpj Dec 20 '15 at 17:21
  • But i will have another problem here.This method passes activity object to that class.But my activity(client) is in a different package(apart from the activity i mentioned above which only is to be opened once for auth).So how will i pass activity object from a different package which is just interacting with the service – Arpit Khurana Dec 22 '15 at 11:23

1 Answers1

0

This answer is a direct reaction to your email referring to the RESTDemo Github project. Quoting:

i checked out your implementation where you passed activity object to initialize drive api in rest.java. I am in a situation where i cant send an activity object to this class.but i have an activity to gain authorization.Is there any way to reinitialize these drive objects without using activity object as i already got authorized once?

I have to admit i don't have time to analyze all the code in your question, so I'll try to focus on the REST singleton class. As you see:

private static com.google.api.services.drive.Drive mGOOSvc;
...
static void init(Activity ctx){
  mGOOSvc = new Drive.Builder(
    AndroidHttp.newCompatibleTransport(), 
    new GsonFactory(),
    GoogleAccountCredential.usingOAuth2(
      ctx,                     // or ctx.getApplicationContext(),
      Collections.singletonList(DriveScopes.DRIVE)
    )
  ).build();
}

needs activity context for GoogleAccountCredential (will except Application context as well). This would be one-time-only initialization and you can use the static mGOOSvc as long as your app's alive.

BUT, the Activity context is also needed in case of execute() failure in order to resolve the UserRecoverableAuthIOException. This exception is resolved by means of startActivityForResult((((UserRecoverableAuthIOException)ex).getIntent()),...) with subsequent onActivityResult(). This error can happen anytime, since the authorization can be revoked from the outside (http://drive.google.com > Settings > Manage Apps > ... > Disconnect from Drive).

The conclusion is, if you decide to ignore the possibility of re-occurrence of the UserRecoverableAuthIOException (you ignore this exception or handle it yourself), you may be able to get away with one-time-only call to init(). Notice that this is exactly what the REST class does, it calls the connect() method once and ignores subsequent execute() errors. And remember, REST is a singleton and as long as the static mGOOSvc is alive, you can address it from Activities or Services.

There is another scenario when you have to call init() again. That is in case of account switch as seen in the REQ_ACCPICK trail here. Your app would certainly need a new mGOOSvc for a new user (and again a new authorization if not authorized already).

So, you may be able to snatch the REST class, and just plug it into your code (non-ui thread calls). And you can drop the .setSelectedAccountName(email) as well if you don't handle multiple users.

Good Luck

seanpj
  • 6,735
  • 2
  • 33
  • 54
  • Even if i handle that exception by restarting that authorisation activity i separately made.Still it should atleast work once by just using application context inside service.No? – Arpit Khurana Dec 24 '15 at 15:39
  • I use that REST class from a SyncService only. 'init()' once on app start in resistant fragment, re-init again for a new user, and then just calling REST.foo()s. Service doesn't even need non-UI thread. – seanpj Dec 24 '15 at 15:51
  • OK I got it. But my case is even more complex. The service and this class (and an auth activity) are in a separate package being communicated by aidl. So I will be able to run it first time when authorising using activity. But second time when I just directly initialise inside service it wont work as I can't send an activity object through aidl. So I still don't have a working solution. – Arpit Khurana Dec 24 '15 at 15:57
  • Looks like they released v3 which specially contains "Authorization information is stored with the app, so subsequent executions will not prompt for authorization" .Though i am still having same problem with it – Arpit Khurana Dec 24 '15 at 20:30