From 9c9ecc5140e6f1784eb63fff8b359337c8cd6f33 Mon Sep 17 00:00:00 2001 From: Peter Serwylo Date: Sat, 22 Mar 2014 08:25:26 +1100 Subject: [PATCH] Fixed issue 472, NPE on android 3.1 (and 3.0). The Activity.getActionBar() method can only be called after setContentView() has been invoked, as described here: http://blog.perpetumdesign.com/2011/08/strange-case-of-dr-action-and-mr-bar.html I couldn't think of any way to enforce this safely (i.e. make the compiler kick up a stink if we didn't do it). As such, I just put a comment above each usage of the ActionBarCompat class. Another outstanding issue is a duplicate of 474, where it crashes when you press the "Up" button from ManageRepos, but I'll create a different issue for that. --- src/org/fdroid/fdroid/AppDetails.java | 8 +++++--- src/org/fdroid/fdroid/ManageRepo.java | 9 +++++++++ src/org/fdroid/fdroid/PreferencesActivity.java | 5 +++++ src/org/fdroid/fdroid/SearchResults.java | 7 ++++++- src/org/fdroid/fdroid/compat/ActionBarCompat.java | 9 +++++++++ src/org/fdroid/fdroid/views/RepoDetailsActivity.java | 9 +++++++++ 6 files changed, 43 insertions(+), 4 deletions(-) diff --git a/src/org/fdroid/fdroid/AppDetails.java b/src/org/fdroid/fdroid/AppDetails.java index 1e311c6b0..206dfd39f 100644 --- a/src/org/fdroid/fdroid/AppDetails.java +++ b/src/org/fdroid/fdroid/AppDetails.java @@ -261,11 +261,13 @@ public class AppDetails extends ListActivity { .bitmapConfig(Bitmap.Config.RGB_565) .build(); - ActionBarCompat abCompat = ActionBarCompat.create(this); - abCompat.setDisplayHomeAsUpEnabled(true); - setContentView(R.layout.appdetails); + // Actionbar cannot be accessed until after setContentView (on 3.0 and 3.1 devices) + // see: http://blog.perpetumdesign.com/2011/08/strange-case-of-dr-action-and-mr-bar.html + // for reason why. + ActionBarCompat.create(this).setDisplayHomeAsUpEnabled(true); + Intent i = getIntent(); Uri data = i.getData(); if (data != null) { diff --git a/src/org/fdroid/fdroid/ManageRepo.java b/src/org/fdroid/fdroid/ManageRepo.java index 102a8faa3..665f8bd6c 100644 --- a/src/org/fdroid/fdroid/ManageRepo.java +++ b/src/org/fdroid/fdroid/ManageRepo.java @@ -27,6 +27,7 @@ import android.support.v4.app.NavUtils; import android.util.Log; import android.view.MenuItem; +import android.widget.LinearLayout; import org.fdroid.fdroid.compat.ActionBarCompat; import org.fdroid.fdroid.views.fragments.RepoListFragment; @@ -49,6 +50,14 @@ public class ManageRepo extends FragmentActivity { ((FDroidApp) getApplication()).applyTheme(this); if (savedInstanceState == null) { + + // Need to set a dummy view (which will get overridden by the fragment manager + // below) so that we can call setContentView(). This is a work around for + // a (bug?) thing in 3.0, 3.1 which requires setContentView to be invoked before + // the actionbar is played with: + // http://blog.perpetumdesign.com/2011/08/strange-case-of-dr-action-and-mr-bar.html + setContentView( new LinearLayout(this) ); + listFragment = new RepoListFragment(); getSupportFragmentManager() .beginTransaction() diff --git a/src/org/fdroid/fdroid/PreferencesActivity.java b/src/org/fdroid/fdroid/PreferencesActivity.java index 569f3dc73..12ac901bd 100644 --- a/src/org/fdroid/fdroid/PreferencesActivity.java +++ b/src/org/fdroid/fdroid/PreferencesActivity.java @@ -59,7 +59,12 @@ public class PreferencesActivity extends PreferenceActivity implements protected void onCreate(Bundle savedInstanceState) { ((FDroidApp) getApplication()).applyTheme(this); super.onCreate(savedInstanceState); + + // Actionbar cannot be accessed until after setContentView (on 3.0 and 3.1 devices) + // see: http://blog.perpetumdesign.com/2011/08/strange-case-of-dr-action-and-mr-bar.html + // for reason why. ActionBarCompat.create(this).setDisplayHomeAsUpEnabled(true); + addPreferencesFromResource(R.xml.preferences); } diff --git a/src/org/fdroid/fdroid/SearchResults.java b/src/org/fdroid/fdroid/SearchResults.java index 426e3f76d..f2a1d5357 100644 --- a/src/org/fdroid/fdroid/SearchResults.java +++ b/src/org/fdroid/fdroid/SearchResults.java @@ -73,9 +73,14 @@ public class SearchResults extends ListActivity { ((FDroidApp) getApplication()).applyTheme(this); super.onCreate(savedInstanceState); - ActionBarCompat.create(this).setDisplayHomeAsUpEnabled(true); + setContentView(R.layout.searchresults); + // Actionbar cannot be accessed until after setContentView (on 3.0 and 3.1 devices) + // see: http://blog.perpetumdesign.com/2011/08/strange-case-of-dr-action-and-mr-bar.html + // for reason why. + ActionBarCompat.create(this).setDisplayHomeAsUpEnabled(true); + // Start a search by just typing setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL); } diff --git a/src/org/fdroid/fdroid/compat/ActionBarCompat.java b/src/org/fdroid/fdroid/compat/ActionBarCompat.java index 52e077673..9f78c0822 100644 --- a/src/org/fdroid/fdroid/compat/ActionBarCompat.java +++ b/src/org/fdroid/fdroid/compat/ActionBarCompat.java @@ -20,6 +20,15 @@ public abstract class ActionBarCompat extends Compatibility { this.activity = activity; } + /** + * Cannot be accessed until after setContentView (on 3.0 and 3.1 devices) has + * been called on the relevant activity. If you don't have a content view + * (e.g. when using fragment manager to add fragments to the activity) then you + * will still need to call setContentView(), with a "new LinearLayout()" or something + * useless like that. + * See: http://blog.perpetumdesign.com/2011/08/strange-case-of-dr-action-and-mr-bar.html + * for details. + */ public abstract void setDisplayHomeAsUpEnabled(boolean value); } diff --git a/src/org/fdroid/fdroid/views/RepoDetailsActivity.java b/src/org/fdroid/fdroid/views/RepoDetailsActivity.java index 533c08242..268557372 100644 --- a/src/org/fdroid/fdroid/views/RepoDetailsActivity.java +++ b/src/org/fdroid/fdroid/views/RepoDetailsActivity.java @@ -15,6 +15,7 @@ import android.os.Parcelable; import android.support.v4.app.FragmentActivity; import android.text.TextUtils; import android.util.Log; +import android.widget.LinearLayout; import android.widget.Toast; import org.fdroid.fdroid.FDroidApp; @@ -41,6 +42,14 @@ public class RepoDetailsActivity extends FragmentActivity { long repoId = getIntent().getLongExtra(RepoDetailsFragment.ARG_REPO_ID, 0); if (savedInstanceState == null) { + + // Need to set a dummy view (which will get overridden by the fragment manager + // below) so that we can call setContentView(). This is a work around for + // a (bug?) thing in 3.0, 3.1 which requires setContentView to be invoked before + // the actionbar is played with: + // http://blog.perpetumdesign.com/2011/08/strange-case-of-dr-action-and-mr-bar.html + setContentView( new LinearLayout(this) ); + RepoDetailsFragment fragment = new RepoDetailsFragment(); fragment.setArguments(getIntent().getExtras()); getSupportFragmentManager()