Apologies, I know there are similar questions on the forum already, but none of them seem to be quite the same issue as I'm running into here. I've just completed an Android tutorial course, and built and released my very first app - I'm a newbie so forgive me if I've done something stupid!
My app works fine on the laptop - on a variety of emulators, with a variety of SDK versions, and also works on a real device when plugged into the laptop with a USB cable. However, when I upload it onto the GooglePlay store (and wait 3 days for it to get reviewed!) it then doesn't work when I download it - onto the same device as was connected via USB.
The app crashes before the first page even opens, and the only error info I can get it the error info provided by GooglePlay - included below. The problem appears to be with my Parse Initialisation, but I don't know what or why? Any help would be much appreciated!
GOOGLEPLAY ERROR LOG:
java.lang.RuntimeException:
at android.app.ActivityThread.handleMakeApplication (ActivityThread.java:7041)
at android.app.ActivityThread.handleBindApplication (ActivityThread.java:6989)
at android.app.ActivityThread.access$1600 (ActivityThread.java:272)
at android.app.ActivityThread$H.handleMessage (ActivityThread.java:2055)
at android.os.Handler.dispatchMessage (Handler.java:107)
at android.os.Looper.loop (Looper.java:237)
at android.app.ActivityThread.main (ActivityThread.java:8016)
at java.lang.reflect.Method.invoke (Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1076)
Caused by: java.lang.IllegalArgumentException:
at com.parse.ParseObjectSubclassingController.registerSubclass (ParseObjectSubclassingController.java:75)
at com.parse.ParseObject.registerSubclass (ParseObject.java:491)
at com.parse.ParseObject.registerParseSubclasses (ParseObject.java:3486)
at com.parse.Parse.initialize (Parse.java:395)
at com.baileycoding.StarterApplication.onCreate (StarterApplication.java:29)
at android.app.Instrumentation.callApplicationOnCreate (Instrumentation.java:1190)
at android.app.ActivityThread.handleMakeApplication (ActivityThread.java:7036)
at android.app.ActivityThread.handleBindApplication (ActivityThread.java:6989)
at android.app.ActivityThread.access$1600 (ActivityThread.java:272)
at android.app.ActivityThread$H.handleMessage (ActivityThread.java:2055)
at android.os.Handler.dispatchMessage (Handler.java:107)
at android.os.Looper.loop (Looper.java:237)
at android.app.ActivityThread.main (ActivityThread.java:8016)
at java.lang.reflect.Method.invoke (Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1076)
APP MANIFEST.XML:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.baileycoding"
android:targetSandboxVersion="1" >
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application
android:name="com.baileycoding.StarterApplication"
android:allowBackup="true"
android:usesCleartextTraffic="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/Theme.AppCompat.Light">
<activity android:name="com.baileycoding.AboutActivity"></activity>
<activity android:name="com.baileycoding.WineResult" />
<activity android:name="com.baileycoding.MyResultsActivity" />
<activity android:name="com.baileycoding.ScoreboardActivity" />
<activity android:name="com.baileycoding.RatingActivity" />
<activity android:name="com.baileycoding.GuessDescriptionActivity" />
<activity android:name="com.baileycoding.GuessTastesActivity" />
<activity android:name="com.baileycoding.GuessGrapeActivity" />
<activity android:name="com.baileycoding.GuessCRVActivity" />
<activity android:name="com.baileycoding.WineListActivity" />
<activity android:name="com.baileycoding.StartGameActivity" />
<activity android:name="com.baileycoding.WineAssignmentActivity" />
<activity android:name="com.baileycoding.PrepActivity" />
<activity android:name="com.baileycoding.WineInfoActivity2" />
<activity
android:name="com.baileycoding.WineInfoActivity"
android:windowSoftInputMode="adjustPan" />
<activity android:name="com.baileycoding.EventIdConfirmationActivity" />
<activity
android:name="com.baileycoding.CreateNewEventActivity"
android:windowSoftInputMode="adjustPan" />
<activity android:name="com.baileycoding.EventMenuActivity" />
<activity android:name="com.baileycoding.InitialMenuActivity" />
<activity
android:name="com.baileycoding.MainActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<meta-data
android:name="preloaded_fonts"
android:resource="@array/preloaded_fonts" />
</application>
</manifest>
BULD.GRADLE (module):
apply plugin: 'com.android.application'
android {
compileSdkVersion 29
buildToolsVersion '22.0.1'
defaultConfig {
applicationId "com.baileycoding.thegrapeunknown"
minSdkVersion 26
targetSdkVersion 29
versionCode 2
versionName '1.1'
multiDexEnabled true
}
dexOptions {
javaMaxHeapSize "4g"
}
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
compile 'com.android.support:appcompat-v7:26.0.0'
compile 'com.parse.bolts:bolts-tasks:1.3.0'
compile 'com.parse:parse-android:1.13.0'
compile 'com.google.android.gms:play-services:9.4.0'
compile 'com.android.support:multidex:1.0.0'
}
BUILD.GRADLE (project):
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
mavenCentral()
jcenter()
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.0.0'
}
}
allprojects {
repositories {
mavenCentral()
google()
}
}
ext {
compileSdkVersion = 22
buildToolsVersion = "23.0.1"
minSdkVersion = 9
targetSdkVersion = 23
defaultVersionCode = 1.1
}
PARSE STARTER ACTIVITY:
package com.baileycoding;
import android.app.Application;
import com.parse.Parse;
import com.parse.ParseACL;
public class StarterApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
// Enable Local Datastore.
Parse.enableLocalDatastore(this);
// Add your initialization code here
Parse.initialize(new Parse.Configuration.Builder(getApplicationContext())
.applicationId("myappID")
.clientKey("xxxxxxxxxxx") //hidden for security - let me know if useful to see
.server("http://18.220.164.203/parse/")
.build()
);
//PARSE SETUP STATEMENTS
//ParseUser.enableAutomaticUser();
ParseACL defaultACL = new ParseACL();
defaultACL.setPublicReadAccess(true);
defaultACL.setPublicWriteAccess(true);
ParseACL.setDefaultACL(defaultACL, true);
}
}
MAIN ACTIVITY. JAVA:
package com.baileycoding;
import android.content.Intent;
import android.os.Bundle;
import android.support.constraint.ConstraintLayout;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import com.parse.FindCallback;
import com.parse.LogInCallback;
import com.parse.ParseAnalytics;
import com.parse.ParseException;
import com.parse.ParseObject;
import com.parse.ParseQuery;
import com.parse.ParseUser;
import com.parse.SignUpCallback;
import com.baileycoding.R;
import java.util.List;
public class MainActivity extends AppCompatActivity implements View.OnKeyListener, View.OnClickListener {
EditText emailEditText;
EditText passwordEditText;
EditText nameEditText;
Button loginButton;
TextView switchTextView;
ConstraintLayout backgroundLayout;
Boolean loginMode; // 1 = signup, 0 = login
String email;
String password;
String name;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTheme(R.style.MyTheme);
setContentView(R.layout.activity_main);
//setTitle("Welcome");
if (getSupportActionBar() != null) {
getSupportActionBar().hide();
}
emailEditText = (EditText) findViewById(R.id.emailEditText);
passwordEditText = (EditText) findViewById(R.id.passwordEditText);
nameEditText = (EditText) findViewById(R.id.nameEditText);
loginButton = (Button) findViewById(R.id.loginButton);
switchTextView = (TextView) findViewById(R.id.switchTextView);
backgroundLayout = findViewById(R.id.backgroundLayout);
passwordEditText.setOnKeyListener(this);
nameEditText.setOnKeyListener(this);
backgroundLayout.setOnClickListener(this);
loginMode = false;
nameEditText.setVisibility(View.GONE);
ParseQuery<ParseObject> query = ParseQuery.getQuery("User");
query.whereNotEqualTo("username","test");
query.findInBackground(new FindCallback<ParseObject>() {
@Override
public void done(List<ParseObject> objects, ParseException e) {
if(e!=null || objects.size()==0){
Toast.makeText(MainActivity.this, "You are not currently logged in", Toast.LENGTH_SHORT).show();
if(e!=null){ e.printStackTrace();}
} else {
if (ParseUser.getCurrentUser() != null) {
String currentUser = ParseUser.getCurrentUser().toString();
//ParseUser.logOut();
Toast.makeText(MainActivity.this, "Currently logged in as " + ParseUser.getCurrentUser().getUsername(), Toast.LENGTH_SHORT).show();
moveToInitialMenu();
}
}
}
});
ParseAnalytics.trackAppOpenedInBackground(getIntent());
}
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_DOWN) {
loginOrSignup(v);
}
return false;
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.backgroundLayout) {
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(getWindow().getDecorView().getRootView().getWindowToken(), 0);
}
}
public void switchToSignup(View view) {
if (loginMode == false) {
loginMode = true;
Log.i("Login mode switched to", "true; signup mode");
loginButton.setText("SIGNUP");
switchTextView.setText("Already have an account? Log in here");
nameEditText.setVisibility(View.VISIBLE);
} else {
loginMode = false;
Log.i("Login mode switched to", "false; login mode");
loginButton.setText("LOGIN");
switchTextView.setText("Don't have an account? Sign up here");
nameEditText.setVisibility(View.GONE);
}
}
public void loginOrSignup(View view) {
loginButton.setEnabled(false);
email = emailEditText.getText().toString();
password = passwordEditText.getText().toString();
name = nameEditText.getText().toString();
if (loginMode) {
if (email.matches("") || password.matches("") || name.matches("")) {
Toast.makeText(this, "Please enter email, password and a display name", Toast.LENGTH_SHORT).show();
} else {
//SIGNUP USER
ParseUser user = new ParseUser();
user.setUsername(email);
user.setPassword(password);
user.put("displayName", name);
user.signUpInBackground(new SignUpCallback() {
@Override
public void done(ParseException e) {
if (e == null) {
Toast.makeText(MainActivity.this, "Successfully created new user", Toast.LENGTH_SHORT).show();
moveToInitialMenu();
} else {
e.printStackTrace();
Toast.makeText(MainActivity.this, "Sign Up failed: " + e.getMessage(), Toast.LENGTH_LONG).show();
loginButton.setEnabled(true);
}
}
});
}
} else {
if (email.matches("") || password.matches("")) {
Toast.makeText(this, "Please enter email and password", Toast.LENGTH_SHORT).show();
} else {
//LOGIN USER
ParseUser.logInInBackground(email, password, new LogInCallback() {
@Override
public void done(ParseUser user, ParseException e) {
if (e == null) {
Toast.makeText(MainActivity.this, "Login successful", Toast.LENGTH_SHORT).show();
moveToInitialMenu();
} else {
loginButton.setEnabled(true);
e.printStackTrace();
Toast.makeText(MainActivity.this, "Login failed: " + e.getMessage(), Toast.LENGTH_LONG).show();
}
}
});
}
}
}
public void moveToInitialMenu() {
Intent intent = new Intent(getApplicationContext(), InitialMenuActivity.class);
startActivity(intent);
}
}
MAIN ACTIVITY.XML:
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/backgroundLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:background="@drawable/backgroundv1"
tools:context=".MainActivity">
<Button
android:id="@+id/loginButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:fontFamily="@font/delius"
android:onClick="loginOrSignup"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:text="LOG IN"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.495"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/nameEditText" />
<EditText
android:id="@+id/emailEditText"
android:layout_width="250dp"
android:layout_height="wrap_content"
android:layout_marginTop="290dp"
android:ems="10"
android:fontFamily="@font/delius"
android:hint="Email address"
android:inputType="textEmailAddress"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.502"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/passwordEditText"
android:layout_width="250dp"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:ems="10"
android:fontFamily="@font/delius"
android:hint="Password"
android:inputType="textPassword"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.502"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/emailEditText" />
<EditText
android:id="@+id/nameEditText"
android:layout_width="250dp"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:ems="10"
android:fontFamily="@font/delius"
android:hint="Name (to display to others)"
android:inputType="textPersonName"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/passwordEditText" />
<TextView
android:id="@+id/switchTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:fontFamily="@font/delius"
android:onClick="switchToSignup"
android:text="Don't have an account yet? Sign up here instead"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.495"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/loginButton" />
</android.support.constraint.ConstraintLayout>