I have set up a button that is meant to check for READ_EXTERNAL_STORAGE
permissions, and then open the users phone storage display images. I’ve done this successfully via the following code:
All of this works perfectly on the first try. The user sees the permission dialog box, presses agree and the image selector opens displaying images on their phone! However, the second time we try to use the same button the app crashes. So basically after the permissions are granted the app crashes every time the button is pressed.
The following error is displayed:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.memory.pod.debug, PID: 26684
java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.pm.PackageManager android.content.Context.getPackageManager()' on a null object reference
at android.content.ContextWrapper.getPackageManager(ContextWrapper.java:98)
at com.memory.pod.camerax.ui.home.HomeActivity.selectImage(HomeActivity.java:144)
at com.memory.pod.camerax.ui.home.HomeActivity.openFileChooser(HomeActivity.java:137)
at com.memory.pod.view.BottomTabView$4.onClick(BottomTabView.java:84)
at android.view.View.performClick(View.java:7259)
at android.view.View.performClickInternal(View.java:7236)
at android.view.View.access$3600(View.java:801)
at android.view.View$PerformClick.run(View.java:27892)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
BottomTabView.java
public class BottomTabView extends FrameLayout implements ViewPager.OnPageChangeListener {
// Initialize content
private ImageView mCenterImageView;
private ImageView mBottomImageView;
private LinearLayout mStartImageView;
private LinearLayout mEndImageView;
private View mIndicator;
private ArgbEvaluator mArgbEvaluator;
private int mCenterColor;
private int mSideColor;
private int mEndViewsTranslationX;
private int mIndicatorTranslationX;
private int mCenterTranslationY;
public BottomTabView(@NonNull Context context) {
this(context, null);
}
public BottomTabView(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public BottomTabView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public void setUpWithViewPager(ViewPager viewPager) {
viewPager.addOnPageChangeListener(this);
mStartImageView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
if (viewPager.getCurrentItem() != 0)
viewPager.setCurrentItem(0);
}
});
mCenterImageView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
if (viewPager.getCurrentItem() != 1)
viewPager.setCurrentItem(1);
}
});
mEndImageView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
if (viewPager.getCurrentItem() != 2)
viewPager.setCurrentItem(2);
}
});
mBottomImageView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
if (viewPager.getCurrentItem() == 1) {
HomeActivity.openFileChooser(getContext());
}
}
});
}
private void init() {
LayoutInflater.from(getContext()).inflate(R.layout.view_bottom_tabs, this, true);
// Initialize bottom tab view content
mCenterImageView = findViewById(R.id.view_bottom_tabs_camera_iv);
mStartImageView = findViewById(R.id.view_bottom_tabs_start_linear_layout);
mEndImageView = findViewById(R.id.view_bottom_tabs_end_linear_layout);
mBottomImageView = findViewById(R.id.view_bottom_tabs_gallery_iv);
// Initialize bottom tab view indicator, meant to indicate the current tab in view
mIndicator = findViewById(R.id.view_bottom_tabs_indicator);
// Set start color and end color for tab view icons
mCenterColor = ContextCompat.getColor(getContext(), R.color.white);
mSideColor = ContextCompat.getColor(getContext(), R.color.black);
// Evaluate the difference between colors
mArgbEvaluator = new ArgbEvaluator();
// Change the padding of tab icons based on view
// This is the min padding accepted (0)
mIndicatorTranslationX = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 140, getResources().getDisplayMetrics());
// Wait till GalleryImageView has been drawn, once GalleryImageView is drawn then proceed with relative padding calculation
mBottomImageView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
// Change padding relative to other icons
mEndViewsTranslationX = (int)((mBottomImageView.getX() - mStartImageView.getX()) - mIndicatorTranslationX);
mBottomImageView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
// Change the position of the center image
mCenterTranslationY = getHeight() - mBottomImageView.getBottom();
}
});
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (position == 0) {
// Change color of icons based on view
setColor(1 - positionOffset);
// Move the start and end images
moveViews(1 - positionOffset);
// Change indicator position
mIndicator.setTranslationX((positionOffset - 1) * mIndicatorTranslationX);
// Change scale of center image
moveAndScaleCenter(1 - positionOffset);
} else if (position == 1) {
// Change color of icons based on view
setColor(positionOffset);
// Move the start and end images
moveViews(positionOffset);
// Change indicator position
mIndicator.setTranslationX(positionOffset * mIndicatorTranslationX);
// Change scale of center image
moveAndScaleCenter(positionOffset);
}
}
@Override
public void onPageSelected(int position) {
}
@Override
public void onPageScrollStateChanged(int state) {
}
private void moveAndScaleCenter(float fractionFromCenter) {
float scale = .7 f + ((1 - fractionFromCenter) * .3 f);
// Change scale of center image
mCenterImageView.setScaleX(scale);
mCenterImageView.setScaleY(scale);
int translation = (int)(fractionFromCenter * mCenterTranslationY * 2);
// Move center image down
mCenterImageView.setTranslationY(translation);
mBottomImageView.setTranslationY(translation);
// Fadeout bottom image
mBottomImageView.setAlpha(1 - fractionFromCenter);
}
private void moveViews(float fractionFromCenter) {
mStartImageView.setTranslationX(fractionFromCenter * mEndViewsTranslationX);
mEndImageView.setTranslationX(-fractionFromCenter * mEndViewsTranslationX);
mIndicator.setAlpha(fractionFromCenter);
mIndicator.setScaleX(fractionFromCenter);
}
private void setColor(float fractionFromCenter) {
int color = (int) mArgbEvaluator.evaluate(fractionFromCenter, mCenterColor, mSideColor);
/* mCenterImageView.setColorFilter(color);
mStartImageView.setColorFilter(color);
mEndImageView.setColorFilter(color);
mBottomImageView.setColorFilter(color); */
}
}
I did a log.d to identify where it fails, please refer to previous code to see log.d.
How can I overcome this issue?
EDIT:
HomeActivity:
public class HomeActivity extends AppCompatActivity {
// Initialize image picker
private static final int PICK_IMAGE_REQUEST = 1;
private static final int REQUEST_CODE_STORAGE_PERMISSION = 1;
private static final int REQUEST_CODE_SELECT_IMAGE = 2;
// TODO: FIREBASE TEMPORARY LOGOUT
private static FirebaseAuth mAuth;
private static final String TAG = "HomeActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
// Initialize views
View background = findViewById(R.id.background_view);
ViewPager viewPager = (ViewPager) findViewById(R.id.view_pager);
// Initialize adapter
HomePagerAdapter adapter = new HomePagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(adapter);
// Initialize BottomTabView
BottomTabView bottomTabView = findViewById(R.id.bottom_tabs);
bottomTabView.setUpWithViewPager(viewPager);
// Initialize TopSearchView
TopSearchView topSearchView = findViewById(R.id.top_search);
topSearchView.setUpWithViewPager(viewPager);
// Initialize TopProfilePictureView
TopProfilePictureView topProfilePictureView = findViewById(R.id.top_profile_picture);
topProfilePictureView.setUpWithViewPager(viewPager);
// Initialize TopVariableFunctionalityButtonView
TopVariableFunctionalityButtonView topVariableFunctionalityButtonView = findViewById(R.id.top_variable_functionality_button);
topVariableFunctionalityButtonView.setUpWithViewPager(viewPager);
// Initialize color for fade effect
final int windowBackgroundColor = ContextCompat.getColor(this, R.color.windowBackground);
final int blackBackgroundColor = ContextCompat.getColor(this, R.color.black);
// Set the default position of the viewPager
viewPager.setCurrentItem(1);
// TODO: Initialize Firebase (TEMPORARY)
//Initialize Firebase Auth
mAuth = FirebaseAuth.getInstance();
bottomTabView.setOnBottomTabViewClickListener(new BottomTabViewClickListener() {
@Override
public void onClick(View v) {
// call your openFileChooser from here,
//REMOVE THE STATIC
openFileChooser(HomeActivity.this);
}
});
// Callback each time the fragment is changed
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (position == 0) {
background.setBackgroundColor(windowBackgroundColor);
background.setAlpha(1 - positionOffset);
} else if (position == 1) {
background.setBackgroundColor(blackBackgroundColor);
background.setAlpha(positionOffset);
}
}
@Override
public void onPageSelected(int position) {
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
}
// Choose file extended from BottomTabView, opens all images on device
// Check for permissions before hand
public void openFileChooser(Context context) {
Log.d("HomeActivity", "This is the last place a log is observed");
if (ContextCompat.checkSelfPermission(getInstance().getApplicationContext(), Manifest.permission.READ_EXTERNAL_STORAGE) !=
PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
((Activity) context),
new String[] {
Manifest.permission.READ_EXTERNAL_STORAGE
},
REQUEST_CODE_STORAGE_PERMISSION
);
} else {
selectImage();
}
}
// Open image selector via phone storage
private void selectImage() {
Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(intent, REQUEST_CODE_SELECT_IMAGE);
}
}
// Request permission results
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_CODE_STORAGE_PERMISSION && grantResults.length > 0) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
selectImage();
} else {
Toast.makeText(this, "Permission Denied!", Toast.LENGTH_SHORT).show();
}
}
}
// TODO: Is it better to use bitmap or URI
// Check if user has selected file and describe next step
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_SELECT_IMAGE && resultCode == RESULT_OK) {
if (data != null) {
// Get image URI
Uri selectedImageUri = data.getData();
if (selectedImageUri != null) {
try {
InputStream inputStream = getContentResolver().openInputStream(selectedImageUri);
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
// Do something with image
// Identify the selected image file, pass onto firebase
File selectedImageFile = new File(getPathFormUri(selectedImageUri));
} catch (Exception exception) {
Toast.makeText(this, exception.getMessage(), Toast.LENGTH_SHORT).show();
}
}
}
}
}
private String getPathFormUri(Uri contentUri) {
String filePath;
Cursor cursor = getContentResolver()
.query(contentUri, null, null, null, null);
if (cursor == null) {
filePath = contentUri.getPath();
} else {
cursor.moveToFirst();
int index = cursor.getColumnIndex("_data");
filePath = cursor.getString(index);
cursor.close();
}
return filePath;
}
// TODO: TEMPORARY LOGOUT
public static void logoutUser(Context context) {
mAuth.signOut();
}
}
BottomTabActivity:
public class BottomTabView extends FrameLayout implements ViewPager.OnPageChangeListener {
// Initialize content
BottomTabViewClickListener bottomTabViewClickListener;
private ImageView mCenterImageView;
private ImageView mBottomImageView;
private LinearLayout mStartImageView;
private LinearLayout mEndImageView;
private View mIndicator;
private ArgbEvaluator mArgbEvaluator;
private int mCenterColor;
private int mSideColor;
private int mEndViewsTranslationX;
private int mIndicatorTranslationX;
private int mCenterTranslationY;
//add this function somewhere
public void setOnBottomTabViewClickListener(BottomTabViewClickListener bottomTabViewClickListener) {
this.bottomTabViewClickListener = bottomTabViewClickListener;
}
public BottomTabView(@NonNull Context context) {
this(context, null);
}
public BottomTabView(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public BottomTabView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public void setUpWithViewPager(ViewPager viewPager) {
viewPager.addOnPageChangeListener(this);
mStartImageView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
if (viewPager.getCurrentItem() != 0)
viewPager.setCurrentItem(0);
}
});
mCenterImageView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
if (viewPager.getCurrentItem() != 1)
viewPager.setCurrentItem(1);
}
});
mEndImageView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
if (viewPager.getCurrentItem() != 2)
viewPager.setCurrentItem(2);
}
});
mBottomImageView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
if (viewPager.getCurrentItem() == 1) {
}
}
});
}
private void init() {
LayoutInflater.from(getContext()).inflate(R.layout.view_bottom_tabs, this, true);
// Initialize bottom tab view content
mCenterImageView = findViewById(R.id.view_bottom_tabs_camera_iv);
mStartImageView = findViewById(R.id.view_bottom_tabs_start_linear_layout);
mEndImageView = findViewById(R.id.view_bottom_tabs_end_linear_layout);
mBottomImageView = findViewById(R.id.view_bottom_tabs_gallery_iv);
// Initialize bottom tab view indicator, meant to indicate the current tab in view
mIndicator = findViewById(R.id.view_bottom_tabs_indicator);
// Set start color and end color for tab view icons
mCenterColor = ContextCompat.getColor(getContext(), R.color.white);
mSideColor = ContextCompat.getColor(getContext(), R.color.black);
// Evaluate the difference between colors
mArgbEvaluator = new ArgbEvaluator();
// Change the padding of tab icons based on view
// This is the min padding accepted (0)
mIndicatorTranslationX = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 140, getResources().getDisplayMetrics());
// Wait till GalleryImageView has been drawn, once GalleryImageView is drawn then proceed with relative padding calculation
mBottomImageView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
// Change padding relative to other icons
mEndViewsTranslationX = (int)((mBottomImageView.getX() - mStartImageView.getX()) - mIndicatorTranslationX);
mBottomImageView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
// Change the position of the center image
mCenterTranslationY = getHeight() - mBottomImageView.getBottom();
}
});
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (position == 0) {
// Change color of icons based on view
setColor(1 - positionOffset);
// Move the start and end images
moveViews(1 - positionOffset);
// Change indicator position
mIndicator.setTranslationX((positionOffset - 1) * mIndicatorTranslationX);
// Change scale of center image
moveAndScaleCenter(1 - positionOffset);
} else if (position == 1) {
// Change color of icons based on view
setColor(positionOffset);
// Move the start and end images
moveViews(positionOffset);
// Change indicator position
mIndicator.setTranslationX(positionOffset * mIndicatorTranslationX);
// Change scale of center image
moveAndScaleCenter(positionOffset);
}
}
@Override
public void onPageSelected(int position) {
}
@Override
public void onPageScrollStateChanged(int state) {
}
private void moveAndScaleCenter(float fractionFromCenter) {
float scale = .7 f + ((1 - fractionFromCenter) * .3 f);
// Change scale of center image
mCenterImageView.setScaleX(scale);
mCenterImageView.setScaleY(scale);
int translation = (int)(fractionFromCenter * mCenterTranslationY * 2);
// Move center image down
mCenterImageView.setTranslationY(translation);
mBottomImageView.setTranslationY(translation);
// Fadeout bottom image
mBottomImageView.setAlpha(1 - fractionFromCenter);
}
private void moveViews(float fractionFromCenter) {
mStartImageView.setTranslationX(fractionFromCenter * mEndViewsTranslationX);
mEndImageView.setTranslationX(-fractionFromCenter * mEndViewsTranslationX);
mIndicator.setAlpha(fractionFromCenter);
mIndicator.setScaleX(fractionFromCenter);
}
private void setColor(float fractionFromCenter) {
int color = (int) mArgbEvaluator.evaluate(fractionFromCenter, mCenterColor, mSideColor);
/* mCenterImageView.setColorFilter(color);
mStartImageView.setColorFilter(color);
mEndImageView.setColorFilter(color);
mBottomImageView.setColorFilter(color); */
}
}
interface BottomTabViewClickListener {
public void onClick();
}
Advertisement
Answer
Your else statement is currently like this, which means you are trying to create a new instance of HomeActivity and call selectimage(), and it does not work that way.
else {
new HomeActivity().selectImage();
}
Please, change it to
else{
selectImage();
}
Please, try this, this should solve your problem.
EDIT: Use of interface and listener pattern
In your BottomTabView add the following
public class BottomTabView extends FrameLayout implements ViewPager.OnPageChangeListener {
// Initialize content
// add this at top level.
BottomTabViewClickListener bottomTabViewClickListener;
//add this function somewhere
public void setOnBottomTabViewClickListener(BottomTabViewClickListener bottomTabViewClickListener){
this.bottomTabViewClickListener = bottomTabViewClickListener;
}
// replace HomeActivity.openFileChooser(getContext());
mBottomImageView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
if (viewPager.getCurrentItem() == 1) {
bottomTabViewClickListener.onClick();
}
}
});
}
//end of BottomTab View
//add this after your BottomTabView not inside
interface BottomTabViewClickListener{
public void onClick();
}
Add the following in your HomeActivity
// Initialize BottomTabView
BottomTabView bottomTabView = findViewById(R.id.bottom_tabs);
bottomTabView.setUpWithViewPager(viewPager);
bottomTabView.setOnBottomTabViewClickListener(new BottomTabViewClickListener() {
@Override
public void onClick() {
// call your openFileChooser from here,
//REMOVE THE STATIC
openFileChooser(HomeActivity.this)
}
});