Categories
discuss

Did Google stop supporting Android Eclipse Plugin?

If the question is not for here, I will remove it.

Does someone knows if Android has stopped supporting the Eclipse Plugin?
Now when you enter their site from googling “Android SDK” you get to download Android Studio, and I can’t find where is the Eclipse plugin…
I remember they said they won’t stop supporting it, but well, its Google after all…
So someone knows?

Answer

Per the Android Development Tools page:

Note: If you have been using Eclipse with ADT, be aware that Android Studio is now the official IDE for Android, so you should migrate to Android Studio to receive all the latest IDE updates. For help moving projects, see Migrating to Android Studio.

Categories
discuss

Toolbar – Switching from drawer to back button with only one Activity

I’ve been searching for a while on how to change between the drawer open/close icon (going from a hamburger to the arrow) to a simple back arrow. My application at the moment only has one Activity which switches between several fragments. At one point, I want to transition between one of the main fragments (ie, one of the fragments in the drawer) to a fragment that hierarchically is under the previous fragment (ie, an “Add New ” fragment). In this new fragment, I want to have the Toolbar to show the back button instead of the drawer button.

I’ve been looking around and trying different solutions for quite a while. Here are the most notable:

At the moment, I’m thinking of a long, arduous method of creating a custom icon that I hide and show (and hide/show the native drawer icon). However, is there a better way to switch between the drawer and back buttons?

As a side yet related question, I’ve been looking at the Material Design docs, and a few examples have an X in the top left corner. How different is that to implement than implementing the drawer vs back/up buttons?

Thanks~

Edit:

I can figure out how to replace the icon, but how would I get the click event?

So far, this was my best lead:

What I’ve tried now:

  • Disabled the DrawerToggle when necessary (ie, mDrawerToggle.setDrawerIndicatorEnabled(useDrawer);)
  • Added logs in onOptionsItemSelected in my NavigationDrawerFragment, my Activity, as well as the DialogFragment I’m currently testing which run if item.getItemId() == android.R.id.home is true. None of these log statements go off

For better context, I now have a full screen fragment which adds a “Save” button to the menu and changes the drawer icon to an “X”. The fragment can get the save menu event, yet not even the Activity and Drawer can get when the X is tapped.

Edit2:

As requested, here is some code. Note that this is all from this Github repo, which I’m actively working on (note that I have a few useless functions here or there from rapid testing).

ActivityMain:

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

    // Add the toolbar
    mToolbar = (Toolbar) findViewById(R.id.toolbar);
    if (mToolbar != null) {
        setSupportActionBar(mToolbar);
    }

    // Initialize the drawer
    mNavigationDrawerFragment = (NavigationDrawerFragment)
            getSupportFragmentManager().findFragmentById(R.id.navigation_drawer);

    // Set up the drawer
    mNavigationDrawerFragment.setUp(
            R.id.navigation_drawer,
            (DrawerLayout) findViewById(R.id.drawer_layout),
            mToolbar);

    // TODO: Check if this helps to catch the main toolbar button click
    getSupportActionBar().setDisplayShowHomeEnabled(true);

    // Get the titles for the Toolbar
    mTitles = getResources().getStringArray(R.array.drawer_items);

    mDrawerPosition = -1;
    if (savedInstanceState == null) {
        // If there was no saved position, then the default, starting position should be used
        forceChangeItemSelected(0);
    }
    else {
        // Otherwise, get the saved position from the bundle
        int position = savedInstanceState.getInt(KEY_DRAWERPOS);
        mNavigationDrawerFragment.setSelectedItem(position);
        // Title needs to be re-set
        getSupportActionBar().setTitle(mTitles[position]);
    }

    // If I include the below bit, then the DrawerToggle doesn't function
        // I don't know how to switch it back and forth
    mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Log.d(LOG_TAG, "Navigation was clicked");

        }
    });
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle action bar item clicks here. The action bar will
    // automatically handle clicks on the Home/Up button, so long
    // as you specify a parent activity in AndroidManifest.xml.
    Log.d(LOG_TAG, "Activity responding to menu click...");
    if(item.getItemId() == android.R.id.home) Log.d(LOG_TAG, "Activity got it....");

    // If the fragment is supposed to handle things, then let it
    if(mIsFragmentHandlingMenus) return false;

    int id = item.getItemId();
    if(id == R.id.save) {
        // This isn't implemented! If chosen, then there's a bug!
        Log.e(LOG_TAG, "onOptionsItemSelected: Save was selected!");
    }

    return super.onOptionsItemSelected(item);
}

@Override
public void fragmentHandlingMenus(boolean isFragmentHandlingMenus) {
    // Simply store the setting
    mIsFragmentHandlingMenus = isFragmentHandlingMenus;

    // Toggle the drawer as necessary
    mNavigationDrawerFragment.toggleDrawerUse(!isFragmentHandlingMenus);
}

NavigationDrawerFragment:

public void toggleDrawerUse(boolean useDrawer) {
    // Enable/Disable the icon being used by the drawer
    mDrawerToggle.setDrawerIndicatorEnabled(useDrawer);

    // TODO: Enable/Disable the drawer even being able to open/close
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    Log.d(LOGTAG, "Drawer responding to menu click...");
    if(item.getItemId() == android.R.id.home) Log.d(LOGTAG, "Drawer got it....");
    if (mDrawerToggle.onOptionsItemSelected(item)) {
        return true;
    }

    return super.onOptionsItemSelected(item);
}

GoalAdderFragment:

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    // Allow this fragment to handle toolbar menu items
    setHasOptionsMenu(true);

    // Set up the toolbar
    ((ActionBarActivity) getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(true);
    ((ActionBarActivity) getActivity()).getSupportActionBar().setHomeAsUpIndicator(android.R.drawable.ic_menu_close_clear_cancel);
    ((ActionBarActivity) getActivity()).getSupportActionBar().setTitle(getResources().getString(R.string.title_addgoal));
}

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);

    // Cache the Activity as the frag handler if necessary
    if(mFragHandler == null)
        mFragHandler = (TransactionHandler.FragmentTransactionHandler) getActivity();
    // Tell the Activity to let fragments handle the menu events
    mFragHandler.fragmentHandlingMenus(true);
}

@Override
public void onDetach() {
    super.onDetach();

    // Tell the Activity that it can now handle menu events once again
    mFragHandler.fragmentHandlingMenus(false);
}

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    inflater.inflate(R.menu.save_menu, menu);
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    Log.d(LOGTAG, "Item id: " + item.getItemId() + " | Save id: " + R.id.save);
    Toast.makeText(getActivity(), "Fragment activated!", Toast.LENGTH_SHORT).show();

    switch (item.getItemId()) {
        case R.id.save:
            return true;
        case android.R.id.home:
            return true;
        default:
            break;
    }

    return false;
}

Solution:

This is the ultimate solution I ended up on, with the help of natario’s answer below:

NavigationDrawerFragment:

private View.OnClickListener mOriginalListener;

public void setUp(int fragmentId, DrawerLayout drawerLayout, Toolbar toolbar) {
     /* Rest of setting up code */

     // Save the default listener after setting everything else up
     mOriginalListener = mDrawerToggle.getToolbarNavigationClickListener();
}

// Tells the toolbar+drawer to switch to the up button or switch back to the normal drawer
public void toggleDrawerUse(boolean useDrawer) {
    // Enable/Disable the icon being used by the drawer
    mDrawerToggle.setDrawerIndicatorEnabled(useDrawer);

    // Switch between the listeners as necessary
    if(useDrawer)
        mDrawerToggle.setToolbarNavigationClickListener(mOriginalListener);
    else
        mDrawerToggle.setToolbarNavigationClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(getActivity(), "Custom listener", Toast.LENGTH_SHORT).show();
            }
        });
}

Answer

Put this code into onCreate() of your Activity. Works well for me. Even using compileSdk 23 and higher.

    drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
    final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    if(toolbar != null) {
        toggle = new ActionBarDrawerToggle(
                this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
        toggle.syncState();
        drawer.setDrawerListener(toggle);
        getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
            @Override
            public void onBackStackChanged() {
                if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
                    getSupportActionBar().setDisplayHomeAsUpEnabled(true); // show back button
                    toolbar.setNavigationOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            onBackPressed();
                        }
                    });
                } else {
                    //show hamburger
                    getSupportActionBar().setDisplayHomeAsUpEnabled(false);
                    toggle.syncState();
                    toolbar.setNavigationOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            drawer.openDrawer(GravityCompat.START);
                        }
                    });
                }
            }
        });
Categories
discuss

How to add a delay before starting a Mocha test case?

I’m writing a unit test for my simple Node.js application using Mocha. The application has a class which connects to a Mongo database, fetch the record, and store the formulated record as a field. Simply, the class looks like this:

SampleClass.prototype.record = []; // Store the loaded record
SampleClass.prototype.init = function(db){
    var self = this;
    self.db = mongoose.connection; // Say we already have mongoose object initialized
    self.db.once('open',function(){
        /* schema & model definitions go here */
        var DataModel = mongoose.model( /* foobar */);
        DataModel.findOne(function(err,record){
           /* error handling goes here */ 

           self.record = record; // Here we fetch & store the data
        });
    });
}

As seen from the snippet above, once the SampleClass.init() is called, the Sample.record will not instantly get populated from the database. The data is asynchronously populated once the event ‘open’ is fired. Thus, there will possibly be a delay after SampleClass.init() until the Sample.record is populated.

So it comes into a complication when I write a Mocha test like this:

var testSampleClass = new SampleClass();

describe('SampleClass init test',function(){
    testSampleClass.init('mydb');
    it('should have 1 record read from mydb',function(){
        assert.equal(testSampleClass.record.length,1);
    });
});

The assertion above will always fail because testSampleClass.record will not get populated straightaway after init. It needs a gap of time to load the data.

How can I delay the test case so it starts a few seconds or more after testSampleClass.init is called? Is it also possible to trigger the test case right after an event of my class is fired? Otherwise, this simple case will always fail which I know this is not correct at all.

Answer

Use before() or beforeEach hooks (see here and here). They take done callback as argument, which you must call when some asynchronous staff will be completed. So you test should looks like:

describe('SampleClass init test',function(){
    before(function(done) {
        testSampleClass.init('mydb', done);
    });
    it('should have 1 record read from mydb',function(){
        assert.equal(testSampleClass.record.length,1);
    });
});

And your init method:

SampleClass.prototype.record = []; // Store the loaded record
SampleClass.prototype.init = function(db, callback){
    var self = this;
    self.db = mongoose.connection; // Say we already have mongoose object initialized
    self.db.once('open',function(){
        /* schema & model definitions go here */
        var DataModel = mongoose.model( /* foobar */);
        DataModel.findOne(function(err,record){
            /* error handling goes here */

            self.record = record; // Here we fetch & store the data
            callback();
        });
    });
}
Categories
discuss

XMLHttpRequest throwing InvalidSateError saying “Object state must be opened”

The code –

"use strict";

var AJAX = function (params) {
    this.server ={};
    this.url = params.url;
    this.method = params.method;
    this.dataType = params.dataType;
    this.formData = params.formData;

    this.init = function(){
        if(typeof XMLHttpRequest != 'undefined'){
            this.server = new XMLHttpRequest();
            this.server.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
            this.server.setRequestHeader('Content-length', this.formData.length);
            this.server.setRequestHeader('Connection', 'close');
            console.log("XMLHttpRequest created.");
            return true;
        }
    };

    this.send = function(){
        if(this.init()){
            this.server.open(this.method, this.url, true);
            this.server.send(this.formData);
        }
    };

};

It is throwing following error :

Error in event handler for contextMenus: InvalidStateError: Failed to execute 'setRequestHeader' on 'XMLHttpRequest': The object's state must be OPENED.

How it’s being used –

var data = new FormData();

data.append('user', 'sachin');
var params = {
    url : 'example.com',
    method : 'post',
    dataType: 'json',
    formData : data
};

var backgroundWindow = chrome.extension.getBackgroundPage();

var ajax = new backgroundWindow.AJAX(params);

ajax.send();

I can’t seem to figure out what’s the reason behind.

Answer

The error is straight forward:

Error in event handler for contextMenus: InvalidStateError: Failed to execute ‘setRequestHeader’ on ‘XMLHttpRequest’: The object’s state must be OPENED.

You need to call .open(..) before setting the request headers.



Given your code, I believe the best way would be to move the call to open in the init(..) function.

var AJAX = function (params) {
    this.server ={};
    this.url = params.url;
    this.method = params.method;
    this.dataType = params.dataType;
    this.formData = params.formData;

    this.init = function(){
        if(typeof XMLHttpRequest != 'undefined'){
            this.server = new XMLHttpRequest();

            //Open first, before setting the request headers.
            this.server.open(this.method, this.url, true);

            //Now set the request headers.
            this.server.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
            //this.server.setRequestHeader('Content-length', this.formData.length);
            //this.server.setRequestHeader('Connection', 'close');
            console.log("XMLHttpRequest created.");
            return true;
        }
    };

    this.send = function(){
        if(this.init()){
            this.server.send(this.formData);
        }
    };

};
Categories
discuss

How to get a dimension value that is a reference to an attribute?

I’ve got these two items in my dimension definitions:

<dimen name="toolbar_search_extended_height">158dp</dimen>
<dimen name="toolbar_search_normal_height">?attr/actionBarSize</dimen>

Now I’d like to get the actual value in pixels at runtime:

height = getResources().getDimensionPixelOffset(R.dimen.toolbar_search_extended_height);
height = getResources().getDimensionPixelOffset(R.dimen.toolbar_search_normal_height);

The first call gives whatever 158dp is in pixels on the device.
The second call yields a NotFoundException:

android.content.res.Resources$NotFoundException: Resource ID #0x7f080032 type #0x2 is not valid

Type 0x2 is: TypedValue#TYPE_ATTRIBUTE:

/** The <var>data</var> field holds an attribute resource
 *  identifier (referencing an attribute in the current theme
 *  style, not a resource entry). */
public static final int TYPE_ATTRIBUTE = 0x02;

What is the preferred method to dereference dimen values that can be either actual values or references to styled attributes?

Answer

This is what I implemented but it feels cumbersome and hacky:

private int getDimension(@DimenRes int resId) {
    final TypedValue value = new TypedValue();
    getResources().getValue(resId, value, true);

    if (value.type == TypedValue.TYPE_ATTRIBUTE) {
        final TypedArray attributes = getTheme().obtainStyledAttributes(new int[]{value.data});
        int dimension = attributes.getDimensionPixelOffset(0, 0);
        attributes.recycle();
        return dimension;
    } else {
        return getResources().getDimensionPixelOffset(resId);
    }
}

I was hoping that the framework would dereference my ?attr/ dimension directly.

Source: stackoverflow
Text is available under the Creative Commons Attribution-ShareAlike License; additional terms may apply. By using this site, you agree to the Privacy Policy, and Copyright Policy. Content is available under CC BY-SA 3.0 unless otherwise noted. The answers/resolutions are collected from stackoverflow, are licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0 © No Copyrights, All Questions are retrived from public domain..