SyncAdapter onPerformSync doesn't always get called - java

I built a SyncAdapter for my app so that I could use Google Cloud Messaging to trigger a sync of database to the server. I am using Volley to actually make the network calls and sync the data, but from what I read when wanting to sync your app you should have a SyncAdapter
My issue is that the onPerformSync() doesn't always run. I will fire the GCM and I always get a log stating that it got through the GCM properly, but my log for the onPerformSync() doesn't always fire. Because it does sometimes I would imagine it is set up properly. But I cannot figure out what is happening when it doesn't
onPerformSync()
#Override
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
Log.d("onPerformSync", "got to the sync");
}
onMessageReceived() in GCM message handler
#Override
public void onMessageReceived(String from, Bundle data) {
if (from.equals("/topics/global")) {
Log.d("gcm topics", data.getString("message"));
try {
if (data.getString("message").equals("update")) {
Log.d("is update", "is message update");
Account newAccount = new Account(ACCOUNT, ACCOUNT_TYPE);
ContentResolver.requestSync(newAccount, AUTHORITY, data);
}
} catch (NullPointerException e) {
Log.e("GCM", e.toString());
}
} else {
String message = data.getString("message");
createNotification(from, message);
}
}
Creating the account in MainActivity
public static Account createSyncAccount(Context context) {
// Create the account type and default account
Account newAccount = new Account(
ACCOUNT, ACCOUNT_TYPE);
// Get an instance of the Android account manager
AccountManager accountManager =
(AccountManager) context.getSystemService(
ACCOUNT_SERVICE);
/*
* Add the account and account type, no password or user data
* If successful, return the Account object, otherwise report an error.
*/
if (accountManager.addAccountExplicitly(newAccount, null, null)) {
/*
* If you don't set android:syncable="true" in
* in your <provider> element in the manifest,
* then call context.setIsSyncable(account, AUTHORITY, 1)
* here.
*/
ContentResolver.setIsSyncable(newAccount, ArmyContract.CONTENT_AUTHORITY, 1);
ContentResolver.setSyncAutomatically(newAccount, ArmyContract.CONTENT_AUTHORITY, true);
return newAccount;
} else {
/*
* The account exists or some other error occurred. Log this, report it,
* or handle it internally.
*/
Log.e("Account Creation", "Error withou dummy accocunt");
return null;
}
}
syncadapter.xml
<?xml version="1.0" encoding="utf-8"?>
<sync-adapter
xmlns:android="http://schemas.android.com/apk/res/android"
android:contentAuthority="com.clashtoolkit.clashtoolkit"
android:accountType="clashtoolkit.com"
android:userVisible="false"
android:supportsUploading="false"
android:allowParallelSyncs="false"
android:isAlwaysSyncable="true"/>
authenticator.xml
<?xml version="1.0" encoding="utf-8"?>
<account-authenticator
xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="clashtoolkit.com"
android:icon="#mipmap/ic_launcher"
android:smallIcon="#mipmap/ic_launcher"
android:label="#string/app_name"/>
AndroidManifext.xml
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS"/>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<permission android:name="com.clashtoolkit.clashtoolkit.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
<uses-permission android:name="com.clashtoolkit.clashtoolkit.permission.C2D_MESSAGE" />
<service
android:name="com.clashtoolkit.clashtoolkit.network.AuthenticatorService">
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator"/>
</intent-filter>
<meta-data
android:name="android.accounts.AccountAuthenticator"
android:resource="#xml/authenticator" />
</service>
<service
android:name="com.clashtoolkit.clashtoolkit.network.SyncService"
android:exported="true"
android:process=":sync">
<intent-filter>
<action android:name="android.content.SyncAdapter"/>
</intent-filter>
<meta-data android:name="android.content.SyncAdapter"
android:resource="#xml/syncadapter" />
</service>

The problem might be in adding these keys to Bundle data :
// Disable sync backoff and ignore sync preferences. In other words...perform sync NOW!
data.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
data.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
ContentResolver.requestSync(newAccount, AUTHORITY, data);

Related

Firebase Notification not sent from console

Firebase notification is not working properly with a specific project.
If I sent notification from the console to all devices it did not work.
If I tried to send a notification via REST. its returns with the below response.
{
"multicast_id": ....,
"success": 0,
"failure": 1,
"canonical_ids": 0,
"results": [
{
"error": "InvalidRegistration"
}
]
}
Update : 2
I have created another project on the firebase console, delete resources and keys from the old project, then I have set up a new android application in the new project.
The result was the same, not receiving any notification The rest result was also the same.
But.
When I add another android application in the same project, then I was able to send notifications from a console-specific to that app, also from the rest API using token.
when I tried the original application token, I have the error. I am not able to find the issue ?. the registration part and initialization part was the same in the application.
Manifest Looks like.
<meta-data
android:name="com.google.firebase.messaging.default_notification_color"
android:resource="#color/purple_700" />
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="#string/default_notification_channel_id" />
<service
android:name=".CustomFirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
App.java
public class App extends Application
{
#Override
public void onCreate() {
super.onCreate();
FirebaseApp.initializeApp(this);
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
NotificationChannel notificationChannel = new NotificationChannel(getString(R.string.default_notification_channel_id),"X1",NotificationManager.IMPORTANCE_DEFAULT);
notificationManager.createNotificationChannel(notificationChannel);
}
}
}
Token Generate.
FirebaseMessaging.getInstance().getToken().addOnCompleteListener(new OnCompleteListener<String>() {
#Override
public void onComplete(#NonNull Task<String> task) {
Log.d(TAG, "onComplete() called with: task = [" + task + "]");
if(task.isSuccessful())
{
Log.d(TAG, "onComplete() called with: task = [" + task.getResult() + "]");
}
}
});
I have tried the same code with different projects and it's working fine.
I don't know what's wrong!.

Android java : Anonymous sign in with Firebase always fails

I was working with firebase to load images from Firebase realtime database into a RecyclerView and then download the selected image into the phone from Firebase Storage. I don't want every one to be able to see the images only if he is signed in using the anonymous sign in methode, so I've made the storage rules like this:
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read : if request.auth != null;
}
}
}
The database rules :
{
"rules": {
".read": true,
".write": false
}
}
I did that, it worked on my 1st app. I wanted to make a 2nd one, so I copied every thing, renamed databases names and storage names.. but the in the log I get this:
error getting token java.util.concurrent.ExecutionException: com.google.firebase.internal.api.FirebaseNoSignedInUserException: Please sign in before trying to get a token.
E/StorageException: StorageException has occurred.
User does not have permission to access this object.
Code: -13021 HttpResult: 403
Could not open resulting stream.
java.io.IOException: Could not open resulting stream.
The code I used in order to sign in Anonymously :
private void signInAnonymously() {
mAuth.signInAnonymously().addOnSuccessListener(this, new OnSuccessListener<AuthResult>() {
#Override
public void onSuccess(AuthResult authResult) {
Log.e("TAG", "success sign");
// do your stuff
}
})
.addOnFailureListener(this, new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception exception) {
Log.e("TAG", "failed sign");
}
});
}
#Override
protected void onStart() {
super.onStart();
sign();
}
private void sign() {
mAuth = FirebaseAuth.getInstance();
FirebaseUser user = mAuth.getCurrentUser();
if (user != null) {
Log.e("TAG", "already sign");
} else {
signInAnonymously();
Log.e("TAG", "sign null");
}
//loadIntAdd();
}
The code I used to download selected image :
StorageReference sr = mStorageRef.child("images/img" +selected_img_num + ".png");
sr.getBytes(1024 * 1024).addOnSuccessListener(new OnSuccessListener<byte[]>() {
#Override
public void onSuccess(byte[] bytes) {
//I save the image here
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Toast.makeText(final_treatement.this, R.string.oper_failed, Toast.LENGTH_LONG).show();
finish();
}
});
My dependencies :
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support:cardview-v7:27.1.1'
implementation 'com.android.support:recyclerview-v7:27.1.1'
implementation 'com.google.firebase:firebase-database:16.0.1'
implementation 'com.google.firebase:firebase-core:16.0.3'
implementation 'com.squareup.picasso:picasso:2.71828'
implementation 'com.firebaseui:firebase-ui-database:0.4.0'
implementation 'com.google.android.gms:play-services-ads:15.0.1'
implementation 'com.google.firebase:firebase-storage:16.0.2'
implementation 'com.google.firebase:firebase-auth:16.0.3'
}
apply plugin: 'com.google.gms.google-services'
My manifest file :
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<application
android:allowBackup="true"
android:hardwareAccelerated="true"
android:icon="#drawable/appicon"
android:label="#string/app_name"
android:largeHeap="true"
android:roundIcon="#drawable/appicon"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".choose_image">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".final_treatement"/>
note that i've activated anonymous sign in in firebase console
I don't know why it worked before and failed here
Please reply if any thing is missing
The problem might not be in your code, make sure you have turned on anonymous sign in from the firebase console
Steps :
Go to Authentication in firebase console.
Select Tab Sign-in Methods
Enable the last option "Anonymous"
You are all set :)
You are getting that error because you are trying to access a file while you are not authenticated yet. Because the Firebase Storage security rules are set to allow only authenticated users, Firebase servers deny your attempt. To solve this, make sure you are first authenticated and then get the images you want. Remember, onCreate() method is called before onStart(), according to the life-cycle of the activity.

Android app with Retrofit2 Crashes without error messages

I'm developing an android application which needs to consume rest apis deployed, for now, on an heroku instance...since i got crashes anytime i try to integrate retrofit as http client I've done the following:
Added retrofit2 as dependendcy (didn't choose the latest version in order to avoid potential maturity problems)
compile 'com.squareup.retrofit2:retrofit:2.0.0'
compile 'com.squareup.retrofit2:converter-gson:2.0.0'
compile 'com.google.code.gson:gson:2.6.2'
I coded an example android app, just to check if something has been done wrong in my original app, using http://httpbin.org/ip
public interface HttpBinService
{
public static class Response
{
private String origin;
public Response(String origin) {
this.origin = origin;
}
public String getOrigin() {
return origin;
}
public void setOrigin(String origin) {
this.origin = origin;
}
}
#GET("ip")
public Call<Response> getIp ();
}
And the main activity
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Retrofit setup
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://httpbin.org")
.addConverterFactory(GsonConverterFactory.create())
.build();
// Service setup
HttpBinService service = retrofit.create(HttpBinService.class);
try {
Call<HttpBinService.Response> call = service.getIp();
Response<HttpBinService.Response> res = call.execute();
if (res.isSuccessful()){
Log.i("PROVARETROFIT", "OK");
((TextView)findViewById(R.id.testo)).setText(res.body().getOrigin());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
I did not forget to ask for internet permissions in my manifest
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="it.spich.provaretrofit">
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
But all i got is that application closes unexpectedly. If t execute it in debug, it executes correctly all steps until call execution. LogCat appears to not say anything useful
04-15 09:48:08.281 6491-6491/? I/art: Late-enabling -Xcheck:jni
04-15 09:48:08.367 6491-6491/? W/System: ClassLoader referenced unknown path: /data/app/it.spich.provaretrofit-1/lib/arm64
04-15 09:48:08.387 6491-6491/it.spich.provaretrofit I/HwCust: Constructor found for class android.app.HwCustHwWallpaperManagerImpl
04-15 09:48:08.577 6491-6491/? I/Process: Sending signal. PID: 6491 SIG: 9
Has someone idea of what is happening there?
Not sure why logcat doesn't show anything useful. But when executing it, it is giving an android.os.NetworkOnMainThreadException, which can explain the problem. So as they say in the comments: try using the enque method with a callback, this also allows you to get rid of the try/catch statement. Try to replace the code after // Service setup in your MainActivity with:
HttpBinService service = retrofit.create(HttpBinService.class);
Call<HttpBinService.Response> call = service.getIp();
call.enqueue(new Callback<HttpBinService.Response>() {
#Override
public void onResponse(Call<HttpBinService.Response> call, Response<HttpBinService.Response> response) {
if (response.isSuccessful()){
Log.i("PROVARETROFIT", "OK");
//((TextView)findViewById(R.id.testo)).setText(res.body().getOrigin());
System.out.println(response.body().getOrigin());
} else {
System.out.println("Unsuccesfull");
}
}
#Override
public void onFailure(Call<HttpBinService.Response> call, Throwable t) {
System.out.println("Call failed");
}
});

Android metadata with manifest file - nullPointerException

I am attempting to access metadata for an activity from the manifest file.
The manifest looks like this :
<activity
android:name="co.uk.benbun.nvrrclubapp.MainActivity"
android:label="#string/app_name" >
<meta-data android:value="newstag" android:name="NEWS"></meta-data>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
The code to access it, looks like this :
try {
ApplicationInfo ai = getPackageManager().getApplicationInfo(this.getPackageName(), PackageManager.GET_META_DATA);
Bundle bundle = ai.metaData;
String myApiKey = bundle.getString("NEWS");
} catch (NameNotFoundException e) {
Log.e("metadata", "Failed to load meta-data, NameNotFound: " + e.getMessage());
feedTag = "NEWS";
} catch (NullPointerException e) {
Log.e("metadata", "Failed to load meta-data, NullPointer: " + e.getMessage());
feedTag = "NEWS";
}
When I execute the code I always get the nullPointerException.
What am I doing wrong?
The documentation for PackageManager.GET_META_DATA says:
ComponentInfo flag: return the metaData data Bundles that are
associated with a component. This applies for any API returning a
ComponentInfo subclass.
You are calling getApplicationInfo() which returns an ApplicationInfo object. ApplicationInfo does not inherit from ComponentInfo, so this object will not have any meta-data. You need to get the ActivityInfo for your MainActivity in order to get the meta-data (ActivityInfo is a subclass of ComponentInfo).

CallBack after Twitter authentication

I'm trying to integrate twitter to my app, but I can't seem to get it to work.
This is my code:
public class OAuthForTwitter extends Activity {
private CommonsHttpOAuthConsumer httpOauthConsumer;
private OAuthProvider httpOauthprovider;
public final static String consumerKey = "{removed}";
public final static String consumerSecret = "{removed}";
private final String CALLBACKURL = "sosInternational:///HierBenIkNu";
private Twitter twitter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
doOAuth();
}
/**
* Opens the browser using signpost jar with application specific
* consumerkey and consumerSecret.
*/
private void doOAuth() {
try {
httpOauthConsumer = new CommonsHttpOAuthConsumer(consumerKey, consumerSecret);
httpOauthprovider = new DefaultOAuthProvider(
"http://twitter.com/oauth/request_token",
"http://twitter.com/oauth/access_token",
"http://twitter.com/oauth/authorize");
String authUrl = httpOauthprovider.retrieveRequestToken(httpOauthConsumer, CALLBACKURL);
this.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(authUrl)));
} catch (Exception e) {
Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
}
}
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Uri uri = intent.getData();
if (uri != null && uri.toString().startsWith(CALLBACKURL)) {
String verifier = uri
.getQueryParameter(oauth.signpost.OAuth.OAUTH_VERIFIER);
try {
// this will populate token and token_secret in consumer
httpOauthprovider.retrieveAccessToken(httpOauthConsumer,
verifier);
// TODO: you might want to store token and token_secret in you
// app settings!!!!!!!!
AccessToken a = new AccessToken(httpOauthConsumer.getToken(),
httpOauthConsumer.getTokenSecret());
// initialize Twitter4J
twitter = new TwitterFactory().getInstance();
twitter.setOAuthConsumer(consumerKey, consumerSecret);
twitter.setOAuthAccessToken(a);
// create a tweet
Date d = new Date(System.currentTimeMillis());
String tweet = "#OAuth working! " + d.toLocaleString();
// send the tweet
twitter.updateStatus(tweet);
} catch (Exception e) {
Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
}
}
}
}
When I'm done authenticating on the Twitter site, it should redirect me back to the app.
But instead, I get this Page not found:
I have this in my AndroidManifest:
<intent-filter>
<action android:name="android.intent.action.VIEW"></action>
<category android:name="android.intent.category.DEFAULT"></category>
<category android:name="android.intent.category.BROWSABLE"></category>
<data android:scheme="sosInternational" android:host="HierBenIkNu"></data>
</intent-filter>
How can I go back to my app with the keys i get back?
Ok, it was quite a dumb mistake.
My <intent-filter> wasn't inside an application..
This is how it is now:
<activity
android:name=".OAuthForTwitter"
android:label="#string/app_name"
android:configChanges="orientation|keyboardHidden"
android:launchMode="singleInstance">
<intent-filter>
<action android:name="android.intent.action.VIEW"></action>
<category android:name="android.intent.category.DEFAULT"></category>
<category android:name="android.intent.category.BROWSABLE"></category>
<data android:scheme="sosInternational" android:host="OAuthForTwitter"></data>
</intent-filter>
</activity>
This kind off works, it just loads the whole app from start.
Isn't there a way to just 'go back' to the last activity without restarting the whole app?
I have solved this. Not exactly the way you have developed, but a slight different way. Here are the steps describing what i did.
Use webview instead of opening it in web browser: One of the key advantage doing it is , you can track the url redirects.
call setWebViewClient method of your webview and override shouldOverrideUrlLoading method of your class, i have used inner class.
You will have url parameter in your method. Check whether it starts with your own call back url or not, (Note: This url contains User Token and user secret that is necessary for authorization).
After you finish your task, you can hide the activity or remove the webView or any thing you desire.
EDIT : This is the oAuth way usually used in web application, so we don't need xAuth way. (In case other community members don't know)
Hope it will help you.
Your callback URL should be "sosInternational://HierBenIkNu" (instead of "sosInternational:///HierBenIkNu") in the Java code.
private final String CALLBACKURL = "sosInternational://HierBenIkNu";

Categories