summaryrefslogtreecommitdiffstats
path: root/samples/browseable/NetworkConnect/src/com.example.android.networkconnect/NetworkFragment.java
diff options
context:
space:
mode:
Diffstat (limited to 'samples/browseable/NetworkConnect/src/com.example.android.networkconnect/NetworkFragment.java')
-rw-r--r--samples/browseable/NetworkConnect/src/com.example.android.networkconnect/NetworkFragment.java289
1 files changed, 289 insertions, 0 deletions
diff --git a/samples/browseable/NetworkConnect/src/com.example.android.networkconnect/NetworkFragment.java b/samples/browseable/NetworkConnect/src/com.example.android.networkconnect/NetworkFragment.java
new file mode 100644
index 000000000..cc41075a1
--- /dev/null
+++ b/samples/browseable/NetworkConnect/src/com.example.android.networkconnect/NetworkFragment.java
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.networkconnect;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.net.URL;
+
+import javax.net.ssl.HttpsURLConnection;
+
+/**
+ * Implementation of headless Fragment that runs an AsyncTask to fetch data from the network.
+ */
+public class NetworkFragment extends Fragment {
+ public static final String TAG = "NetworkFragment";
+
+ private static final String URL_KEY = "UrlKey";
+
+ private DownloadCallback mCallback;
+ private DownloadTask mDownloadTask;
+ private String mUrlString;
+
+ /**
+ * Static initializer for NetworkFragment that sets the URL of the host it will be downloading
+ * from.
+ */
+ public static NetworkFragment getInstance(FragmentManager fragmentManager, String url) {
+ // Recover NetworkFragment in case we are re-creating the Activity due to a config change.
+ // This is necessary because NetworkFragment might have a task that began running before
+ // the config change and has not finished yet.
+ // The NetworkFragment is recoverable via this method because it calls
+ // setRetainInstance(true) upon creation.
+ NetworkFragment networkFragment = (NetworkFragment) fragmentManager
+ .findFragmentByTag(NetworkFragment.TAG);
+ if (networkFragment == null) {
+ networkFragment = new NetworkFragment();
+ Bundle args = new Bundle();
+ args.putString(URL_KEY, url);
+ networkFragment.setArguments(args);
+ fragmentManager.beginTransaction().add(networkFragment, TAG).commit();
+ }
+ return networkFragment;
+ }
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ // Retain this Fragment across configuration changes in the host Activity.
+ setRetainInstance(true);
+ mUrlString = getArguments().getString(URL_KEY);
+ }
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ // Host Activity will handle callbacks from task.
+ mCallback = (DownloadCallback)context;
+ }
+
+ @Override
+ public void onDetach() {
+ super.onDetach();
+ // Clear reference to host Activity.
+ mCallback = null;
+ }
+
+ @Override
+ public void onDestroy() {
+ // Cancel task when Fragment is destroyed.
+ cancelDownload();
+ super.onDestroy();
+ }
+
+ /**
+ * Start non-blocking execution of DownloadTask.
+ */
+ public void startDownload() {
+ cancelDownload();
+ mDownloadTask = new DownloadTask();
+ mDownloadTask.execute(mUrlString);
+ }
+
+ /**
+ * Cancel (and interrupt if necessary) any ongoing DownloadTask execution.
+ */
+ public void cancelDownload() {
+ if (mDownloadTask != null) {
+ mDownloadTask.cancel(true);
+ mDownloadTask = null;
+ }
+ }
+
+ /**
+ * Implementation of AsyncTask that runs a network operation on a background thread.
+ */
+ private class DownloadTask extends AsyncTask<String, Integer, DownloadTask.Result> {
+
+ /**
+ * Wrapper class that serves as a union of a result value and an exception. When the
+ * download task has completed, either the result value or exception can be a non-null
+ * value. This allows you to pass exceptions to the UI thread that were thrown during
+ * doInBackground().
+ */
+ class Result {
+ public String mResultValue;
+ public Exception mException;
+ public Result(String resultValue) {
+ mResultValue = resultValue;
+ }
+ public Result(Exception exception) {
+ mException = exception;
+ }
+ }
+
+ /**
+ * Cancel background network operation if we do not have network connectivity.
+ */
+ @Override
+ protected void onPreExecute() {
+ if (mCallback != null) {
+ NetworkInfo networkInfo = mCallback.getActiveNetworkInfo();
+ if (networkInfo == null || !networkInfo.isConnected() ||
+ (networkInfo.getType() != ConnectivityManager.TYPE_WIFI
+ && networkInfo.getType() != ConnectivityManager.TYPE_MOBILE)) {
+ // If no connectivity, cancel task and update Callback with null data.
+ mCallback.updateFromDownload(null);
+ cancel(true);
+ }
+ }
+ }
+
+ /**
+ * Defines work to perform on the background thread.
+ */
+ @Override
+ protected Result doInBackground(String... urls) {
+ Result result = null;
+ if (!isCancelled() && urls != null && urls.length > 0) {
+ String urlString = urls[0];
+ try {
+ URL url = new URL(urlString);
+ String resultString = downloadUrl(url);
+ if (resultString != null) {
+ result = new Result(resultString);
+ } else {
+ throw new IOException("No response received.");
+ }
+ } catch(Exception e) {
+ result = new Result(e);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Send DownloadCallback a progress update.
+ */
+ @Override
+ protected void onProgressUpdate(Integer... values) {
+ super.onProgressUpdate(values);
+ if (values.length >= 2) {
+ mCallback.onProgressUpdate(values[0], values[1]);
+ }
+ }
+
+ /**
+ * Updates the DownloadCallback with the result.
+ */
+ @Override
+ protected void onPostExecute(Result result) {
+ if (result != null && mCallback != null) {
+ if (result.mException != null) {
+ mCallback.updateFromDownload(result.mException.getMessage());
+ } else if (result.mResultValue != null) {
+ mCallback.updateFromDownload(result.mResultValue);
+ }
+ mCallback.finishDownloading();
+ }
+ }
+
+ /**
+ * Override to add special behavior for cancelled AsyncTask.
+ */
+ @Override
+ protected void onCancelled(Result result) {
+ }
+
+ /**
+ * Given a URL, sets up a connection and gets the HTTP response body from the server.
+ * If the network request is successful, it returns the response body in String form. Otherwise,
+ * it will throw an IOException.
+ */
+ private String downloadUrl(URL url) throws IOException {
+ InputStream stream = null;
+ HttpsURLConnection connection = null;
+ String result = null;
+ try {
+ connection = (HttpsURLConnection) url.openConnection();
+ // Timeout for reading InputStream arbitrarily set to 3000ms.
+ connection.setReadTimeout(3000);
+ // Timeout for connection.connect() arbitrarily set to 3000ms.
+ connection.setConnectTimeout(3000);
+ // For this use case, set HTTP method to GET.
+ connection.setRequestMethod("GET");
+ // Already true by default but setting just in case; needs to be true since this request
+ // is carrying an input (response) body.
+ connection.setDoInput(true);
+ // Open communications link (network traffic occurs here).
+ connection.connect();
+ publishProgress(DownloadCallback.Progress.CONNECT_SUCCESS);
+ int responseCode = connection.getResponseCode();
+ if (responseCode != HttpsURLConnection.HTTP_OK) {
+ throw new IOException("HTTP error code: " + responseCode);
+ }
+ // Retrieve the response body as an InputStream.
+ stream = connection.getInputStream();
+ publishProgress(DownloadCallback.Progress.GET_INPUT_STREAM_SUCCESS, 0);
+ if (stream != null) {
+ // Converts Stream to String with max length of 500.
+ result = readStream(stream, 500);
+ publishProgress(DownloadCallback.Progress.PROCESS_INPUT_STREAM_SUCCESS, 0);
+ }
+ } finally {
+ // Close Stream and disconnect HTTPS connection.
+ if (stream != null) {
+ stream.close();
+ }
+ if (connection != null) {
+ connection.disconnect();
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Converts the contents of an InputStream to a String.
+ */
+ private String readStream(InputStream stream, int maxLength) throws IOException {
+ String result = null;
+ // Read InputStream using the UTF-8 charset.
+ InputStreamReader reader = new InputStreamReader(stream, "UTF-8");
+ // Create temporary buffer to hold Stream data with specified max length.
+ char[] buffer = new char[maxLength];
+ // Populate temporary buffer with Stream data.
+ int numChars = 0;
+ int readSize = 0;
+ while (numChars < maxLength && readSize != -1) {
+ numChars += readSize;
+ int pct = (100 * numChars) / maxLength;
+ publishProgress(DownloadCallback.Progress.PROCESS_INPUT_STREAM_IN_PROGRESS, pct);
+ readSize = reader.read(buffer, numChars, buffer.length - numChars);
+ }
+ if (numChars != -1) {
+ // The stream was not empty.
+ // Create String that is actual length of response body if actual length was less than
+ // max length.
+ numChars = Math.min(numChars, maxLength);
+ result = new String(buffer, 0, numChars);
+ }
+ return result;
+ }
+ }
+}