Android Google changeListener - android

I am trying to run Android Google Drive. I am using the github sample code Android Demos.
I have added google play services to my project. This is the error Iam getting:
The import com.google.android.gms.drive.events.DriveEvent.ChangeListener cannot be resolved
After reading this page (Error com.google.android.gms.drive.DriveApi.DriveContentsResult cannot be resolved)
I changed it to
The import com.google.android.gms.drive.events.ChangeListener
But, the error still exists within those two methods:
Listener cannot be resolved to a type
-
private void toggle() {
if (mSelectedFileId == null) {
return;
}
synchronized (mSubscriptionStatusLock) {
DriveFile file = Drive.DriveApi.getFile(getGoogleApiClient(),
mSelectedFileId);
if (!isSubscribed) {
Log.d(TAG, "Starting to listen to the file changes.");
file.addChangeListener(getGoogleApiClient(), changeListener);//error
isSubscribed = true;
} else {
Log.d(TAG, "Stopping to listen to the file changes.");
file.removeChangeListener(getGoogleApiClient(), changeListener);//error
isSubscribed = false;
}
}
refresh();
}
-
final private Listener<ChangeEvent> changeListener = new Listener<ChangeEvent>() {//error
#Override
public void onEvent(ChangeEvent event) {
mLogTextView.setText(String.format("File change event: %s", event));
}
};
Can anybody enlighten me on this issue?
Github code
https://github.com/googledrive/android-demos

I'm using this code:
import com.google.android.gms.common.api.PendingResult;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.drive.Drive;
import com.google.android.gms.drive.DriveApi;
import com.google.android.gms.drive.DriveResource;
import com.google.android.gms.drive.events.ChangeEvent;
import com.google.android.gms.drive.events.ChangeListener;
PendingResult<DriveApi.DriveIdResult> pendingResult = Drive.DriveApi.fetchDriveId(mGoogleApiClient, id);
pendingResult.setResultCallback(new ResultCallback<DriveApi.DriveIdResult>() {
#Override
public void onResult(#NonNull DriveApi.DriveIdResult driveIdResult) {
if (!driveIdResult.getStatus().isSuccess()) {
Log.d(TAG, String.format("fetch drive id error: %s", driveIdResult.getStatus().getStatusMessage()));
return;
}
mCurrentDriveId = driveIdResult.getDriveId();
DriveResource resource = mCurrentDriveId.asDriveResource();
Log.d(TAG, "received driveid from id");
PendingResult<Status> pendingChange = resource.addChangeListener(mGoogleApiClient, new ChangeListener() {
#Override
public void onChange(ChangeEvent changeEvent) {
onResourceChange(changeEvent);
}
});
pendingChange.setResultCallback(new ResultCallback<Status>() {
#Override
public void onResult(#NonNull Status status) {
if (status.isSuccess()) {
Log.d(TAG, "change listener success");
} else {
Log.d(TAG, String.format("change listener error: %s", status.getStatusMessage()));
}
}
});
}
});
But it looks like the changes are not too much realtime.
Anyway getFile() is obsolete.

Related

Flutter grant permission

I struggle to ask ACCESS_FINE_LOCATION on a XIAOMI REDMI NOTE 4 which is running Android 6.0.
I tried to use geocoder and location. It works on iphone and a samsung on Android 8 (don't know the name).
TL;DR : the permission is never ask and not visible in app settings and Xiaomi.
Here is my code:
Android
import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import java.util.Arrays;
import io.flutter.app.FlutterActivity;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugins.GeneratedPluginRegistrant;
public class MainActivity extends FlutterActivity {
private static final String CHANNEL_GPS = "runtimepermission/access_fine_location";
private PermissionCallback getAccessFineLocationPermissionCallback;
private boolean rationaleJustShown = false;
private static final int GET_ACCESS_FINE_LOCATION_PERMISSION_REQUEST_ID = 1234;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
new MethodChannel(getFlutterView(), CHANNEL_GPS).setMethodCallHandler(
new MethodChannel.MethodCallHandler() {
#Override
public void onMethodCall(MethodCall call, final MethodChannel.Result result) {
getAccessFineLocationPermissionCallback = new PermissionCallback() {
#Override
public void granted() {
rationaleJustShown = false;
result.success(0);
}
#Override
public void denied() {
rationaleJustShown = false;
result.success(1);
}
};
if (call.method.equals("hasPermission")) {
askPermission();
}
}
});
}
private void askPermission() {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
GET_ACCESS_FINE_LOCATION_PERMISSION_REQUEST_ID);
}
#Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
System.out.println("requestCode:" + requestCode);
for (int grantResult : grantResults) {
System.out.println("grandResult[i]:" + grantResult);
}
switch (requestCode) {
case GET_ACCESS_FINE_LOCATION_PERMISSION_REQUEST_ID:
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
getAccessFineLocationPermissionCallback.granted();
} else {
getAccessFineLocationPermissionCallback.denied();
}
return;
}
}
public interface PermissionCallback {
void granted();
void denied();
}
Flutter:
static const _methodChannel = const MethodChannel('runtimepermission/access_fine_location');
Future<PermissionState> canGetLocation() async {
try {
final int result = await _methodChannel.invokeMethod('hasPermission');
return new Future.value(PermissionState.values.elementAt(result));
} on PlatformException catch (e) {
print('Exception ' + e.toString());
}
return new Future.value(PermissionState.DENIED);
}
It throws a PlatformException with an error message: The user explicitly denied the use of location services for this app or location services are currently disabled in Settings.
On the phone, the popup never shows up, and the permission is not displayed in settings > installed app > my app > permissions
Can you help me ?
Thanks.
Cache problem.
If you're experiencing odd problems like I had, clean the cache :)
Had the same problem. I was able to solve mine by enabling the app to auto init and dont optimize battery. It seems to be a xiaomi only problem with no plugins or flutter dev solution so far. My recommendation is to make a screen teaching the user what to do and redirect them

Android app + Drive API with full-drive access ... what's the procedure?

I'm looking for some guidance related to accessing Google Drive with an Android app.
1) I need to be able to read files uploaded by users outside of my app. Does this mean I need full-drive access? (If the app could create a folder and then see all files uploaded by the user that exist in this folder, that would be great, but I don't think it works this way.)
2) If I need full-drive access, it seems that Googles "Drive API for Android" doesn't support this, and I need to use the REST api. I think this is true.
3) I need an Auth 2.0 client ID from Google. If I use the rest API, does this mean I need to use a "Web Application" ID? I think I need this because I want an "auth code". I wasn't able to get it working with an "Android" type ID.
4) I'm currently using "Google Sign-In" for Android to handle the login and provide an auth code. I can then convert this into a Token + Refresh Token, and save these so I can get new tokens after an hour in some fashion. Is this manually handling of refresh tokens required?
It's getting ugly, but I think that since I need (?) full-drive access then this is the procedure.
Thanks for any guidance.
Edit: The question has been identified as a duplicate. The link provided gives an answer for question #2, but doesn't address the other questions.
I agree the question is messy...
I'm answering my own question.
I struggled with this because A) Google's REST example uses an outdated login process, B) The "Sign In" example uses code that doesn't work with "full-access" scope, and C) the there were too many vastly different code examples when trying to put it all together.
To quickly answer my questions as I see it now:
1) Yes, full-drive access is required to read files uploaded outside my app.
2) Yes, I need to use REST api.
3) Yes, I need a "Web Application" client ID.
4) Google Sign-In seems the best way currently to sign in, and using a GoogleCredential object along with the Drive api abject will handle the token refreshes automatically, as long as you keep around a refresh token.
In case anyone else is struggling with accessing Drive with full-access from Android using the latest "Sign-In" procedure and REST v3, below is my sample code.
In addition to the "Web application" OAuth client ID, you also need to create an "Android" type ID with a matching package name and certificate fingerprint in order for the Sign-In to work. Also note that you'll have different certificates for your dev and production versions. The IDs/codes from these Android clients do not need to be entered into the app.
build.gradle : app
// Google Sign In
compile 'com.google.android.gms:play-services-auth:10.0.1'
// Drive REST API
compile('com.google.apis:google-api-services-drive:v3-rev54-1.22.0') {
exclude group: 'org.apache.httpcomponents'
}
Activity
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// Callback from Signin (Auth.GoogleSignInApi.getSignInIntent)
if (requestCode == 1) {
GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
_googleApi.handleSignInResult(result);
}
}
A "GoogleApi" class to do the work
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import com.google.android.gms.auth.api.Auth;
import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
import com.google.android.gms.auth.api.signin.GoogleSignInResult;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.common.api.Scope;
import com.google.android.gms.common.api.Status;
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeTokenRequest;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.googleapis.auth.oauth2.GoogleTokenResponse;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.services.drive.Drive;
import com.google.api.services.drive.model.File;
import com.google.api.services.drive.model.FileList;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
public class GoogleApi implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
private Context _context;
private Handler _handler;
private GoogleCredential _credential;
private Drive _drive;
private GoogleApiClient _googleApiClient; // only set during login process
private Activity _activity; // launch intent for login (UI)
// Saved to data store
private boolean _loggedIn;
private String _refreshToken; // store, even if user is logged out as we may need to reuse
private static final String ClientID = "xxxxxx.apps.googleusercontent.com"; // web client
private static final String ClientSecret = "xxxxx"; // web client
private class FileAndErrorMsg {
public File file;
public String errorMsg;
public FileAndErrorMsg (File file_, String errorMsg_) { file = file_; errorMsg = errorMsg_; }
}
private class FileListAndErrorMsg {
public List<File> fileList;
public String errorMsg;
public FileListAndErrorMsg (List<File> fileList_, String errorMsg_) { fileList = fileList_; errorMsg = errorMsg_; }
}
// -------------------
// Constructor
// -------------------
public GoogleApi (Context context) {
_context = context;
_handler = new Handler();
loadFromPrefs(); // loggedIn, refreshToken
// create credential; will refresh itself automatically (in Drive calls) as long as valid refresh token exists
HttpTransport transport = new NetHttpTransport();
JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
_credential = new GoogleCredential.Builder()
.setTransport(transport)
.setJsonFactory(jsonFactory)
.setClientSecrets(ClientID, ClientSecret) // .addRefreshListener
.build();
_credential.setRefreshToken(_refreshToken);
// Get app name from Manifest (for Drive builder)
ApplicationInfo appInfo = context.getApplicationInfo();
String appName = appInfo.labelRes == 0 ? appInfo.nonLocalizedLabel.toString() : context.getString(appInfo.labelRes);
_drive = new Drive.Builder(transport, jsonFactory, _credential).setApplicationName(appName).build();
}
// -------------------
// Auth
// -------------------
// https://developers.google.com/identity/sign-in/android/offline-access#before_you_begin
// https://developers.google.com/identity/sign-in/android/offline-access#enable_server-side_api_access_for_your_app
// https://android-developers.googleblog.com/2016/02/using-credentials-between-your-server.html
// https://android-developers.googleblog.com/2016/05/improving-security-and-user-experience.html
public boolean isLoggedIn () {
return _loggedIn;
}
public void startAuth(Activity activity) {
startAuth(activity, false);
}
public void startAuth(Activity activity, boolean forceRefreshToken) {
_activity = activity;
_loggedIn = false;
saveToPrefs();
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestScopes(new Scope("https://www.googleapis.com/auth/drive"))
.requestServerAuthCode(ClientID, forceRefreshToken) // if force, guaranteed to get back refresh token, but will show "offline access?" if Google already issued refresh token
.build();
_googleApiClient = new GoogleApiClient.Builder(activity)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(Auth.GOOGLE_SIGN_IN_API, gso)
.build();
_googleApiClient.connect();
}
#Override
public void onConnected(Bundle connectionHint) {
// Called soon after .connect()
// This is only called when starting our Login process. Sign Out first so select-account screen shown. (OK if not already signed in)
Auth.GoogleSignInApi.signOut(_googleApiClient).setResultCallback(new ResultCallback<Status>() {
#Override
public void onResult(Status status) {
// Start sign in
Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(_googleApiClient);
_activity.startActivityForResult(signInIntent, 1); // Activity's onActivityResult will use the same code: 1
}
});
}
#Override
public void onConnectionSuspended(int cause) {
authDone("Connection suspended.");
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) { authDone("Connection failed."); }
public void handleSignInResult(GoogleSignInResult result) {
// Callback from Activity > onActivityResult
if (result.isSuccess()) {
GoogleSignInAccount acct = result.getSignInAccount();
String authCode = acct.getServerAuthCode();
new Thread(new ContinueAuthWithAuthCode_Background(authCode)).start();
}
else authDone("Login canceled or unable to connect to Google."); // can we get better error message?
}
private class ContinueAuthWithAuthCode_Background implements Runnable {
String _authCode;
public ContinueAuthWithAuthCode_Background (String authCode) {
_authCode = authCode;
}
public void run() {
// Convert authCode to tokens
GoogleTokenResponse tokenResponse = null;
String errorMsg = null;
try {
tokenResponse = new GoogleAuthorizationCodeTokenRequest(new NetHttpTransport(), JacksonFactory.getDefaultInstance(), "https://www.googleapis.com/oauth2/v4/token", ClientID, ClientSecret, _authCode, "").execute();
}
catch (IOException e) { errorMsg = e.getLocalizedMessage(); }
final GoogleTokenResponse tokenResponseFinal = tokenResponse;
final String errorMsgFinal = errorMsg;
_handler.post(new Runnable() { public void run() {
// Main thread
GoogleTokenResponse tokenResponse = tokenResponseFinal;
String errorMsg = errorMsgFinal;
if (tokenResponse != null && errorMsg == null) {
_credential.setFromTokenResponse(tokenResponse); // this will keep old refresh token if no new one sent
_refreshToken = _credential.getRefreshToken();
_loggedIn = true;
saveToPrefs();
// FIXME: if our refresh token is bad and we're not getting a new one, how do we deal with this?
Log("New refresh token: " + tokenResponse.getRefreshToken());
}
else if (errorMsg == null) errorMsg = "Get token error."; // shouldn't get here
authDone(errorMsg);
} });
}
}
private void authDone(String errorMsg) {
// Disconnect (we only need googleApiClient for login process)
if (_googleApiClient != null && _googleApiClient.isConnected()) _googleApiClient.disconnect();
_googleApiClient = null;
}
/*
public void signOut() {
Auth.GoogleSignInApi.signOut(_googleApiClient).setResultCallback(new ResultCallback<Status>() {
#Override
public void onResult(Status status) {
}
});
}
public void revokeAccess() {
// FIXME: I don't know yet, but this may revoke access for all android devices
Auth.GoogleSignInApi.revokeAccess(_googleApiClient).setResultCallback(new ResultCallback<Status>() {
#Override
public void onResult(Status status) {
}
});
}
*/
public void LogOut() {
_loggedIn = false;
saveToPrefs(); // don't clear refresh token as we may need again
}
// -------------------
// API Calls
// -------------------
public void makeApiCall() {
new Thread(new TestApiCall_Background()).start();
}
private class TestApiCall_Background implements Runnable {
public void run() {
FileAndErrorMsg fileAndErr = getFolderFromName_b("Many Files", null);
if (fileAndErr.errorMsg != null) Log("getFolderFromName_b error: " + fileAndErr.errorMsg);
else {
FileListAndErrorMsg fileListAndErr = getFileListInFolder_b(fileAndErr.file);
if (fileListAndErr.errorMsg != null)
Log("getFileListInFolder_b error: " + fileListAndErr.errorMsg);
else {
Log("file count: " + fileListAndErr.fileList.size());
for (File file : fileListAndErr.fileList) {
//Log(file.getName());
}
}
}
_handler.post(new Runnable() { public void run() {
// Main thread
} });
}
}
private FileAndErrorMsg getFolderFromName_b (String folderName, File parent) {
// parent can be null for top level
// Working with folders: https://developers.google.com/drive/v3/web/folder
File folder = null;
folderName = folderName.replace("'", "\\'"); // escape '
String q = String.format(Locale.US, "mimeType='application/vnd.google-apps.folder' and '%s' in parents and name='%s' and trashed=false", parent == null ? "root" : parent.getId(), folderName);
String errorMsg = null;
try {
FileList result = _drive.files().list().setQ(q).setPageSize(1000).execute();
int foundCount = 0;
for (File file : result.getFiles()) {
foundCount++;
folder = file;
}
if (foundCount == 0) errorMsg = "Folder not found: " + folderName;
else if (foundCount > 1) errorMsg = "More than one folder found with name (" + foundCount + "): " + folderName;
}
catch (IOException e) { errorMsg = e.getLocalizedMessage(); }
if (errorMsg != null) folder = null;
return new FileAndErrorMsg(folder, errorMsg);
}
private FileListAndErrorMsg getFileListInFolder_b (File folder) {
// folder can be null for top level; does not return subfolder names
List<File> fileList = new ArrayList<File>();
String q = String.format(Locale.US, "mimeType != 'application/vnd.google-apps.folder' and '%s' in parents and trashed=false", folder == null ? "root" : folder.getId());
String errorMsg = null;
try {
String pageToken = null;
do {
FileList result = _drive.files().list().setQ(q).setPageSize(1000).setPageToken(pageToken).execute();
fileList.addAll(result.getFiles());
pageToken = result.getNextPageToken();
} while (pageToken != null);
}
catch (IOException e) { errorMsg = e.getLocalizedMessage(); }
if (errorMsg != null) fileList = null;
return new FileListAndErrorMsg(fileList, errorMsg);
}
// -------------------
// Misc
// -------------------
private void Log(String msg) {
Log.v("ept", msg);
}
// -------------------
// Load/Save Tokens
// -------------------
private void loadFromPrefs() {
SharedPreferences pref = _context.getSharedPreferences("prefs", Context.MODE_PRIVATE);
_loggedIn = pref.getBoolean("GoogleLoggedIn", false);
_refreshToken = pref.getString("GoogleRefreshToken", null);
}
private void saveToPrefs() {
SharedPreferences.Editor editor = _context.getSharedPreferences("prefs", Context.MODE_PRIVATE).edit();
editor.putBoolean("GoogleLoggedIn", _loggedIn);
editor.putString("GoogleRefreshToken", _refreshToken);
editor.apply(); // async
}
}
The latest example in https://developers.google.com/drive/v3/web/quickstart/android works out of the box.
Just do the following:
1 - Go to Google API console and create an OAuth2 Client ID using your package name and debug/release key as your Signing-certificate fingerprint.
2 - Enable Google Drive API
3 - Apply the following code
build.gradle : app
compile 'com.google.android.gms:play-services-auth:10.0.1'
compile 'pub.devrel:easypermissions:0.2.1'
compile('com.google.api-client:google-api-client-android:1.22.0') {
exclude group: 'org.apache.httpcomponents'
}
compile('com.google.apis:google-api-services-drive:v3-rev57-1.22.0') {
exclude group: 'org.apache.httpcomponents'
}
Activity
In this code just change the scope to DriveScopes.DRIVE for full drive access
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.api.client.extensions.android.http.AndroidHttp;
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential;
import com.google.api.client.googleapis.extensions.android.gms.auth.GooglePlayServicesAvailabilityIOException;
import com.google.api.client.googleapis.extensions.android.gms.auth.UserRecoverableAuthIOException;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.client.util.ExponentialBackOff;
import com.google.api.services.drive.DriveScopes;
import com.google.api.services.drive.model.*;
import android.Manifest;
import android.accounts.AccountManager;
import android.app.Activity;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.text.method.ScrollingMovementMethod;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import pub.devrel.easypermissions.AfterPermissionGranted;
import pub.devrel.easypermissions.EasyPermissions;
public class MainActivity extends Activity
implements EasyPermissions.PermissionCallbacks {
GoogleAccountCredential mCredential;
private TextView mOutputText;
private Button mCallApiButton;
ProgressDialog mProgress;
static final int REQUEST_ACCOUNT_PICKER = 1000;
static final int REQUEST_AUTHORIZATION = 1001;
static final int REQUEST_GOOGLE_PLAY_SERVICES = 1002;
static final int REQUEST_PERMISSION_GET_ACCOUNTS = 1003;
private static final String BUTTON_TEXT = "Call Drive API";
private static final String PREF_ACCOUNT_NAME = "accountName";
private static final String[] SCOPES = { DriveScopes.DRIVE };
/**
* 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);
mCallApiButton = new Button(this);
mCallApiButton.setText(BUTTON_TEXT);
mCallApiButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mCallApiButton.setEnabled(false);
mOutputText.setText("");
getResultsFromApi();
mCallApiButton.setEnabled(true);
}
});
activityLayout.addView(mCallApiButton);
mOutputText = new TextView(this);
mOutputText.setLayoutParams(tlp);
mOutputText.setPadding(16, 16, 16, 16);
mOutputText.setVerticalScrollBarEnabled(true);
mOutputText.setMovementMethod(new ScrollingMovementMethod());
mOutputText.setText(
"Click the \'" + BUTTON_TEXT +"\' button to test the API.");
activityLayout.addView(mOutputText);
mProgress = new ProgressDialog(this);
mProgress.setMessage("Calling Drive API ...");
setContentView(activityLayout);
// Initialize credentials and service object.
mCredential = GoogleAccountCredential.usingOAuth2(
getApplicationContext(), Arrays.asList(SCOPES))
.setBackOff(new ExponentialBackOff());
}
/**
* Attempt to call the API, after verifying that all the preconditions are
* satisfied. The preconditions are: Google Play Services installed, an
* account was selected and the device currently has online access. If any
* of the preconditions are not satisfied, the app will prompt the user as
* appropriate.
*/
private void getResultsFromApi() {
if (! isGooglePlayServicesAvailable()) {
acquireGooglePlayServices();
} else if (mCredential.getSelectedAccountName() == null) {
chooseAccount();
} else if (! isDeviceOnline()) {
mOutputText.setText("No network connection available.");
} else {
new MakeRequestTask(mCredential).execute();
}
}
/**
* Attempts to set the account used with the API credentials. If an account
* name was previously saved it will use that one; otherwise an account
* picker dialog will be shown to the user. Note that the setting the
* account to use with the credentials object requires the app to have the
* GET_ACCOUNTS permission, which is requested here if it is not already
* present. The AfterPermissionGranted annotation indicates that this
* function will be rerun automatically whenever the GET_ACCOUNTS permission
* is granted.
*/
#AfterPermissionGranted(REQUEST_PERMISSION_GET_ACCOUNTS)
private void chooseAccount() {
if (EasyPermissions.hasPermissions(
this, Manifest.permission.GET_ACCOUNTS)) {
String accountName = getPreferences(Context.MODE_PRIVATE)
.getString(PREF_ACCOUNT_NAME, null);
if (accountName != null) {
mCredential.setSelectedAccountName(accountName);
getResultsFromApi();
} else {
// Start a dialog from which the user can choose an account
startActivityForResult(
mCredential.newChooseAccountIntent(),
REQUEST_ACCOUNT_PICKER);
}
} else {
// Request the GET_ACCOUNTS permission via a user dialog
EasyPermissions.requestPermissions(
this,
"This app needs to access your Google account (via Contacts).",
REQUEST_PERMISSION_GET_ACCOUNTS,
Manifest.permission.GET_ACCOUNTS);
}
}
/**
* 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) {
mOutputText.setText(
"This app requires Google Play Services. Please install " +
"Google Play Services on your device and relaunch this app.");
} else {
getResultsFromApi();
}
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) {
SharedPreferences settings =
getPreferences(Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
editor.putString(PREF_ACCOUNT_NAME, accountName);
editor.apply();
mCredential.setSelectedAccountName(accountName);
getResultsFromApi();
}
}
break;
case REQUEST_AUTHORIZATION:
if (resultCode == RESULT_OK) {
getResultsFromApi();
}
break;
}
}
/**
* Respond to requests for permissions at runtime for API 23 and above.
* #param requestCode The request code passed in
* requestPermissions(android.app.Activity, String, int, String[])
* #param permissions The requested permissions. Never null.
* #param grantResults The grant results for the corresponding permissions
* which is either PERMISSION_GRANTED or PERMISSION_DENIED. Never null.
*/
#Override
public void onRequestPermissionsResult(int requestCode,
#NonNull String[] permissions,
#NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
EasyPermissions.onRequestPermissionsResult(
requestCode, permissions, grantResults, this);
}
/**
* Callback for when a permission is granted using the EasyPermissions
* library.
* #param requestCode The request code associated with the requested
* permission
* #param list The requested permission list. Never null.
*/
#Override
public void onPermissionsGranted(int requestCode, List<String> list) {
// Do nothing.
}
/**
* Callback for when a permission is denied using the EasyPermissions
* library.
* #param requestCode The request code associated with the requested
* permission
* #param list The requested permission list. Never null.
*/
#Override
public void onPermissionsDenied(int requestCode, List<String> list) {
// Do nothing.
}
/**
* 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.
* #return true if Google Play Services is available and up to
* date on this device; false otherwise.
*/
private boolean isGooglePlayServicesAvailable() {
GoogleApiAvailability apiAvailability =
GoogleApiAvailability.getInstance();
final int connectionStatusCode =
apiAvailability.isGooglePlayServicesAvailable(this);
return connectionStatusCode == ConnectionResult.SUCCESS;
}
/**
* Attempt to resolve a missing, out-of-date, invalid or disabled Google
* Play Services installation via a user dialog, if possible.
*/
private void acquireGooglePlayServices() {
GoogleApiAvailability apiAvailability =
GoogleApiAvailability.getInstance();
final int connectionStatusCode =
apiAvailability.isGooglePlayServicesAvailable(this);
if (apiAvailability.isUserResolvableError(connectionStatusCode)) {
showGooglePlayServicesAvailabilityErrorDialog(connectionStatusCode);
}
}
/**
* 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) {
GoogleApiAvailability apiAvailability = GoogleApiAvailability.getInstance();
Dialog dialog = apiAvailability.getErrorDialog(
MainActivity.this,
connectionStatusCode,
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;
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()
.setPageSize(10)
.setFields("nextPageToken, files(id, name)")
.execute();
List<File> files = result.getFiles();
if (files != null) {
for (File file : files) {
fileInfo.add(String.format("%s (%s)\n",
file.getName(), file.getId()));
}
}
return fileInfo;
}
#Override
protected void onPreExecute() {
mOutputText.setText("");
mProgress.show();
}
#Override
protected void onPostExecute(List<String> output) {
mProgress.hide();
if (output == null || output.size() == 0) {
mOutputText.setText("No results returned.");
} else {
output.add(0, "Data retrieved using the Drive API:");
mOutputText.setText(TextUtils.join("\n", output));
}
}
#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.");
}
}
}
}

Cordova - How to display 360 images on Android

I am building a cordova application which fetches JSON data from the server. One piece of information that I receive is the URL of 360 online images. I need to display those images in a photo sphere viewer (for Android). I' ve seen this API (needs cordova google play services plugin) and this library but I haven't managed to successfully use them inside the application.
Does anyone know a way of doing that? Can I open that type of image in a native intent?
Thanks in advance
Actually I managed to make it work. I am posting the solution, in case someone else finds it useful. Also created a plugin for that which can be found here
creating a simple plugin, I call from cordova the plugin class which downloads using AsyncTask an image from a URL and onPostExecute I call the Panorama activity that shows the viewer.
Intent intent = new Intent(cordova.getActivity().getApplicationContext(), PanoramaActivity.class);
intent.putExtra("filepath", file.getAbsolutePath());
cordova.getActivity().startActivity(intent);.
PanoramaActivity.java
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.panorama.Panorama;
import com.google.android.gms.panorama.PanoramaApi.PanoramaResult;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import java.io.File;
import android.os.Environment;
public class PanoramaActivity extends Activity implements ConnectionCallbacks, OnConnectionFailedListener{
File file;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mClient = new GoogleApiClient.Builder(this, this, this)
.addApi(Panorama.API)
.build();
Intent i= getIntent();
Bundle b = i.getExtras();
file = new File(b.getString("filepath"));
#Override
public void onStart() {
super.onStart();
mClient.connect();
}
#Override
public void onConnected(Bundle connectionHint) {
Uri uri = Uri.fromFile(file);//Uri.parse(path);//Uri.fromFile(file);
Panorama.PanoramaApi.loadPanoramaInfo(mClient, uri).setResultCallback(
new ResultCallback<PanoramaResult>() {
#Override
public void onResult(PanoramaResult result) {
if (result.getStatus().isSuccess()) {
Intent viewerIntent = result.getViewerIntent();
Log.i(TAG, "found viewerIntent: " + viewerIntent);
if (viewerIntent != null) {
startActivity(viewerIntent);
}
} else {
Log.e(TAG, "error: " + result);
}
}
});
}
#Override
public void onConnectionSuspended(int cause) {
Log.i(TAG, "connection suspended: " + cause);
}
#Override
public void onConnectionFailed(ConnectionResult status) {
Log.e(TAG, "connection failed: " + status);
}
#Override
public void onStop() {
super.onStop();
mClient.disconnect();
Log.e(TAG, "ON Stop ");
}

Enable Gmail login in my application

These are the minimum details i need to login to my application:
guid(Global Unique ID)
fname
lname
email
gender
for Facebook login it was clear and could do this:
JSONObject json = Util.parseJson(facebook.request("me");
to get all the data specified above.
Is, there a way i can do something similar and simple with Gmail?
I read this which says:
No, there is no such SDK(like facebook) and if you want to access the
emails of Gmail then you need to implement your own email client and
for that follow the above link shared by Spk.
But i only want few user details, I don't need anything related to his mails etc.., which come under Authorization (if I am right). I only need Authentication:
I also read this, but doesn't look like it will help any.
This is the code i have now:
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks;
import com.google.android.gms.common.GooglePlayServicesClient.OnConnectionFailedListener;
import com.google.android.gms.plus.GooglePlusUtil;
import com.google.android.gms.plus.PlusClient;
import android.os.Bundle;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.content.IntentSender.SendIntentException;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends Activity implements ConnectionCallbacks,
OnConnectionFailedListener {
private static final int REQUEST_CODE_RESOLVE_ERR = 7;
private ProgressDialog mConnectionProgressDialog;
private PlusClient mPlusClient;
private ConnectionResult mConnectionResult;
private String TAG = "GmailLogin";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
int errorCode = GooglePlusUtil.checkGooglePlusApp(this);
if (errorCode != GooglePlusUtil.SUCCESS) {
GooglePlusUtil.getErrorDialog(errorCode, this, 0).show();
} else {
mPlusClient = new PlusClient.Builder(this, this, this)
.setVisibleActivities( "http://schemas.google.com/AddActivity",
"http://schemas.google.com/BuyActivity").build();
mConnectionProgressDialog = new ProgressDialog(this);
mConnectionProgressDialog.setMessage("Signing in...");
Button signInButton = (Button) findViewById(R.id.sign_in_button);
signInButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (mConnectionResult == null) {
mConnectionProgressDialog.show();
} else {
try {
mConnectionResult
.startResolutionForResult(
MainActivity.this,
REQUEST_CODE_RESOLVE_ERR);
} catch (SendIntentException e) {
// Try connecting again.
mConnectionResult = null;
mPlusClient.connect();
}
}
}
});
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#Override
public void onConnectionFailed(ConnectionResult result) {
if (result.hasResolution()) {
try {
result.startResolutionForResult(this, REQUEST_CODE_RESOLVE_ERR);
} catch (SendIntentException e) {
mPlusClient.connect();
}
}
// Save the result and resolve the connection failure upon a user click.
mConnectionResult = result;
}
#Override
protected void onActivityResult(int requestCode, int responseCode,
Intent intent) {
if (requestCode == REQUEST_CODE_RESOLVE_ERR
&& responseCode == RESULT_OK) {
mConnectionResult = null;
mPlusClient.connect();
}
}
#Override
public void onConnected() {
String accountName = mPlusClient.getAccountName();
Toast.makeText(this, accountName + " is connected.", Toast.LENGTH_LONG)
.show();
}
#Override
public void onDisconnected() {
Log.d(TAG, "disconnected");
}
#Override
protected void onStart() {
super.onStart();
mPlusClient.connect();
}
#Override
protected void onStop() {
super.onStop();
mPlusClient.disconnect();
}
}
Any help is greatly appreciated, Thank You.
You should not use Gmail for user authentication using Google accounts. You can use Google + Sign-in for Android instead. This will allow you to access the user's profile information upon getting the required permissions using OAuth. Check out the guide here:
https://developers.google.com/+/mobile/android/sign-in
Those attributes would be passed as claims in the original authentication, as well as on each subsequent authentication.
Google allows you to request that they include up to Country, Email, First Name, Language, and Last Name as claims on the authentication token.
Since they do not provide a uuid, you will have to create one off of the email address. There is no support (that I could find) for retrieving gender.

Failure in resolving TextToSpeech class for Android Development

I'm new to Android development, Eclipse and Java (done mostly .Net and IVR programming up till now), so when I tried to compile and run a sample app I found for TTS on the Droid, I wasn't surprised that I got a runtime error right away. The error is:
dalvikvm Failed resolving com/sample/TTSapp/AndroidTTSapp; interface 4 'android/speech/tts/TextToSpeech$OnInitListner;'
I suppose the OnInitListner method must be in one of the classes that was installed when I installed the Android SDK (release 1.6 R1 I believe), but I'm not sure how to import the associated class module into the current program. I can't find a speech/tts/TextToSpeech directory anywhere on my system. Do I need to download this directory from somewhere? Following is the Java source code for the demo TTS program I'm trying to run:
package com.sample.TTSApp;
import android.app.Activity;
import android.os.Bundle;
import android.speech.tts.TextToSpeech;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import com.sample.TTSApp.R;
import java.util.Locale;
import java.util.Random;
public class AndroidTTSapp extends Activity implements
TextToSpeech.OnInitListener
{
private static final String TAG = "TextToSpeechDemo";
private TextToSpeech mTts;
private Button mAgainButton;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Initialize text-to-speech. This is an asynchronous operation.
// The OnInitListener (second argument) is called after initialization completes.
// Instantiate TextToSpeech.OnInitListener
mTts = new TextToSpeech(this, this);
mAgainButton = (Button) findViewById(R.id.again_button);
mAgainButton.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
sayHello();
}
});
}
#Override
public void onDestroy()
{ // Don't forget to shutdown!
if (mTts != null)
{
mTts.stop();
mTts.shutdown();
}
super.onDestroy();
}
// Implements TextToSpeech.OnInitListener.
public void onInit(int status)
{
// status can be either TextToSpeech.SUCCESS or TextToSpeech.ERROR.
if (status == TextToSpeech.SUCCESS)
{
int result = mTts.setLanguage(Locale.US);
if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED)
{
// Language data is missing or the language is not supported.
Log.e(TAG, "Language is not available.");
}
else
{
// Check the documentation for other possible result codes. For example, the language may be available for the locale
// but not for the specified country and variant.
// The TTS engine has been successfully initialized. Allow the user to press the button for the app to speak again.
mAgainButton.setEnabled(true);
// Greet the user
sayHello();
}
}
else
{
// Initialization failed.
Log.e(TAG, "Could not initialize TextToSpeech.");
}
};
private static final Random RANDOM = new Random();
private static final String[] HELLOS =
{
"Hello World", "This is Text to speech demo by Zahid Shaikh"
};
private void sayHello()
{
// Select a random hello.
int i =0;
int helloLength = HELLOS.length;
String hello = HELLOS[i];
i++;
if(i == helloLength) i =0;
mTts.speak(hello,TextToSpeech.QUEUE_FLUSH,null);
}
}
Thanks in advance for any assitance anyone can give a beginner like myself.
Don Tilley
On device or emulator in */system/tts/lang_pico/* must be tts-lang-files (*.bin).
It's init TTS example:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
........
........
initTTS();
}
private void initTTS() {
Intent checkIntent = new Intent();
checkIntent.setAction(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);
startActivityForResult(checkIntent, MY_DATA_CHECK_CODE);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode == MY_DATA_CHECK_CODE) {
if(resultCode == TextToSpeech.Engine.CHECK_VOICE_DATA_PASS) {
mTts = new TextToSpeech(this, this);
} else {
Intent installIntent = new Intent();
installIntent.setAction(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
startActivity(installIntent);
}
}
}
public void onInit(int status) {
if(status == TextToSpeech.SUCCESS) {
int result = mTts.setLanguage(Locale.US);
if(result == TextToSpeech.LANG_AVAILABLE
|| result == TextToSpeech.LANG_COUNTRY_AVAILABLE) {
mTts.speak("Start system", TextToSpeech.QUEUE_FLUSH, null);
}
}
}

Categories

Resources