1

I want to change the User password inside ProfileActivity.java using Retrofit library from a pop up Dialog.
The scenario is the Dialog will show after User click the Edit Button in the MainActivity.java.

I already been succeed at showing the Dialog and puts if-else on it, but the problem is i can't send the data to the Retrofit method that are in the ProfileActivity.java.

I don't know how and where to call the Retrofit methods, that making the app crashed.

Here are my code ProfileActivity.java with Retrofit method's in the bottom:

public class ProfileActivity extends AppCompatActivity implements ChangePassDialog.ChangePassDialogListener {

    private String oldPassExtra, newPassExtra;

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

    Button btnEditPass = findViewById(R.id.btn_editPass);

        btnEditPass.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                ChangePass changePassDialog = new ChangePass();
                changePassDialog.show(getSupportFragmentManager(),"Pass Dialog Changer");


      }
        });    
    }

    public void changePassApi(String oldPass, String newPass) {
        RestApi api = RetroFit.getInstanceRetrofit();
        Call<ResponseRegister> registerCall = api.changePassword(
                oldPass,
                newPass
        );
        registerCall.enqueue(new Callback<ResponseRegister>() {
            // If response success
            @Override
            public void onResponse(Call<ResponseRegister> call, Response<ResponseRegister> response) {
                if (response.isSuccessful()) {
                    String result = response.body().getResult();
                    String msg = response.body().getMsg();
                    if (result.equals("1")) {
                        Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
                    } else {
                        Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_LONG).show();
                    }
                } else {
                    Toast.makeText(getApplicationContext(), "Connection Failed!", Toast.LENGTH_LONG).show();
                }
            }

            // if response fail
            @Override
            public void onFailure(Call<ResponseRegister> call, Throwable t) {
                Toast.makeText(getApplicationContext(), "Connection Problem", Toast.LENGTH_LONG).show();
            }
        });
    }
}

And here are my ChangePassDialog.java

    public class ChangePass extends DialogFragment {
    ProfileActivity profileActivity;
    private EditText edtOldPass, edtNewPass;
    private TextView okPass, cancelPass;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.dialog_changepass, container, false);
        okPass = view.findViewById(R.id.ok_pass);
        cancelPass = view.findViewById(R.id.cancel_pass);
        edtOldPass = view.findViewById(R.id.edit_passOld);
        edtNewPass = view.findViewById(R.id.edit_passNew);

        profileActivity = new ProfileActivity();

        cancelPass.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                getDialog().dismiss();
            }
        });

        okPass.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String inputOld = edtOldPass.getText().toString().trim();
                String inputNew = edtNewPass.getText().toString().trim();
                if (inputOld.isEmpty()) {
                    edtOldPass.requestFocus();
                    edtOldPass.setError("Field password lama tidak boleh kosong");
                    // Toast.makeText(getActivity(),"hallo",Toast.LENGTH_SHORT).show();
                } else if (inputNew.isEmpty()) {
                    edtNewPass.requestFocus();
                    edtNewPass.setError("Field password baru tidak boleh kosong");
                } else if (inputOld.equals(inputNew)) {
                    edtNewPass.requestFocus();
                    edtNewPass.setError("Password tidak boleh sama");
                } else {
                    profileActivity.changePassApi(inputOld, inputNew);
                }
            }
        });
        return view;
    }
}

And Dialog.xml

    <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <EditText
        android:id="@+id/edit_passOld"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/activity_vertical_margin"
        android:layout_marginBottom="10dp"
        android:hint="Password lama"
        android:inputType="textPassword"
        android:paddingStart="25dp"
        android:paddingLeft="25dp"
        android:paddingEnd="0dp"
        android:paddingRight="0dp" />

    <EditText
        android:id="@+id/edit_passNew"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/edit_passOld"
        android:layout_alignParentStart="true"
        android:layout_alignParentLeft="true"
        android:hint="Password baru"
        android:inputType="textPassword"
        android:paddingStart="25dp"
        android:paddingLeft="25dp"
        android:paddingEnd="0dp"
        android:paddingRight="0dp" />

    <TextView
        android:id="@+id/ok_pass"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/edit_passNew"
        android:layout_alignParentEnd="true"
        android:layout_alignParentRight="true"
        android:layout_marginTop="30dp"
        android:layout_marginEnd="25dp"
        android:layout_marginBottom="@dimen/activity_vertical_margin"
        android:layout_marginRight="25dp"
        android:text="OK"
        android:textColor="@color/purple"
        android:textSize="16sp" />

    <TextView
        android:id="@+id/cancel_pass"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/edit_passNew"
        android:layout_alignParentStart="true"
        android:layout_alignParentLeft="true"
        android:layout_marginBottom="@dimen/activity_vertical_margin"
        android:layout_marginStart="25dp"
        android:layout_marginLeft="25dp"
        android:layout_marginTop="30dp"
        android:text="CANCEL"
        android:textColor="@color/purple"
        android:textSize="16sp" />
</RelativeLayout>

Log Cat

01-25 14:40:15.630 2102-2102/com.supermalkarawaci.redeempointapps E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.supermalkarawaci.redeempointapps, PID: 2102
java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.Context android.content.Context.getApplicationContext()' on a null object reference
    at android.content.ContextWrapper.getApplicationContext(ContextWrapper.java:107)
    at com.supermalkarawaci.redeempointapps.activity.ProfileActivity$4.onResponse(ProfileActivity.java:340)
    at retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall$1$1.run(ExecutorCallAdapterFactory.java:70)
    at android.os.Handler.handleCallback(Handler.java:739)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:148)
    at android.app.ActivityThread.main(ActivityThread.java:5480)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

Appreciate any helps!

Muhammad Faisal
  • 305
  • 2
  • 15

2 Answers2

1

profileActivity = new ProfileActivity();

you can't get ProfileActivity object like this, and never do it. It initializes brand new instance of ProfileActivity which has nothing to do with your running activity.

Do it like this.

profileActivity = (ProfileActivity) getActivity();

Option 2.

Prefer this one

Place your public void changePassApi() method inside your Dialog fragment

rgaraisayev
  • 368
  • 3
  • 11
  • Wow it works sir, thank you so much! Yes i didn't mastering yet the basic of java, initialization, etc that makes me confused about this. the **option 1** work fine, but now, im trying your **option 2**, because i need to clarify the old password to the existing database – Muhammad Faisal Jan 25 '19 at 07:54
  • The **two option** above is working, thanks a lot! – Muhammad Faisal Jan 25 '19 at 08:06
  • Not a good practice, 1. getActivity will not always be ProfileActivity. 2. getActivity() can return null if activity isFinishing: throws exception. 3. storing activity instance in a member of other class might lead to ActivityLeak if the class instance is in the memory – Vikas Borkar Jan 25 '19 at 08:10
  • @VikasBorkar I offered a fix for his current problem. And then as you notice, i showed him more correct way of doing it with option 2 – rgaraisayev Jan 25 '19 at 08:28
  • @FaisalIchal welcome, please take Vikas Borkar's comment into account and prefer option 2 – rgaraisayev Jan 25 '19 at 08:31
  • Sure man, even it is a bit complex for me to understand as an amateur, but i'll try thanks Vikas – Muhammad Faisal Jan 25 '19 at 09:28
0

First of all, you should never create an instance of any activity directly.

  1. Remove profileActivity = new ProfileActivity(); (never do this)
  2. Define interface for fragment Callbacks(can be named anything)
  3. Make host activity implement Callbacks
  4. Get the activity reference in onAttach() and typecast to Callbacks and make sure you set it to null in onDetach() to prevent leak (Best practice)
  5. Invoke Callback method when required

Sample Fragment Callbacks implementation.

Host Activity:

public class MainActivity extends AppCompatActivity implements LoginFragment.Callbacks {
    .
    .
    .
    @Override
    public void changePassword(String newPassword) {
        //do retrofit call
    }
    .
    .
    .
}

Dialog Fragment:

public class LoginFragment extends DialogFragment {

    private Callbacks mCallbacks;

    private Button btnOk;
    .
    .
    .
    private void setViews() {
        btnOk.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mCallbacks.changePassword("1234");
            }
        });
    }
    .
    .
    .
    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        mCallbacks = (Callbacks) context;
    }

    @Override
    public void onDetach() {
        super.onDetach();
        mCallbacks = null;
    }

    public interface Callbacks {
        void changePassword(String newPassword);
    }
}

Tip: Additionally, you can handle typecast exception and callback reference null check when invoking Callback methods.

Vikas Borkar
  • 417
  • 4
  • 7
  • So the first answer may lead to memory leak? You mean undeleted stack of activities? But how if my activity were just accessed once? I mean the dialog is only showed once in the B activity, then after it done change the password it will dismissed then the back button just brought B activity to A activity, nowhere else. **CMIIW** – Muhammad Faisal Jan 25 '19 at 08:27