Very Useful Android Java Code Snippets for Beginners


second version 2016 updated to android marshmallow

Simple as copy-paste

Why reinvent the wheel ?

Before you begin:

I wish I had this reference book when I started programming android apps.

The snippets are arranged by difficulty level. In almost all cases you need to import what’s not imported already (alt+enter in InteliJ Studio->import). In some cases the code requires a library to run (mentioned).

You might want to look at permissions that are required for some actions. Setting a wallpaper requires “set wallpaper” permission. I didn’t mention them, since they are easy to spot (you’ll get an error with what you should put in the manifest)

Copyright © 2016 by AndroidAdvance.com All rights reserved. No part of this publication may be reproduced or distributed in any form or by any means, or stored in a database or retrieval system, without the prior written permission of the publisher.

THE WORK IS PROVIDED “AS IS.” AndroidAdvance AND ITS LICENSORS MAKE NO

GUARANTEES OR WARRANTIES AS TO THE ACCURACY, ADEQUACY OR

COMPLETENESS OF OR RESULTS TO BE OBTAINED FROM USING THE WORK,

INCLUDING ANY INFORMATION THAT CAN BE ACCESSED THROUGH THE WORK

VIA HYPERLINK OR OTHERWISE, AND EXPRESSLY DISCLAIM ANY WARRANTY,

EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO IMPLIED WARRANTIES OF

MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.

AndroidAdvance.com and its licensors do not warrant or guarantee that the functions contained in the work will meet your requirements or that its operation will be uninterrupted or error free. Neither AndroidAdvance.comn or its licensors shall be liable to you or anyone else for any inaccuracy, error or omission, regardless of cause, in the work or for any damages resulting therefrom. AndroidAdvance.comhas no responsibility for the content of any information accessed through the work. Under no circumstances shall AndroidAdvance.com and/or its licensors be liable for any indirect, incidental, special, punitive, consequential or similar damages that result from the use of or inability to use the work, even if any of them has been advised of the possibility of such damages. This limitation of liability shall apply to any claim or cause whatsoever whether such claim or cause arises in contract, tort or otherwise.

Table of Contents

Always updated Template apps.

Gradle configuration

This is the standard way to do stuff in Android. It’s the guideline that most big companies follow. You can do things differently but by doing so you make sure that everything looks clean.

Create a Base Application class

Best libraries: Logging

Best libraries: Support libraries

Best libraries: Dagger2

Best libraries: RxJava

Best libraries: Retrofit

Best libraries: DbFlow

Best libraries: Butterknife

Best libraries: EventBus

Best libraries: Image handling

Best libraries: Permission Management

Best libraries: Easy GPS location

Fragments: Ensure that the app doesn’t quit when you use fragments

Fragments: Base Fragment with Butterknife

Get string length

Convert String to Int and vice-versa

Get name of current method

Helper class for Network:

A countdown timer

Convert String to Date in Java

Creating JSON data

Java tutorial

Create a simple alert dialog with 2 buttons

Use animations

Make activity fullscreen

Store the number of times the app was launched

Start a browser activity

Download a file in java (async)

Capture screen shots in Java

For audio apps, make the volume buttons of the phone modify audio level for music and not for phone ringtone

Use autocomplete edit text instead of simple edit text

Check if an app is installed (by package name)

Get User Email address

Create a sha-256 hash

Get Device ID

Write file to SD Card

Directory listing

Read a file from RAW directory

Read a file from assets

Set the entire font for the activity

Start an email intent

Show a notification on status bar

Set a ringtone/alarm or notification sound

Start the ads activity with a random number :)

Play a radio from internet (or a stream)

Share something

Send a SMS

Get Battery Level

Autostart app on boot

Setting toast to appear on some parts

Parsing / Reading XML file

Press once again to exit

Hash the user password before storing/sending it to the server

Get debug info hardware from the user

Use a DialogFactory for creating dialogs

Create a circle imageView

Intercept unauthorised requests to an api

Get Stuff form Internet

Have a Preference Helper Class

Make your app not crash/delete fils on change orientation

Have a cool looking edittext

Ask user to rate your app

A huge resource of cool libraries in android

[Shameless self promotion] Add facebook/google/twitter login to your app

[Shameless self promotion] Add a survey to your app

Always updated Template apps.

Beginner level:

https://github.com/AndreiD/UltimateAndroidAppTemplate

Advanced Level:

https://github.com/AndreiD/UltimateAndroidTemplateRx (RxJava, Lambdas)

This two templates will be always updated to the latest stuff. You can just fork/download them and use them as a starting point in your app.

Gradle configuration

the build.gradle file is the file that tells how the app is configured and what libraries it uses.

we have 2 gradle files. one in the project, and one for the module app.

The build.gradle file in the main project should look something like this:

buildscript {

    repositories {

        jcenter()

        mavenCentral()

    }

    dependencies {

        classpath 'com.android.tools.build:gradle:1.5.0'

        classpath 'me.tatarka:gradle-retrolambda:3.2.4'

        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'

    }

}

allprojects {

    repositories {

        jcenter()

        repositories {

            maven { url "https://jitpack.io" }

        }

    }

}

task clean(type: Delete) {

    delete rootProject.buildDir

}

the build.gradle file in the app module should look something like this

apply plugin: 'com.android.application'

apply plugin: 'com.neenbedankt.android-apt'

apply plugin: 'me.tatarka.retrolambda'

apply from: '../config/quality.gradle'

android {

  compileSdkVersion 23

  buildToolsVersion "23.0.2"

  dexOptions {

    incremental true

  }

  signingConfigs {

    config {

      keyAlias 'keystore'

      keyPassword '123123'

      storeFile file('keystore/keystore.jks')

      storePassword '123123'

    }

  }

  defaultConfig {

    applicationId "com.androidadvance.hive"

    minSdkVersion 22

    targetSdkVersion 23

    versionCode 1

    versionName "1.0"

    testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

   }

  buildTypes {

    release {

      minifyEnabled true

      shrinkResources true

      proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

      signingConfig signingConfigs.config

    }

    debug {

      applicationIdSuffix ".debug"

      debuggable true

    }

  }

  compileOptions {

    sourceCompatibility JavaVersion.VERSION_1_8

    targetCompatibility JavaVersion.VERSION_1_8

  }

  packagingOptions {

    exclude 'META-INF/DEPENDENCIES'

    exclude 'LICENSE.txt'

    exclude 'META-INF/LICENSE'

    exclude 'META-INF/LICENSE.txt'

    exclude 'META-INF/NOTICE'

    exclude 'LICENSE.txt'

  }

  lintOptions {

    warning 'InvalidPackage'

    abortOnError false

    lintConfig file("${project.rootDir}/config/quality/lint/lint.xml")

  }

  configurations.all {

    resolutionStrategy {

      force 'com.android.support:support-annotations:23.0.1'

    }

  }

}

dependencies {

  compile fileTree(dir: 'libs', include: ['*.jar'])

  //----- Support Libs

  compile "com.android.support:appcompat-v7:23.1.1"

  compile "com.android.support:design:23.1.1"

  compile "com.android.support:recyclerview-v7:23.1.1"

  compile "com.android.support:cardview-v7:23.1.1"

  //----- Dagger

  compile "com.google.dagger:dagger:2.0.2"

  apt "com.google.dagger:dagger-compiler:2.0.2"

  provided 'org.glassfish:javax.annotation:10.0-b28' //Required by Dagger2

  //----- Rx

  compile "io.reactivex:rxandroid:1.1.0"

  compile "io.reactivex:rxjava:1.1.0"

  //compile "com.jakewharton.rxbinding:rxbinding:0.3.0"

  //----- Retrofit

  compile 'com.squareup.retrofit2:retrofit:2.0.0-beta3'

  compile "com.squareup.retrofit2:converter-gson:2.0.0-beta3"

  compile "com.squareup.retrofit2:adapter-rxjava:2.0.0-beta3"

  compile 'com.squareup.okhttp3:logging-interceptor:3.0.1'

 etc…… more libraries here

  //----- Testing

  testCompile 'junit:junit:4.12'

  androidTestApt "com.google.dagger:dagger-compiler:2.0.2"

  androidTestCompile "com.android.support.test:runner:0.4"

  androidTestCompile "com.android.support.test:rules:0.4"

  androidTestCompile "com.android.support.test.espresso:espresso-core:2.2.1"

  androidTestCompile "org.hamcrest:hamcrest-library:1.3"

  androidTestCompile "org.hamcrest:hamcrest-library:1.3"

}

This is the standard way to do stuff in Android. It’s the guideline that most big companies follow. You can do things differently but by doing so you make sure that everything looks clean.

Create a Base Application class

In the base application class you will initialize most of the libraries that require you do to so. Like Caligraphy for fonts, Timber, KLog, dbFlow and many others.

public class BaseApplication extends Application {

 

  @Override public void onCreate() {

    super.onCreate();

    boolean isDebuggable = (0 != (getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE));

    if (isDebuggable) {

      KLog.init(true);

    } else {

      KLog.init(false);

    }

    //------ database init ------

    FlowManager.init(this);

  //---more libraries init here

  }

}

add it to the manifest

 <application
     android:icon="@drawable/ic_launcher"
     android:label="@string/app_name"
     android:name=".BaseApplication"
     android:theme="@style/AppTheme">
   <activity
       android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"
       android:name=".view.main.MainActivity"
       android:theme="@style/AppTheme">
     <intent-filter>
       <action android:name="android.intent.action.MAIN"/>

       <category android:name="android.intent.category.LAUNCHER"/>
     </intent-filter>
   </activity>
   <activity
       android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"
       android:label="@string/settings"
       android:name=".view.settings.SettingsActivity"
       android:parentActivityName=".view.main.MainActivity"
       android:theme="@style/AppTheme"
       tools:ignore="UnusedAttribute">
     <meta-data
         android:name="android.support.PARENT_ACTIVITY"
         android:value="com.androidadvance.hive.view.main.MainActivity"/>
   </activity>

...

Best libraries: Logging

for logging you have basically two choices. Either Timber or KLog.

You have to initialize them in your base application class

 //----- Logging
 compile 'com.github.zhaokaiqiang.klog:library:1.3.0'

boolean isDebuggable = (0 != (getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE));

   if (isDebuggable) {
     KLog.init(true);
   } else {
     KLog.init(false);
   }

Best libraries: Support libraries

please check and update the version to the latest

no app without the support libraries:

   //----- Support Libs
 compile "com.android.support:appcompat-v7:version"
 compile "com.android.support:design:version"
 compile "com.android.support:recyclerview-v7:version"
 compile "com.android.support:cardview-v7:version"

Best libraries: Dagger2

Dagger2 has a steep learning curve, and you’ll probably want to skip it if you’re new to android development, but eventually you’ll endup using it

  compile "com.google.dagger:dagger:2.0.2"
 apt "com.google.dagger:dagger-compiler:2.0.2"
 provided 'org.glassfish:javax.annotation:10.0-b28' //Required by Dagger2

Best libraries: RxJava

RxJava is also hard to learn so skip it if you’re new. However if you invest time in learning it, coding becomes easy as pie

  compile "io.reactivex:rxandroid:1.1.0"
 compile "io.reactivex:rxjava:1.1.0"

Best libraries: Retrofit

The number 1 choice for developers that need a library to talk to the internet. You might want to import the gson converter, the rxjava adapter and the logging interceptor too.

   //----- Retrofit
 compile 'com.squareup.retrofit2:retrofit:2.0.0-beta3'
 compile "com.squareup.retrofit2:converter-gson:2.0.0-beta3"
 compile "com.squareup.retrofit2:adapter-rxjava:2.0.0-beta3"
 compile 'com.squareup.okhttp3:logging-interceptor:3.0.1'

Best libraries: DbFlow

This is a simple database library that uses annotations to get work done. It’s easy to learn

   //---- DbFlow
 apt "com.github.Raizlabs.DBFlow:dbflow-processor:3.0.0-beta1"
 compile "com.github.Raizlabs.DBFlow:dbflow-core:3.0.0-beta1"
 compile "com.github.Raizlabs.DBFlow:dbflow:3.0.0-beta1"

Best libraries: Butterknife

This library will make your code look much cleaner if you have lots of controls in one place. However if your app has little controls you might want to avoid using it.

   //----- Butterknife
 compile "com.jakewharton:butterknife:7.0.1"

Best libraries: EventBus

This library will make the communication inside your app much cleaner if you don’t plan to use RxJava

  //----- Eventbuss
 compile "de.greenrobot:eventbus:2.4.0"

Best libraries: Image handling

If you plan to use lots of images, we recommend Picasso. There’s also a library called Glide, that has even more cool stuff, but it requires support-v4.

 //----- Picasso
 compile "com.squareup.picasso:picasso:2.5.2"

Best libraries: Permission Management

Android 6  comes with a change in the permission system, similar to iOS one. You might want to use this cool library instead of the android documentation.

//--- Permission Management

compile 'com.karumi:dexter:2.2.1'

Best libraries: Easy GPS location

A very easy to use gps helper library. It requires RxJava however

 compile 'pl.charmas.android:android-reactive-location:0.8@aar'

Fragments: Ensure that the app doesn’t quit when you use fragments

 @Override public void onBackPressed() {
   if (getFragmentManager().getBackStackEntryCount() > 0) {
     getFragmentManager().popBackStack();
   } else {
     super.onBackPressed();
   }
 }

Fragments: Base Fragment with Butterknife

public class BaseFragment extends Fragment {

 @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
   super.onViewCreated(view, savedInstanceState);
   ButterKnife.bind(this, view);
 }

 @Override public void onDestroyView() {
   super.onDestroyView();
   ButterKnife.unbind(this);
 }
}

Get string length

String s1 = "This book is awesome!!!";
int strLength  = s1.length();
System.out.println("The lengt of the string : "+strLength);

Convert String to Int and vice-versa

String a = String.valueOf(2);   //integer to numeric string

int i = Integer.parseInt(a); //numeric string to an int

Get name of current method

String methodName = Thread.currentThread().getStackTrace()[1].getMethodName();

Helper class for Network:


public class NetworkUtil {

   public static boolean isHttpStatusCode(Throwable throwable, int statusCode) {
       return throwable instanceof HttpException
               && ((HttpException) throwable).code() == statusCode;
   }

   public static boolean isNetworkConnected(Context context) {
       ConnectivityManager cm =
               (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
       NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
       return activeNetwork != null && activeNetwork.isConnectedOrConnecting();
   }

 public static boolean isHttp404(Throwable error) {
   return error instanceof HttpException && ((HttpException) error).code() == 404;
 }

 public static boolean isConflictError(Throwable error) {
   return error instanceof HttpException && ((HttpException) error).code() == 409;
 }

 public static boolean isForbidden(Throwable error) {
   return error instanceof HttpException && ((HttpException) error).code() == 403;
 }
}

A countdown timer

mTimer = new MyTimer(200, 200);

mTimer.start();

public class MyTimer extends CountDownTimer {

        public MyTimer(long millisInFuture, long countDownInterval) {

            super(millisInFuture, countDownInterval);

          }

        public void onFinish() {

            do_your_thing();

        }

        public void onTick(long millisUntilFinished) {      

        }

    }

Convert String to Date in Java

SimpleDateFormat format = new SimpleDateFormat( "dd.MM.yyyy" );

Date date = format.parse( myString );

Creating JSON data

JSONObject json = new JSONObject();

json.put("city", "New York");

json.put("population", "123123213");

...

String output = json.toString();

Java tutorial

Need help with java ? Search google with: Java Video Tutorial by Derek Banas

Create a simple alert dialog with 2 buttons

 AlertDialog.Builder builder = new AlertDialog.Builder(mContext);

     

                    builder.setMessage(“the_message”)

                            .setCancelable(false)

                            .setNegativeButton("go to url", new DialogInterface.OnClickListener() {

                                @Override

                                public void onClick(DialogInterface dialogInterface, int i) {

                                    Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(the_link));

                                    startActivity(browserIntent);

                                }

                            })

                            .setPositiveButton("cancel", new DialogInterface.OnClickListener() {

                                public void onClick(DialogInterface dialog, int id) {

                                    dialog.dismiss();

                                    //add your code here...

                                }

                            });

             

                AlertDialog alert = builder.create();

                alert.show();

Use animations

Animation slide_left = AnimationUtils.loadAnimation(this, R.anim.wave_scale);

Animation slide_right = AnimationUtils.loadAnimation(this, R.anim.wave_scale);

textView_splash_title.startAnimation(slide_left);

textView_splash_subtitle.startAnimation(slide_right);

Make activity fullscreen

requestWindowFeature(Window.FEATURE_NO_TITLE);

getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,

WindowManager.LayoutParams.FLAG_FULLSCREEN);

Store the number of times the app was launched

  SharedPreferences prefs1 = mContext.getSharedPreferences("PREFS", 0);

   launched_times = prefs1.getInt("launched_times",0);

   Log.i("______launched times________", String.valueOf(launched_times));

   SharedPreferences.Editor editor = prefs1.edit();

   editor.putInt("launched_times",launched_times+1);

   editor.commit();

Start a browser activity

 Intent browserIntent = new Intent(Intent.ACTION_VIEW,                                         Uri.parse("https://play.google.com/store/apps/details?id=com.bobysan.freemusicdownload"));

startActivity(browserIntent);

Download a file in java (async)

  private class DownloadSong extends AsyncTask<String, Integer, Boolean> {

        private String source;

        private String download_mp3_link = "";

        private String song_title = "";

        protected Boolean doInBackground(String... params) {

            String DownloadUrl = params[0];

            String fileName = params[1];

            try {

                File root = android.os.Environment.getExternalStorageDirectory();

                File dir = new File(root.getAbsolutePath() + "/MusicFree");

                if (dir.exists() == false) {

                    dir.mkdirs();

                }

                URL url = new URL(DownloadUrl); // you can write here any link

                File file = new File(dir, fileName);

                song_title = fileName;

                long startTime = System.currentTimeMillis();

                Log.d("DownloadManager", "download begining");

                Log.d("DownloadManager", "download url:" + url);

                Log.d("DownloadManager", "downloaded file name:" + fileName);

                                /* Open a connection to that URL. */

                URLConnection ucon = url.openConnection();

                                /*

                 * Define InputStreams to read from the URLConnection.

                                 */

                InputStream input = ucon.getInputStream();

                int fileLength = ucon.getContentLength();

                OutputStream output = new FileOutputStream(root.getAbsolutePath() + "/MusicFree/" + fileName + ".mp3");

                download_mp3_link = (root.getAbsolutePath() + "/MusicFree/" + fileName + ".mp3");

                byte data[] = new byte[1024];

                long total = 0;

                int count;

                while ((count = input.read(data)) != -1) {

                    total += count;

                    // publishing the progress....

                    publishProgress((int) (total * 100 / fileLength));

                    output.write(data, 0, count);

                }

                output.flush();

                output.close();

                input.close();

                return true;

            } catch (IOException e) {

                Log.d("DownloadManager", "Error: " + e);

                return false;

            }

        }

        @Override

        protected void onProgressUpdate(Integer... values) {

            // TODO Auto-generated method stub

            super.onProgressUpdate(values);

            mProgressDialog.setProgress(values[0]);

                        /*

             contentText =  String.valueOf(values[0]) + "% complete";

                 notification.setLatestEventInfo(getApplicationContext(), contentTitle, contentText, contentIntent);

                 notificationManager.notify(HELLO_ID, notification);

                 */

        }

        protected void onPreExecute() {

            super.onPreExecute();

            mProgressDialog.show();

            // ------ taskbar notification ---------

            /*

            String ns = Context.NOTIFICATION_SERVICE;

                        notificationManager = (NotificationManager) getSystemService(ns);

                        icon = R.drawable.ic_launcher;

                        tickerText = "Downloading...";

                        time = System.currentTimeMillis();

            notification = new Notification(icon, tickerText, time);

            contentTitle = "MusicFree download in progress";

            contentText = "0% complete";

            Intent notificationIntent = new Intent(Intent.ACTION_VIEW);

            notificationIntent.setDataAndType(Uri.fromFile(new File(android.os.Environment.getExternalStorageDirectory().getAbsolutePath() + "/MusicFree/"+song_name+".mp3")), "audio/*");

            contentIntent = PendingIntent.getActivity(getApplicationContext(), 0, notificationIntent, 0);

            notification.setLatestEventInfo(getApplicationContext(), contentTitle, contentText, contentIntent);

            notificationManager.notify(HELLO_ID, notification);

                        */

        }

        protected void onPostExecute(Boolean result) {

            super.onPostExecute(result);

            if (result) {

                Toast.makeText(mContext, "file downloaded to SDcard/MusicFree", Toast.LENGTH_LONG).show();

            }

            try {

                mProgressDialog.dismiss();

            } catch (Exception e) {

                Log.e("EXCEPTION", "!!!!");

            }

        }

    }

Capture screen shots in Java

   Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();

   Rectangle screenRectangle = new Rectangle(screenSize);

   Robot robot = new Robot();

   BufferedImage image = robot.createScreenCapture(screenRectangle);

   ImageIO.write(image, "png", new File(fileName));

For audio apps, make the volume buttons of the phone modify audio level for music and not for phone ringtone

mContext = MainActivity.this;

mContext.setVolumeControlStream(AudioManager.STREAM_MUSIC);

Use autocomplete edit text instead of simple edit text

String STYLES[] = {"Alternative Rock", "Ambient", "Blues", "Classical", "Country", "Dance", "Deep House", "Disco", "Drum & Bass"};

 ArrayAdapter<String> ac_adapter = new ArrayAdapter<String>(mContext,

                android.R.layout.simple_dropdown_item_1line, STYLES);

 AutoCompleteTextView editText_main_search = (AutoCompleteTextView) findViewById(R.id.editText_main_search);

editText_main_search.setAdapter(ac_adapter);

editText_main_search.setThreshold(1);

Check if an app is installed (by package name)

public static boolean isAppInstalled(Context ctx,String uri) {

                PackageManager pm = ctx.getPackageManager();

                boolean installed = false;

                try {

                        pm.getPackageInfo(uri, PackageManager.GET_ACTIVITIES);

                        installed = true;

                } catch (PackageManager.NameNotFoundException e) {

                        installed = false;

                }

                return installed;

        }

Get User Email address

        public static class UserEmailFetcher {

                public static String getEmail(Context context) {

                        AccountManager accountManager = AccountManager.get(context);

                        Account account = getAccount(accountManager);

                        if (account == null) {

                                return null;

                        } else {

                                return account.name;

                        }

                }

                private static Account getAccount(AccountManager accountManager) {

                        Account[] accounts = accountManager.getAccountsByType("com.google");

                        Account account;

                        if (accounts.length > 0) {

                                account = accounts[0];

                        } else {

                                account = null;

                        }

                        return account;

                }

        }

Create a sha-256 hash

        public static final String sha256(final String s) {

                try {

                        MessageDigest digest = MessageDigest.getInstance("SHA-256");

                        digest.update(s.getBytes());

                        byte messageDigest[] = digest.digest();

                        // Create Hex String

                        StringBuffer hexString = new StringBuffer();

                        for (int i = 0; i < messageDigest.length; i++) {

                                String h = Integer.toHexString(0xFF & messageDigest[i]);

                                while (h.length() < 2)

                                        h = "0" + h;

                                hexString.append(h);

                        }

                        return hexString.toString();

                } catch (NoSuchAlgorithmException e) {

                        e.printStackTrace();

                }

                return "";

        }

Get Device ID

        public static String get_device_id(Context ctx) {

                final TelephonyManager tm = (TelephonyManager) ctx.getSystemService(Context.TELEPHONY_SERVICE);

                final String tmDevice, tmSerial, tmPhone, androidId;

                try {

                        tmDevice = "" + tm.getDeviceId();

                        tmSerial = "" + tm.getSimSerialNumber();

                        androidId = "" + android.provider.Settings.Secure.getString(ctx.getContentResolver(), android.provider.Settings.Secure.ANDROID_ID);

                        UUID deviceUuid = new UUID(androidId.hashCode(), ((long) tmDevice.hashCode() << 32) | tmSerial.hashCode());

                        String deviceId = deviceUuid.toString();

                        return deviceId;

                } catch (Exception ex) {

                        return "nodeviceid";

                }

        }

Write file to SD Card

        public static Boolean writeToSDFile(String directory, String file_name, String text) {

                // Find the root of the external storage.

                // See

                // http://developer.android.com/guide/topics/data/data-storage.html#filesExternal

                File root = Environment.getExternalStorageDirectory();

                // See

                // http://stackoverflow.com/questions/3551821/android-write-to-sd-card-folder

                File dir = new File(root.getAbsolutePath() + "/" + directory);

                dir.mkdirs();

                File file = new File(dir, file_name);

                try {

                        FileOutputStream f = new FileOutputStream(file);

                        PrintWriter pw = new PrintWriter(f);

                        pw.println(text);

                        pw.flush();

                        pw.close();

                        f.close();

                        // Log.v(TAG, "file written to sd card");

                        return true;

                } catch (FileNotFoundException e) {

                        e.printStackTrace();

                        // Log.i(TAG, "******* File not found. Did you" +

                        // " add a WRITE_EXTERNAL_STORAGE permission to the manifest?");

                        return false;

                } catch (IOException e) {

                        e.printStackTrace();

                        return false;

                }

        }

Directory listing

File dir = new File("directoryName");

  String[] children = dir.list();

  if (children == null) {

      // Either dir does not exist or is not a directory

  } else {

      for (int i=0; i < children.length; i++) {

          // Get filename of file or directory

          String filename = children[i];

      }

  }

 

  // It is also possible to filter the list of returned files.

  // This example does not return any files that start with `.'.

  FilenameFilter filter = new FilenameFilter() {

      public boolean accept(File dir, String name) {

          return !name.startsWith(".");

      }

  };

  children = dir.list(filter);

 

  // The list of files can also be retrieved as File objects

  File[] files = dir.listFiles();

 

  // This filter only returns directories

  FileFilter fileFilter = new FileFilter() {

      public boolean accept(File file) {

          return file.isDirectory();

      }

  };

  files = dir.listFiles(fileFilter);

Read a file from RAW directory

        public static void readRaw(Context ctx, int res_id) {

                InputStream is = ctx.getResources().openRawResource(res_id);

                InputStreamReader isr = new InputStreamReader(is);

                BufferedReader br = new BufferedReader(isr, 8192);                                  nbsp;                                                                               

                try {

                        String test;

                        while (true) {

                                test = br.readLine();

                                if (test == null)         break;

                        }

                        isr.close();

                        is.close();

                        br.close();

                } catch (IOException e) {

                        e.printStackTrace();

                }

        }

Read a file from assets

        public static String getQuestions(Context ctx, String file_name) {

                AssetManager assetManager = ctx.getAssets();

                ByteArrayOutputStream outputStream = null;

                InputStream inputStream = null;

                try {

                        inputStream = assetManager.open(file_name);

                        outputStream = new ByteArrayOutputStream();

                        byte buf[] = new byte[1024];

                        int len;

                        try {

                                while ((len = inputStream.read(buf)) != -1) {

                                        outputStream.write(buf, 0, len);

                                }

                                outputStream.close();

                                inputStream.close();

                        } catch (IOException e) {

                        }

                } catch (IOException e) {

                }

                return outputStream.toString();

        }

Set the entire font for the activity

dependencies {
   compile
'uk.co.chrisjenx:calligraphy:2.1.0'
}

Add your custom fonts to assets/ 

Define your default font using CalligraphyConfig, in your Application class in the #onCreate() method.

@Override
public void onCreate() {
   
super.onCreate();
   CalligraphyConfig
.initDefault(new CalligraphyConfig.Builder()
                           .setDefaultFontPath(
"fonts/Roboto-RobotoRegular.ttf")
                           .setFontAttrId(R
.attr.fontPath)
                           .build()
           );
   
//....
}

@Override
protected void attachBaseContext(Context newBase) {
   
super.attachBaseContext(CalligraphyContextWrapper.wrap(newBase));
}

Start an email intent

Toast.makeText(InfoActivity.this, "thank you for the feedback", Toast.LENGTH_LONG).show();

                                Intent intent = new Intent(Intent.ACTION_SEND);

                                intent.setType("plain/text");

                                intent.putExtra(Intent.EXTRA_EMAIL, new String[] { "android@ofertaweb.ro" });

                                intent.putExtra(Intent.EXTRA_SUBJECT, "Free Music Download bobysan Feedback");

                                startActivity(intent);

Show a notification on status bar

 String ns = Context.NOTIFICATION_SERVICE;

                NotificationManager mNotificationManager = (NotificationManager)

                        getSystemService(ns);

                int icon = R.drawable.ic_launcher;

                CharSequence showText = "Playing " + song_name;

                long time = System.currentTimeMillis();

                Notification notification = new Notification(icon, showText, time);

                CharSequence contentTitle = "Free Music";

                CharSequence contentText = "Playing " + song_name;

                Intent notifIntent = new Intent(mContext, LibraryActivity.class);

                notification.flags = Notification.FLAG_ONGOING_EVENT;

                notification.flags |= Notification.FLAG_NO_CLEAR;

                notification.flags |= Notification.FLAG_AUTO_CANCEL;

                notifIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP |

                        Intent.FLAG_ACTIVITY_SINGLE_TOP);

                PendingIntent contentIntent = PendingIntent.getActivity(mContext, 0,

                        notifIntent,

                        PendingIntent.FLAG_CANCEL_CURRENT);

                notification.setLatestEventInfo(mContext, contentTitle, contentText,

                        contentIntent);

                mNotificationManager.notify(1337, notification);

--------------------

    public void clearNotification() {

        try {

            NotificationManager notificationManager = (NotificationManager) mContext

                    .getSystemService(Context.NOTIFICATION_SERVICE);

            notificationManager.cancel(1337);

        } catch (Exception ex) {

        }

    }

Set a ringtone/alarm or notification sound

 ContentValues values = new ContentValues();

                            values.put(MediaStore.MediaColumns.DATA, download_mp3_link);

                            values.put(MediaStore.MediaColumns.TITLE, song_name);

                            values.put(MediaStore.MediaColumns.MIME_TYPE, "audio/mp3");

                            values.put(MediaStore.Audio.Media.ARTIST, song_name);

                            values.put(MediaStore.Audio.Media.IS_RINGTONE, true);

                            values.put(MediaStore.Audio.Media.IS_NOTIFICATION, true);

                            values.put(MediaStore.Audio.Media.IS_ALARM, true);

                            values.put(MediaStore.Audio.Media.IS_MUSIC, true);

                            Uri uri = MediaStore.Audio.Media.getContentUriForPath(download_mp3_link);

                            Uri newUri = getContentResolver().insert(uri, values);

                            try {

                RingtoneManager.setActualDefaultRingtoneUri(mContext, RingtoneManager.TYPE_ALARM, newUri);

                                Toast.makeText(mContext, R.string.ringtone_set, Toast.LENGTH_LONG).show();

                            } catch (Throwable t) {

                                Toast.makeText(mContext, R.string.error_ringtone, Toast.LENGTH_LONG).show();

                            }

Start the ads activity with a random number :)

 Random rand = new Random();

        int r_nr = rand.nextInt(10);

        if(r_nr == 1){

            startActivity(new Intent(mContext,ActivityAds.class));

        }

Play a radio from internet (or a stream)

 mPlayer = new MediaPlayer();

        mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);

        try {

            mPlayer.setDataSource(MusicService.this, Uri.parse(url));

            mPlayer.prepare();

            mPlayer.start();

        } catch (IOException e) {

            //Log.e(TAG, "Could not open file for playback.");

            Toast.makeText(MusicService.this, "error connecting. try with a wi-fi connection...", Toast.LENGTH_LONG).show();

        }

        mPlayer.setOnErrorListener(this);

        mPlayer.setOnErrorListener(new OnErrorListener() {

            public boolean onError(MediaPlayer mp, int what, int extra) {

                onError(mPlayer, what, extra);

                return true;

            }

        });

    }

    public void pauseMusic() {

        if (mPlayer.isPlaying()) {

            mPlayer.pause();

            length = mPlayer.getCurrentPosition();

        }

    }

    public void resumeMusic() {

        if (mPlayer.isPlaying() == false) {

            mPlayer.seekTo(length);

            mPlayer.start();

        }

    }

    public void stopMusic() {

        mPlayer.stop();

        mPlayer.release();

        mPlayer = null;

    }

    @Override

    public void onDestroy() {

        super.onDestroy();

        mNotificationManager.cancel(1773);

        if (mPlayer != null) {

            try {

                mPlayer.stop();

                mPlayer.release();

            } finally {

                mPlayer = null;

            }

        }

    }

    public boolean onError(MediaPlayer mp, int what, int extra) {

        Toast.makeText(this, "error on loading", Toast.LENGTH_SHORT).show();

        if (mPlayer != null) {

            try {

                mPlayer.stop();

                mPlayer.release();

            } finally {

                mPlayer = null;

            }

        }

        return false;

    }

Share something

        Intent sharingIntent = new Intent(

                        Intent.ACTION_SEND);

                sharingIntent.setType("text/plain");

                String shareBody = "xxxxxxxxxx on Android https://play.google.com/store/apps/details?id=xxxxxxxxxx";

                sharingIntent.putExtra(Intent.EXTRA_SUBJECT,

                        "xxxxxxxxxxxxx");

                sharingIntent

                        .putExtra(Intent.EXTRA_TEXT, shareBody);

                startActivity(Intent.createChooser(sharingIntent,

                        "Choose sharing method"));

Send a SMS

Uri smsUri = Uri.parse("sms:100861");
Intent intent = new Intent(Intent.ACTION_VIEW, smsUri);
intent.putExtra("sms_body", "shenrenkui");
startActivity(intent);

Get Battery Level

private BroadcastReceiver mBatInfoReceiver = new BroadcastReceiver(){

        @Override

        public void onReceive(Context ctx, Intent intent) {

          int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);

          Log.i("Battery Level", String.valueOf(level) + "%");

        }

      };

Autostart app on boot

in AndroidManifest.xml (application-part):

<receiver android:enabled="true" android:name=".BootUpReceiver"

        android:permission="android.permission.RECEIVE_BOOT_COMPLETED">

        <intent-filter>

                <action android:name="android.intent.action.BOOT_COMPLETED" />

                <category android:name="android.intent.category.DEFAULT" />

        </intent-filter>

</receiver>

[..]

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

public class BootUpReceiver extends BroadcastReceiver{

        @Override

        public void onReceive(Context context, Intent intent) {

                Intent i = new Intent(context, MyActivity.class);  

                i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

                context.startActivity(i);  

        }

}

Setting toast to appear on some parts

Toast toast = Toast.makeText(context, "In Center", Toast.LENGTH_LONG);

//For Top

toast.setGravity(Gravity.TOP, 0, 0);

//For Center

toast.setGravity(Gravity.CENTER, 0, 0);

toast.show();

Parsing / Reading XML file

<?xml version="1.0"?>

<cars>

    <car>

        <name>BMW</name>

        <grade>A</grade>

    </car>

    <car>

        <name>FORD</name>

        <grade>B</grade>

    </car>

    <car>

        <name>FIAT</name>

        <grade>C</grade>

    </car>

</cars>

import java.io.File;

import javax.xml.parsers.DocumentBuilder;

import javax.xml.parsers.DocumentBuilderFactory;

 

import org.w3c.dom.Document;

import org.w3c.dom.Element;

import org.w3c.dom.Node;

import org.w3c.dom.NodeList;

 

public class XMLParser {

 

    public void getAllUserNames(String fileName) {

        try {

            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

            DocumentBuilder db = dbf.newDocumentBuilder();

            File file = new File(fileName);

            if (file.exists()) {

                Document doc = db.parse(file);

                Element docEle = doc.getDocumentElement();

 

                System.out.println("Root element of the document: "

                        + docEle.getNodeName());

 

                NodeList carList = docEle.getElementsByTagName("car");

 

                if (carList != null && carList.getLength() > 0) {

                    for (int i = 0; i < carList.getLength(); i++) {

 

                        Node node = carList.item(i);

 

                        if (node.getNodeType() == Node.ELEMENT_NODE) {

 

                           

                            Element e = (Element) node;

                            NodeList nodeList = e.getElementsByTagName("name");

                            System.out.println("Name: "

                                    + nodeList.item(0).getChildNodes().item(0)

                                            .getNodeValue());

 

                            nodeList = e.getElementsByTagName("grade");

                            System.out.println("Grade: "

                                    + nodeList.item(0).getChildNodes().item(0)

                                            .getNodeValue());

 

                                                  }

                    }

                } else {

                    System.exit(1);

                }

            }

        } catch (Exception e) {

            System.out.println(e);

        }

    }

    public static void main(String[] args) {

 

        XMLParser parser = new XMLParser();

        parser.getAllUserNames("test.xml");

    }

}

Press once again to exit

private static long back_pressed;

@Override

public void onBackPressed()

{

        if (back_pressed + 2000 > System.currentTimeMillis()) super.onBackPressed();

        else Toast.makeText(getBaseContext(), "Press once again to exit!", Toast.LENGTH_SHORT).show();

        back_pressed = System.currentTimeMillis();

}

Hash the user password before storing/sending it to the server

import java.security.MessageDigest;

import java.security.NoSuchAlgorithmException;

public class HashThePasswordHelper {

  public static String getHash(String plaintext) {

    MessageDigest md = null;

    try {

      md = MessageDigest.getInstance("SHA-256");

    } catch (NoSuchAlgorithmException e) {

      try {

        md = MessageDigest.getInstance("SHA-1");

      } catch (NoSuchAlgorithmException e1) {

        return plaintext;

      }

    }

    md.update(plaintext.getBytes());

    byte byteData[] = md.digest();

    StringBuffer sb = new StringBuffer();

    for (int i = 0; i < byteData.length; i++) {

      sb.append(Integer.toString((byteData[i] & 0xff) + 0x100, 16).substring(1));

    }

    StringBuffer hexString = new StringBuffer();

    for (int i = 0; i < byteData.length; i++) {

      String hex = Integer.toHexString(0xff & byteData[i]);

      if (hex.length() == 1) hexString.append('0');

      hexString.append(hex);

    }

    return hexString.toString();

  }

}

Get debug info hardware from the user

  public static String get_debug_hardwere_info() {

    String s = "";

    s += "OS Version: " + System.getProperty("os.version") + "(" + android.os.Build.VERSION.INCREMENTAL + ")";

    s += " | OS API Level: " + android.os.Build.VERSION.RELEASE + "(" + android.os.Build.VERSION.SDK_INT + ")";

    s += " | Device: " + android.os.Build.DEVICE;

    s += " | Model (and Product): " + android.os.Build.MODEL + " " + android.os.Build.MANUFACTURER + " (" + android.os.Build.PRODUCT + ")";

    return s;

  }

Use a DialogFactory for creating dialogs

public final class DialogFactory {

 public static Dialog createSimpleOkDialog(Context context, String title, String message) {
   AlertDialog.Builder alertDialog = new AlertDialog.Builder(context).setTitle(title).setMessage(message).setNeutralButton(R.string.dialog_action_ok, null);
   return alertDialog.create();
 }

 public static Dialog createGenericErrorDialog(Context context, String message) {
   AlertDialog.Builder alertDialog = new AlertDialog.Builder(context, R.style.AppCompatAlertDialogErrorStyle).setTitle(context.getString(R.string.generic_error_title))
       .setMessage(message)
       .setNeutralButton(R.string.dialog_action_ok, null);
   return alertDialog.create();
 }

 public static ProgressDialog createProgressDialog(Context context, String message) {
   ProgressDialog progressDialog = new ProgressDialog(context);
   progressDialog.setMessage(message);
   return progressDialog;
 }

 public static ProgressDialog createProgressDialog(Context context, @StringRes int messageResource) {
   return createProgressDialog(context, context.getString(messageResource));
 }

 public static Snackbar showErrorSnackBar(Activity mContext, View rootView, String error_message) {
   Snackbar snack_error = Snackbar.make(rootView, error_message, Snackbar.LENGTH_LONG);
   View view = snack_error.getView();
   TextView tv = (TextView) view.findViewById(android.support.design.R.id.snackbar_text);
   tv.setTextColor(ContextCompat.getColor(mContext, R.color.material_red));
   return snack_error;
 }

 public static ProgressBar DProgressBar(Context context) {

   ViewGroup layout = (ViewGroup) ((Activity) context).findViewById(android.R.id.content).getRootView();
   ProgressBar mProgressBar = new ProgressBar(context, null, android.R.attr.progressBarStyleLarge);
   mProgressBar.setIndeterminate(true);
   RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT);
   RelativeLayout rl = new RelativeLayout(context);
   rl.setGravity(Gravity.CENTER);
   rl.addView(mProgressBar);
   layout.addView(rl, params);
   return mProgressBar;
 }
}

Create a circle imageView


import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Paint;

import com.squareup.picasso.Transformation;

public class CircleTransform implements Transformation {
   private int x;
   private int y;

   @Override
   public Bitmap transform(Bitmap source) {
       int size = Math.min(source.getWidth(), source.getHeight());

        x = (source.getWidth() - size) / 2;
        y = (source.getHeight() - size) / 2;

       Bitmap squaredBitmap = Bitmap.createBitmap(source, x, y, size, size);
       if (squaredBitmap != source) {
           source.recycle();
       }

       Bitmap.Config config = source.getConfig() != null ? source.getConfig() : Bitmap.Config.ARGB_8888;
       Bitmap bitmap = Bitmap.createBitmap(size, size, config);

       Canvas canvas = new Canvas(bitmap);
       Paint paint = new Paint();
       BitmapShader shader = new BitmapShader(squaredBitmap, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP);
       paint.setShader(shader);
       paint.setAntiAlias(true);

       float r = size/2f;
       canvas.drawCircle(r, r, r, paint);

       squaredBitmap.recycle();
       return bitmap;
   }

   @Override
   public String key() {
       return "circle(x=" + x + ",y=" + y + ")";
   }
}

Intercept unauthorised requests to an api

a cool interceptor that will save you lots of trouble. You may want to combine it with EventBus. Here I use Dagger2 to inject eventbuss into this class.

import okhttp3.Interceptor;
import okhttp3.Response;

public class UnauthorisedInterceptor implements Interceptor {

 @Inject EventBus eventBus;

 public UnauthorisedInterceptor(Context context) {
   BaseApplication.get(context).getApplicationComponent().inject(this);
 }

 @Override public Response intercept(Chain chain) throws IOException {
   Response response = chain.proceed(chain.request());
   if (response.code() == 401) {
     new Handler(Looper.getMainLooper()).post(() -> eventBus.post(new AuthenticationErrorEvent()));
   }
   return response;
 }
}

Get Stuff form Internet

here’s the most clean way to do it.

public interface APIService {

 String ENDPOINT = "http://server_info.com/api/v2/";
 String API_KEY = "23e143494c02115cca9337e1e96b00e0";

 @FormUrlEncoded @POST("/register_new_account") Observable<RegistrationAndLoginResponse> registerNewUser(@Field("fullname") String fullname, @Field("address") String address,
     @Field("email") String email, @Field("phone") String phone, @Field("password") String password, @Field("onesignal_id") String onesignal_id, @Field("google_reg_id") String google_reg_id,
     @Field("birthday") String birthday, @Field("additional_details") String additional_details, @Field("current_lat") String current_lat, @Field("current_lng") String current_lng);

 @FormUrlEncoded @POST("/login_account") Observable<RegistrationAndLoginResponse> loginAccount(@Field("email") String email, @Field("password") String password);

  class Factory {

   public static APIService create(Context context) {

     OkHttpClient.Builder builder = new OkHttpClient().newBuilder();
     builder.readTimeout(10, TimeUnit.SECONDS);
     builder.connectTimeout(5, TimeUnit.SECONDS);

     if (BuildConfig.DEBUG) {
       HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
       interceptor.setLevel(HttpLoggingInterceptor.Level.BASIC);
       builder.addInterceptor(interceptor);
     }

     //Extra Headers
     builder.addInterceptor(chain -> {
       Request request = chain.request().newBuilder().addHeader("Authorization", API_KEY).build();
       return chain.proceed(request);
     });

     builder.addInterceptor(new UnauthorisedInterceptor(context));
     OkHttpClient client = builder.build();

     Retrofit retrofit =
         new Retrofit.Builder().baseUrl(APIService.ENDPOINT).client(client).addConverterFactory(GsonConverterFactory.create()).addCallAdapterFactory(RxJavaCallAdapterFactory.create()).build();

     return retrofit.create(APIService.class);
   }
 }


}

Have a Preference Helper Class

It’s just much easier to work with shared preferences

public class PreferencesHelper {

 private static SharedPreferences mPref;

 public static final String PREF_FILE_NAME = "myapp_shared_prefs";
 private static final String KEY_USER_ID = "user_id";
 private static final String KEY_NOTIFICATIONS_PREFERENCES = "notifications_preferences";
 private static final String KEY_NOTIFICATIONS_LOCATION = "location_preferences";
 private static final String KEY_NOTIFICATIONS_DELETED = "notifications_deleted";

 public PreferencesHelper(Context context) {
   mPref = context.getSharedPreferences(PREF_FILE_NAME, Context.MODE_PRIVATE);
 }

 public void clear() {
   mPref.edit().clear().apply();
 }

 public long getUserId() {
   return mPref.getLong(KEY_USER_ID, -1);
 }

 public void setUserId(long userId) {
   mPref.edit().putLong(KEY_USER_ID, userId).apply();
 }


 public void setLocationPrefs(boolean accepts) {
   mPref.edit().putBoolean(KEY_NOTIFICATIONS_LOCATION, accepts).apply();
 }

 public boolean getLocationPrefs() {
   return mPref.getBoolean(KEY_NOTIFICATIONS_LOCATION, true);
 }

}

Make your app not crash/delete fils on change orientation

Method 1 (simple, not exactly correct). in the manifest add

android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"

Method 2: check the following library https://github.com/frankiesardo/icepick

Have a cool looking edittext

 <android.support.design.widget.TextInputLayout
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           >

           <EditText
               android:id="@+id/loginactivity_password"
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
               android:hint="@string/prompt_password"

               android:imeOptions="actionDone"
               android:inputType="textPassword"
               android:maxLines="1"
               android:singleLine="true"
               android:drawableEnd="@drawable/ic_key"
               />

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

Ask user to rate your app

https://github.com/hotchemi/Android-Rate 

Simple as:

AppRate.with(this)
     .setInstallDays(0) // default 10, 0 means install day.
     .setLaunchTimes(3) // default 10
     .setRemindInterval(2) // default 1
     .setShowLaterButton(true) // default true
     .setDebug(false) // default false
     .setOnClickButtonListener(new OnClickButtonListener() { // callback listener.
         @Override
         public void onClickButton(int which) {
             Log.d(MainActivity.class.getName(), Integer.toString(which));
         }
     })
     .monitor();

 // Show a dialog if meets conditions
 AppRate.showRateDialogIfMeetsConditions(this);

A huge resource of cool libraries in android

https://android-arsenal.com/

[Shameless self promotion] Add facebook/google/twitter login to your app

Checkout this library https://github.com/AndreiD/FacebookTwitterGoogleLogins

and please star the repository if you’re there

[Shameless self promotion] Add a survey to your app

Checkout this library https://github.com/AndreiD/surveylib 

and please star the repository if you’re there

$15 in credit if you buy a droplet (VPS) from DigitalOcean

goo.gl/HJl3YU

Is this book not enough for you? Checkout our FREE youtube tutorials

https://www.youtube.com/user/SuperAndroidTutorial

Is this book not enough for you? Checkout our FREE youtube tutorials

https://www.youtube.com/user/SuperAndroidTutorial