Java – Firebase UI output is blank after generating a signed APK with minifyEnabled set to true

Firebase UI output is blank after generating a signed APK with minifyEnabled set to true… here is a solution to the problem.

Firebase UI output is blank after generating a signed APK with minifyEnabled set to true

My app production is almost complete, but I’ve been trying to solve the last problem. I made an activity that displays data fetched from Firestore using FirestoreRecyclerAdapter, and it works perfectly when running on my emulator. But after I use minifyEnabled:true to generate a signed APK and install it on my device, RecyclerView only shows blank. When I set minifyEnabled:false, it worked just fine. Below is my code

For the TransactionRecycler class

public class TransactionRecycler {
    private String payment_date;
    private String status;
    private String meternumber;
    public String reference;
    private String amount_formatted;
    private Double PurchasedUnits;
    private Double FreeUnits;
    private Double Vat;
    public String id;
    private String payment_time;
    public String state;
    private String token;
    public String address;
    private String account_name;

public TransactionRecycler(String payment_date, String status, String meternumber, String reference, String id, String amount_formatted, String payment_time, String token, Double PurchasedUnits,
                               Double FreeUnits, Double Vat, String account_name, String address){
        this.payment_date = payment_date;
        this.status = status;
        this.meternumber = meternumber;
        this.reference = reference;
        this.amount_formatted = amount_formatted;
        this. PurchasedUnits = PurchasedUnits;
        this. Vat = Vat;
        this.state = state;
        this. FreeUnits = FreeUnits;
        this.id = id;
        this.payment_time = payment_time;
        this.token = token;
        this.account_name = account_name;
        this.address = address;
    }

public TransactionRecycler(){};

public void setDate(String payment_date){this.payment_date = payment_date; }
    public void setId(String id){this.id =id; }
    public void setState(String id){this.state =state; }

public String getPayment_date(){return payment_date; }
    public String getStatus(){return status; }
    public String getMeternumber(){return meternumber; }
    public String getReference(){return reference; }
    public String getAddress(){return address; }

public String getAmount_formatted(){return amount_formatted; }
    public Double getPurchasedUnits(){return PurchasedUnits; }
    public Double getFreeUnits(){return FreeUnits; }
    public Double getVat(){return Vat; }
    public String getId(){return id; }
    public String getstate(){return state; }
   public String getPayment_time(){return payment_time; }
    public String getToken(){return token; }
    public String getAccount_name(){return account_name; }
}

For TRAnsactionActivity

public class TransactionActivity extends AppCompatActivity {

private static final String TAG = "TransactionActivity";
    private final static String strUrlId= "https://www.eliminateramp.com?exmen=";
    private static final int SERVICE_CHARGE = 100;
    private static final String PLEASE_CHECK_YOUR_METER_NUMBER_AND_TRY_AGAIN = "Please check your meter number and try again";
    private static final String COULDN_T_CONNECT_AT_THIS_TIME_PLEASE_TRY_AGAIN = "Couldn't connect at this time, Please try again";
    private static final String FAILLED = "failled";
    private static final String NOT_FOUND = "NOT FOUND";
    private FirebaseAuth mAuth;
    private String userID;
    private RecyclerView mTransactionList;
    protected LinearLayoutManager linearLayoutManager;
    public FirestoreRecyclerAdapter adapter;
    private SlidingUpPanelLayout mLayout;

private NavigationView navigation;
    private DrawerLayout mDrawerLayout;
    private ActionBarDrawerToggle mToggle;
    private static final String SPACE = " ";
    private static String phone;
    private Button charname;
    private TextView emailview;
    private TextView fullnameview;
    private TextView priceidrc;
    private static TextView post_meterno;
    private static String aamount;

public boolean onCreateOptionsMenu(Menu menu){
        getMenuInflater().inflate(R.menu.top_menu, menu);
        return super.onCreateOptionsMenu(menu);
    }

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_transaction);
        mTransactionList = findViewById(R.id.friend_listinc);
        mLayout = findViewById(R.id.sliding_layout);
        mAuth = FirebaseAuth.getInstance();
        userID = mAuth.getCurrentUser().getUid();
        Log.d("Error","The UID is: "+userID);

Toolbar mToolbar = findViewById(R.id.nav_actionbar);
        setSupportActionBar(mToolbar);
        mDrawerLayout = findViewById(R.id.drawerlayout);
        navigation = findViewById(R.id.navigationview);

mToggle = new ActionBarDrawerToggle(this, mDrawerLayout, R.string.open, R.string.close);

View headerView = navigation.getHeaderView(0);

emailview = headerView.findViewById(R.id.emailviewraw);
        fullnameview = headerView.findViewById(R.id.fullnameviewid);
        charname = headerView.findViewById(R.id.charnameid);

mDrawerLayout.addDrawerListener(mToggle);
        mToggle.syncState();
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);

try{
            getUserData();
        }catch (Exception e){
            Log.d(TAG, "Failed to get user Data at this time coz: "+e);

}

ImageView imgview = findViewById(R.id.closeid);
        imgview.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mLayout.animate();
                mLayout.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
            }
        });

charname.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivity(new Intent(TransactionActivity.this,UpdateActivity.class));
            }
        });

init();
        inTrans();
        initInstances();

}

@Override
    public boolean onOptionsItemSelected(MenuItem item) {

return mToggle.onOptionsItemSelected(item) || super.onOptionsItemSelected(item);
    }

private void initInstances() {
          getSupportActionBar().setHomeButtonEnabled(true);
           getSupportActionBar().setDisplayHomeAsUpEnabled(true);

navigation = findViewById(R.id.navigationview);
        navigation.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) {
                int id = menuItem.getItemId();
                switch (id) {
                    case R.id.nav_home:
                        Do some thing here
                         add navigation drawer item onclick method here
                        Intent intent2 = new Intent(TransactionActivity.this, HomePageActivity.class);
                        startActivity(intent2);
                        break;
                    case R.id.nav_estimate:
                        mDrawerLayout.closeDrawer(GravityCompat.START);
                        break;
                    Do some thing here
                     add navigation drawer item onclick method here
                    case R.id.nav_Settings:
                        Do some thing here
                         add navigation drawer item onclick method here
                        Intent intent3 = new Intent(TransactionActivity.this, HelpActivity.class);
                        startActivity(intent3);
                        break;

case R.id.nav_hc:
                        Do some thing here
                         add navigation drawer item onclick method here
                        Intent intent = new Intent(TransactionActivity.this, HelpCenterActivity.class);
                        startActivity(intent);
                        break;
                }
                return false;
            }
        });

}

private void init(){
        linearLayoutManager = new LinearLayoutManager(getApplicationContext(), LinearLayoutManager.VERTICAL, false);
        mTransactionList.setLayoutManager(linearLayoutManager);
    }

private void inTrans(){

Query query = FirebaseFirestore.getInstance().collection("user-orders").document(userID).collection("successful-orders").orderBy("RequestedOn");

FirestoreRecyclerOptions<TransactionRecycler> response = new FirestoreRecyclerOptions.Builder<TransactionRecycler>()
                .setQuery(query, TransactionRecycler.class)
                .build();

adapter = new FirestoreRecyclerAdapter<TransactionRecycler, TransactionRecyclerHolder>(response){

@Override
            public TransactionRecyclerHolder onCreateViewHolder(ViewGroup parent, int viewType) {

View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.transaction_list, parent, false);
                return new TransactionRecyclerHolder(view);
            }
            @Override
            public void onError(@NonNull FirebaseFirestoreException e) {
                Log.e("error", e.getMessage());
            }

@Override
            protected void onBindViewHolder(@NonNull TransactionRecyclerHolder holder, int position, @NonNull final TransactionRecycler model) {

progressBar.setVisibility(View.GONE);
                holder.setDate(model.getPayment_date());
                holder.setStatus(model.getStatus());
                holder.setReference(model.getId());
                holder.setMeterno(model.getMeternumber());
                holder.setPrice(model.getAmount_formatted());

mLayout.setFadeOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        mLayout.animate();
                        mLayout.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
                    }
                });
                holder.itemView.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        mLayout.setPanelHeight(4);

mLayout.animate();
                        mLayout.setAnchorPoint(2.0f);
                        mLayout.setPanelState(SlidingUpPanelLayout.PanelState.EXPANDED);

priceidrc = findViewById(R.id.priceidsc);
                        TextView dateidrc = findViewById(R.id.dateidsc);
                        TextView referenceidrc = findViewById(R.id.refidsc);
                        TextView statusidrc = findViewById(R.id.statusidsc);
                        TextView meternoidrc = findViewById(R.id.meternoidsc);
                        TextView paymenttimeidrc = findViewById(R.id.paymenttimeidsc);
                        TextView tokenidrc = findViewById(R.id.tokenidsc);
                        TextView creditidrc = findViewById(R.id.creditidsc);
                        TextView freeunitsrc = findViewById(R.id.freeunitsidsc);
                        TextView vatrc = findViewById(R.id.vatidsc);
                        TextView accountnameidrc = findViewById(R.id.namesidsc);
                        TextView address = findViewById(R.id.addressidsc);

dateidrc.setText(model.getPayment_date());
                        statusidrc.setText(model.getStatus());
                        meternoidrc.setText(model.getMeternumber());

priceidrc.setText(model.getAmount_formatted());
                        referenceidrc.setText(model.getId());
                        paymenttimeidrc.setText(model.getPayment_time());
                        tokenidrc.setText(model.getToken());
                        creditidrc.setText(String.valueOf(model.getPurchasedUnits()));
                        freeunitsrc.setText(String.valueOf(model.getFreeUnits()));
                        vatrc.setText(String.valueOf(model.getVat()));
                        accountnameidrc.setText(model.getAccount_name());
                        address.setText(model.getAddress());
                    }
                });

}
        };

adapter.notifyDataSetChanged();
        mTransactionList.setAdapter(adapter);
    }

@Override
    public void onStart() {
        super.onStart();
        adapter.startListening();
    }

@Override
    public void onStop() {
        super.onStop();
        adapter.stopListening();
    }

public static class TransactionRecyclerHolder extends RecyclerView.ViewHolder {

View mView;

TransactionRecyclerHolder(View itemView) {
            super(itemView);

mView = itemView;
        }

public void setDate(String payment_date) {
            TextView post_date = mView.findViewById(R.id.dateid);
            post_date.setText(payment_date);
        }

public void setStatus(String status) {
            TextView post_status = mView.findViewById(R.id.statusid);
            post_status.setText(status);
        }

public void setMeterno(String meterno) {
            post_meterno = mView.findViewById(R.id.meternoid);
            post_meterno.setText(meterno);
        }

public void setReference(String reference) {
            TextView post_ref = mView.findViewById(R.id.refid);
            post_ref.setText(reference);
        }

public void setPrice(String price) {
            TextView post_price = mView.findViewById(R.id.priceid);
            post_price.setText(price);
            aamount=price;
        }

}

public void getUserData() {
        mAuth= FirebaseAuth.getInstance();

try{
            userID = mAuth.getCurrentUser().getUid();
        }catch(Exception e){

Log.d(TAG, "Failled somehow");
        }

Log.d(TAG, "ur uid => "+userID);
        DocumentReference mDocRef = FirebaseFirestore.getInstance().collection("users").document(userID);

mDocRef.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
            @Override
            public void onComplete(@NonNull Task<DocumentSnapshot> task) {
                if(task.isSuccessful()){
                    DocumentSnapshot doc = task.getResult();
                    String firstname = doc.get("firstName").toString();
                    String lastname = doc.get("lastName").toString();
                    phone = doc.get("phone").toString();
                    String fullname = firstname+SPACE+lastname;
                    fullnameview.setText(fullname);

try{
                        emailview.setText(mAuth.getCurrentUser().getEmail());
                    }catch(Exception e){
                        Log.d(TAG,"oops");
                    }

charname.setText(firstname.substring(0,1));

}
            }
        });
    }

public void buyAgain(View view){

final Dialog dialog = new Dialog(TransactionActivity.this);
        dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
        dialog.setCanceledOnTouchOutside(false);
        dialog.setContentView(R.layout.progressbar);

String price = aamount.replaceAll("\\D","");
       final int amount = Integer.parseInt(price) + SERVICE_CHARGE;
       final String mn = post_meterno.getText().toString();
       final String ml = "amstadam";
        final String ref = String.valueOf(Calendar.getInstance().getTimeInMillis());

@SuppressLint("StaticFieldLeak") AsyncTask<String, String, String> jesgetnames = new AsyncTask<String, String, String>() {
            @Override
            protected String doInBackground(String... params) {

String geMtName;
                String urlMtName = strUrlId+post_meterno.getText().toString().trim();
                Log.d(TAG, "The string:" + urlMtName);

try {
                    URL url = new URL(urlMtName);
                    HttpURLConnection con = (HttpURLConnection) url.openConnection();
                    con.setRequestMethod("GET");
                    con.connect();

BufferedReader bf = new BufferedReader(new InputStreamReader(con.getInputStream()));

String value = bf.readLine();

Log.d(TAG, "Value of value is: " + value);

if(value.equals("not found")){

geMtName = "NOT FOUND";

}else{

JSONObject parentJson = new JSONObject(value);
                        String meternumber = parentJson.getString("xmen");
                        String metername = parentJson.getString("x_name");
                        String address = parentJson.getString("address");
                        geMtName = metername+"/"+meternumber+"/"+address;

}

Log.d(TAG, "meter name after get is: " + geMtName);

} catch (Exception e) {
                     Log.d(TAG, "Faiiled coz: "+ e);
                    geMtName = "failled";
                    e.printStackTrace();
                }
                Log.d(TAG, "I dont know if this works " + geMtName);

return geMtName;

}

@Override
            protected void onPostExecute(String s) {
                super.onPostExecute(s);

Log.d(TAG, "pnPostExecute value: " + s);

String nameSubString;
                String numberSubString;
                String addressSubString;

 date and time creation
                String stringDate = String.valueOf(DateFormat.getDateTimeInstance());
                Date stringTime = Calendar.getInstance().getTime();
                SimpleDateFormat curFormater = new SimpleDateFormat("dd/MM/yyyy", Locale.ENGLISH);
                SimpleDateFormat timeFormat = new SimpleDateFormat("h:mm a", Locale.ENGLISH);
                Date dateObj = new Date();
                Date timeObj = new Date();
                try {
                    dateObj = curFormater.parse(stringDate);
                    timeObj = timeFormat.parse(String.valueOf(stringTime));
                } catch (ParseException e) {
                    e.printStackTrace();
                }
                SimpleDateFormat postFormater = new SimpleDateFormat("d MMMM yyyy", Locale.ENGLISH);
                SimpleDateFormat postTimeFormater = new SimpleDateFormat("h:mm a", Locale.ENGLISH);

String newDateStr = postFormater.format(dateObj);
                String newtimeStr = postTimeFormater.format(timeObj);

switch (s) {
                    case FAILLED:
                        nameSubString = FAILLED;
                        numberSubString = FAILLED;
                        addressSubString = FAILLED;
                        break;
                    case NOT_FOUND:
                        nameSubString = FAILLED;
                        numberSubString = FAILLED;
                        addressSubString = FAILLED;
                        break;
                    default:
                        String[] split = s.split("/");
                        nameSubString = split[0];
                        numberSubString = split[1];
                        addressSubString = split[2];
                        break;
                }

switch (s) {

TODO: Change toast message to reflect Json in future
                    case FAILLED:
                        TODO: after creating layout, go back to previous layout and display toast message
                        Toast.makeText(TransactionActivity.this, COULDN_T_CONNECT_AT_THIS_TIME_PLEASE_TRY_AGAIN, Toast.LENGTH_SHORT).show();
                        dialog.dismiss();
                        break;
                    case NOT_FOUND:
                        Toast.makeText(TransactionActivity.this, PLEASE_CHECK_YOUR_METER_NUMBER_AND_TRY_AGAIN, Toast.LENGTH_SHORT).show();
                        dialog.dismiss();

break;
                    default:
                        Intent meterIntent = new Intent(TransactionActivity.this, MeterNumActivity.class);
                        meterIntent.putExtra("meternum", mn);
                        meterIntent.putExtra("meterprice", amount);
                        meterIntent.putExtra("meterlocation", ml);
                        meterIntent.putExtra("reference", ref);
                        meterIntent.putExtra("meteracctname", nameSubString);
                        meterIntent.putExtra("meteracctnumber", numberSubString);
                        meterIntent.putExtra("meteracctaddress", addressSubString);
                        meterIntent.putExtra("uuid", userID);
                        meterIntent.putExtra("date_created", newDateStr);
                        meterIntent.putExtra("time_created", newtimeStr);
                        meterIntent.putExtra("phone", phone);
                        meterIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                        startActivity(meterIntent);
                        break;
                }

}
        };

dialog.show();
        jesgetnames.execute();

}

@Override
    public void onBackPressed() {
        Intent i= new Intent(this,HomePageActivity.class);
        startActivity(i);
        finish();
    }
}

My Gradle is

android {
    compileSdkVersion 26
    defaultConfig {
        applicationId "com.example.menofx.xmen"
        minSdkVersion 21
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation 'com.android.support:appcompat-v7:26.1.0'
    implementation 'com.android.support:cardview-v7:26.1.0'
    implementation 'com.firebaseui:firebase-ui-firestore:3.1.3'
    implementation 'com.firebaseui:firebase-ui-auth:3.1.3'
    implementation 'com.firebaseui:firebase-ui-database:3.1.3'
    implementation "com.google.firebase:firebase-firestore:11.8.0"
    implementation "com.android.support:recyclerview-v7:26.1.0"
    implementation "com.google.android.gms:play-services-auth:11.8.0"
    implementation 'co.paystack.android:paystack:3.0.9'
    implementation 'br.com.simplepass:loading-button-android:1.8.4'
    implementation 'com.github.faruktoptas:FancyShowCaseView:1.0.0'
    implementation 'com.sothree.slidinguppanel:library:3.4.0'
    implementation 'com.android.support:design:26.1.0'
    implementation 'com.android.support:support-v4:26.1.0'
    implementation 'com.android.support:support-vector-drawable:26.1.0'
    implementation 'com.android.support.constraint:constraint-layout:1.0.2'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:0.5'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:2.2.2'
    implementation project(':library')
}

apply plugin: 'com.google.gms.google-services'

My proguard rule is empty if needed. This is my first time using the firebase UI, so I don’t even know how to diagnose the problem. Thanks for your help.

Solution

You need to put your POJO (model classes) in one package and add that package to your obfuscator rule, or you can add all the packages where these classes are (I personally just use one package for them). Firebase uses reflection during serialization and deserialization, so it cannot use obfuscated class names. Once you’ve put them all in one package, add this rule to your obfuscation file to prevent obfuscation:

-keep class package.to.pojos.** { *; }

If you want to leave them in place, simply add a rule for each model class using the -keep class keyword, followed by the full path to the class (per package).

Related Problems and Solutions