-1

I know this is a question already asked but I am having difficulty understanding what is wrong with my code as I have not used any timers, AsyncTasks or other handlers. I am using Volley to make requests to my web service that's it. The app runs on all emulators but once I try to run it on a device I get this error

E/AndroidRuntime: FATAL EXCEPTION: AcquireTokenRequestHandlerThread
                  Process: com.microsoft.graph.helpdesk, PID: 18915
                  android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
                      at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:7146)
                      at android.view.ViewRootImpl.invalidateChildInParent(ViewRootImpl.java:1033)
                      at android.view.ViewGroup.invalidateChild(ViewGroup.java:4971)
                      at android.view.View.invalidateInternal(View.java:12704)
                      at android.view.View.invalidate(View.java:12668)
                      at android.view.View.setFlags(View.java:10690)
                      at android.view.View.setVisibility(View.java:7136)
                      at com.microsoft.graph.helpdesk.SignInActivity.onSuccess(SignInActivity.java:73)
                      at com.microsoft.graph.helpdesk.SignInActivity.onSuccess(SignInActivity.java:35)
                      at com.microsoft.graph.auth.AuthenticationManager$2.onSuccess(AuthenticationManager.java:118)
                      at com.microsoft.graph.auth.AuthenticationManager$2.onSuccess(AuthenticationManager.java:115)
                      at com.microsoft.aad.adal.AcquireTokenRequest$CallbackHandler$2.run(AcquireTokenRequest.java:904)
                      at android.os.Handler.handleCallback(Handler.java:739)
                      at android.os.Handler.dispatchMessage(Handler.java:95)
                      at android.os.Looper.loop(Looper.java:145)
                      at android.os.HandlerThread.run(HandlerThread.java:61)

The app starts and allows me to log in but then crashes immediately with the error above.

This is the code for my sign in activity:

public class SignInActivity
        extends BaseActivity
        implements AuthenticationCallback<AuthenticationResult> {

    private FirebaseAnalytics mFirebaseAnalytics;

    @InjectView(layout_diagnostics)
    protected View mDiagnosticsLayout;

    @InjectView(view_diagnosticsdata)
    protected TextView mDiagnosticsTxt;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(activity_signin);
        onSignInO365Clicked();

        setTitle(R.string.app_name);

        // Obtain the FirebaseAnalytics instance.
        mFirebaseAnalytics = FirebaseAnalytics.getInstance(this);

        ButterKnife.inject(this);
    }

    @OnClick(o365_signin)
    public void onSignInO365Clicked() {
        try {
            authenticate();
        } catch (IllegalArgumentException e) {
            warnBadClient();
        }
    }

    @Override
    public void onSuccess(AuthenticationResult authenticationResult) {
        // reset anything that may have gone wrong...
        mDiagnosticsLayout.setVisibility(INVISIBLE);
        mDiagnosticsTxt.setText("");

        // get rid of this Activity so that users can't 'back' into it
        finish();

        // save our auth token to use later
        SharedPrefsUtil.persistAuthToken(authenticationResult);

        // get the user display name
        final String userDisplayableId =
                authenticationResult
                        .getUserInfo()
                        .getDisplayableId();

        // get the index of their '@' in the name (to determine domain)
        final int at = userDisplayableId.indexOf("@");

        // parse-out the tenant
        final String tenant = userDisplayableId.substring(at + 1);

        SharedPrefsUtil.persistUserTenant(tenant);
        SharedPrefsUtil.persistUserID(authenticationResult);

        // go to our main activity
        start();
    }

    @Override
    public void onError(Exception e) {
        e.printStackTrace();

        //Show the localized message supplied with the exception or
        //or a default message from the string resources if a
        //localized message cannot be obtained
        String msg;
        if (null == (msg = e.getLocalizedMessage())) {
            msg = getString(sign_in_err);
            Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
        } else {
            mDiagnosticsTxt.setText(msg);
            mDiagnosticsLayout.setVisibility(VISIBLE);
        }
    }

    private void warnBadClient() {
        Toast.makeText(this,
                warning_client_id_redirect_uri_incorrect,
                Toast.LENGTH_LONG)
                .show();
    }

    private void authenticate() throws IllegalArgumentException {
        validateOrganizationArgs();
        mAuthenticationManager.connect(this);
    }

    private void validateOrganizationArgs() throws IllegalArgumentException {
        UUID.fromString(ServiceConstants.CLIENT_ID);
        URI.create(ServiceConstants.REDIRECT_URI);
    }

    private void start() {
        Intent appLaunch = new Intent(this, MainActivity.class);
        startActivity(appLaunch);
    }

}

and also for the activity I am trying to view:

public class MainActivity extends AzureAppCompatActivity {

    // Instantiate the Firebase Analytics
    private FirebaseAnalytics mFirebaseAnalytics;

    private TextView txtRequest;

    // Instantiate the RequestQueue.
    private RequestQueue mQueue;


    private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
            = new BottomNavigationView.OnNavigationItemSelectedListener() {

        @Override
        public boolean onNavigationItemSelected(@NonNull MenuItem item) {
            switch (item.getItemId()) {
                case R.id.navigation_myTickets:

                    //Fragment code
                    setTitle("My Tickets");
                    MyTicketFragment mtf = new MyTicketFragment();
                    android.support.v4.app.FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
                    fragmentTransaction.replace(R.id.content, mtf, "FragmentName");
                    fragmentTransaction.commit();
                    return true;
                case R.id.navigation_closed:

                    //Fragment code
                    setTitle("Closed Tickets");
                    ClosedFragment cf = new ClosedFragment();
                    android.support.v4.app.FragmentTransaction fragmentTransaction2 = getSupportFragmentManager().beginTransaction();
                    fragmentTransaction2.replace(R.id.content, cf, "FragmentName");
                    fragmentTransaction2.commit();

                    return true;
                case R.id.navigation_unassigned:

                    //Fragment code
                    setTitle("Unassigned Tickets");
                    UnassignedFragment uf = new UnassignedFragment();
                    android.support.v4.app.FragmentTransaction fragmentTransaction3 = getSupportFragmentManager().beginTransaction();
                    fragmentTransaction3.replace(R.id.content, uf, "FragmentName");
                    fragmentTransaction3.commit();

                    return true;
                case R.id.navigation_over10days:

                    //Fragment code
                    setTitle("Tickets over 10 days");
                    Over10DayFragment of = new Over10DayFragment();
                    android.support.v4.app.FragmentTransaction fragmentTransaction4 = getSupportFragmentManager().beginTransaction();
                    fragmentTransaction4.replace(R.id.content, of, "FragmentName");
                    fragmentTransaction4.commit();

                    return true;
            }
            return false;
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        BottomNavigationView navigation = (BottomNavigationView) findViewById(R.id.navigation);
        navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);

        if (savedInstanceState == null) {
            navigation.getMenu().performIdentifierAction(R.id.navigation_myTickets, 0);
        }
    }

    @Override
    protected AzureADModule getAzureADModule() {
        AzureADModule.Builder builder = new AzureADModule.Builder(this);
        builder.validateAuthority(true)
                .authenticationResourceId(ServiceConstants.AUTHENTICATION_RESOURCE_ID)
                .authorityUrl(ServiceConstants.AUTHORITY_URL)
                .redirectUri(ServiceConstants.REDIRECT_URI)
                .clientId(ServiceConstants.CLIENT_ID);
        return builder.build();
    }

    @Override
    protected Object[] getModules() {
        return new Object[]{new AzureModule()};
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.snippet_list_menu, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle item selection
        switch (item.getItemId()) {
            case R.id.AddTicket:
                Intent AddTicketIntent = new Intent(MainActivity.this,
                        AddTicket.class);
                startActivity(AddTicketIntent);
                return true;
            case R.id.disconnect:
                //SignOut Needs to go here
                // drop the application shared preferences to clear any old auth tokens
                getSharedPreferences(AppModule.PREFS, Context.MODE_PRIVATE)
                        .edit() // get the editor
                        .clear() // clear it
                        .apply(); // asynchronously apply

                mAuthenticationManager.disconnect();
                Intent login = new Intent(this, SignInActivity.class);
                login.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                startActivity(login);
            default:
                return super.onOptionsItemSelected(item);
        }
    }
}

I'm not sure if they are helpful in finding an answer or if the fragments the activity is creating are the problem but I can't see how. Also I have gotten the app to work on a mobile twice before but immediately crashes once I open it again. Many thanks for your help. -Adam.

Adam Lee
  • 66
  • 1
  • 15

1 Answers1

1

The authentication manager is handling authentication in a background thread and is calling onSuccess on the same thread. You will need to move back to the main thread to manipulate the view (set visibility, text, etc.)

ghost537
  • 76
  • 1
  • 5
  • Thanks for your quick response. Could you explain with a bit more detail as I'm to new threads and I'm having difficulty understanding? Or could you show me how to move back to the main thread? – Adam Lee Mar 29 '18 at 21:39
  • Thank you I found the problem, much appreciated marked as answered. – Adam Lee Mar 29 '18 at 22:03