at the beginning I would like to point out that I am a beginner in java and android.
I'm coding an application, which I can use to send a image of test result to database. For that moment I've got possibility to upload picture to Firebase Storage and I have no clue what should I do next, to import(?) image from storage to a database of specific user.
public class BloodTestActivity extends AppCompatActivity {
ImageView imageSelected;
Button btn_uploadImage, btn_sendImage;
Uri uri;
StorageReference storageReference;
ProgressDialog progressDialog;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_blood_test);
imageSelected = findViewById(R.id.viewSelectedImage);
btn_uploadImage = findViewById(R.id.btn_uploadImage);
btn_sendImage = findViewById(R.id.btn_sendImage);
btn_uploadImage.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
UploadImage();
}
});
btn_sendImage.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
sendImage();
}
});
}
private void sendImage() {
progressDialog = new ProgressDialog(this);
progressDialog.setTitle("Sending test result...");
progressDialog.show();
SimpleDateFormat format = new SimpleDateFormat("yyy_MM_dd_HH_mm_ss", Locale.CANADA);
Date now = new Date();
String fileName = format.format(now);
storageReference = FirebaseStorage.getInstance().getReference("images/" + fileName);
storageReference.putFile(uri).addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
#Override
public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
imageSelected.setImageURI(null);
Toast.makeText(BloodTestActivity.this, "Success!", Toast.LENGTH_SHORT).show();
if (progressDialog.isShowing())
progressDialog.dismiss();
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
if (progressDialog.isShowing())
progressDialog.dismiss();
Toast.makeText(BloodTestActivity.this, "Failed!", Toast.LENGTH_SHORT).show();
}
});
}
private void UploadImage() {
Dexter.withContext(this)
.withPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
.withListener(new PermissionListener() {
#Override
public void onPermissionGranted(PermissionGrantedResponse permissionGrantedResponse) {
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(intent, 101);
}
#Override
public void onPermissionDenied(PermissionDeniedResponse permissionDeniedResponse) {
Toast.makeText(BloodTestActivity.this, "Permission Denied", Toast.LENGTH_SHORT).show();
}
#Override
public void onPermissionRationaleShouldBeShown(PermissionRequest permissionRequest, PermissionToken permissionToken) {
permissionToken.cancelPermissionRequest();
}
}).check();
}
#Override
protected void onActivityResult(int requestCode, int resultCode, #Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 101 && resultCode == RESULT_OK){
uri = data.getData();
imageSelected.setImageURI(uri);
}
}
}
I am using in-app purchase.
I make purchases in a specific Activity and I store in sharedpreferences.
There is no problem so far. Now I want to use these sharedpreferences` in another class and check that if a payment is made a specific task is to be done.
My code :
`#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_premium);
btnBuy = (Button) findViewById(R.id.btnBuy);
btnBuy.setEnabled(false);
btnBuy.setOnClickListener(this);
mBillingProcessor = new BillingProcessor(
this,
BAZAAR_RSA_KEY,
NIVAD_APPLICATION_ID,
NIVAD_APPLICATION_SECRET,
MarketName.CAFE_BAZAAR,
this
);
}
#Override
public void onClick(View v) {
mBillingProcessor.purchase(this, FULL_VERSION_SKU);
}
#Override
public void onBillingInitialized() {
btnBuy.setEnabled(true);
btnBuy.setBackgroundColor(getResources().getColor( R.color.colorBoy));
btnBuy.setText(R.string.Buy);
}
#Override
public void onProductPurchased(String sku, TransactionDetails transactionDetails) {
if (FULL_VERSION_SKU.equals(sku)) {
PreferenceManager.getDefaultSharedPreferences(PremiumActivity.this)
.edit().putBoolean("is_full_version", true).apply();
Toast.makeText(PremiumActivity.this, R.string.Lol, Toast.LENGTH_LONG).show();
}
}
#Override
public void onPurchaseHistoryRestored() {
if(mBillingProcessor.isPurchased(FULL_VERSION_SKU)){
PreferenceManager.getDefaultSharedPreferences(PremiumActivity.this)
.edit().putBoolean("is_full_version", true).apply();
} else {
PreferenceManager.getDefaultSharedPreferences(PremiumActivity.this)
.edit().putBoolean("is_full_version", false).apply();
}
}
#Override
public void onBillingError(int errorCode, Throwable throwable){
if (errorCode == 205){
Toast.makeText(PremiumActivity.this, R.string.nivad_protects_this_application, Toast.LENGTH_LONG).show();
}
}
#Override
protected void onDestroy() {
if (mBillingProcessor != null) {
mBillingProcessor.release();
}
super.onDestroy();
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (!mBillingProcessor.handleActivityResult(requestCode, resultCode, data)) {
super.onActivityResult(requestCode, resultCode, data);
}
} `
getDefaultSharedPreferences will create a shared preference file for Internal Activity only.
You should get an instance of SharedPreferences like this:
SharedPreferences preferences = context.getSharedPreferences("NAME",MODE_PRIVATE);
2.In another activity, just get the data out like what you do inside the previous Activity
SharedPreferences preferences = getSharedPreferences("NAME", MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
editor.getBoolean("is_full_version", false);
I am trying to use the Speech recognition without dialog with RecognitionListener, but it doesn't work. The following codes are the way now I use, and the bottom codes (test part) are for checking that could phone receive my commanding. Additionally, I have added permissions Audio. Finally, I don't want there have any button.
This is my first time asking question, so hope I dont violate any unspoken rules.
Thank you very much,
public class Step extends AppCompatActivity {
private final int SPEECH_RECOGNITION_CODE = 1;
private TextView txtOutput;
private Button btnMicrophone;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_step);
txtOutput = (TextView) findViewById(R.id.txt_output);
btnMicrophone = (Button) findViewById(R.id.btn_mic);
startSpeechToText();
btnMicrophone.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startSpeechToText();
}
});
}
private void startSpeechToText() {
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.getDefault());
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
intent.putExtra(RecognizerIntent.EXTRA_PROMPT, "SPEAK");
try {
startActivityForResult(intent, SPEECH_RECOGNITION_CODE);
} catch (ActivityNotFoundException a) {
Toast.makeText(getApplicationContext(), "Sorry! Speech recognition is not supported in this device.", Toast.LENGTH_SHORT).show();
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case SPEECH_RECOGNITION_CODE: {
if (resultCode == RESULT_OK && null != data) {
ArrayList<String> result = data
.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
String text = result.get(0);
txtOutput.setText(text);
test(txtOutput.getText().toString());
startSpeechToText();
}
break;
}
}
}
public void test (String com) {
for(int i=0;i<com.length();i++) {
if (com.startsWith("good",i)) {
com = com + "123";
Toast.makeText(CookStep.this, com, Toast.LENGTH_SHORT).show();
}
}
if(com.compareToIgnoreCase("OK")==0){
com=com+"456";
Toast.makeText(CookStep.this, com, Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(CookStep.this, com, Toast.LENGTH_SHORT).show();
}
}
}
I am new to android development , my app is ready but on some devices it lags when opening the navigation drawer and somethings on onclick event.I tried to use threads so that each event occurs on new thread but still i see lag please help me with this.
public class MainActivity extends BaseActivity implements OnClickListener {
private Button scanBtn,scanBtn2,scanBtn3;
Animation animTranslate,animAlpha;
LinearLayout lv;
Handler handler;
String[] permissions;
#Override
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_base);
super.onCreate(savedInstanceState);
permissions = new String[3];
permissions[0] = Manifest.permission.WRITE_EXTERNAL_STORAGE;
permissions[1] = Manifest.permission.READ_EXTERNAL_STORAGE;
permissions[2]=Manifest.permission.CAMERA;
ActivityCompat.requestPermissions(
this,
permissions,
5
);
scanBtn = (Button)findViewById(R.id.scanner);
scanBtn2=(Button)findViewById(R.id.scanner2);
scanBtn3=(Button)findViewById(R.id.scanner3);
lv=(LinearLayout)findViewById(R.id.container);
animTranslate= AnimationUtils.loadAnimation(MainActivity.this,R.anim.anim_translate);
animAlpha=AnimationUtils.loadAnimation(MainActivity.this,R.anim.anim_alpha);
scanBtn.setOnClickListener(MainActivity.this);
scanBtn2.setOnClickListener(MainActivity.this);
scanBtn3.setOnClickListener(MainActivity.this);
}
public void onClick(View v){
if(v.getId()==R.id.scanner){
v.startAnimation(animTranslate);
lv.startAnimation(animAlpha);
final IntentIntegrator scanIntegrator = new IntentIntegrator(this);
scanIntegrator.setDesiredBarcodeFormats(IntentIntegrator.QR_CODE_TYPES);
scanIntegrator.setPrompt(" ");
scanIntegrator.setCameraId(0);
scanIntegrator.setBeepEnabled(true);
scanIntegrator.setBarcodeImageEnabled(false);
handler=new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
scanIntegrator.initiateScan();
}
},500);
}else if(v.getId()==R.id.scanner2){
v.startAnimation(animTranslate);
lv.startAnimation(animAlpha);
final IntentIntegrator scanIntegrator2=new IntentIntegrator(this);
scanIntegrator2.setDesiredBarcodeFormats(IntentIntegrator.ONE_D_CODE_TYPES);
scanIntegrator2.setPrompt(" ");
scanIntegrator2.setCameraId(0);
scanIntegrator2.setBeepEnabled(true);
scanIntegrator2.setBarcodeImageEnabled(true);
handler=new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
scanIntegrator2.initiateScan();
}
},500);
}else if(v.getId()==R.id.scanner3){
v.startAnimation(animTranslate);
lv.startAnimation(animAlpha);
final Intent intent=new Intent(this,GenerateQRCodeActivity.class);
handler=new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
startActivity(intent); overridePendingTransition(R.anim.slide_in_right,R.anim.slide_out_left);
}
},500);
}
}
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
IntentResult scanningResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent);
if (scanningResult != null) {
String scanContent = scanningResult.getContents();
String scanFormat = scanningResult.getFormatName();
Intent intent1=new Intent(this,ResultActivity.class);
intent1.putExtra("con",scanContent);
if(scanContent != null && scanFormat != null) {
startActivity(intent1);
}
}
else{
Toast toast = Toast.makeText(getApplicationContext(),
"No scan data received!", Toast.LENGTH_SHORT);
toast.show();
}
}
#Override
public boolean onKeyDown(int keyCode, KeyEvent e) {
if (keyCode == KeyEvent.KEYCODE_MENU) {
// your action...
if (!mDrawerLayout.isDrawerOpen(mDrawerList)) {
mDrawerLayout.openDrawer(mDrawerList);
}
else{
mDrawerLayout.closeDrawer(mDrawerList);
}
return true;
}
return super.onKeyDown(keyCode, e);
}
#Override
public void onBackPressed() {
if(mDrawerLayout.isDrawerOpen(mDrawerList)){
mDrawerLayout.closeDrawer(mDrawerList);
}
else{
finish();
}
}
}
In my android app there are at least 4 places where the user can click a photo and then get a dialog saying "take a photo or pick from gallery to change your picture"
in each of these places its a different activity with different variables and different xml element names and I have a lot of really similar code, repeated through out my app.
I want to take it out in a separate class, but is so tangled up in the code that it feels like a heart transplantation. My object-oriented skills and Java skills arent that great (3 months experience) and maybe there is some workaround in Android that I dont know about.
I will provide two examples from different classes so you get an idea of how much repetition there is. I will be very grateful if someone could help me produce a separate object out of this
Settings class, image changer excerpt:
iUserAvatarSettings.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
final Dialog dialog = new Dialog(Settings.this, R.style.no_title_dialog);
if (!userHasProvidedOwnPhoto) {
dialog.setContentView(R.layout.signup_avatar_upload_dialog);
} else {
dialog.setContentView(R.layout.signup_avatar_upload_dialog_2);
bDeleteAvatar = (Button) dialog.findViewById(R.id.bDeleteAvatar);
try {
bDeleteAvatar.setTypeface(font1);
} catch (Exception e) {
}
bDeleteAvatar.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
setAvatarPath("default_avatar");
userHasProvidedOwnPhoto = false;
if (userSex.equals("Male") || userSex.equals("")) {
iUserAvatarSettings.setImageResource(R.drawable.avatar_default_male);
} else {
iUserAvatarSettings.setImageResource(R.drawable.avatar_default_female);
}
dialog.dismiss();
}
});
}
Button bTakeAPhoto = (Button) dialog.findViewById(R.id.bTakeAPhoto);
Button bSelectPhotoFromFile = (Button) dialog.findViewById(R.id.bSelectPhotoFromFile);
Button bCancelAvatarUpload = (Button) dialog.findViewById(R.id.bCancelAvatarUpload);
try {
bTakeAPhoto.setTypeface(font1);
bSelectPhotoFromFile.setTypeface(font1);
bCancelAvatarUpload.setTypeface(font1);
} catch (Exception e) {
}
bTakeAPhoto.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if (isIntentAvailable(Settings.this, MediaStore.ACTION_IMAGE_CAPTURE)) {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(takePictureIntent, TAKE_IMAGE_WITH_CAMERA);
dialog.dismiss();
} else {
toastMaker.toast(net.asdqwe.activities.Settings.this, Configurationz.ErrorMessages.DEVICE_UNABLE_TO_TAKE_PHOTOS, Toast.LENGTH_LONG);
dialog.dismiss();
userHasProvidedOwnPhoto = false;
}
}
});
bSelectPhotoFromFile.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Intent getImageFromGallery = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
getImageFromGallery.setType("image/*");
startActivityForResult(getImageFromGallery, PICK_IMAGE);
//avatarPath = saveUserAvatar.getUserAvatar().toString();
//setAvatarPath(saveUserAvatar.getUserAvatar().toString()); // this remains under question
dialog.dismiss();
}
});
bCancelAvatarUpload.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
dialog.dismiss();
}
});
dialog.show();
}
}); // end of image button on click handling
public static boolean isIntentAvailable(Context context, String action) {
final PackageManager packageManager = context.getPackageManager();
final Intent intent = new Intent(action);
List<ResolveInfo> list = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
return list.size() > 0;
}
private void handleSmallCameraPhoto(Intent intent) {
try {
Bundle extras = intent.getExtras();
mImageBitmap = (Bitmap) extras.get("data");
iUserAvatarSettings.setImageBitmap(mImageBitmap);
saveUserAvatar.SaveImage(this, mImageBitmap);
userHasProvidedOwnPhoto = true;
setAvatarPath(saveUserAvatar.getUserAvatar().toString());
} catch (Exception e) {
toastMaker.toast(net.asdqwe.activities.Settings.this, Configurationz.ErrorMessages.TAKING_PHOTO_FAILED, Toast.LENGTH_LONG);
userHasProvidedOwnPhoto = false;
}
}
private void handleGalleryPhoto(Intent intent) {
try {
Uri _uri = intent.getData();
Cursor cursor = getContentResolver().query(_uri, new String[] { android.provider.MediaStore.Images.ImageColumns.DATA }, null, null, null);
cursor.moveToFirst();
final String imageFilePath = cursor.getString(0);
cursor.close();
mImageBitmap2 = BitmapFactory.decodeFile(imageFilePath);
iUserAvatarSettings.setImageBitmap(mImageBitmap2);
saveUserAvatar.SaveImage(this, mImageBitmap2);
userHasProvidedOwnPhoto = true;
setAvatarPath(saveUserAvatar.getUserAvatar().toString());
} catch (Exception e) {
toastMaker.toast(net.zxcasd.activities.Settings.this, Configurationz.ErrorMessages.PICKING_PHOTO_FROM_GALLERY_FAILED, Toast.LENGTH_LONG);
userHasProvidedOwnPhoto = false;
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == TAKE_IMAGE_WITH_CAMERA && resultCode == RESULT_OK && null != data) {
handleSmallCameraPhoto(data);
} else if (requestCode == PICK_IMAGE && resultCode == RESULT_OK && null != data) {
handleGalleryPhoto(data);
}
}
Signup class, image excerpt:
iUserAvatar.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
final Dialog dialog = new Dialog(Signup.this, R.style.no_title_dialog);
if (!userHasProvidedOwnPhoto) {
dialog.setContentView(R.layout.signup_avatar_upload_dialog);
} else {
dialog.setContentView(R.layout.signup_avatar_upload_dialog_2);
bDeleteAvatar = (Button) dialog.findViewById(R.id.bDeleteAvatar);
try {
bDeleteAvatar.setTypeface(font1);
} catch (Exception e) {
}
bDeleteAvatar.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
userHasProvidedOwnPhoto = false;
if (userSex.equals("Male") || userSex.equals("")) {
iUserAvatar.setImageResource(R.drawable.avatar_default_male);
} else {
iUserAvatar.setImageResource(R.drawable.avatar_default_female);
}
dialog.dismiss();
}
});
}
Button bTakeAPhotoSignupPage = (Button) dialog.findViewById(R.id.bTakeAPhoto);
Button bSelectPhotoFromFileSignupPage = (Button) dialog.findViewById(R.id.bSelectPhotoFromFile);
Button bCancelAvatarUpload = (Button) dialog.findViewById(R.id.bCancelAvatarUpload);
try {
bTakeAPhotoSignupPage.setTypeface(font1);
bSelectPhotoFromFileSignupPage.setTypeface(font1);
bCancelAvatarUpload.setTypeface(font1);
} catch (Exception e) {
}
bTakeAPhotoSignupPage.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if (isIntentAvailable(Signup.this, MediaStore.ACTION_IMAGE_CAPTURE)) {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(takePictureIntent, TAKE_IMAGE_WITH_CAMERA);
dialog.dismiss();
} else {
toastMaker.toast(net.asdqwe.activities.Signup.this, Configurationz.ErrorMessages.DEVICE_UNABLE_TO_TAKE_PHOTOS, Toast.LENGTH_LONG);
dialog.dismiss();
userHasProvidedOwnPhoto = false;
}
}
});
bSelectPhotoFromFileSignupPage.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Intent getImageFromGallery = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
getImageFromGallery.setType("image/*");
startActivityForResult(getImageFromGallery, PICK_IMAGE);
dialog.dismiss();
}
});
bCancelAvatarUpload.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// and deal with photo
dialog.dismiss();
}
});
dialog.show();
}
});
public static boolean isIntentAvailable(Context context, String action) {
final PackageManager packageManager = context.getPackageManager();
final Intent intent = new Intent(action);
List<ResolveInfo> list = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
return list.size() > 0;
}
private void handleSmallCameraPhoto(Intent intent) {
try {
Bundle extras = intent.getExtras();
mImageBitmap = (Bitmap) extras.get("data");
iUserAvatar.setImageBitmap(mImageBitmap);
saveUserAvatar.SaveImage(this, mImageBitmap);
userHasProvidedOwnPhoto = true;
} catch (Exception e) {
toastMaker.toast(net.asdqwe.activities.Signup.this, Configurationz.ErrorMessages.TAKING_PHOTO_FAILED, Toast.LENGTH_LONG);
userHasProvidedOwnPhoto = false;
}
}
private void handleGalleryPhoto(Intent intent) {
try {
Uri _uri = intent.getData();
Cursor cursor = getContentResolver().query(_uri, new String[] { android.provider.MediaStore.Images.ImageColumns.DATA }, null, null, null);
cursor.moveToFirst();
final String imageFilePath = cursor.getString(0);
cursor.close();
mImageBitmap2 = BitmapFactory.decodeFile(imageFilePath);
iUserAvatar.setImageBitmap(mImageBitmap2);
saveUserAvatar.SaveImage(this, mImageBitmap2);
userHasProvidedOwnPhoto = true;
} catch (Exception e) {
toastMaker.toast(net.asdqwe.activities.Signup.this, Configurationz.ErrorMessages.PICKING_PHOTO_FROM_GALLERY_FAILED, Toast.LENGTH_LONG);
userHasProvidedOwnPhoto = false;
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == TAKE_IMAGE_WITH_CAMERA && resultCode == RESULT_OK && null != data) {
handleSmallCameraPhoto(data);
} else if (requestCode == PICK_IMAGE && resultCode == RESULT_OK && null != data) {
handleGalleryPhoto(data);
}
}
Move isIntentAvailable(Context, String) into an utility class. This could be helpful in some other uses cases.
Create a abstract class (refered to as MyAbstractActivity) which contains all common activity methods: handleSmallCameraPhoto(Intent), handleGalleryPhoto(Intent) and onActivityResult(int, int, Intent)
iUserAvatarSettings / iUserAvatar must be part of MyAbstractActivity. If you need different implementations of it for different use cases, create an abstract getter, f.e. getUserAvatarButton().
setAvatarPath(String) is only used in one of the two classes. Make it an abstract method in MyAbstractActivity and implement it as needed.
Let the other classes inherit from MyAbstractActivity.
Transform the OnClickListener into a class.
As constructor arguments you pass a subclass of MyAbstractActivity and a Button (iUserAvatarSettings / iUserAvatar).
Use the constructor arguments to refer to methods and variables like userHasProvidedOwnPhoto.
The iUserAvatar.setOnClickListener(...) part will remain in MyAbstractActivity.
Now you have removed the code redundancy, but there will be more to do. Probably encapsulation isn't good enough right now.
Think about moving methods and variables to the OnClickListener class or away from it.
Think about moving methods and variables to MyAbstractActivity or away from it.
Ask some more experienced developer to make a review. (Don't ask him to refactor it.)
If you have specific refactoring question come back her and ask the community.
You can check my project Android Image Helper on Github
An activity with image selection feature would look something like this:
public class ImageUploaderActivity extends Activity implements ImageChooseCallback {
ImageUploadEngine uploadEngine;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
uploadEngine = new ImageUploadEngine.Builder(this, savedInstanceState).build();
//...
someButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
uploadEngine.performImageAsk(R.string.app_name, R.string.newphoto, R.string.oldphoto, R.string.choose, R.drawable.ic_launcher);
}
});
}
#Override
public void onSaveInstanceState(Bundle outState) {
uploadEngine.onSaveInstanceState(outState);
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if(uploadEngine.onActivityResult(requestCode, resultCode, data)) return;
super.onActivityResult(requestCode, resultCode, data);
}
#Override
public void onCanceled(ImageUploadEngine engine) {
//notifying you that the user has canceled the request
}
#Override
public void onChosen(ImageUploadEngine engine, String path, boolean newPicture) {
//notifying you that the user has selected an image
}
#Override
public void onError(ImageUploadEngine engine, Exception ex) {
//notifying you that an error has occurred
}
}
The uploadEngine also exposes functions performImageTake and performImageChoose in case the default dialog (to choose) does not suit your needs.
FYI:
This project aims to provide an easy-to-use instrument for android developers to use when they need the user to select an image on their android phone and upload it. If the process has any errors on specific phones, these should be handled inside the library to provide the same user experience on all phones.
Here is my approach to refactor your code. I have tried to give some justification about my design as well.
1) You could abstract away the dialog into a widget.
You can create a PhotoPickerDialog that extends Dialog and provides desired functionality. Then you can re-use this widget at different places / activities.
public class PhotoPickerDialog extends Dialog {
//private fields
private Typeface buttonFont = Typeface.DEFAULT;
private Button bTakeAPhoto;
private Button bSelectPhotoFromFile;
private Button bCancelAvatarUpload;
private IMediaHelper mMediaHelper;
public PhotoPickerDialog(Context context) {
super(context);
initView();
}
public PhotoPickerDialog(Context context, int theme) {
super(context, theme);
initView();
}
protected PhotoPickerDialog(Context context, boolean cancelable, OnCancelListener cancelListener) {
super(context, cancelable, cancelListener);
initView();
}
protected void setLayoutResource() {
setContentView(R.layout.signup_avatar_upload_dialog);
}
protected void initView() {
setLayoutResource();
bTakeAPhoto = (Button) findViewById(R.id.bTakeAPhoto);
bSelectPhotoFromFile = (Button) findViewById(R.id.bSelectPhotoFromFile);
bCancelAvatarUpload = (Button) findViewById(R.id.bCancelAvatarUpload);
bCancelAvatarUpload.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
dismiss();
}
});
}
/**
* Set the type face of the button, in case you want to have different
* font at different times.
*/
public void setButtonFont(Typeface font) {
buttonFont = font;
bTakeAPhoto.setTypeface(buttonFont);
bSelectPhotoFromFile.setTypeface(buttonFont);
bCancelAvatarUpload.setTypeface(buttonFont);
}
/**
* Set the media helper object which implements the functionality to
* take and pick photo. So the task delegated to this media helper.
*/
public void setMediaHelper(IMediaHelper mediaHelper) {
mMediaHelper = mediaHelper;
bTakeAPhoto.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mMediaHelper.takePhoto();
dismiss();
}
});
bSelectPhotoFromFile.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mMediaHelper.pickPhoto();
dismiss();
}
});
}
}
Since you need two different types of dialogs depending on whether the user has a avatar photo already or not. Let us create a PhotoPickerDialogWithDelete that extends PhotoPickerDialog. Then we just have to override a few methods and add the delete callback to make our new type of dialog without re-writing a lot of code.
public class PhotoPickerDialogWithDelete extends PhotoPickerDialog {
private Button bDeleteAvatar;
public PhotoPickerDialogWithDelete(Context context) {
super(context);
}
public PhotoPickerDialogWithDelete(Context context, int theme) {
super(context, theme);
}
protected PhotoPickerDialogWithDelete(Context context, boolean cancelable, OnCancelListener cancelListener) {
super(context, cancelable, cancelListener);
}
#Override
protected void setLayoutResource() {
setContentView(R.layout.signup_avatar_upload_dialog_2);
}
#Override
protected void initView() {
super.initView();
bDeleteAvatar = (Button) findViewById(R.id.bDeleteAvatar);
}
#Override
public void setButtonFont(Typeface font) {
super.setButtonFont(font);
bDeleteAvatar.setTypeface(font);
}
public void setOnDeleteListener(final View.OnClickListener listener) {
bDeleteAvatar.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
dismiss();
listener.onClick(v);
}
});
}
}
2) Move the photo intent related functionality to an abstract base class MediaSupportActivity.
All the activities that need to support taking/picking photos just need to extend from this class and implement two simple methods handleSmallCameraPhoto(Bitmap) and handleGalleryPhoto(Bitmap). They do not have to explicitly care about how to take/pick photos, but only care about how to handle them (which can be different for different activities).
public abstract class MediaSupportActivity extends Activity implements IMediaHelper {
private static final int TAKE_IMAGE_WITH_CAMERA = 1;
private static final int PICK_IMAGE = 2;
protected abstract void handleSmallCameraPhoto(Bitmap image);
protected abstract void handleGalleryPhoto(Bitmap image);
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == TAKE_IMAGE_WITH_CAMERA && resultCode == RESULT_OK && null != data) {
Bundle extras = data.getExtras();
Bitmap bitmap = (Bitmap) extras.get("data");
handleSmallCameraPhoto(bitmap);
}
else if (requestCode == PICK_IMAGE && resultCode == RESULT_OK && null != data) {
Uri _uri = data.getData();
Cursor cursor = getContentResolver().query(_uri, new String[] { android.provider.MediaStore.Images.ImageColumns.DATA }, null, null, null);
cursor.moveToFirst();
final String imageFilePath = cursor.getString(0);
cursor.close();
Bitmap bitmap = BitmapFactory.decodeFile(imageFilePath);
handleGalleryPhoto(bitmap);
}
}
#Override
public void takePhoto() {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(takePictureIntent, MediaSupportActivity.TAKE_IMAGE_WITH_CAMERA);
}
#Override
public void pickPhoto() {
Intent getImageFromGallery = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
getImageFromGallery.setType("image/*");
startActivityForResult(getImageFromGallery, MediaSupportActivity.PICK_IMAGE);
}
}
3) There is an interface IBBMediaHelper that defined two methods: takePhoto() and pickPhoto(). We pass a IBBMediaHelper instance to the dialog, so that that dialog does not have full access to the other properties and methods of the MediaSupportActivity
public interface IMediaHelper {
public void takePhoto();
public void pickPhoto();
}
4) Here is a sample how to use the above classes together:
public class MainActivity extends MediaSupportActivity {
Button iUserAvatarSettings;
private boolean userHasProvidedOwnPhoto = false;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
iUserAvatarSettings = (Button) findViewById(R.id.button);
iUserAvatarSettings.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
final PhotoPickerDialog dialog;
if (!userHasProvidedOwnPhoto) {
dialog = new PhotoPickerDialog(MainActivity.this);
}
else {
dialog = new PhotoPickerDialogWithDelete(MainActivity.this);
((PhotoPickerDialogWithDelete)dialog).setOnDeleteListener(onAvatarDelete);
}
dialog.setButtonFont(Typeface.MONOSPACE);
dialog.setMediaHelper(MainActivity.this);
dialog.show();
}
});
}
#Override
protected void handleSmallCameraPhoto(Bitmap image) {
// sample code
ImageView imageView = (ImageView) findViewById(R.id.imageView);
imageView.setImageBitmap(image);
}
#Override
protected void handleGalleryPhoto(Bitmap image) {
// sample code
ImageView imageView = (ImageView) findViewById(R.id.imageView);
imageView.setImageBitmap(image);
}
private OnClickListener onAvatarDelete = new OnClickListener() {
#Override
public void onClick(View v) {
// this is executed when delete is clicked
// check for male/female and update the avatar
// etc. etc.
}
};
}
Update: You can also create an avatar widget AvatarWidget class that contains an ImageView and PhotoPickerDialog. This class encapsulates the logic of displaying an avatar, clicking on it to show the menu and setting the default avatar when delete is clicked. Then in the main activity, simply declare your avatar widget via code or XML and call the setters on it when the image is received.