/* * Copyright (C) 2023 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.android.development; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.net.http.HttpEngine; import android.net.http.HttpException; import android.net.http.UploadDataProvider; import android.net.http.UploadDataSink; import android.net.http.UrlRequest; import android.net.http.UrlResponseInfo; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.widget.EditText; import android.widget.TextView; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.Channels; import java.nio.channels.WritableByteChannel; import java.util.concurrent.Executor; import java.util.concurrent.Executors; /** * Activity for managing HttpEngine interactions. */ public class HttpEngineActivity extends Activity { private static final String TAG = HttpEngineActivity.class.getSimpleName(); private HttpEngine mHttpEngine; private String mUrl; private TextView mResultText; private TextView mReceiveDataText; class SimpleUrlRequestCallback implements UrlRequest.Callback { private ByteArrayOutputStream mBytesReceived = new ByteArrayOutputStream(); private WritableByteChannel mReceiveChannel = Channels.newChannel(mBytesReceived); @Override public void onRedirectReceived( UrlRequest request, UrlResponseInfo info, String newLocationUrl) { Log.i(TAG, "****** onRedirectReceived ******"); request.followRedirect(); } @Override public void onResponseStarted(UrlRequest request, UrlResponseInfo info) { Log.i(TAG, "****** Response Started ******"); Log.i(TAG, "*** Headers Are *** " + info.getHeaders()); request.read(ByteBuffer.allocateDirect(32 * 1024)); } @Override public void onReadCompleted( UrlRequest request, UrlResponseInfo info, ByteBuffer byteBuffer) { byteBuffer.flip(); Log.i(TAG, "****** onReadCompleted ******" + byteBuffer); try { mReceiveChannel.write(byteBuffer); } catch (IOException e) { Log.i(TAG, "IOException during ByteBuffer read. Details: ", e); } byteBuffer.clear(); request.read(byteBuffer); } @Override public void onSucceeded(UrlRequest request, UrlResponseInfo info) { Log.i(TAG, "****** Request Completed, status code is " + info.getHttpStatusCode() + ", total received bytes is " + info.getReceivedByteCount()); final String receivedData = mBytesReceived.toString(); final String url = info.getUrl(); final String text = "Completed " + url + " (" + info.getHttpStatusCode() + ")"; HttpEngineActivity.this.runOnUiThread(new Runnable() { @Override public void run() { mResultText.setText(text); mReceiveDataText.setText(receivedData); promptForURL(url); } }); } @Override public void onFailed(UrlRequest request, UrlResponseInfo info, HttpException error) { Log.i(TAG, "****** onFailed, error is: " + error.getMessage()); final String url = mUrl; final String text = "Failed " + mUrl + " (" + error.getMessage() + ")"; HttpEngineActivity.this.runOnUiThread(new Runnable() { @Override public void run() { mResultText.setText(text); promptForURL(url); } }); } @Override public void onCanceled(UrlRequest request, UrlResponseInfo info) { Log.i(TAG, "****** onCanceled ******"); } } @Override protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.http_engine_activity); mResultText = (TextView) findViewById(R.id.resultView); mReceiveDataText = (TextView) findViewById(R.id.dataView); mReceiveDataText.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { promptForURL(mUrl); } }); HttpEngine.Builder myBuilder = new HttpEngine.Builder(this); myBuilder.setEnableHttpCache(HttpEngine.Builder.HTTP_CACHE_IN_MEMORY, 100 * 1024) .setEnableHttp2(true) .setEnableQuic(true); mHttpEngine = myBuilder.build(); String appUrl = (getIntent() != null ? getIntent().getDataString() : null); if (appUrl == null) { promptForURL("https://"); } else { startWithURL(appUrl); } } private void promptForURL(String url) { Log.i(TAG, "No URL provided via intent, prompting user..."); AlertDialog.Builder alert = new AlertDialog.Builder(this); alert.setTitle("Enter a URL"); LayoutInflater inflater = getLayoutInflater(); View alertView = inflater.inflate(R.layout.http_engine_dialog, null); final EditText urlInput = (EditText) alertView.findViewById(R.id.urlText); urlInput.setText(url); final EditText postInput = (EditText) alertView.findViewById(R.id.postText); alert.setView(alertView); alert.setPositiveButton("Load", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int button) { String url = urlInput.getText().toString(); String postData = postInput.getText().toString(); startWithURL(url, postData); } }); alert.show(); } private void applyPostDataToUrlRequestBuilder( UrlRequest.Builder builder, Executor executor, String postData) { if (postData != null && postData.length() > 0) { builder.setHttpMethod("POST"); builder.addHeader("Content-Type", "application/x-www-form-urlencoded"); // TODO: make android.net.http.apihelpers.UploadDataProviders accessible. builder.setUploadDataProvider(new UploadDataProvider() { @Override public long getLength() { return postData.length(); } @Override public void read(UploadDataSink uploadDataSink, ByteBuffer byteBuffer) { byteBuffer.put(postData.getBytes()); uploadDataSink.onReadSucceeded(/*finalChunk*/ false); } @Override public void rewind(UploadDataSink uploadDataSink) { // noop uploadDataSink.onRewindSucceeded(); } }, executor); } } private void startWithURL(String url) { startWithURL(url, null); } private void startWithURL(String url, String postData) { Log.i(TAG, "UrlRequest started: " + url); mUrl = url; Executor executor = Executors.newSingleThreadExecutor(); UrlRequest.Callback callback = new SimpleUrlRequestCallback(); UrlRequest.Builder builder = mHttpEngine.newUrlRequestBuilder(url, executor, callback); applyPostDataToUrlRequestBuilder(builder, executor, postData); builder.build().start(); } }