Threads at onCreate() method execute before setting the view - java

I'm creating my splash screen for app. While loading it executes 4 methods. First one checks if Internet permission is granted, second one sends request to API to check if it is Online, third one is getting Token from Firebase and the fourth one is checking if user is already logged-in. I'm doing it using 4 threads. Each method in case of error sets the flag as false. Then when all the threads end their work (I used .join()) The last method checks the state of flag and launch new activity or just display Error and try everything once again.
The problem I have is that I'm getting the view after all the threads finish their work. For example I have black screen, then message ("Error occured") and only after that I can see UI. But on Error the UI is refreshed, so one more time I have black screen, then result and UI for 1sec until another restart.
My question is, can I in some way stop these Threads until my UI is ready ?
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
setContentView(R.layout.activity_splash);
checkProgress = findViewById(R.id.checkProgressText);
auth = FirebaseAuth.getInstance();
tokenUtils = new TokenUtils();
requestQueue = Volley.newRequestQueue(getApplicationContext());
animatedCircleLoadingView = findViewById(R.id.circle_loading_view);
//starting the animation
startLoading();
Thread[] checkers = new Thread[4];
checkers[0] = new Thread(this::checkInternetPermissions);
checkers[1] = new Thread(this::checkConnection);
checkers[2] = new Thread(this::getUserAuth);
checkers[3] = new Thread(this::getUserToken);
for (Thread t : checkers) {
try {
t.start();
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
changeActivity();
}
Check internet permission method:
private void checkInternetPermissions() {
checkProgress.setText(getString(R.string.check_internet_permissions_text));
if (ContextCompat.checkSelfPermission(this, Manifest.permission.INTERNET)
!= PackageManager.PERMISSION_GRANTED)
requestPermissions(new String[]{Manifest.permission.INTERNET}, 1);
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
if (requestCode != 1) {
connectionFlag = false;
}
}
Check connection method:
private void checkConnection() {
checkProgress.setText(getString(R.string.checking_api_connection));
RequestFuture<String> requestFuture = RequestFuture.newFuture();
StringRequest request = new StringRequest
(Request.Method.GET, API_CHECK,
requestFuture,
requestFuture);
requestQueue.add(request);
String response = null;
try {
response = requestFuture.get(5, TimeUnit.SECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
this.connectionFlag = false;
}
if (!Objects.equals(response, "ok"))
this.connectionFlag = false;
}
Get user token method:
private void getUserToken() {
checkProgress.setText(getString(R.string.getting_user_auth_token));
String token = null;
try {
token = tokenUtils.getFirebaseToken();
} catch (ExecutionException | InterruptedException e) {
this.connectionFlag = false;
}
if (Objects.isNull(token) || Objects.requireNonNull(token).isEmpty())
this.connectionFlag = false;
}
And finally get user auth method:
private void getUserAuth() {
checkProgress.setText(getString(R.string.checking_user_auth));
authStateListener = firebaseAuth -> {
firebaseUser = firebaseAuth.getCurrentUser();
if (Objects.isNull(firebaseUser) || Objects.requireNonNull(firebaseUser.getEmail()).isEmpty()) {
this.authFlag = false;
}
};
}
Last method which handle the states of flags:
private void changeActivity() {
checkProgress.setText(getString(R.string.finalizing_text_progress));
if (connectionFlag && authFlag) {
startActivity(new Intent(SplashActivity.this, MapActivity.class));
} else if (!connectionFlag) {
Toast.makeText(getApplicationContext(), "Error occurred.", Toast.LENGTH_LONG).show();
finish();
startActivity(getIntent());
} else {
startActivity(new Intent(SplashActivity.this, LoginActivity.class));
}
}

Yes, You can try it with handler thread with some delay then it will work fine or you can start your thread on onResume() method at the time of onResume your view will have been created

I think, your way wrong. Because, API request working on asynchronous. Your app should run like this;
Check Internet connection.
API Request.
Get token in API Request onSuccess method.
Get User Auth.
I think, you shouldn't use Thread.

Related

Getting android view for static class

Ive been trying to get a class in my android app to be able to post snackbars. The class is a manager for a bluetooth connection, and ive needed to make it static in my main activity to achieve this. For this reason i cant send android context classes to it or store within in, making me unable to get the view needed to make a snackbar. All throughout the bluetooth service class i use the method runOnUiThread(() -> snackbarMsg to try to show snackbars. It used to work when i sent view as a parameter of the constructor, but only for the first time the main screen showed, if i switched activity and back it would stop working, and this also caused a memory leak. Any other way to solve this? Any help is appreciated.
Currently code looks like this in main activity:
public class MainActivity extends AppCompatActivity implements AdapterView.OnItemSelectedListener{
//Initialise some static variables needed across the whole program
private static BluetoothService bluetoothService;
private static final String TAG = MainActivity.class.getSimpleName();
protected static ArrayList<LocationData> locations = new ArrayList<>();
protected static List<String> categories = new ArrayList<>();
Spinner spinner;
private static boolean doneFirstRun = false;
protected static LocationData selectedLocation = new LocationData();
MainActivity instance = this;
public MainActivity() {
}
/*
TODO: view in bluetooth service causes memory leak
*/
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try{
if (!doneFirstRun){
//Getting storage
readFromFile(MainActivity.this);
//adding example location
if (locations.size()==0){
LocationData spaceRay = new LocationData();
spaceRay.setName("SpaceRay");
spaceRay.setLatitude(59.40384);
spaceRay.setLongitude(17.95228);
spaceRay.setInclination(-85);
spaceRay.setDirection(200);
spaceRay.setAltitude(99990);
locations.add(spaceRay);
}
//Checking and asking for relevant permissions
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
#SuppressLint("InlinedApi") String[] permissions = {Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_CONNECT, Manifest.permission.BLUETOOTH_SCAN};
ActivityCompat.requestPermissions(instance, permissions, 1);
}
bluetoothService = new BluetoothService();
//starting bluetooth networking activity on new thread
startBluetoothService();
doneFirstRun = true;
}
} catch (Exception exception){
Log.e(TAG, "Failed to do first run through of code: ", exception);
}
...
protected void startBluetoothService(){
try {
Log.i(TAG, "New thread started");
bluetoothService.run(MainActivity.this);
} catch (Exception e) {
Log.e(TAG, "Bluetooth service failed: ", e);
snackbarMsg("Bluetooth service failed");
}
}
and like this is bluetooth_service class:
public class BluetoothService extends AppCompatActivity{
private static final String TAG = BluetoothService.class.getSimpleName();
private OutputStream outputStream;
private InputStream inputStream;
private BluetoothSocket socket;
private boolean writing = false;
public BluetoothService(){
}//constructor
...
//method to show snackbar message at the bottom of the screen
public Runnable snackbarMsg (String msg){
try {
View view = findViewById(R.id.btn_connect);
Snackbar snackbar = Snackbar.make(view, msg, BaseTransientBottomBar.LENGTH_SHORT);
snackbar.show();
} catch (Exception exception){
Log.e(TAG, "Could not show snackbar", exception);
}
return null;
}
Error message looks like this:
java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.pm.ApplicationInfo android.content.Context.getApplicationInfo()' on a null object reference
at android.content.ContextWrapper.getApplicationInfo(ContextWrapper.java:190)
at android.view.ContextThemeWrapper.getTheme(ContextThemeWrapper.java:174)
at android.content.Context.obtainStyledAttributes(Context.java:809)
at androidx.appcompat.app.AppCompatDelegateImpl.createSubDecor(AppCompatDelegateImpl.java:852)
at androidx.appcompat.app.AppCompatDelegateImpl.ensureSubDecor(AppCompatDelegateImpl.java:819)
at androidx.appcompat.app.AppCompatDelegateImpl.findViewById(AppCompatDelegateImpl.java:640)
at androidx.appcompat.app.AppCompatActivity.findViewById(AppCompatActivity.java:261)
at antennalocator.util.BluetoothService.snackbarMsg(BluetoothService.java:204)
at antennalocator.util.BluetoothService.lambda$write$4$antennalocator-util-BluetoothService(BluetoothService.java:159)
at antennalocator.util.BluetoothService$$ExternalSyntheticLambda5.run(Unknown Source:4)
at java.lang.Thread.run(Thread.java:1012)
Managed to get around this issue. The way i solved this was by sending the view as a parameter in each method that needed to post snackbars: for example the write method:
public void write(String s, View view) {
if (!writing){
new Thread(() -> {
try {
writing = true;
outputStream.write(s.getBytes());
runOnUiThread(snackbarMsg("Sent data", view));
lockout(3000);
writing = false;
} catch (Exception exception) {
writing = false;
Log.e(TAG, "Error occurred when sending data, restarting bluetooth service", exception);
runOnUiThread(() -> snackbarMsg("Could not send data, restarting bluetooth service", view));
disconnect();
}
}).start();
} else{
runOnUiThread(snackbarMsg("Please wait a bit before sending data", view));
}
}
in main:
bluetoothService.write(ConvertToString(selectedLocation), findViewById(R.id.btn_connect));

How to handle the Usb Permission Dialog Event on android when using the "device filter" solution?

I'm trying to handle the event when a user presses "ok" or "cancel" on the automatic permission dialog presented when I connect a "known" USB device to the android phone.
I'm using the android.usb.host library and can send and receive between the android phone and the device. Futhermore do I handle the "USB_DEVICE_ATTACHED" and "USB_DEVICE_DETACHED" using a BroadcastReceiver without any problems.
I want to enable a sort of "autoconnect" feature and therefore I need to know when the user has pressed "ok" in the automatically displayed permission dialog, but I can't find anything online at all. All I find is "bypass dialog", but this is not what I want or need.
When I connect the usb device to the android phone, a permission dialog is automatically displayed because I use the "device_filter.xml" solution from androids documentation which can be seen here Android Usb Docs.
This is how I handle the USB_DEVICE_ATTATCHED and USB_DEVICE_DETACHED events:
public NativeUsbService(ReactApplicationContext reactContext) {
...
// register device attached/detached event listeners
IntentFilter filter = new IntentFilter();
filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
reactContext.registerReceiver(usbReceiver, filter);
...
}
And then the Broadcast Receiver:
private final BroadcastReceiver usbReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
synchronized (this) {
UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if(device != null){
usbDevice = device;
} else {
Log.d(TAG, "onReceive: DEVICE WAS ATTACHED AND WAS NULL :(");
}
}
} else if (action.equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) {
Log.d(TAG, "onReceive: Device was detached!");
if(connection != null) {
connection.releaseInterface(usbDeviceInterface);
connection.close();
}
connection = null;
usbDevice = null;
endpointIn = null;
endpointOut = null;
}
}
};
I have tried multiple different approaches, but nothing has worked.
I have tried getting the user response in from the intent, like with a manual permission request like below:
private final BroadcastReceiver usbReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
synchronized (this) {
UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if(device != null){
usbDevice = device;
// THIS DOES NOT WORK ↓↓↓
if(intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
// The code never gets here...
}
} else {
Log.d(TAG, "onReceive: DEVICE WAS ATTACHED AND WAS NULL :(");
sendEvent("onDeviceAttached", false);
}
}
} else if (action.equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) {
if(connection != null) {
connection.releaseInterface(usbDeviceInterface);
connection.close();
}
connection = null;
usbDevice = null;
endpointIn = null;
endpointOut = null;
}
}
};
I have also tried by adding a usb permission listener to the broadcast receiver by first adding the action name to my class variables:
private static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION";
Then adding the action to my intent filter like so:
public NativeUsbService(ReactApplicationContext reactContext) {
// register device attached/detached event listeners
IntentFilter filter = new IntentFilter();
filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
filter.addAction(ACTION_USB_PERMISSION); // added action to my intent filter
reactContext.registerReceiver(usbReceiver, filter);
}
And finally reacting to the action like so:
private final BroadcastReceiver usbReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
synchronized (this) {
UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if(device != null){
usbDevice = device;
}
}
} else if (action.equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) {
Log.d(TAG, "onReceive: Device was detached!");
if(connection != null) {
connection.releaseInterface(usbDeviceInterface);
connection.close();
}
connection = null;
usbDevice = null;
endpointIn = null;
endpointOut = null;
sendEvent("onDeviceDetached", true);
}
else if (action.equals(ACTION_USB_PERMISSION)) {
Log.d(TAG, "onReceive: ACTION_USB_PERMISSION");
if(intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
Log.d(TAG, "onReceive: EXTRA_PERMISSION_GRANTED = true");
} else Log.d(TAG, "onReceive: EXTRA_PERMISSION_GRANTED = false");
}
}
};
Please make me aware of any missing information.
Any help is greatly appreciated.
Answering my own question in case someone else is facing the same issue.
I though about manually requesting the permission again, after permission was granted, since it is possible to handle this manual permission request when user presses an option in the dialog. I discarded this idea, not because it wouldn't work, but because I saw it as unecessary for the user to also have to press another dialog after the initial (automatic dialog).
I must add that I have not implemented this solution, so I do not know with certainty that it would prompt the user again, but I have had trouble with the manual permission request previously. If you want to try this approach the method belongs to the UsbManager class and is invoke like so usbManger.requestPermission(usbDevice).
I ended up with a solution where I start a thread which runs a loop calling usbManager.hasPermission(usbDevice) until it has permission and then emits an event (emitting this event is my use case, implement it how you like).
The solution can be seen here:
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbManager;
import com.facebook.react.bridge.ReactApplicationContext;
...
private static volatile boolean permissionThreadShouldStop = false;
private static Thread activePermissionThread = null;
...
public static void usbPermissionEventEmitter(ReactApplicationContext reactContext, UsbManager usbManager, UsbDevice usbDevice) {
if((activePermissionThread != null && activePermissionThread.isAlive())) {
activePermissionThread.interrupt();
}
permissionThreadShouldStop = false;
activePermissionThread = new Thread(new Runnable() {
#Override
public void run() {
while (!usbManager.hasPermission(usbDevice) && !permissionThreadShouldStop) {
try {
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
break;
}
}
if(usbManager.hasPermission(usbDevice)) {
sendEvent(reactContext, "onUsbPermissionGranted", true);
}
}
});
activePermissionThread.start();
}
The ReactApplicationContext can be swapped with the normal android context. But this is for a react native module, so I use the reactContext.
I hope this will be helpful for someone, because i'm honestly really surpriced how scarse the android documentation is in regards to implementing Usb functionality using the android.hardware.usb library.
Also in general when searching for information online, I have often found myself lost since there is very little information on this subject.

Alarm ringing stops when cleared from ram

I am making an alarm clock which asks user to do a particular work in order to close the alarm when it rings. It is working fine but the problem is that if the user closes the alarm app from the recent activities while the alarm is ringing, the alarm stops ringing. I want that even if the user clears the app while its ringing, it should not stop ringing. It should only stop once the task given is completed. How can I implement this?
Edit #1: Activity that is called when alarm rings
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.v(LOG_TAG, "in AlarmAlert");
unlockScreen();
setContentView(R.layout.activity_alarm_alert);
Bundle bundle = this.getIntent().getExtras();
alarm = (Alarm) bundle.getSerializable("alarm");
alarmDatabase = new AlarmDatabase(this);
//Uri uri = alarm.getRingtonePath();
question = (TextView)findViewById(R.id.question);
answer = (TextView) findViewById(R.id.answer);
oldColors = answer.getTextColors();
diff = alarm.getDifficulty().toString();
questionString = GenerateMathsQuestion.generateQuestion(diff);
question.setText(questionString);
actualAnswer = EvaluateString.evaluate(questionString);
AudioManager am = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
int result = am.requestAudioFocus(focusChangeListener,
AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
mediaPlayer = new MediaPlayer();
mediaPlayer.setVolume(1.0f, 1.0f);
mediaPlayer.setLooping(true);
mediaPlayer.setAudioStreamType(AudioManager.STREAM_ALARM);
try {
mediaPlayer.setDataSource(this, Uri.parse(alarm.getRingtonePath()));
mediaPlayer.prepare();
} catch (IOException e) {
e.printStackTrace();
}
mediaPlayer.start();
}
if(alarm.getIsVibrate()) {
vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
long[] pattern = {1000, 200, 200, 200};
vibrator.vibrate(pattern, 0);
}
}
public void closeAlarm(){
Log.v(LOG_TAG, "will now stop");
mediaPlayer.stop();
if(vibrator!=null)
vibrator.cancel();
Log.v(LOG_TAG, "will now release");
mediaPlayer.release();
Log.v(LOG_TAG, "id of ringing alarm: " + alarm.getAlarmId());
alarm.setIsActive(false);
alarmDatabase.updateData(alarm);
cursor = alarmDatabase.sortQuery();
while(cursor.moveToNext()){
int id = cursor.getInt(cursor.getColumnIndex(AlarmDatabase.COLUMN_UID));
currentAlarm = alarmDatabase.getAlarm(id);
Log.v(LOG_TAG, "id of next alarm " + id);
if(currentAlarm != null) {
if (currentAlarm.getIsActive() == true) {
currentAlarm.scheduleAlarm(this, true);
break;
}
}
}
this.finish();
}
You should use Services. Take a look at it, that is what you want it. Generally you can make it to run an operation, and a service wont return any result. But it runs indefinitely even when you kill the app from task manager or free RAM.
I suggest this tutorial for reading about services.
UPDATE
Implement your activity with the service in the following way so it can talk with the layout and stops the alarm when required.
public class HelloService extends Service {
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
// Handler that receives messages from the thread
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
#Override
public void handleMessage(Message msg) {
// Normally we would do some work here, like download a file.
// For our sample, we just sleep for 5 seconds.
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// Restore interrupt status.
Thread.currentThread().interrupt();
}
// Stop the service using the startId, so that we don't stop
// the service in the middle of handling another job
stopSelf(msg.arg1);
}
}
#Override
public void onCreate() {
// Start up the thread running the service. Note that we create a
// separate thread because the service normally runs in the process's
// main thread, which we don't want to block. We also make it
// background priority so CPU-intensive work will not disrupt our UI.
HandlerThread thread = new HandlerThread("ServiceStartArguments",
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
// Get the HandlerThread's Looper and use it for our Handler
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
// For each start request, send a message to start a job and deliver the
// start ID so we know which request we're stopping when we finish the job
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
mServiceHandler.sendMessage(msg);
// If we get killed, after returning from here, restart
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
// We don't provide binding, so return null
return null;
}
#Override
public void onDestroy() {
Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
}
}

Google Cloud Messaging - Check if device is already registered

I just set up GCM in my Android App. But I have the problem that I don't know how to check if the device is already registered. I work with the new google play services library.
The register part looks like this:
#Override
protected String doInBackground(String... arg0) {
String msg = "";
try {
if (gcm == null) {
gcm = GoogleCloudMessaging.getInstance(context_app);
}
regid = gcm.register(SENDER_ID);
msg = "Dvice registered, registration ID=" + regid;
Log.d("111", msg);
sendRegistrationIdToBackend(regid);
} catch (IOException ex) {
msg = "Error :" + ex.getMessage();
}
return msg;
}
How can I modify this that it checks if the device is already registered?
Store the registration id in a databade table or shared preference and when app starting..check whether it is null or not
Google has provided very clear documentation with code.You should use following code:
// Make sure the device has the proper dependencies.
GCMRegistrar.checkDevice(this);
// Make sure the manifest was properly set - comment out this line
// while developing the app, then uncomment it when it's ready.
GCMRegistrar.checkManifest(this);
registerReceiver(mHandleMessageReceiver,
new IntentFilter(DISPLAY_MESSAGE_ACTION));
final String regId = GCMRegistrar.getRegistrationId(this);
if (regId.equals("")) {
// Automatically registers application on startup.
GCMRegistrar.register(this, SENDER_ID);
} else {
// Device is already registered on GCM, check server.
if (GCMRegistrar.isRegisteredOnServer(this)) {
// Skips registration.
mDisplay.append(getString(R.string.already_registered) + "\n");
} else {
// Try to register again, but not in the UI thread.
// It's also necessary to cancel the thread onDestroy(),
// hence the use of AsyncTask instead of a raw thread.
final Context context = this;
mRegisterTask = new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
boolean registered =
ServerUtilities.register(context, regId);
// At this point all attempts to register with the app
// server failed, so we need to unregister the device
// from GCM - the app will try to register again when
// it is restarted. Note that GCM will send an
// unregistered callback upon completion, but
// GCMIntentService.onUnregistered() will ignore it.
if (!registered) {
GCMRegistrar.unregister(context);
}
return null;
}
#Override
protected void onPostExecute(Void result) {
mRegisterTask = null;
}
};
mRegisterTask.execute(null, null, null);
}
}
#Override
protected void onDestroy() {
if (mRegisterTask != null) {
mRegisterTask.cancel(true);
}
unregisterReceiver(mHandleMessageReceiver);
GCMRegistrar.onDestroy(this);
super.onDestroy();
}
private final BroadcastReceiver mHandleMessageReceiver =
new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String newMessage = intent.getExtras().getString(EXTRA_MESSAGE);
mDisplay.append(newMessage + "\n");
}
};
when you get registration Id, Store it in SharedPreferences, for example:
SharedPreferences shp = context.getSharedPreferences("anyNameYouLike",MODE_PRIVATE);
SharedPreferences.Editor editor=shp.edit();
editor.putString("RegID",registrationID).commit;
In the next time before you register check the "anyNameYouLike" if it contain field called RegID Like this:
private boolean isRegistered(Context context){
SharedPreferences shp = context.getSharedPreferences("anyNameYouLike",PRIVATE_MODE);
return shp.contains("RegID");
}

Getting a Malformed access token "t ​ype":"OAuthException","code":190

I am writing an android application to get the Facebook user albums and photos and display in my Android application.
I have created a Facebook App with APP_ID 281846961912565.
While creating the Facebook instance, I am passing this id as follows
facebook = new Facebook(APP_ID);
Using this instance, I am able to login to my FB account post on messages on facebook wall programatically.
After logging in, I get an access_token.
I'm using the access token to get the album ids using facebook.request("https://graph.facebook.com/me/albums?access_token="+facebook.getAccessToken());
Now I get {"error":{"message":"Malformed access token ACCESSTOKENACCESSTOKEN?access_token=ACCESSTOKENACCESSTOKEN","t‌​ype":"OAuthException","code":190}}
Can any of you please help me resolve this issue and point out what i am doing wrong.
My code is as follows:
private static final String[] PERMISSIONS = new String[] { "publish_stream","user_photos" };
public boolean saveCredentials(Facebook facebook) {
Editor editor = getApplicationContext().getSharedPreferences(KEY,
Context.MODE_PRIVATE).edit();
editor.putString(TOKEN, facebook.getAccessToken());
editor.putLong(EXPIRES, facebook.getAccessExpires());
return editor.commit();
}
public boolean restoreCredentials(Facebook facebook) {
SharedPreferences sharedPreferences = getApplicationContext()
.getSharedPreferences(KEY, Context.MODE_PRIVATE);
facebook.setAccessToken(sharedPreferences.getString(TOKEN, null));
facebook.setAccessExpires(sharedPreferences.getLong(EXPIRES, 0));
return facebook.isSessionValid();
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
facebook = new Facebook(APP_ID);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.facebook_dialog);
String facebookMessage = getIntent().getStringExtra("facebookMessage");
if (facebookMessage == null) {
facebookMessage = "Test wall post";
}
messageToPost = facebookMessage;
}
R.layout.facebook_dialog is the dialog which pops up asking if a message should be shared on facebook or not. If yes the following method is called.
public void share(View button) {
if (!facebook.isSessionValid()) {
loginAndPostToWall();
} else {
postToWall(messageToPost);
}
}
public void loginAndPostToWall() {
facebook.authorize(this, PERMISSIONS, Facebook.FORCE_DIALOG_AUTH,
new LoginDialogListener());
}
class LoginDialogListener implements DialogListener {
public void onComplete(Bundle values) {
saveCredentials(facebook);
if (messageToPost != null) {
postToWall(messageToPost);
}
}
public void onFacebookError(FacebookError error) {
showToast("Authentication with Facebook failed!");
finish();
}
public void onError(DialogError error) {
showToast("Authentication with Facebook failed!");
finish();
}
public void onCancel() {
showToast("Authentication with Facebook cancelled!");
finish();
}
}
public void postToWall(String message) {
Bundle parameters = new Bundle();
parameters.putString("message", message);
parameters.putString("description", "topic share");
try {
facebook.request("me");
String response = facebook.request("me/feed", parameters, "POST");
Log.d("Tests", "got response: " + response);
if (response == null || response.equals("")
|| response.equals("false")) {
showToast("Blank response.");
} else {
showToast("Message posted to your facebook wall!");
}
getImagesFromUserAlbum();
finish();
} catch (Exception e) {
showToast("Failed to post to wall!");
e.printStackTrace();
finish();
}
}
Later when I do a `private void getImagesFromUserAlbum() {
facebook.getAccessToken();
JSONArray albumss = null;
String response = null;
try {
response = facebook.request("me/albums");
// `
I get the error
{"error":{"message":"Malformed access token ACCESSTOKEN?access_token=ACCESSTOKEN","type":"OAuthException","code":190}}
Thanks for your help.
The code above is now the working copy. Thanks to Bartek.
If you look at the Errors page in the documentation you will see that when you get error 190 you should authorise/reauthorise the user.
I suspect that this happened to you because you first logged in, then added the permissions to access the albums to your application BUT did not log out and log back in. Hence, you need to obtain a new access token which will grant the new permissions to your application.
Please check is there &expires in your access token if yes then remove it because it is not part of access_token and try after that.

Categories