Friday, October 5, 2018

Java HttpURLConnection | HttpsURLConnection > Implement Pause / Resume in File Downloading > How to Extract Chunked Data When Using HttpURLConnection > Download File as Chunk

It's very important (as well as beneficial) if we can pause / resume our download specially when downloading big file.
Below is a code snippet to download data chunk by chunk. So you can now pause / resume your download whenever you want.
package com.pkm;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List;
import java.util.Map;

public class JavaChunkDataDownload {
    public static enum Method {
        GET, POST, PUT, PATCH, DELETE
    }

    public static enum Type {
        XML, JSON, URL_ENCODED;
    };

    private static String fileName = "file2.zip";

    public static void main(String[] args) throws Exception {
        String URI = "http://ipv4.download.thinkbroadband.com/100MB.zip?t=" + System.currentTimeMillis();
        //URI = "http://localhost/ForDownload.zip?t=" + System.currentTimeMillis();
        Response response = execute(URI, Method.GET, "", Type.URL_ENCODED);
        println("Get_Result:\n" + lengthString(response.toString().replace("\n", ""), 400) + "...");
    }

    public static Response execute(String url, Method method, String data, Type type) {
        return execute(url, method, data, type, null, null);
    }

    private static Response execute(String requestURL, Method requestMethod, String requestData, Type dataType, Map headers, Integer timeOutMilli) {
        Long started = System.currentTimeMillis();
        String httpResponse = "", responseMessage = "";
        Integer httpCode = 0;
        timeOutMilli = timeOutMilli == null ? 1000 * 30 : timeOutMilli; /* Default read & write timeout */

        HttpURLConnection connection = null;
        try {
            String contentType = "", accept = "", contentLength = "" + requestData.length();
            switch (dataType) {
                case XML:
                    contentType = "text/xml; charset=utf-8";
                    accept = "text/xml";
                    break;
                case JSON:
                    contentType = "application/json";
                    break;
                case URL_ENCODED:
                    contentType = "application/x-www-form-urlencoded";
                    break;
            }

            connection = (HttpURLConnection) new URL(requestURL).openConnection();
            connection.setRequestMethod(requestMethod.name());
            connection.setConnectTimeout(timeOutMilli);
            connection.setReadTimeout(timeOutMilli);
            connection.setRequestProperty("Pragma", "no-cache");
            connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36");

            File outputFileCache = new File(fileName);
            if (outputFileCache.exists()) {
                connection.setAllowUserInteraction(true);
                connection.setRequestProperty("Range", "bytes=" + outputFileCache.length() + "-");
            }

            if (headers != null && headers.size() > 0) {
                for (Object name : headers.keySet().toArray()) {
                    connection.setRequestProperty(name.toString(), headers.get(name.toString()).toString());
                }
            }

            if (requestData.length() > 0) {
                connection.setDoInput(true);
                connection.setDoOutput(true);

                if (accept.length() > 0) {
                    connection.setRequestProperty("Accept", accept);
                }
                if (contentType.length() > 0) {
                    connection.setRequestProperty("Content-Type", contentType);
                }

                connection.setRequestProperty("Content_length", contentLength);
                OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream(), "UTF-8");
                writer.write(requestData);
                writer.flush();
                writer.close();
            }

            httpCode = connection.getResponseCode();
            Boolean validResponse = httpCode >= 200 && httpCode <= 299;
            responseMessage = connection.getResponseMessage();
            String responseContentType = connection.getContentType();
            if (responseContentType == null || responseContentType.length() == 0) {
                responseContentType = "txt";
            }
            else {
                if (responseContentType.contains(";")) {
                    responseContentType = responseContentType.substring(0, responseContentType.indexOf(";"));
                }
                if (responseContentType.contains("text/html")) {
                    responseContentType = "html";
                }
                else if (responseContentType.contains("image/")) {
                    responseContentType = "image";
                }
                else if (responseContentType.contains("/")) {
                    String[] parts = responseContentType.split("/");
                    responseContentType = parts[parts.length - 1];
                }
            }
            Map<String, List<String>> map = connection.getHeaderFields();
            for (Map.Entry<String, List<String>> entry : map.entrySet()) {
                System.out.println("Key : " + entry.getKey() + " ,Value : " + entry.getValue());
            }

            if (validResponse) {
                Long downloadedSize = 0L;
                String connectionField = connection.getHeaderField("content-range");
                if (connectionField != null) {
                    String[] connectionRanges = connectionField.substring("bytes=".length()).split("-");
                    downloadedSize = Long.valueOf(connectionRanges[0]);
                }
                if (connectionField == null && outputFileCache.exists()) {
                    outputFileCache.delete();
                }
                outputFileCache = new File(fileName);
                if (!outputFileCache.exists()) {
                    outputFileCache.createNewFile();
                    outputFileCache.setExecutable(true);
                }
                Long fileLength = connection.getContentLength() + downloadedSize;
                RandomAccessFile output = new RandomAccessFile(outputFileCache, "rw");
                output.seek(downloadedSize);

                InputStream is = connection.getInputStream();
                BufferedInputStream input = new BufferedInputStream(is);

                int readSize = 1024;
                byte data[] = new byte[readSize];
                int count = 0;
                int progress = 0;
                Integer pauseAt = 180;

                Long timeNeeded = System.currentTimeMillis();
                while ((count = input.read(data, 0, readSize)) >= 0 && progress != 100)
                {
                    downloadedSize += count;
                    output.write(data, 0, count);
                    progress = (int) ((downloadedSize * 100) / fileLength);
                    Long mb = output.length() / (1024 * 1024);
                    System.out.println("PROGRESS = " + (progress) + "%, DOWNLOADED=" + mb + " MB, TYPE=" + responseContentType);
                    if (progress >= pauseAt) {
                        break;
                    }
                }
                output.close();
                input.close();

                timeNeeded = (System.currentTimeMillis() - timeNeeded) / 1000;
                System.out.println("TIME REQUIRED=" + timeNeeded);
            }
        }
        catch (Exception ex) {
            httpResponse = "";
            responseMessage = ex.getMessage();
        }
        finally {
            try {
                connection.disconnect();
            }
            catch (Exception ex10) {
            }
        }

        return new Response(requestURL, httpCode, responseMessage, httpResponse, (System.currentTimeMillis() - started));
    }

    public static class Response {
        private String url;
        private Integer code;
        private String message;
        private String body;
        private Long time;

        public Response(String url, Integer code, String message, String body, Long time) {
            this.url = url;
            this.code = code;
            this.message = message;
            this.body = body;
            this.time = time;
        }

        @Override
        public String toString() {
            return "{URL=" + this.url + ",Code=" + this.code + ", Message=" + this.message + ", Time=" + this.time + " Millis, Body=" + this.body + "}";
        }

        public Integer getCode() {
            return this.code;
        }

        public String getMessage() {
            return this.message;
        }

        public String getBody() {
            return this.body;
        }

        public Long getTime() {
            return this.time;
        }
    }

    public static void println(Object o) {
        System.out.println("" + o);
    }

    public static String lengthString(String s, Integer m) {
        return s.length() > m ? s.substring(0, m) : s;
    }
}

Google Drive REST API V3 > Upload / Insert / Create Files Into Google Drive

So the very first step to upload a file to google drive using REST API V3, you need to create a project into your google account, then create a API key / value pair to authorize your application.

Follow below link to create your application and authorize your application:

CLICK HERE FOR MORE DETAILS

So at this stage, I assumed that you already have ACCESS KEY, which is the only thing when you communicate with Google REST API.

Below are some code snippet written in JAVA to upload files into GOOGLE DRIVE, you can do that same in other language, because I am not using any CLIENT LIBRARY, it's RAW REST API
The below link is GOOGLE DRIVE REST API V3 documentation link
CLICK HERE FOR MORE DETAILS

BELOW IS A FULL CODE SNIPPET TO UPLOAD/CREATE A FILE INTO GOODLE DRIVE USING REST API V3

package com.pkm;

import java.io.*;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import sun.misc.BASE64Encoder;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;

/**
 * Created by [PRITOM KUMAR] on 05-Oct-18.
 */
public class GoogleDriveFileUpload {
    public static void main(String[] args) throws Exception {
        /* I assumed you already have ACCESS TOKEN */
        String AUTH_TOKEN = "ya29...………….._BmTq3YOBNmQ";

        /* Collect your bytes from any file */
        byte[] content = "SUCCEED".getBytes(Charset.defaultCharset());

        /* JSON ENCODED PARAMS [name: 'Mine.txt', mimeType: 'text/plain'] */
        String params = "{\"name\":\"Mine.txt\",\"mimeType\":\"text\\/plain\"}";

        String boundary = "=====" + System.currentTimeMillis() + "============";
        String postBody = "--" + boundary + "\n" +
                "Content-Type: application/json; charset=UTF-8\n\n\n" +
                params + "\n" +
                "--" + boundary + "\n" +
                "Content-Type: text/plain\n" +
                "Content-Transfer-Encoding: base64\n\n\n" +
                base64Encode(content) +
                "\n--" + boundary + "--";

        String URI = "https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart";
        Map headers = new HashMap();
        headers.put("content-type", "multipart/related; boundary=" + boundary);
        headers.put("Content-Transfer-Encoding", "binary");
        headers.put("MIME-Version", "1.0");
        headers.put("Authorization", "Bearer " + AUTH_TOKEN);

        Map result = HttpJavaClient.doPost(URI, postBody, HttpJavaClient.Type.HTTP, headers);

        System.out.println(result);
        File outputFile = (File) result.get("outputFile");
        if (outputFile != null) {
            List<String> lines = Files.readAllLines(outputFile.toPath(), Charset.defaultCharset());
            for (String line : lines) {
                System.out.println(line);
            }
        }
    }

    public static String base64Encode(byte[] data) {
        BASE64Encoder base64Encoder = new BASE64Encoder();
        return base64Encoder.encodeBuffer(data);
    }
}

class HttpJavaClient {
    public static enum Method {
        GET, POST, PUT, PATCH, DELETE
    }

    public static enum Type {
        XML, JSON, URL_ENCODED, HTTP;
    }

    public static Map doPost(String url, String data, Type type, Map headers) {
        return execute(url, Method.POST, data, type, headers, null);
    }

    private static Map execute(String requestURL, Method requestMethod, String requestData, Type dataType, Map headers, Integer timeOutMilli) {
        String httpResponse = "", responseMessage = "";
        Integer httpCode = 0;
        timeOutMilli = timeOutMilli == null ? 1000 * 30 : timeOutMilli; /* Default read & write timeout */

        Map r = new HashMap();
        HttpsURLConnection connection = null;
        try {
            String contentType = "", accept = "", contentLength = "" + requestData.length();
            switch (dataType) {
                case XML:
                    contentType = "text/xml; charset=utf-8";
                    accept = "text/xml";
                    break;
                case JSON:
                    contentType = "application/json";
                    break;
                case URL_ENCODED:
                    contentType = "application/x-www-form-urlencoded";
                    break;
            }

            connection = (HttpsURLConnection) new URL(requestURL).openConnection();
            connection.setRequestMethod(requestMethod.name());
            connection.setConnectTimeout(timeOutMilli);
            connection.setReadTimeout(timeOutMilli);
            connection.setRequestProperty("Pragma", "no-cache");

            if (headers != null && headers.size() > 0) {
                for (Object name : headers.keySet().toArray()) {
                    if (name.toString().endsWith("tls")) {
                        //headers.put("tls", "TLSv1.2");
                        //headers.put("tls", "TLSv1");
                        SSLContext sc = SSLContext.getInstance(headers.get(name.toString()).toString());
                        sc.init(null, null, new java.security.SecureRandom());
                        connection.setSSLSocketFactory(sc.getSocketFactory());
                    }
                    else {
                        connection.setRequestProperty(name.toString(), headers.get(name.toString()).toString());
                    }
                }
            }

            if (requestData.length() > 0) {
                connection.setDoInput(true);
                connection.setDoOutput(true);

                if (accept.length() > 0) {
                    connection.setRequestProperty("Accept", accept);
                }
                if (contentType.length() > 0) {
                    connection.setRequestProperty("Content-Type", contentType);
                }

                connection.setRequestProperty("Content_length", contentLength);
                OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream(), "UTF-8");
                writer.write(requestData);
                writer.flush();
                writer.close();
            }

            httpCode = connection.getResponseCode();
            responseMessage = connection.getResponseMessage();

            if (httpCode >= 200 && httpCode <= 299) {
                String responseContentType = connection.getContentType();
                if (responseContentType == null || responseContentType.length() == 0) {
                    responseContentType = "txt";
                }
                else {
                    if (responseContentType.contains(";")) {
                        responseContentType = responseContentType.substring(0, responseContentType.indexOf(";"));
                    }
                    if (responseContentType.contains("text/html")) {
                        responseContentType = "html";
                    }
                    else if (responseContentType.contains("image/")) {
                        responseContentType = "image";
                    }
                    else if (responseContentType.contains("/")) {
                        String[] parts = responseContentType.split("/");
                        responseContentType = parts[parts.length - 1];
                    }
                }
                Map<String, List<String>> map = connection.getHeaderFields();
                for (Map.Entry<String, List<String>> entry : map.entrySet()) {
                    System.out.println("Key=" + entry.getKey() + " ,Value=" + entry.getValue());
                }
                System.out.println("Key=ResponseType ,Value=" + responseContentType);

                File tempFile = File.createTempFile("hello", ".tmp");
                RandomAccessFile output = new RandomAccessFile(tempFile, "rw");

                InputStream is = connection.getInputStream();
                BufferedInputStream input = new BufferedInputStream(is);


                Integer fileLength = connection.getContentLength();
                int readSize = 1024, downloadedSize = 0;
                byte data[] = new byte[readSize];
                int count = 0;
                int progress = 0;

                Long timeNeeded = System.currentTimeMillis();
                while ((count = input.read(data, 0, readSize)) >= 0 && progress != 100)
                {
                    downloadedSize += count;
                    output.write(data, 0, count);
                    progress = (int) ((downloadedSize * 100) / fileLength);
                    Long mb = output.length() / (1024 * 1024);
                    System.out.println("PROGRESS = " + (progress) + "%, DOWNLOADED=" + mb + " MB, TYPE=" + responseContentType);
                }
                output.close();
                input.close();
                r.put("outputFile", tempFile);

                timeNeeded = (System.currentTimeMillis() - timeNeeded) / 1000;
                System.out.println("TIME REQUIRED=" + timeNeeded + " SECONDS!!!");
            }
        }
        catch (Exception ex) {
            httpResponse = "";
            responseMessage = ex.getMessage();
        }
        finally {
            try {
                connection.disconnect();
            }
            catch (Exception ex10) {
            }
        }
        r.put("code", httpCode);
        r.put("message", responseMessage);
        r.put("output", httpResponse);
        return r;
    }
}
And output / response from GOOGLE DRIVE REST API V3 is as below:
{
    "kind": "drive#file",
    "id": "1KRehy6fX-6WwWi4AAERUklXki8EqwoWh",
    "name": "Mine.txt",
    "mimeType": "text/plain"
}

Tuesday, October 2, 2018

Emulator: ERROR: x86 emulation currently requires hardware acceleration

I tried to run my Hello World application in Android Studio. I got the following error:
* Emulator: ERROR: x86 emulation currently requires hardware acceleration!
* Please ensure Intel HAXM is properly installed and usable.
* CPU acceleration status: HAX kernel module is not installed!
So solution is as below:
* Open SDK Manager (In Android Studio, go to Tools > Android > SDK Manager) and Download Intel x86 Emulator Accelerator (HAXM installer) if you haven't.
* Now go to your SDK directory C:\users\%USERNAME%\AppData\Local\Android\sdk\extras\intel\Hardware_Accelerated_Execution_Manager\ and run the file named intelhaxm-android.exe.
* In case you get an error like "Intel virtualization technology (vt,vt-x) is not enabled". Go to your BIOS settings and enable Hardware Virtualization.
* Restart Android Studio and then try to start the AVD again.

Saturday, September 15, 2018

Android studio 3.X does not render xml file due to java class missing

java.lang.ClassNotFoundException: android.view.View$OnUnhandledKeyEventListener
In my very first project is obviously Hello world and I just open android studio and click on the empty activity and then w8 for the build to finish but it doesn't render the app as I expected. I reinstalled android studios and JDK10. but it doesn't work. the project shows fine in virtual device and I can even edit hello world to any string, but in XML view, it just shows a white screen and its showing every time. Below is the stack trace:
java.lang.ClassNotFoundException: android.view.View$OnUnhandledKeyEventListener 
at org.jetbrains.android.uipreview.ModuleClassLoader.load(ModuleClassLoader.java:180) 
at com.android.tools.idea.rendering.RenderClassLoader.findClass(RenderClassLoader.java:61) 
at org.jetbrains.android.uipreview.ModuleClassLoader.findClass(ModuleClassLoader.java:118) 
at java.lang.ClassLoader.loadClass(ClassLoader.java:424) 
at java.lang.ClassLoader.loadClass(ClassLoader.java:357) 
at org.jetbrains.android.uipreview.ModuleClassLoader.loadClass(ModuleClassLoader.java:213) 
at android.support.v7.widget.ActionBarContainer.(ActionBarContainer.java:62) 
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) 
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) 
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) 
at java.lang.reflect.Constructor.newInstance(Constructor.java:423) 
at org.jetbrains.android.uipreview.ViewLoader.createNewInstance(ViewLoader.java:481) 
at org.jetbrains.android.uipreview.ViewLoader.loadClass(ViewLoader.java:264) 
at org.jetbrains.android.uipreview.ViewLoader.loadView(ViewLoader.java:222) 
at com.android.tools.idea.rendering.LayoutlibCallbackImpl.loadView(LayoutlibCallbackImpl.java:209) 
at android.view.BridgeInflater.loadCustomView(BridgeInflater.java:337) 
at android.view.BridgeInflater.loadCustomView(BridgeInflater.java:348) 
at android.view.BridgeInflater.createViewFromTag(BridgeInflater.java:248) 
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:730) 
at android.view.LayoutInflater.rInflate_Original(LayoutInflater.java:863) 
at android.view.LayoutInflater_Delegate.rInflate(LayoutInflater_Delegate.java:72) 
at android.view.LayoutInflater.rInflate(LayoutInflater.java:837) 
at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:824) 
at android.view.LayoutInflater.inflate(LayoutInflater.java:515) 
at android.view.LayoutInflater.inflate(LayoutInflater.java:423) 
at com.android.layoutlib.bridge.bars.BridgeActionBar.(BridgeActionBar.java:89) 
at com.android.layoutlib.bridge.bars.AppCompatActionBar.(AppCompatActionBar.java:68) 
at com.android.layoutlib.bridge.impl.Layout.createActionBar(Layout.java:277) 
at com.android.layoutlib.bridge.impl.Layout.(Layout.java:161) 
at com.android.layoutlib.bridge.impl.RenderSessionImpl.inflate(RenderSessionImpl.java:288) 
at com.android.layoutlib.bridge.Bridge.createSession(Bridge.java:384) 
at com.android.tools.idea.layoutlib.LayoutLibrary.createSession(LayoutLibrary.java:193) 
at com.android.tools.idea.rendering.RenderTask.createRenderSession(RenderTask.java:544) 
at com.android.tools.idea.rendering.RenderTask.lambda$inflate$3(RenderTask.java:678) 
at java.util.concurrent.FutureTask.run(FutureTask.java:266) 
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) 
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) 
at java.lang.Thread.run(Thread.java:745)
First of all we are developing an app, so we don't need to face these types of issues because it has no valid reason to get such errors. So I suggest you use always stable version of every dependency, and this is because of unstable version using.
The issue you faced was due to using alpha version of AppCompact library. Use stable version to get rid of this issue now and in future.
FIRST Currently 27.1.1 is latest version of AppComactV7. You can see Maven Repo link to find out latest.
final def SUPPORT_VERSION = "27.1.1"
implementation "com.android.support:appcompat-v7:$SUPPORT_VERSION"
implementation "com.android.support:design:$SUPPORT_VERSION" // use only if already using
I use definitions to have same version for all support versions. Also ignore design library if you are not already using it.
SECOND You also need to downgrade your compileSdkVersion & targetSdkVersion to remove some gradle warnings.
compileSdkVersion 27
targetSdkVersion 27
Below is full example of build.gradle (Module:app)
apply plugin: 'com.android.application'

android {
    compileSdkVersion 28
    defaultConfig {
        applicationId "com.example.hp.myapplication"
        minSdkVersion 15
        targetSdkVersion 27
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}
final def SUPPORT_VERSION = "27.1.1"
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "com.android.support:appcompat-v7:$SUPPORT_VERSION"
    implementation "com.android.support:design:$SUPPORT_VERSION"

    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    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.support:gridlayout-v7:27.1.1'
}