I want to disable file access within an Android WebView I'm creating using react-native's built-in WebView component.
The Android WebView docs say "File access is enabled by default.", and this is a security concern for my organization.
The react-native 0.31 docs mention a getWebViewHandle method that can be used to access the underlying WebView node; if this worked, then I could (presumably) write:
import { WebView, Platform } from 'react-native';
//...
var reactWebview = <Webview [props here] />
if (Platform.OS === 'android') {
var webview = reactWebview.getWebViewHandle();
webview.setAllowFileAccess(false);
}
However, later versions of the react-native docs don't mention getWebViewHandle, and when I run code like this in react-native 0.44 on an Android device, I get the error webview.getWebViewHandle is not a function.
My questions are:
Is file access enabled by default for the Android WebViews created by react-native?
If so, how can we disable this file access? Could we accomplish this by extending the WebView class, or would we need to fork and modify react-native?
Thanks for your time!
Questions 1: As see from the source code of ReactWebViewManager.java, RN does not call WebView.setAllowFileAccess, so file access is enabled by Android WebView, not by RN.
Questions 2: You can create a custom WebView to do what you need, or just get the reference of your WebView from a Native Module, then you can access all the apis of Android WebView like setAllowFileAccess in that Native Module.
Native Module
public class WebViewSettingModule extends ReactContextBaseJavaModule {
public WebViewSettingModule(ReactApplicationContext reactContext) {
super(reactContext);
}
#Override
public String getName() {
return "WebViewSetting";
}
#ReactMethod
public void setWebView() {
Activity activity = getCurrentActivity();
//the id for the ReactRootView is always be 1
#IdRes int id = 1;
View view = activity.findViewById(id);
if (view instanceof ReactRootView) {
ReactRootView reactRootView = (ReactRootView) view;
//make sure the WebView is directly child of ReactRootView
reactRootView.setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() {
#Override
public void onChildViewAdded(View parent, final View child) {
if (child instanceof WebView) {
Log.e("onChildViewAdded: ", ((WebView) child).getUrl());
//get the reference to the WebView and setAllowFileAccess
((WebView) child).getSettings().setAllowFileAccess(false);
}
}
#Override
public void onChildViewRemoved(View parent, View child) {
}
});
}
}
}
index.android.js
import React, {Component} from "react";
import {AppRegistry, View, WebView} from "react-native";
//the native module
import MyWebView from "./src/MyWebView";
export default class WebViewSetting extends Component {
componentDidMount() {
//notify native code to modify WebView setting
MyWebView.setWebView();
}
render() {
return (
<View style={{flex: 1}}>
<WebView
source={{uri: 'https://github.com/'}}
style={{marginTop: 20}}/>
</View>
);
}
}
AppRegistry.registerComponent('WebViewSetting', () => WebViewSetting);
the whole code can be found here
node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/ReactWebViewManager.java
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.facebook.react.views.webview;
import javax.annotation.Nullable;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Picture;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.MediaStore;
import android.text.TextUtils;
import android.util.Log;
import android.view.ViewGroup.LayoutParams;
import android.webkit.ConsoleMessage;
import android.webkit.GeolocationPermissions;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.webkit.JavascriptInterface;
import android.webkit.ValueCallback;
import android.webkit.WebSettings;
import com.facebook.common.logging.FLog;
import com.facebook.react.bridge.ActivityEventListener;
import com.facebook.react.common.ReactConstants;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.LifecycleEventListener;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableMapKeySetIterator;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.common.MapBuilder;
import com.facebook.react.common.build.ReactBuildConfig;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.uimanager.SimpleViewManager;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.UIManagerModule;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.uimanager.events.ContentSizeChangeEvent;
import com.facebook.react.uimanager.events.Event;
import com.facebook.react.uimanager.events.EventDispatcher;
import com.facebook.react.views.webview.events.TopLoadingErrorEvent;
import com.facebook.react.views.webview.events.TopLoadingFinishEvent;
import com.facebook.react.views.webview.events.TopLoadingStartEvent;
import com.facebook.react.views.webview.events.TopMessageEvent;
import org.json.JSONObject;
import org.json.JSONException;
#ReactModule(name = ReactWebViewManager.REACT_CLASS)
public class ReactWebViewManager extends SimpleViewManager<WebView> {
protected static final String REACT_CLASS = "RCTWebView";
private static final String HTML_ENCODING = "UTF-8";
private static final String HTML_MIME_TYPE = "text/html; charset=utf-8";
private static final String BRIDGE_NAME = "__REACT_WEB_VIEW_BRIDGE";
private static final String HTTP_METHOD_POST = "POST";
public static final int COMMAND_GO_BACK = 1;
public static final int COMMAND_GO_FORWARD = 2;
public static final int COMMAND_RELOAD = 3;
public static final int COMMAND_STOP_LOADING = 4;
public static final int COMMAND_POST_MESSAGE = 5;
public static final int COMMAND_INJECT_JAVASCRIPT = 6;
private static final String BLANK_URL = "about:blank";
public static final int INPUT_FILE_REQUEST_GALLERY_IMAGE = 1001;
public static final int REQUEST_SELECT_FILE_LEGACY = 1012;
private WebViewConfig mWebViewConfig;
private
#Nullable
WebView.PictureListener mPictureListener;
private ValueCallback<Uri[]> mFilePathCallbackArr;
private ValueCallback<Uri> mFilePathCallback; // Legacy (Android 4.1+)
private String mCameraPhotoPath;
protected static class ReactWebViewClient extends WebViewClient {
private boolean mLastLoadFailed = false;
#Override
public void onPageFinished(WebView webView, String url) {
super.onPageFinished(webView, url);
if (!mLastLoadFailed) {
ReactWebView reactWebView = (ReactWebView) webView;
reactWebView.callInjectedJavaScript();
reactWebView.linkBridge();
emitFinishEvent(webView, url);
}
}
#Override
public void onPageStarted(WebView webView, String url, Bitmap favicon) {
super.onPageStarted(webView, url, favicon);
mLastLoadFailed = false;
dispatchEvent(
webView,
new TopLoadingStartEvent(
webView.getId(),
createWebViewEvent(webView, url)));
}
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.startsWith("http://") || url.startsWith("https://") ||
url.startsWith("file://")) {
return false;
} else {
try {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
view.getContext().startActivity(intent);
} catch (ActivityNotFoundException e) {
FLog.w(ReactConstants.TAG, "activity not found to handle uri scheme for: " + url, e);
}
return true;
}
}
#Override
public void onReceivedError(
WebView webView,
int errorCode,
String description,
String failingUrl) {
super.onReceivedError(webView, errorCode, description, failingUrl);
mLastLoadFailed = true;
emitFinishEvent(webView, failingUrl);
WritableMap eventData = createWebViewEvent(webView, failingUrl);
eventData.putDouble("code", errorCode);
eventData.putString("description", description);
dispatchEvent(
webView,
new TopLoadingErrorEvent(webView.getId(), eventData));
}
#Override
public void doUpdateVisitedHistory(WebView webView, String url, boolean isReload) {
super.doUpdateVisitedHistory(webView, url, isReload);
dispatchEvent(
webView,
new TopLoadingStartEvent(
webView.getId(),
createWebViewEvent(webView, url)));
}
private void emitFinishEvent(WebView webView, String url) {
dispatchEvent(
webView,
new TopLoadingFinishEvent(
webView.getId(),
createWebViewEvent(webView, url)));
}
private WritableMap createWebViewEvent(WebView webView, String url) {
WritableMap event = Arguments.createMap();
event.putDouble("target", webView.getId());
event.putString("url", url);
event.putBoolean("loading", !mLastLoadFailed && webView.getProgress() != 100);
event.putString("title", webView.getTitle());
event.putBoolean("canGoBack", webView.canGoBack());
event.putBoolean("canGoForward", webView.canGoForward());
return event;
}
}
protected static class ReactWebView extends WebView implements LifecycleEventListener {
private
#Nullable
String injectedJS;
private boolean messagingEnabled = false;
private class ReactWebViewBridge {
ReactWebView mContext;
ReactWebViewBridge(ReactWebView c) {
mContext = c;
}
#JavascriptInterface
public void postMessage(String message) {
mContext.onMessage(message);
}
}
public ReactWebView(ThemedReactContext reactContext) {
super(reactContext);
}
#Override
public void onHostResume() {
// do nothing
}
#Override
public void onHostPause() {
// do nothing
}
#Override
public void onHostDestroy() {
cleanupCallbacksAndDestroy();
}
public void setInjectedJavaScript(#Nullable String js) {
injectedJS = js;
}
public void setMessagingEnabled(boolean enabled) {
if (messagingEnabled == enabled) {
return;
}
messagingEnabled = enabled;
if (enabled) {
addJavascriptInterface(new ReactWebViewBridge(this), BRIDGE_NAME);
linkBridge();
} else {
removeJavascriptInterface(BRIDGE_NAME);
}
}
public void callInjectedJavaScript() {
if (getSettings().getJavaScriptEnabled() &&
injectedJS != null &&
!TextUtils.isEmpty(injectedJS)) {
loadUrl("javascript:(function() {\n" + injectedJS + ";\n})();");
}
}
public void linkBridge() {
if (messagingEnabled) {
if (ReactBuildConfig.DEBUG && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
// See isNative in lodash
String testPostMessageNative = "String(window.postMessage) === String(Object.hasOwnProperty).replace('hasOwnProperty', 'postMessage')";
evaluateJavascript(testPostMessageNative, new ValueCallback<String>() {
#Override
public void onReceiveValue(String value) {
if (value.equals("true")) {
FLog.w(ReactConstants.TAG, "Setting onMessage on a WebView overrides existing values of window.postMessage, but a previous value was defined");
}
}
});
}
loadUrl("javascript:(" +
"window.originalPostMessage = window.postMessage," +
"window.postMessage = function(data) {" +
BRIDGE_NAME + ".postMessage(String(data));" +
"}" +
")");
}
}
public void onMessage(String message) {
dispatchEvent(this, new TopMessageEvent(this.getId(), message));
}
private void cleanupCallbacksAndDestroy() {
setWebViewClient(null);
destroy();
}
}
public ReactWebViewManager() {
mWebViewConfig = new WebViewConfig() {
public void configWebView(WebView webView) {
}
};
}
public ReactWebViewManager(WebViewConfig webViewConfig) {
mWebViewConfig = webViewConfig;
}
#Override
public String getName() {
return REACT_CLASS;
}
#Override
protected WebView createViewInstance(final ThemedReactContext reactContext) {
ReactWebView webView = new ReactWebView(reactContext);
webView.setWebChromeClient(new WebChromeClient() {
#Override
public boolean onConsoleMessage(ConsoleMessage message) {
if (ReactBuildConfig.DEBUG) {
return super.onConsoleMessage(message);
}
return true;
}
#Override
public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) {
callback.invoke(origin, true, false);
}
private File createImageFile() throws IOException {
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES);
File imageFile = new File(
storageDir,
imageFileName + ".jpg"
);
return imageFile;
}
private Intent getVideoCaptureIntent() {
Intent recordVideoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
recordVideoIntent
.resolveActivity(
reactContext
.getCurrentActivity()
.getPackageManager()
);
recordVideoIntent.putExtra("type", "foobar");
return recordVideoIntent;
}
public boolean onShowFileChooser(
WebView webView,
ValueCallback<Uri[]> filePathCallback,
WebChromeClient.FileChooserParams fileChooserParams
) {
if (mFilePathCallbackArr != null) {
mFilePathCallbackArr.onReceiveValue(null);
}
mFilePathCallbackArr = filePathCallback;
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
ComponentName comp = takePictureIntent
.resolveActivity(
reactContext
.getCurrentActivity()
.getPackageManager()
);
if (comp != null) {
File photoFile = null;
try {
photoFile = createImageFile();
takePictureIntent.putExtra("PhotoPath", mCameraPhotoPath);
} catch (IOException ex) {
FLog.e(ReactConstants.TAG, "Unable to create Image File", ex);
}
if (photoFile != null) {
mCameraPhotoPath = "file:" + photoFile.getAbsolutePath();
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,
Uri.fromFile(photoFile));
} else {
takePictureIntent = null;
}
}
Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
contentSelectionIntent.setType("*/*");
Intent videoCaptureIntent = getVideoCaptureIntent();
Intent[] intentArray;
if (takePictureIntent != null) {
intentArray = new Intent[]{takePictureIntent};
} else {
intentArray = new Intent[0];
}
intentArray = new Intent[]{
intentArray[0],
videoCaptureIntent
};
Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
chooserIntent.putExtra(Intent.EXTRA_TITLE, "Image Chooser");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);
reactContext.getCurrentActivity().startActivityForResult(chooserIntent, INPUT_FILE_REQUEST_GALLERY_IMAGE);
return true;
}
});
reactContext.addLifecycleEventListener(webView);
reactContext.addActivityEventListener(new ActivityEventListener() {
// Android 5+
#Override
public void onActivityResult (Activity activity, int requestCode, int resultCode, Intent data) {
if(requestCode != INPUT_FILE_REQUEST_GALLERY_IMAGE || mFilePathCallbackArr == null) {
return;
}
Uri[] results = null;
if(resultCode == Activity.RESULT_OK) {
if(data == null || data.getData() == null) {
if(mCameraPhotoPath != null) {
results = new Uri[]{Uri.parse(mCameraPhotoPath)};
}
} else {
String dataString = data.getDataString();
if (dataString != null) {
results = new Uri[]{Uri.parse(dataString)};
}
}
}
if(results == null) {
mFilePathCallbackArr.onReceiveValue(new Uri[]{});
}
else {
mFilePathCallbackArr.onReceiveValue(results);
}
mFilePathCallbackArr = null;
return;
}
#Override
public void onNewIntent(Intent intent) {}
});
mWebViewConfig.configWebView(webView);
webView.getSettings().setBuiltInZoomControls(true);
webView.getSettings().setDisplayZoomControls(false);
webView.getSettings().setDomStorageEnabled(true);
webView.setLayoutParams(
new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT));
if (ReactBuildConfig.DEBUG && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
WebView.setWebContentsDebuggingEnabled(true);
}
return webView;
}
#ReactProp(name = "javaScriptEnabled")
public void setJavaScriptEnabled(WebView view, boolean enabled) {
view.getSettings().setJavaScriptEnabled(enabled);
}
#ReactProp(name = "scalesPageToFit")
public void setScalesPageToFit(WebView view, boolean enabled) {
view.getSettings().setUseWideViewPort(!enabled);
}
#ReactProp(name = "domStorageEnabled")
public void setDomStorageEnabled(WebView view, boolean enabled) {
view.getSettings().setDomStorageEnabled(enabled);
}
#ReactProp(name = "userAgent")
public void setUserAgent(WebView view, #Nullable String userAgent) {
if (userAgent != null) {
// TODO(8496850): Fix incorrect behavior when property is unset (uA == null)
view.getSettings().setUserAgentString(userAgent);
}
}
#ReactProp(name = "mediaPlaybackRequiresUserAction")
public void setMediaPlaybackRequiresUserAction(WebView view, boolean requires) {
view.getSettings().setMediaPlaybackRequiresUserGesture(requires);
}
#ReactProp(name = "allowUniversalAccessFromFileURLs")
public void setAllowUniversalAccessFromFileURLs(WebView view, boolean allow) {
view.getSettings().setAllowUniversalAccessFromFileURLs(allow);
}
#ReactProp(name = "injectedJavaScript")
public void setInjectedJavaScript(WebView view, #Nullable String injectedJavaScript) {
((ReactWebView) view).setInjectedJavaScript(injectedJavaScript);
}
#ReactProp(name = "messagingEnabled")
public void setMessagingEnabled(WebView view, boolean enabled) {
((ReactWebView) view).setMessagingEnabled(enabled);
}
#ReactProp(name = "source")
public void setSource(WebView view, #Nullable ReadableMap source) {
if (source != null) {
if (source.hasKey("html")) {
String html = source.getString("html");
if (source.hasKey("baseUrl")) {
view.loadDataWithBaseURL(
source.getString("baseUrl"), html, HTML_MIME_TYPE, HTML_ENCODING, null);
} else {
view.loadData(html, HTML_MIME_TYPE, HTML_ENCODING);
}
return;
}
if (source.hasKey("uri")) {
String url = source.getString("uri");
String previousUrl = view.getUrl();
if (previousUrl != null && previousUrl.equals(url)) {
return;
}
if (source.hasKey("method")) {
String method = source.getString("method");
if (method.equals(HTTP_METHOD_POST)) {
byte[] postData = null;
if (source.hasKey("body")) {
String body = source.getString("body");
try {
postData = body.getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
postData = body.getBytes();
}
}
if (postData == null) {
postData = new byte[0];
}
view.postUrl(url, postData);
return;
}
}
HashMap<String, String> headerMap = new HashMap<>();
if (source.hasKey("headers")) {
ReadableMap headers = source.getMap("headers");
ReadableMapKeySetIterator iter = headers.keySetIterator();
while (iter.hasNextKey()) {
String key = iter.nextKey();
if ("user-agent".equals(key.toLowerCase(Locale.ENGLISH))) {
if (view.getSettings() != null) {
view.getSettings().setUserAgentString(headers.getString(key));
}
} else {
headerMap.put(key, headers.getString(key));
}
}
}
view.loadUrl(url, headerMap);
return;
}
}
view.loadUrl(BLANK_URL);
}
#ReactProp(name = "onContentSizeChange")
public void setOnContentSizeChange(WebView view, boolean sendContentSizeChangeEvents) {
if (sendContentSizeChangeEvents) {
view.setPictureListener(getPictureListener());
} else {
view.setPictureListener(null);
}
}
#ReactProp(name = "mixedContentMode")
public void setMixedContentMode(WebView view, #Nullable String mixedContentMode) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (mixedContentMode == null || "never".equals(mixedContentMode)) {
view.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_NEVER_ALLOW);
} else if ("always".equals(mixedContentMode)) {
view.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
} else if ("compatibility".equals(mixedContentMode)) {
view.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE);
}
}
}
#Override
protected void addEventEmitters(ThemedReactContext reactContext, WebView view) {
view.setWebViewClient(new ReactWebViewClient());
}
#Override
public
#Nullable
Map<String, Integer> getCommandsMap() {
return MapBuilder.of(
"goBack", COMMAND_GO_BACK,
"goForward", COMMAND_GO_FORWARD,
"reload", COMMAND_RELOAD,
"stopLoading", COMMAND_STOP_LOADING,
"postMessage", COMMAND_POST_MESSAGE,
"injectJavaScript", COMMAND_INJECT_JAVASCRIPT
);
}
#Override
public void receiveCommand(WebView root, int commandId, #Nullable ReadableArray args) {
switch (commandId) {
case COMMAND_GO_BACK:
root.goBack();
break;
case COMMAND_GO_FORWARD:
root.goForward();
break;
case COMMAND_RELOAD:
root.reload();
break;
case COMMAND_STOP_LOADING:
root.stopLoading();
break;
case COMMAND_POST_MESSAGE:
try {
JSONObject eventInitDict = new JSONObject();
eventInitDict.put("data", args.getString(0));
root.loadUrl("javascript:(function () {" +
"var event;" +
"var data = " + eventInitDict.toString() + ";" +
"try {" +
"event = new MessageEvent('message', data);" +
"} catch (e) {" +
"event = document.createEvent('MessageEvent');" +
"event.initMessageEvent('message', true, true, data.data, data.origin, data.lastEventId, data.source);" +
"}" +
"document.dispatchEvent(event);" +
"})();");
} catch (JSONException e) {
throw new RuntimeException(e);
}
break;
case COMMAND_INJECT_JAVASCRIPT:
root.loadUrl("javascript:" + args.getString(0));
break;
}
}
#Override
public void onDropViewInstance(WebView webView) {
super.onDropViewInstance(webView);
((ThemedReactContext) webView.getContext()).removeLifecycleEventListener((ReactWebView) webView);
((ReactWebView) webView).cleanupCallbacksAndDestroy();
}
private WebView.PictureListener getPictureListener() {
if (mPictureListener == null) {
mPictureListener = new WebView.PictureListener() {
#Override
public void onNewPicture(WebView webView, Picture picture) {
dispatchEvent(
webView,
new ContentSizeChangeEvent(
webView.getId(),
webView.getWidth(),
webView.getContentHeight()));
}
};
}
return mPictureListener;
}
private static void dispatchEvent(WebView webView, Event event) {
ReactContext reactContext = (ReactContext) webView.getContext();
EventDispatcher eventDispatcher =
reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher();
eventDispatcher.dispatchEvent(event);
}
}
I could not find this solution anywhere so I thought I would share, I hope it helps...
To enable the browse button to work and subsequently allow file access you can replace the above and this react-native files with these modified versions:
node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ThemedReactContext.java
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.facebook.react.uimanager;
import javax.annotation.Nullable;
import android.app.Activity;
import android.content.Context;
import com.facebook.react.bridge.ActivityEventListener;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.LifecycleEventListener;
//
/**
* Wraps {#link ReactContext} with the base {#link Context} passed into the constructor.
* It provides also a way to start activities using the viewContext to which RN native views belong.
* It delegates lifecycle listener registration to the original instance of {#link ReactContext}
* which is supposed to receive the lifecycle events. At the same time we disallow receiving
* lifecycle events for this wrapper instances.
* TODO: T7538544 Rename ThemedReactContext to be in alignment with name of ReactApplicationContext
*/
public class ThemedReactContext extends ReactContext {
private final ReactApplicationContext mReactApplicationContext;
public ThemedReactContext(ReactApplicationContext reactApplicationContext, Context base) {
super(base);
initializeWithInstance(reactApplicationContext.getCatalystInstance());
mReactApplicationContext = reactApplicationContext;
}
#Override
public void addLifecycleEventListener(LifecycleEventListener listener) {
mReactApplicationContext.addLifecycleEventListener(listener);
}
#Override
public void removeLifecycleEventListener(LifecycleEventListener listener) {
mReactApplicationContext.removeLifecycleEventListener(listener);
}
#Override
public void addActivityEventListener(ActivityEventListener listener) {
mReactApplicationContext.addActivityEventListener(listener);
}
#Override
public void removeActivityEventListener(ActivityEventListener listener) {
mReactApplicationContext.removeActivityEventListener(listener);
}
#Override
public boolean hasCurrentActivity() {
return mReactApplicationContext.hasCurrentActivity();
}
#Override
public
#Nullable
Activity getCurrentActivity() {
return mReactApplicationContext.getCurrentActivity();
}
}
The second file is listed above
Related
I tried to write a "login" App using MVP model. And I use WAMP to build up my server. I'm sure that my php documents have no problem.
Here is the structure of my App:
enter image description here
And here are the files:
User.java
package com.example.android.login.mvp;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.Toast;
import com.example.android.login.R;
import com.example.android.login.mvp.bean.User;
import com.example.android.login.mvp.presenter.UserLoginPresenter;
import com.example.android.login.mvp.view.IUserLoginView;
public class UserLoginActivity extends AppCompatActivity implements IUserLoginView
{
private EditText mEtUsername, mEtPassword;
private Button mBtnLogin, mBtnClear;
private ProgressBar mPbLoading;
private UserLoginPresenter mUserLoginPresenter = new UserLoginPresenter(this);
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_user_login);
initViews();
}
private void initViews()
{
mEtUsername = (EditText) findViewById(R.id.id_et_username);
mEtPassword = (EditText) findViewById(R.id.id_et_password);
mBtnClear = (Button) findViewById(R.id.id_btn_clear);
mBtnLogin = (Button) findViewById(R.id.id_btn_login);
mPbLoading = (ProgressBar) findViewById(R.id.id_pb_loading);
mBtnLogin.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
mUserLoginPresenter.login();
}
});
mBtnClear.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
mUserLoginPresenter.clear();
}
});
}
#Override
public String getUserName()
{
return mEtUsername.getText().toString();
}
#Override
public String getPassword()
{
return mEtPassword.getText().toString();
}
#Override
public void clearUserName()
{
mEtUsername.setText("");
}
#Override
public void clearPassword()
{
mEtPassword.setText("");
}
#Override
public void showLoading()
{
mPbLoading.setVisibility(View.VISIBLE);
}
#Override
public void hideLoading()
{
mPbLoading.setVisibility(View.GONE);
}
#Override
public void toMainActivity(User user)
{
Toast.makeText(this, user.getUsername() +
" login success , to MainActivity", Toast.LENGTH_SHORT).show();
}
#Override
public void showFailedError()
{
Toast.makeText(this,
"login failed", Toast.LENGTH_SHORT).show();
}
}
IUserBiz.java
package com.example.android.login.mvp.biz;
/**
* Created by zhy on 15/6/19.
*/
public interface IUserBiz
{
public void login(String username, String password, OnLoginListener loginListener);
}
OnLoginListener.java
package com.example.android.login.mvp.biz;
import com.example.android.login.mvp.bean.User;
/**
* Created by zhy on 15/6/19.
*/
public interface OnLoginListener
{
void loginSuccess(User user);
void loginFailed();
}
UserBiz.java
package com.example.android.login.mvp.biz;
import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;
import com.example.android.login.R;
import com.example.android.login.mvp.bean.User;
/**
* Created by zhy on 15/6/19.
*/
public class UserBiz extends AppCompatActivity implements IUserBiz
{
private User user;
private OnLoginListener loginListener;
#Override
public void login(final String username, final String password, final OnLoginListener mLoginListener)
{
user = new User();
user.setUsername(username);
user.setPassword(password);
user.setStatus(0);
loginListener = mLoginListener;
LoginAsyncTask task = new LoginAsyncTask();
String test1Url = getString(R.string.server_ip)+"/server/login.php";
task.execute(test1Url);
}
/**
* Update the UI with the given earthquake information.
*/
private void updateUi(User mUser) {
if (mUser.getStatus()==1)
{
loginListener.loginSuccess(user);
} else
{
loginListener.loginFailed();
}
}
private class LoginAsyncTask extends AsyncTask<String, Void, User> {
#Override
protected User doInBackground(String... urls) {
if (urls.length < 1 || urls[0] == null) {
return null;
}
// Perform the HTTP request for earthquake data and process the response.
User mUser = Utils.fetchEarthquakeData(urls[0],user);
return mUser;
}
#Override
protected void onPostExecute(User result) {
if(result==null){
return;
}
updateUi(result);
}
}
}
Utils.java
package com.example.android.login.mvp.biz;
import android.text.TextUtils;
import android.util.Log;
import com.example.android.login.mvp.bean.User;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
/**
* Utility class with methods to help perform the HTTP request and
* parse the response.
*/
public final class Utils {
/** Tag for the log messages */
public static final String LOG_TAG = Utils.class.getSimpleName();
/**
* Query the USGS dataset and return an {#link User} object to represent a single earthquake.
*/
public static User fetchEarthquakeData(String requestUrl, User user) {
// Create URL object
URL url = createUrl(requestUrl);
// Perform HTTP request to the URL and receive a JSON response back
String jsonResponse = null;
try {
jsonResponse = makeHttpRequest(url, user);
} catch (IOException e) {
Log.e(LOG_TAG, "Error closing input stream", e);
}
// Extract relevant fields from the JSON response and create an {#link User} object
User earthquake = extractFeatureFromJson(jsonResponse);
// Return the {#link User}
return earthquake;
}
/**
* Returns new URL object from the given string URL.
*/
private static URL createUrl(String stringUrl) {
URL url = null;
try {
url = new URL(stringUrl);
} catch (MalformedURLException e) {
Log.e(LOG_TAG, "Error with creating URL ", e);
}
return url;
}
/**
* Make an HTTP request to the given URL and return a String as the response.
*/
private static String makeHttpRequest(URL url, User user) throws IOException {
String jsonResponse = "";
// If the URL is null, then return early.
if (url == null) {
return jsonResponse;
}
HttpURLConnection urlConnection = null;
InputStream inputStream = null;
try {
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setReadTimeout(10000 /* milliseconds */);
urlConnection.setConnectTimeout(15000 /* milliseconds */);
urlConnection.setRequestMethod("POST");
urlConnection.setDoOutput(true);
String params="app_user_name="+user.getUsername()+'&'+"app_password="+user.getPassword();
OutputStream out=urlConnection.getOutputStream();
out.write(params.getBytes());//post提交参数
out.flush();
out.close();
// If the request was successful (response code 200),
// then read the input stream and parse the response.
if (urlConnection.getResponseCode() == 200) {
inputStream = urlConnection.getInputStream();
jsonResponse = readFromStream(inputStream);
} else {
Log.e(LOG_TAG, "Error response code: " + urlConnection.getResponseCode());
}
} catch (IOException e) {
Log.e(LOG_TAG, "Problem retrieving the earthquake JSON results.", e);
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
if (inputStream != null) {
inputStream.close();
}
}
return jsonResponse;
}
/**
* Convert the {#link InputStream} into a String which contains the
* whole JSON response from the server.
*/
private static String readFromStream(InputStream inputStream) throws IOException {
StringBuilder output = new StringBuilder();
if (inputStream != null) {
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, Charset.forName("UTF-8"));
BufferedReader reader = new BufferedReader(inputStreamReader);
String line = reader.readLine();
while (line != null) {
output.append(line);
line = reader.readLine();
}
}
return output.toString();
}
/**
* Return an {#link User} object by parsing out information
* about the first earthquake from the input earthquakeJSON string.
*/
private static User extractFeatureFromJson(String earthquakeJSON) {
// If the JSON string is empty or null, then return early.
if (TextUtils.isEmpty(earthquakeJSON)) {
return null;
}
try {
JSONObject baseJsonResponse = new JSONObject(earthquakeJSON);
// If there are results in the features array
if (baseJsonResponse.length() > 0) {
int status = baseJsonResponse.getInt("status");
// Create a new {#link User} object
User user=new User();
user.setStatus(status);
return user;
}
} catch (JSONException e) {
Log.e(LOG_TAG, "Problem parsing the earthquake JSON results", e);
}
return null;
}
}
UserLoginPresenter.java
package com.example.android.login.mvp.presenter;
import com.example.android.login.mvp.bean.User;
import com.example.android.login.mvp.biz.IUserBiz;
import com.example.android.login.mvp.biz.OnLoginListener;
import com.example.android.login.mvp.biz.UserBiz;
import com.example.android.login.mvp.view.IUserLoginView;
/**
* Created by zhy on 15/6/19.
*/
public class UserLoginPresenter {
private IUserBiz userBiz;
private IUserLoginView userLoginView;
public UserLoginPresenter(IUserLoginView userLoginView) {
this.userLoginView = userLoginView;
this.userBiz = new UserBiz();
}
public void login() {
userLoginView.showLoading();
userBiz.login(userLoginView.getUserName(), userLoginView.getPassword(), new OnLoginListener() {
#Override
public void loginSuccess(final User user) {
userLoginView.toMainActivity(user);
userLoginView.hideLoading();
}
#Override
public void loginFailed() {
userLoginView.showFailedError();
userLoginView.hideLoading();
}
});
}
public void clear() {
userLoginView.clearUserName();
userLoginView.clearPassword();
}
}
IUserLoginView.java
package com.example.android.login.mvp.view;
import com.example.android.login.mvp.bean.User;
/**
* Created by zhy on 15/6/19.
*/
public interface IUserLoginView
{
String getUserName();
String getPassword();
void clearUserName();
void clearPassword();
void showLoading();
void hideLoading();
void toMainActivity(User user);
void showFailedError();
}
UserLoginActivity.java
package com.example.android.login.mvp;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.Toast;
import com.example.android.login.R;
import com.example.android.login.mvp.bean.User;
import com.example.android.login.mvp.presenter.UserLoginPresenter;
import com.example.android.login.mvp.view.IUserLoginView;
public class UserLoginActivity extends AppCompatActivity implements IUserLoginView
{
private EditText mEtUsername, mEtPassword;
private Button mBtnLogin, mBtnClear;
private ProgressBar mPbLoading;
private UserLoginPresenter mUserLoginPresenter = new UserLoginPresenter(this);
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_user_login);
initViews();
}
private void initViews()
{
mEtUsername = (EditText) findViewById(R.id.id_et_username);
mEtPassword = (EditText) findViewById(R.id.id_et_password);
mBtnClear = (Button) findViewById(R.id.id_btn_clear);
mBtnLogin = (Button) findViewById(R.id.id_btn_login);
mPbLoading = (ProgressBar) findViewById(R.id.id_pb_loading);
mBtnLogin.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
mUserLoginPresenter.login();
}
});
mBtnClear.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
mUserLoginPresenter.clear();
}
});
}
#Override
public String getUserName()
{
return mEtUsername.getText().toString();
}
#Override
public String getPassword()
{
return mEtPassword.getText().toString();
}
#Override
public void clearUserName()
{
mEtUsername.setText("");
}
#Override
public void clearPassword()
{
mEtPassword.setText("");
}
#Override
public void showLoading()
{
mPbLoading.setVisibility(View.VISIBLE);
}
#Override
public void hideLoading()
{
mPbLoading.setVisibility(View.GONE);
}
#Override
public void toMainActivity(User user)
{
Toast.makeText(this, user.getUsername() +
" login success , to MainActivity", Toast.LENGTH_SHORT).show();
}
#Override
public void showFailedError()
{
Toast.makeText(this,
"login failed", Toast.LENGTH_SHORT).show();
}
}
But I got something wrong like this:
enter image description here
I have no idea about it. How to solve it?
the issue is here
this.userBiz = new UserBiz();
UserBiz is an activity so activity gets their context when they are being started with Intent (by OS) but creating an object of an activity will not provide any contenxt hence the null exception at
#Override
public void login(final String username, final String password, final OnLoginListener mLoginListener)
{
user = new User();
user.setUsername(username);
user.setPassword(password);
user.setStatus(0);
loginListener = mLoginListener;
LoginAsyncTask task = new LoginAsyncTask();
String test1Url = getString(R.string.server_ip)+"/server/login.php";
// no context here due to new UserBiz
// getString required context
task.execute(test1Url);
}
Solution : you can use enums or static final constants to avoid context and also would be wise to substitute UserBiz as separate class instead of an activity
I am trying to make an app which uses last.fm's web API, sends a query for similar artists and returns all the names of the similar artists. It seems as though I manage to connect and get the xml response properly. However, I cannot extract the value of the name-attribute. I am using artistName = xmlData.getAttributeValue(null, "name"); but all it gives me is null.
import android.app.Activity;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;
import java.util.*;
#SuppressWarnings("FieldCanBeLocal")
public class MainActivity extends Activity implements Observer {
private final String INPUTERROR = "Invalid/missing artist name.";
private NetworkCommunication nc;
private ArrayList<String> list;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
nc = new NetworkCommunication();
nc.register(this);
list = new ArrayList<>();
ListView lv = (ListView)findViewById(R.id.ListView_similarArtistsList);
ArrayAdapter adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, list);
lv.setAdapter(adapter);
}
public void searchButton_Clicked(View v){
EditText inputField = (EditText)findViewById(R.id.editText_artistName);
String searchString = inputField.getText().toString();
searchString = cleanSearchString(searchString);
if(validateSearchString(searchString)){
nc.setSearchString(searchString);
nc.execute();
}
else{
Toast.makeText(MainActivity.this, INPUTERROR, Toast.LENGTH_SHORT).show();
}
}
private String cleanSearchString(String oldSearchString){
String newString = oldSearchString.trim();
newString = newString.replace(" ", "");
return newString;
}
private boolean validateSearchString(String searchString){
boolean rValue = true;
if(TextUtils.isEmpty(searchString)){
rValue = false;
}
return rValue;
}
#Override
public void update(String artistName) {
list.add(artistName);
}
}
Here is my Network Communications class:
import android.os.AsyncTask;
import android.util.Log;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
#SuppressWarnings("FieldCanBeLocal")
class NetworkCommunication extends AsyncTask<Object, String, Integer> implements Subject {
private final String MYAPIKEY = "--------------------------";
private final String ROOT = "http://ws.audioscrobbler.com/2.0/";
private final String METHOD = "?method=artist.getsimilar";
private ArrayList<Observer> observers;
private int amountOfArtists = 0;
private String foundArtistName;
private String searchString;
NetworkCommunication(){
observers = new ArrayList<>();
}
void setSearchString(String newSearchString){
searchString = newSearchString;
}
private XmlPullParser sendRequest(){
try{
URL url = new URL(ROOT + METHOD + "&artist=" + searchString + "&api_key=" + MYAPIKEY);
XmlPullParser receivedData = XmlPullParserFactory.newInstance().newPullParser();
receivedData.setInput(url.openStream(), null);
return receivedData;
}
catch (IOException | XmlPullParserException e){
Log.e("ERROR", e.getMessage(), e);
}
return null;
}
private int tryProcessData(XmlPullParser xmlData){
int artistsFound = 0;
String artistName;
int eventType;
try{
while ((eventType = xmlData.next()) != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG) {
if(xmlData.getName().equals("name")){
artistName = xmlData.getAttributeValue(null, "name");
publishProgress(artistName);
artistsFound++;
}
}
}
}
catch (IOException | XmlPullParserException e){
Log.e("ERROR", e.getMessage(), e);
}
if (artistsFound == 0) {
publishProgress();
}
return artistsFound;
}
#Override
protected Integer doInBackground(Object... params) {
XmlPullParser data = sendRequest();
if(data != null){
return tryProcessData(data);
}
else{
return null;
}
}
#Override
protected void onProgressUpdate(String... values){
/*
if (values.length == 0) {
//No data found...
}
*/
if (values.length == 1) {
setFoundArtistName(values[0]);
notifyObserver();
}
super.onProgressUpdate(values);
}
private void setFoundArtistName(String newArtistName){
foundArtistName = newArtistName;
}
#Override
public void register(Observer newObserver) {
observers.add(newObserver);
}
#Override
public void unregister(Observer deleteObserver) {
observers.remove(deleteObserver);
}
#Override
public void notifyObserver() {
for (Observer o : observers) {
Log.i("my tag.... ", "name = " + foundArtistName);
o.update(foundArtistName);
}
}
}
Here's a screenshot of the xml response in Google Chrome:
The only thing I am interested in extracting at this moment is the the value of the Name-Element.
I am logging the value of foundArtistName (in the method notifyObserver) it gives me A LOT of "my tag.... name = null my tag.... name = null my tag.... name = null etc.."
EDIT: I tried using getText() instead of getAttributeValue(), but it also gives me null.
I found the solution. I was using: artistName = xmlData.getAttributeValue(null, "name");, when I really should've used: artistName = xmlData.nextText();
I facing to problem when use Google Cloud Printing when Print Button was clicked.
This's error show in debug mode
Uncaught TypeError: Cannot read property 'o' of null", source: https://www.google.com/cloudprint/client/2026774633-dialog_mobile__vi.js (295)
Does anyone has a solution for this problem?
Edit 1
This's my code for class PrintDialogActivity.java
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.ContentResolver;
import android.content.Intent;
import android.os.Bundle;
import android.util.Base64;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
#SuppressLint("SetJavaScriptEnabled")
public class PrintDialogActivity extends Activity {
private static final String PRINT_DIALOG_URL = "https://www.google.com/cloudprint/dialog.html";
private static final String JS_INTERFACE = "AndroidPrintDialog";
private static final String CONTENT_TRANSFER_ENCODING = "base64";
private static final String ZXING_URL = "http://zxing.appspot.com";
private static final int ZXING_SCAN_REQUEST = 65743;
/**
* Post message that is sent by Print Dialog web page when the printing dialog
* needs to be closed.
*/
private static final String CLOSE_POST_MESSAGE_NAME = "cp-dialog-on-close";
/**
* Web view element to show the printing dialog in.
*/
private WebView dialogWebView;
/**
* Intent that started the action.
*/
Intent cloudPrintIntent;
#SuppressLint("JavascriptInterface") #Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.print_dialog);
dialogWebView = (WebView) findViewById(R.id.webview);
cloudPrintIntent = this.getIntent();
WebSettings settings = dialogWebView.getSettings();
settings.setJavaScriptEnabled(true);
settings.setDomStorageEnabled(true);
dialogWebView.setWebViewClient(new PrintDialogWebClient());
dialogWebView.addJavascriptInterface(
new PrintDialogJavaScriptInterface(), JS_INTERFACE);
dialogWebView.loadUrl(PRINT_DIALOG_URL);
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
if (requestCode == ZXING_SCAN_REQUEST && resultCode == RESULT_OK) {
dialogWebView.loadUrl(intent.getStringExtra("SCAN_RESULT"));
}
}
final class PrintDialogJavaScriptInterface {
public String getType() {
return cloudPrintIntent.getType();
}
public String getTitle() {
return cloudPrintIntent.getExtras().getString("title");
}
public String getContent() {
try {
ContentResolver contentResolver = getContentResolver();
InputStream is = contentResolver.openInputStream(cloudPrintIntent.getData());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[4096];
int n = is.read(buffer);
while (n >= 0) {
baos.write(buffer, 0, n);
n = is.read(buffer);
}
is.close();
baos.flush();
return Base64.encodeToString(baos.toByteArray(), Base64.DEFAULT);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
public String getEncoding() {
return CONTENT_TRANSFER_ENCODING;
}
public void onPostMessage(String message) {
if (message.startsWith(CLOSE_POST_MESSAGE_NAME)) {
finish();
}
}
}
private final class PrintDialogWebClient extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.startsWith(ZXING_URL)) {
Intent intentScan = new Intent("com.google.zxing.client.android.SCAN");
intentScan.putExtra("SCAN_MODE", "QR_CODE_MODE");
try {
startActivityForResult(intentScan, ZXING_SCAN_REQUEST);
} catch (ActivityNotFoundException error) {
view.loadUrl(url);
}
} else {
view.loadUrl(url);
}
return false;
}
#Override
public void onPageFinished(WebView view, String url) {
if (PRINT_DIALOG_URL.equals(url)) {
// Submit print document.
view.loadUrl("javascript:printDialog.setPrintDocument(printDialog.createPrintDocument("
+ "window." + JS_INTERFACE + ".getType(),window." + JS_INTERFACE + ".getTitle(),"
+ "window." + JS_INTERFACE + ".getContent(),window." + JS_INTERFACE + ".getEncoding()))");
// Add post messages listener.
view.loadUrl("javascript:window.addEventListener('message',"
+ "function(evt){window." + JS_INTERFACE + ".onPostMessage(evt.data)}, false)");
}
}
}
}
Now, I found solution for my issue:
Maybe the problem come from the Android SDK version. You can see this link: https://developers.google.com/cloud-print/docs/android. (It's mean GCP only support for Android version 4.3 below).
Although, I tested on the emulator 4.2 but it still unlucky.
So that, I changed the direction to the Android Print Framework with a little difficult.
Firstly, I must install Cloud Print Services on emulator.
Secondly, I login Google Account Services for printing function enable and connect to my printer.
Thirdly, I use this code for exec print action
private void printDoc() {
PrintManager printManager = (PrintManager) this.getSystemService(Context.PRINT_SERVICE);
String jobName = mFileName;
Intent myIntent = getIntent();
Uri docUri = myIntent != null ? myIntent.getData() : null;
PrintDocumentAdapter pda = new PrintDocumentAdapter(){
#Override
public void onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes, CancellationSignal cancellationSignal, LayoutResultCallback callback, Bundle extras){
if (cancellationSignal.isCanceled()) {
callback.onLayoutCancelled();
//return;
}
// Compute the expected number of printed pages
int pages = computePageCount(newAttributes);
PrintDocumentInfo pdi = new PrintDocumentInfo.Builder(mFileName).setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT).setPageCount(pages).build();
callback.onLayoutFinished(pdi, true);
}
#Override
public void onWrite(PageRange[] pages, ParcelFileDescriptor destination, CancellationSignal cancellationSignal, WriteResultCallback callback){
InputStream input = null;
OutputStream output = null;
try {
input = new FileInputStream(docUri.toString());
output = new FileOutputStream(destination.getFileDescriptor());
byte[] buf = new byte[1024];
int bytesRead;
while ((bytesRead = input.read(buf)) > 0) {
output.write(buf, 0, bytesRead);
}
callback.onWriteFinished(pages);
} catch (FileNotFoundException ee){
//Catch exception
} catch (Exception e) {
//Catch exception
} finally {
try {
input.close();
output.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
};
printManager.print(jobName, pda, null);
}
I am using telegram API to develop my application. Here is the whole code.
app_info.java:
package com.example.mytelegram;
import org.telegram.api.engine.AppInfo;
public class app_info extends AppInfo {
public app_info(int apiId, String deviceModel, String systemVersion,
String appVersion, String langCode) {
super(apiId, deviceModel, systemVersion, appVersion, langCode);
// TODO Auto-generated constructor stub
}
MemoryApiState.java:
package com.example.mytelegram;
import org.telegram.api.TLConfig;
import org.telegram.api.TLDcOption;
import org.telegram.api.engine.storage.AbsApiState;
import org.telegram.mtproto.state.AbsMTProtoState;
import org.telegram.mtproto.state.ConnectionInfo;
import org.telegram.mtproto.state.KnownSalt;
import java.util.ArrayList;
import java.util.HashMap;
public class MemoryApiState implements AbsApiState {
private HashMap<Integer, ConnectionInfo[]> connections = new HashMap<Integer, ConnectionInfo[]>();
private HashMap<Integer, byte[]> keys = new HashMap<Integer, byte[]>();
private HashMap<Integer, Boolean> isAuth = new HashMap<Integer, Boolean>();
private int primaryDc = 2; // I have tested application with primaryDc=0 to 10
public MemoryApiState(boolean isTest) {
connections.put(1, new ConnectionInfo[]{
new ConnectionInfo(1, 0, isTest ? "149.154.167.40" : "149.154.167.50", 443)
});
}
#Override
public synchronized int getPrimaryDc() {
return primaryDc;
}
#Override
public synchronized void setPrimaryDc(int dc) {
primaryDc = dc;
}
#Override
public synchronized boolean isAuthenticated(int dcId) {
if (isAuth.containsKey(dcId)) {
return isAuth.get(dcId);
}
return false;
}
#Override
public synchronized void setAuthenticated(int dcId, boolean auth) {
isAuth.put(dcId, auth);
}
#Override
public synchronized void updateSettings(TLConfig config) {
connections.clear();
HashMap<Integer, ArrayList<ConnectionInfo>> tConnections = new HashMap<Integer, ArrayList<ConnectionInfo>>();
int id = 0;
for (TLDcOption option : config.getDcOptions()) {
if (!tConnections.containsKey(option.getId())) {
tConnections.put(option.getId(), new ArrayList<ConnectionInfo>());
}
tConnections.get(option.getId()).add(new ConnectionInfo(id++, 0, option.getIpAddress(), option.getPort()));
}
for (Integer dc : tConnections.keySet()) {
connections.put(dc, tConnections.get(dc).toArray(new ConnectionInfo[0]));
}
}
#Override
public synchronized byte[] getAuthKey(int dcId) {
return keys.get(dcId);
}
#Override
public synchronized void putAuthKey(int dcId, byte[] key) {
keys.put(dcId, key);
}
#Override
public synchronized ConnectionInfo[] getAvailableConnections(int dcId) {
if (!connections.containsKey(dcId)) {
return new ConnectionInfo[0];
}
return connections.get(dcId);
}
#Override
public synchronized AbsMTProtoState getMtProtoState(final int dcId) {
return new AbsMTProtoState() {
private KnownSalt[] knownSalts = new KnownSalt[0];
#Override
public byte[] getAuthKey() {
return MemoryApiState.this.getAuthKey(dcId);
}
#Override
public ConnectionInfo[] getAvailableConnections() {
return MemoryApiState.this.getAvailableConnections(dcId);
}
#Override
public KnownSalt[] readKnownSalts() {
return knownSalts;
}
#Override
protected void writeKnownSalts(KnownSalt[] salts) {
knownSalts = salts;
}
};
}
#Override
public synchronized void resetAuth() {
isAuth.clear();
}
#Override
public synchronized void reset() {
isAuth.clear();
keys.clear();
}
}
MainActivity.java:
package com.example.mytelegram;
import java.io.IOException;
import org.telegram.api.TLAbsUpdates;
import org.telegram.api.TLConfig;
import org.telegram.api.TLNearestDc;
import org.telegram.api.auth.TLAuthorization;
import org.telegram.api.auth.TLSentCode;
import org.telegram.api.engine.ApiCallback;
import org.telegram.api.engine.AppInfo;
import org.telegram.api.engine.LoggerInterface;
import org.telegram.api.engine.RpcException;
import org.telegram.api.engine.TelegramApi;
import org.telegram.api.requests.TLRequestAuthSendCall;
import org.telegram.api.requests.TLRequestAuthSendCode;
import org.telegram.api.requests.TLRequestAuthSignIn;
import org.telegram.api.requests.TLRequestHelpGetConfig;
import org.telegram.api.requests.TLRequestHelpGetNearestDc;
import org.telegram.api.requests.TLRequestUpdatesGetState;
import org.telegram.api.updates.TLState;
import android.support.v7.app.ActionBarActivity;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.util.Log;
public class MainActivity extends ActionBarActivity {
static Context context;
private static AppInfo ai;
private static void createApi() throws IOException {
String res = "No test";
boolean useTest = res.equals("test");
apiState = new MemoryApiState(useTest);
ai=new AppInfo(App_ID, "Sony", "???", "???", "en");
api = new TelegramApi(apiState, ai, new ApiCallback() {
#Override
public void onAuthCancelled(TelegramApi api) {
}
#Override
public void onUpdatesInvalidated(TelegramApi api) {
}
#Override
public void onUpdate(TLAbsUpdates updates) {
Log.d("tlg", "onUpdate(TLAbsUpdates updates)");
}
});
}
private static void login() throws IOException {
//TLNearestDc dcInfo = api.doRpcCallNonAuth(new TLRequestHelpGetNearestDc());
// api.switchToDc(dcInfo.getNearestDc());
//************ Exception raises here ************
TLConfig config = api.doRpcCallNonAuth(new TLRequestHelpGetConfig()); // I have An IOException Here (e=timeout Exception)
apiState.updateSettings(config);
Log.d("tlg","completed.");
String phone = "+9891********"; // I have tested app with the phone without plus too
Log.d("tlg","Sending sms to phone " + phone + "...");
TLSentCode sentCode = null;
try {
api.doRpcCallNonAuth(new TLRequestAuthSendCode(phone, 1, app_id, "__hash__", "en"));
} catch (RpcException e) {
if (e.getErrorCode() == 303) {
int destDC;
if (e.getErrorTag().startsWith("NETWORK_MIGRATE_")) {
destDC = Integer.parseInt(e.getErrorTag().substring("NETWORK_MIGRATE_".length()));
} else if (e.getErrorTag().startsWith("PHONE_MIGRATE_")) {
destDC = Integer.parseInt(e.getErrorTag().substring("PHONE_MIGRATE_".length()));
} else if (e.getErrorTag().startsWith("USER_MIGRATE_")) {
destDC = Integer.parseInt(e.getErrorTag().substring("USER_MIGRATE_".length()));
} else {
throw e;
}
api.switchToDc(destDC);
sentCode = api.doRpcCallNonAuth(new TLRequestAuthSendCode(phone, 0, app_id, "__hash__", "en"));
} else {
throw e;
}
}
Log.d("tlg","Activation code:");
String code = "123";
TLAuthorization auth = api.doRpcCallNonAuth(new TLRequestAuthSignIn(phone, sentCode.getPhoneCodeHash(), code));
apiState.setAuthenticated(apiState.getPrimaryDc(), true);
Log.d("tlg","Activation complete.");
TLState state = api.doRpcCall(new TLRequestUpdatesGetState());
Log.d("tlg","loaded.");
}
static MemoryApiState apiState;
static TelegramApi api;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
context=getApplicationContext();
try {
createApi();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
login();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//workLoop();
}
}
I have added telegram-api library (telegram-api-1.1.127-shadow.jar) in my eclipse 'libs' folder. The problem is Timeout Exception in MainActivity.java when I want to call RPC. Where is the problem? What should I do? My OS is windows 8.1 . I am searching for 2 whole days. I have read github codes too. They are very complicated for me. I can not figure out what should I do?
I have added INTERNET and ACCESS_NETWORK_STATE permisions to AndroidManifest.xml. I allways check the network connection when my application runs.
I found the problem in your code:
In MemoryApiState you should put, in the 'connections' map, 'primaryDc' as key and not '1'.
I have created a music player application for Android with swipe(with tabs). Whenever I'm switching to another fragment, while the music is playing and trying to play a web stream from a list, I got this error:
FATAL EXCEPTION: main
Process: com.example.npopa.musicplayern, PID: 15665
java.lang.IllegalStateException
at android.media.MediaPlayer._setAudioStreamType(Native Method)
at android.media.MediaPlayer.setAudioStreamType(MediaPlayer.java:1868)
at com.example.npopa.musicplayern.MusicService.initStreamPlayer(MusicService.java:72)
at com.example.npopa.musicplayern.RadioStreaming$2.onItemClick(RadioStreaming.java:88)
Here's my code:
package com.example.npopa.musicplayern;
import android.content.Context;
import android.support.v4.app.Fragment;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;
import com.example.npopa.musicplayern.adapter.RadioAdapter;
import com.example.npopa.musicplayern.helpers.Streams;
import java.util.ArrayList;
public class RadioStreaming extends Fragment {
private EditText et_streamNameRS, et_streamUrlRS;
private static ArrayList<Streams> m_streamsList;
private DBHelper m_myDBHelp;
private MusicService m_musicSrv;
public static Context m_context;
private ListView m_listView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.radio_streaming, container, false);
m_context = getContext();
et_streamNameRS = (EditText) rootView.findViewById(R.id.et_streamNameRS);
et_streamUrlRS = (EditText) rootView.findViewById(R.id.et_streamUrlRS);
Button bt_addStreamRS = (Button) rootView.findViewById(R.id.bt_addStreamRS);
m_listView = (ListView) rootView.findViewById(R.id.stream_listRS);
m_myDBHelp = new DBHelper();
m_streamsList = m_myDBHelp.selectStreamsList();
if (m_streamsList.isEmpty()) {
Toast.makeText(RadioStreaming.m_context, "There's no streams in DB. ADD streams!", Toast.LENGTH_LONG).show();
}
Log.d("RadioStreaming", " -------- TAG select m_streamsList ------" + m_streamsList.toString());
RadioAdapter adapt = new RadioAdapter(m_context, m_streamsList);
m_listView.setAdapter(adapt);
bt_addStreamRS.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String name = et_streamNameRS.getText().toString();
String url = et_streamUrlRS.getText().toString();
m_myDBHelp.insertStreamIntoDB(name, url);
m_listView.invalidate();
m_streamsList = null;
m_streamsList = m_myDBHelp.selectStreamsList();
RadioAdapter adapt2 = new RadioAdapter(m_context, m_streamsList);
m_listView.setAdapter(adapt2);
et_streamNameRS.setText("");
et_streamUrlRS.setText("");
}
});
m_listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
if (!Functions.checkNetworkState(m_context)) {
Toast.makeText(getContext(), "Enable WIFI or Mobile Data!", Toast.LENGTH_LONG).show();
} else {
m_musicSrv = MusicService.getInstance();
if (MusicService.m_player != null) {
if (m_musicSrv.isPlaying()) {
if (m_musicSrv.is_playing_music) {
new SongDetails().stopHandler();
MusicService.m_player.stop();
MusicService.m_player.reset(); //It requires again setDataSource for player object.
// MusicService.m_player.stop();
MusicService.m_player.release();
// MusicService.m_player = null; // Initilize to null so it can be used later
m_musicSrv.initStreamPlayer(m_context);
m_musicSrv.setStreamList(m_streamsList);
m_musicSrv.setStream(Integer.parseInt(view.getTag().toString()));
m_musicSrv.playStream();
}
else {
MusicService.m_player.reset();
m_musicSrv.initStreamPlayer(m_context);
m_musicSrv.setStreamList(m_streamsList);
m_musicSrv.setStream(Integer.parseInt(view.getTag().toString()));
m_musicSrv.playStream();
}
}
else {
m_musicSrv.initStreamPlayer(m_context);
m_musicSrv.setStreamList(m_streamsList);
m_musicSrv.setStream(Integer.parseInt(view.getTag().toString()));
m_musicSrv.playStream();
}
}
else {
m_musicSrv.initStreamPlayer(m_context);
m_musicSrv.setStreamList(m_streamsList);
m_musicSrv.setStream(Integer.parseInt(view.getTag().toString()));
m_musicSrv.playStream();
}
}
}
});
return rootView;
}
}
package com.example.npopa.musicplayern;
import android.content.ContentUris;
import android.content.Context;
import android.graphics.Bitmap;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.PowerManager;
import android.util.Log;
import android.widget.Toast;
import com.example.npopa.musicplayern.helpers.Streams;
import com.example.npopa.musicplayern.helpers.Song;
import java.util.ArrayList;
import java.util.Random;
public class MusicService implements MediaPlayer.OnCompletionListener, MediaPlayer.OnPreparedListener, MediaPlayer.OnErrorListener, MediaPlayer.OnBufferingUpdateListener {
public static MediaPlayer m_player;
public static String m_songTitle = "";
public static String m_artistName = "";
private static String m_streamNAME, m_streamURL;
public static Bitmap m_albumImageMS;
public static ArrayList<Song> m_songs;
public static ArrayList<Streams> m_streams;
public static int m_songPosn;
public static int m_streamPosition;
private static int m_streamID;
public static boolean m_shuffle = false;
public static boolean m_repeat = false;
public boolean is_playing_music = false;
public boolean is_playing_streams = false;
private Random m_rand;
private DBHelper m_db;
private static MusicService myService = new MusicService();
// a private Constructor prevents any other class from instantiating
private MusicService() {
}
public static MusicService getInstance() {
if (myService == null) {
myService = new MusicService();
}
return myService;
}
public void initSongPlayer(Context context) {
m_songs = new ArrayList<Song>();
m_songPosn = 0;
if (m_player == null) {
m_player = new MediaPlayer();
}
m_player.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK);
m_player.setAudioStreamType(AudioManager.STREAM_MUSIC);
m_player.setOnPreparedListener(this);
m_player.setOnCompletionListener(this);
m_player.setOnErrorListener(this);
}
public void initStreamPlayer(Context context) {
m_streams = new ArrayList<Streams>();
m_streamPosition = 0;
if (m_player == null) {
m_player = new MediaPlayer();
}
m_player.setOnPreparedListener(this);
m_player.setOnBufferingUpdateListener(this);
m_player.setOnErrorListener(this);
m_player.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK);
m_player.setAudioStreamType(AudioManager.STREAM_MUSIC);
}
public void setSong(int songIndex) {
m_songPosn = songIndex;
}
public void setSongList(ArrayList<Song> theSongs) {
Log.d("MusicService", "setSongList() - playlist nou marime: " + theSongs.size());
if (theSongs.size() > 0){
m_songs = theSongs;
m_db = new DBHelper();
m_db.deleteAllSongsPlaylist();
m_db.setSongsLastPlaylist(m_songs);
}
}
public void setStream(int streamIndex) {
m_streamPosition = streamIndex;
}
public void setStreamList(ArrayList<Streams> theStreams) {
m_streams = theStreams;
}
public void playSong(Context context) {
if(m_songs.size() == 0) {
Log.d("MusicService", "playSong() - playlist 0 elemente");
}
else {
is_playing_music = true;
is_playing_streams = false;
m_player.reset();
Song playSong = m_songs.get(m_songPosn);
m_songTitle = playSong.getTitle();
m_artistName = playSong.getArtist();
m_albumImageMS = playSong.getAlbumImage();
long currSong = playSong.getID();
Log.d("MusicService", "---------------------CURRENT SONG ID " + String.valueOf(currSong));
Uri trackUri = ContentUris.withAppendedId(android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, currSong);
try {
m_player.setDataSource(context, trackUri);
} catch (Exception e) {
Log.e("MusicService", "Error setting data source", e);
}
m_player.prepareAsync();
m_db = new DBHelper();
m_db.updatePlayerInfo(String.valueOf(currSong), 1);
}
}
public void playStream() {
is_playing_music = false;
is_playing_streams = true;
m_player.reset();
Streams currStream = m_streams.get(m_streamPosition);
m_streamID = currStream.getId();
m_streamNAME = currStream.getName();
m_streamURL = currStream.getUrl();
try { // http://usa8-vn.mixstream.net:8138
m_player.setDataSource(m_streamURL);
Log.d("MusicService", "------------------ playStream() ---------------------" + m_streamURL);
} catch (Exception e) {
// e.printStackTrace();
Log.d("MusicService", "---------- Error setting stream data source", e);
}
m_player.prepareAsync();
DBHelper dbhelp = new DBHelper();
dbhelp.updatePlayerInfo(String.valueOf(m_streamID), 0);
}
public Bitmap getAlbumImageMS() {
return m_albumImageMS;
}
public String getMusicTitle() {
return m_songTitle;
}
public String getMusicArtist() {
return m_artistName;
}
public int getCurrentPosition() {
return m_player.getCurrentPosition();
}
public int getDuration() {
return m_player.getDuration();
}
public boolean isPlaying() {
if (m_player == null){
return false;
}
else {
return m_player.isPlaying();
}
}
public void seekTO(int posn) {
m_player.seekTo(posn);
}
#Override
public boolean onError(MediaPlayer mp, int what, int extra) {
if (is_playing_streams) {
Toast.makeText(MainActivity.m_context, "Playback Error - INVALID URL", Toast.LENGTH_LONG).show();
}
Log.v("MusicService", "Playback Error");
mp.reset();
return false;
}
#Override
public void onCompletion(MediaPlayer mp) {
if (m_player.getCurrentPosition() > 0) {
mp.reset();
playNextSong();
}
}
#Override
public void onPrepared(MediaPlayer mp) {
mp.start();
if (is_playing_music) {
Log.d("MusicService", "---------- Playing MUSIC--- :" + getMusicTitle() + " - - by - - " + getMusicArtist());
}
else if (is_playing_streams) {
Toast.makeText(MainActivity.m_context, "Playing STREAM:" + m_streamNAME + " from " + m_streamURL, Toast.LENGTH_LONG).show();
Log.d("MusicService", "---------- Playing STREAM:" + m_streamNAME + " from " + m_streamURL);
}
}
#Override
public void onBufferingUpdate(MediaPlayer mediaPlayer, int percent) {
Log.d("MusicService", "---------- Buffering " + percent);
Toast.makeText(MainActivity.m_context, "Buffering: " + percent, Toast.LENGTH_LONG).show();
}
public void playNextSong() {
m_rand = new Random();
if (m_repeat == true) {
int pos1 = m_songPosn;
m_songPosn = pos1;
} else if (m_shuffle == true) {
int newSong = m_songPosn;
while (newSong == m_songPosn) {
newSong = m_rand.nextInt(m_songs.size());
}
m_songPosn = newSong;
} else if (m_repeat == true && m_shuffle == true) {
int pos2 = m_songPosn;
m_songPosn = pos2;
} else {
////////////////////////////////////////////
m_songPosn++;
if (m_songPosn >= m_songs.size()) {
m_songPosn = 0;
}
}
playSong(MainActivity.m_context);
}
public void playPrevSong() {
if (m_shuffle == true) {
int newSong = m_songPosn;
while (newSong == m_songPosn) {
newSong = m_rand.nextInt(m_songs.size());
}
m_songPosn = newSong;
} else {
m_songPosn--;
if (m_songPosn < 0) {
m_songPosn = m_songs.size() - 1;
}
}
playSong(MainActivity.m_context);
}
public void playNextStream() {
m_streamPosition++;
if (m_streamPosition >= m_streams.size()) {
m_streamPosition = 1;
}
playStream();
}
public void playPrevStream() {
m_streamPosition--;
if (m_streamPosition < 1) {
m_streamPosition = m_streams.size() - 1;
}
playStream();
}
}
Every kind of help is appreciated. Thanks in advance.