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).