Attempt to get length of null array on NfcManager.start() - java

I'm using react native v0.49 and installed react-native-nfc-manager .
when I try to use nfc I get the error
Attempt to get length of null array
so I checked on the nfc folder and I find the issue into the plugin installation
#ReactMethod
public void start(Callback callback) {
NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(context);
if (nfcAdapter != null) {
Log.d(LOG_TAG, "start");
callback.invoke(null);
} else {
Log.d(LOG_TAG, "not support in this device");
callback.invoke("no nfc support");
}
}
as you can see when the nfcAdapter is not null it
callback.invoke(null)
so here is the problem, so I tried to change it to
callback.invoke(LOG_TAG)
and it's not show the error but I don't get any nfc tag, is show me undefined but not error.
what can i do?
here is all the NfcManager.java file
package community.revteltech.nfc;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.util.Base64;
import android.util.Log;
import android.provider.Settings;
import com.facebook.react.bridge.*;
import com.facebook.react.modules.core.RCTNativeAppEventEmitter;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentFilter.MalformedMimeTypeException;
import android.net.Uri;
import android.nfc.FormatException;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.nfc.NfcEvent;
import android.nfc.Tag;
import android.nfc.TagLostException;
import android.nfc.tech.Ndef;
import android.nfc.tech.NdefFormatable;
import android.os.Parcelable;
import org.json.JSONObject;
import org.json.JSONException;
import java.util.*;
import static android.app.Activity.RESULT_OK;
import static android.os.Build.VERSION_CODES.LOLLIPOP;
import static com.facebook.react.bridge.UiThreadUtil.runOnUiThread;
class NfcManager extends ReactContextBaseJavaModule implements ActivityEventListener, LifecycleEventListener {
private static final String LOG_TAG = "NfcManager";
private final List<IntentFilter> intentFilters = new ArrayList<IntentFilter>();
private final ArrayList<String[]> techLists = new ArrayList<String[]>();
private Context context;
private ReactApplicationContext reactContext;
private Boolean isForegroundEnabled = false;
private Boolean isResumed = false;
public NfcManager(ReactApplicationContext reactContext) {
super(reactContext);
context = reactContext;
this.reactContext = reactContext;
reactContext.addActivityEventListener(this);
reactContext.addLifecycleEventListener(this);
Log.d(LOG_TAG, "NfcManager created");
}
#Override
public String getName() {
return "NfcManager";
}
#ReactMethod
public void start(Callback callback) {
NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(context);
if (nfcAdapter != null) {
Log.d(LOG_TAG, "start");
callback.invoke(null);
} else {
Log.d(LOG_TAG, "not support in this device");
callback.invoke("no nfc support");
}
}
#ReactMethod
public void isEnabled(Callback callback) {
Log.d(LOG_TAG, "isEnabled");
NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(context);
if (nfcAdapter != null) {
callback.invoke(null, nfcAdapter.isEnabled());
} else {
callback.invoke(null, false);
}
}
#ReactMethod
public void goToNfcSetting(Callback callback) {
Log.d(LOG_TAG, "goToNfcSetting");
Activity currentActivity = getCurrentActivity();
currentActivity.startActivity(new Intent(Settings.ACTION_NFC_SETTINGS));
callback.invoke();
}
#ReactMethod
public void getLaunchTagEvent(Callback callback) {
Activity currentActivity = getCurrentActivity();
Intent launchIntent = currentActivity.getIntent();
WritableMap nfcTag = parseNfcIntent(launchIntent);
callback.invoke(null, nfcTag);
}
#ReactMethod
private void registerTagEvent(Callback callback) {
Log.d(LOG_TAG, "registerTag");
isForegroundEnabled = true;
// capture all mime-based dispatch NDEF
IntentFilter ndef = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
try {
ndef.addDataType("*/*");
} catch (MalformedMimeTypeException e) {
throw new RuntimeException("fail", e);
}
intentFilters.add(ndef);
// capture all rest NDEF, such as uri-based
intentFilters.add(new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED));
techLists.add(new String[]{Ndef.class.getName()});
// for those without NDEF, get them as tags
intentFilters.add(new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED));
if (isResumed) {
enableDisableForegroundDispatch(true);
}
callback.invoke();
}
#ReactMethod
private void unregisterTagEvent(Callback callback) {
Log.d(LOG_TAG, "registerTag");
isForegroundEnabled = false;
intentFilters.clear();
if (isResumed) {
enableDisableForegroundDispatch(false);
}
callback.invoke();
}
#Override
public void onHostResume() {
Log.d(LOG_TAG, "onResume");
isResumed = true;
if (isForegroundEnabled) {
enableDisableForegroundDispatch(true);
}
}
#Override
public void onHostPause() {
Log.d(LOG_TAG, "onPause");
isResumed = false;
enableDisableForegroundDispatch(false);
}
#Override
public void onHostDestroy() {
Log.d(LOG_TAG, "onDestroy");
}
private void enableDisableForegroundDispatch(boolean enable) {
Log.i(LOG_TAG, "enableForegroundDispatch, enable = " + enable);
NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(context);
Activity currentActivity = getCurrentActivity();
if (nfcAdapter != null && !currentActivity.isFinishing()) {
try {
if (enable) {
nfcAdapter.enableForegroundDispatch(currentActivity, getPendingIntent(), getIntentFilters(), getTechLists());
} else {
nfcAdapter.disableForegroundDispatch(currentActivity);
}
} catch (IllegalStateException e) {
Log.w(LOG_TAG, "Illegal State Exception starting NFC. Assuming application is terminating.");
}
}
}
private PendingIntent getPendingIntent() {
Activity activity = getCurrentActivity();
Intent intent = new Intent(activity, activity.getClass());
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
return PendingIntent.getActivity(activity, 0, intent, 0);
}
private IntentFilter[] getIntentFilters() {
return intentFilters.toArray(new IntentFilter[intentFilters.size()]);
}
private String[][] getTechLists() {
return techLists.toArray(new String[0][0]);
}
private void sendEvent(String eventName,
#Nullable WritableMap params) {
getReactApplicationContext()
.getJSModule(RCTNativeAppEventEmitter.class)
.emit(eventName, params);
}
private void sendEventWithJson(String eventName,
JSONObject json) {
try {
WritableMap map = JsonConvert.jsonToReact(json);
sendEvent(eventName, map);
} catch (JSONException ex) {
Log.d(LOG_TAG, "fireNdefEvent fail: " + ex);
}
}
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Log.d(LOG_TAG, "onReceive " + intent);
}
};
#Override
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
Log.d(LOG_TAG, "onActivityResult");
}
#Override
public void onNewIntent(Intent intent) {
Log.d(LOG_TAG, "onNewIntent " + intent);
WritableMap nfcTag = parseNfcIntent(intent);
if (nfcTag != null) {
sendEvent("NfcManagerDiscoverTag", nfcTag);
}
}
private WritableMap parseNfcIntent(Intent intent) {
Log.d(LOG_TAG, "parseIntent " + intent);
String action = intent.getAction();
Log.d(LOG_TAG, "action " + action);
if (action == null) {
return null;
}
WritableMap parsed = null;
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
// Parcelable[] messages = intent.getParcelableArrayExtra((NfcAdapter.EXTRA_NDEF_MESSAGES));
if (action.equals(NfcAdapter.ACTION_NDEF_DISCOVERED)) {
Ndef ndef = Ndef.get(tag);
Parcelable[] messages = intent.getParcelableArrayExtra((NfcAdapter.EXTRA_NDEF_MESSAGES));
parsed = ndef2React(ndef, messages);
} else if (action.equals(NfcAdapter.ACTION_TECH_DISCOVERED)) {
for (String tagTech : tag.getTechList()) {
Log.d(LOG_TAG, tagTech);
if (tagTech.equals(NdefFormatable.class.getName())) {
// fireNdefFormatableEvent(tag);
} else if (tagTech.equals(Ndef.class.getName())) { //
Ndef ndef = Ndef.get(tag);
parsed = ndef2React(ndef, new NdefMessage[] { ndef.getCachedNdefMessage() });
}
}
} else if (action.equals(NfcAdapter.ACTION_TAG_DISCOVERED)) {
parsed = tag2React(tag);
}
return parsed;
}
private WritableMap tag2React(Tag tag) {
try {
JSONObject json = Util.tagToJSON(tag);
return JsonConvert.jsonToReact(json);
} catch (JSONException ex) {
return null;
}
}
private WritableMap ndef2React(Ndef ndef, Parcelable[] messages) {
try {
JSONObject json = buildNdefJSON(ndef, messages);
return JsonConvert.jsonToReact(json);
} catch (JSONException ex) {
return null;
}
}
JSONObject buildNdefJSON(Ndef ndef, Parcelable[] messages) {
JSONObject json = Util.ndefToJSON(ndef);
// ndef is null for peer-to-peer
// ndef and messages are null for ndef format-able
if (ndef == null && messages != null) {
try {
if (messages.length > 0) {
NdefMessage message = (NdefMessage) messages[0];
json.put("ndefMessage", Util.messageToJSON(message));
// guessing type, would prefer a more definitive way to determine type
json.put("type", "NDEF Push Protocol");
}
if (messages.length > 1) {
Log.d(LOG_TAG, "Expected one ndefMessage but found " + messages.length);
}
} catch (JSONException e) {
// shouldn't happen
Log.e(Util.TAG, "Failed to convert ndefMessage into json", e);
}
}
return json;
}
}
my nfc component
import React, { Component } from 'react';
import {
View,
Text,
Button,
Platform,
TouchableOpacity,
Linking
} from 'react-native';
import NfcManager, {NdefParser} from 'react-native-nfc-manager';
class NFC extends Component {
constructor(props) {
super(props);
this.state = {
supported: true,
enabled: false,
tag: {},
}
}
componentDidMount() {
NfcManager.start({
onSessionClosedIOS: () => {
console.log('ios session closed');
}
})
.then(result => {
console.log('start OK', result);
})
.catch(error => {
console.warn('start fail', error);
this.setState({supported: false});
})
if (Platform.OS === 'android') {
NfcManager.getLaunchTagEvent()
.then(tag => {
console.log('launch tag', tag);
if (tag) {
this.setState({ tag });
}
})
.catch(err => {
console.log(err);
})
NfcManager.isEnabled()
.then(enabled => {
this.setState({ enabled });
})
.catch(err => {
console.log(err);
})
}
}
render() {
let { supported, enabled, tag } = this.state;
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>{`Is NFC supported ? ${supported}`}</Text>
<Text>{`Is NFC enabled (Android only)? ${enabled}`}</Text>
<TouchableOpacity style={{ marginTop: 20 }} onPress={this._startDetection}>
<Text style={{ color: 'blue' }}>Start Tag Detection</Text>
</TouchableOpacity>
<TouchableOpacity style={{ marginTop: 20 }} onPress={this._stopDetection}>
<Text style={{ color: 'red' }}>Stop Tag Detection</Text>
</TouchableOpacity>
<TouchableOpacity style={{ marginTop: 20 }} onPress={this._clearMessages}>
<Text>Clear</Text>
</TouchableOpacity>
<TouchableOpacity style={{ marginTop: 20 }} onPress={this._goToNfcSetting}>
<Text >Go to NFC setting</Text>
</TouchableOpacity>
<Text style={{ marginTop: 20 }}>{`Current tag JSON: ${JSON.stringify(tag)}`}</Text>
</View>
)
}
_onTagDiscovered = tag => {
console.log('Tag Discovered', tag);
this.setState({ tag });
let url = this._parseUri(tag);
if (url) {
Linking.openURL(url)
.catch(err => {
console.warn(err);
})
}
}
_startDetection = () => {
NfcManager.registerTagEvent(this._onTagDiscovered)
.then(result => {
console.log('registerTagEvent OK', result)
})
.catch(error => {
console.warn('registerTagEvent fail', error)
})
}
_stopDetection = () => {
NfcManager.unregisterTagEvent()
.then(result => {
console.log('unregisterTagEvent OK', result)
})
.catch(error => {
console.warn('unregisterTagEvent fail', error)
})
}
_clearMessages = () => {
this.setState({tag: null});
}
_goToNfcSetting = () => {
if (Platform.OS === 'android') {
NfcManager.goToNfcSetting()
.then(result => {
console.log('goToNfcSetting OK', result)
})
.catch(error => {
console.warn('goToNfcSetting fail', error)
})
}
}
_parseUri = (tag) => {
let result = NdefParser.parseUri(tag.ndefMessage[0]),
uri = result && result.uri;
if (uri) {
console.log('parseUri: ' + uri);
return uri;
}
return null;
}
}
export default NFC;

Be careful, NFC could not be available in all devices, so you have to check first:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
if (nfcAdapter == null){
Toast.makeText(this, "NFC not available", Toast.LENGTH_SHORT).show();
return;
}else{
Toast.makeText(this, "NFC is avalable", Toast.LENGTH_SHORT).show();
}
....
....
}
If NFC is not present that will be the reason to get the error message:
Attempt to get length of null array on NfcManager.start()

Related

Targeting 31+ SDK requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent?

I'm trying to run my app on an android 12 device. the app has a GPS functionality.
When I run my app, I get the usual allow GPS pop up on the phone but then as soon as I 'allow' it, the app crashes and I see this error in Android Studio:
2022-10-07 18:08:53.471 11854-11854/com.example.app E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.app, PID: 11854
java.lang.RuntimeException: Unable to start service com.marianhello.bgloc.service.LocationServiceImpl#936d3d2 with null: java.lang.IllegalArgumentException: com.example.app: Targeting S+ (version 31 and above) requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent.
Strongly consider using FLAG_IMMUTABLE, only use FLAG_MUTABLE if some functionality depends on the PendingIntent being mutable, e.g. if it needs to be used with inline replies or bubbles.
edit:
even though the code is large, I share it as suggested in the comments.
package com.marianhello.bgloc.service;
import android.accounts.Account;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import androidx.annotation.Nullable;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import com.marianhello.bgloc.Config;
import com.marianhello.bgloc.ConnectivityListener;
import com.marianhello.bgloc.sync.NotificationHelper;
import com.marianhello.bgloc.PluginException;
import com.marianhello.bgloc.PostLocationTask;
import com.marianhello.bgloc.ResourceResolver;
import com.marianhello.bgloc.data.BackgroundActivity;
import com.marianhello.bgloc.data.BackgroundLocation;
import com.marianhello.bgloc.data.ConfigurationDAO;
import com.marianhello.bgloc.data.DAOFactory;
import com.marianhello.bgloc.data.LocationDAO;
import com.marianhello.bgloc.data.LocationTransform;
import com.marianhello.bgloc.headless.AbstractTaskRunner;
import com.marianhello.bgloc.headless.ActivityTask;
import com.marianhello.bgloc.headless.LocationTask;
import com.marianhello.bgloc.headless.StationaryTask;
import com.marianhello.bgloc.headless.Task;
import com.marianhello.bgloc.headless.TaskRunner;
import com.marianhello.bgloc.headless.TaskRunnerFactory;
import com.marianhello.bgloc.provider.LocationProvider;
import com.marianhello.bgloc.provider.LocationProviderFactory;
import com.marianhello.bgloc.provider.ProviderDelegate;
import com.marianhello.bgloc.sync.AccountHelper;
import com.marianhello.bgloc.sync.SyncService;
import com.marianhello.logging.LoggerManager;
import com.marianhello.logging.UncaughtExceptionLogger;
import org.chromium.content.browser.ThreadUtils;
import org.json.JSONException;
import static com.marianhello.bgloc.service.LocationServiceIntentBuilder.containsCommand;
import static com.marianhello.bgloc.service.LocationServiceIntentBuilder.containsMessage;
import static com.marianhello.bgloc.service.LocationServiceIntentBuilder.getCommand;
import static com.marianhello.bgloc.service.LocationServiceIntentBuilder.getMessage;
public class LocationServiceImpl extends Service implements ProviderDelegate, LocationService {
public static final String ACTION_BROADCAST = ".broadcast";
public static final int MSG_ON_ERROR = 100;
public static final int MSG_ON_LOCATION = 101;
public static final int MSG_ON_STATIONARY = 102;
public static final int MSG_ON_ACTIVITY = 103;
public static final int MSG_ON_SERVICE_STARTED = 104;
public static final int MSG_ON_SERVICE_STOPPED = 105;
public static final int MSG_ON_ABORT_REQUESTED = 106;
public static final int MSG_ON_HTTP_AUTHORIZATION = 107;
private static int NOTIFICATION_ID = 1;
private ResourceResolver mResolver;
private Config mConfig;
private LocationProvider mProvider;
private Account mSyncAccount;
private org.slf4j.Logger logger;
private final IBinder mBinder = new LocalBinder();
private HandlerThread mHandlerThread;
private ServiceHandler mServiceHandler;
private LocationDAO mLocationDAO;
private PostLocationTask mPostLocationTask;
private String mHeadlessTaskRunnerClass;
private TaskRunner mHeadlessTaskRunner;
private long mServiceId = -1;
private static boolean sIsRunning = false;
private boolean mIsInForeground = false;
private static LocationTransform sLocationTransform;
private static LocationProviderFactory sLocationProviderFactory;
private class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
}
/**
* When binding to the service, we return an interface to our messenger
* for sending messages to the service.
*/
#Override
public IBinder onBind(Intent intent) {
logger.debug("Client binds to service");
return mBinder;
}
#Override
public void onRebind(Intent intent) {
logger.debug("Client rebinds to service");
super.onRebind(intent);
}
#Override
public boolean onUnbind(Intent intent) {
// All clients have unbound with unbindService()
logger.debug("All clients have been unbound from service");
return true; // Ensures onRebind() is called when a client re-binds.
}
#Override
public void onCreate() {
super.onCreate();
sIsRunning = false;
UncaughtExceptionLogger.register(this);
logger = LoggerManager.getLogger(LocationServiceImpl.class);
logger.info("Creating LocationServiceImpl");
mServiceId = System.currentTimeMillis();
// Start up the thread running the service. Note that we create a
// separate thread because the service normally runs in the process's
// main thread, which we don't want to block. We also make it
// background priority so CPU-intensive work will not disrupt our UI.
if (mHandlerThread == null) {
mHandlerThread = new HandlerThread("LocationServiceImpl.Thread", Process.THREAD_PRIORITY_BACKGROUND);
}
mHandlerThread.start();
// An Android service handler is a handler running on a specific background thread.
mServiceHandler = new ServiceHandler(mHandlerThread.getLooper());
mResolver = ResourceResolver.newInstance(this);
mSyncAccount = AccountHelper.CreateSyncAccount(this, mResolver.getAccountName(),
mResolver.getAccountType());
String authority = mResolver.getAuthority();
ContentResolver.setIsSyncable(mSyncAccount, authority, 1);
ContentResolver.setSyncAutomatically(mSyncAccount, authority, true);
mLocationDAO = DAOFactory.createLocationDAO(this);
mPostLocationTask = new PostLocationTask(mLocationDAO,
new PostLocationTask.PostLocationTaskListener() {
#Override
public void onRequestedAbortUpdates() {
handleRequestedAbortUpdates();
}
#Override
public void onHttpAuthorizationUpdates() {
handleHttpAuthorizationUpdates();
}
#Override
public void onSyncRequested() {
SyncService.sync(mSyncAccount, mResolver.getAuthority(), false);
}
}, new ConnectivityListener() {
#Override
public boolean hasConnectivity() {
return isNetworkAvailable();
}
});
registerReceiver(connectivityChangeReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
NotificationHelper.registerServiceChannel(this);
}
#Override
public void onDestroy() {
logger.info("Destroying LocationServiceImpl");
// workaround for issue #276
if (mProvider != null) {
mProvider.onDestroy();
}
if (mHandlerThread != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
mHandlerThread.quitSafely();
} else {
mHandlerThread.quit(); //sorry
}
}
if (mPostLocationTask != null) {
mPostLocationTask.shutdown();
}
unregisterReceiver(connectivityChangeReceiver);
sIsRunning = false;
super.onDestroy();
}
#Override
public void onTaskRemoved(Intent rootIntent) {
logger.debug("Task has been removed");
// workaround for issue #276
Config config = getConfig();
if (config.getStopOnTerminate()) {
logger.info("Stopping self");
stopSelf();
} else {
logger.info("Continue running in background");
}
super.onTaskRemoved(rootIntent);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent == null) {
// when service was killed and restarted we will restart service
start();
return START_STICKY;
}
boolean containsCommand = containsCommand(intent);
logger.debug(
String.format("Service in [%s] state. cmdId: [%s]. startId: [%d]",
sIsRunning ? "STARTED" : "NOT STARTED",
containsCommand ? getCommand(intent).getId() : "N/A",
startId)
);
if (containsCommand) {
LocationServiceIntentBuilder.Command cmd = getCommand(intent);
processCommand(cmd.getId(), cmd.getArgument());
} else {
// Could be a BOOT-event, or the OS just randomly restarted the service...
startForegroundService();
}
if (containsMessage(intent)) {
processMessage(getMessage(intent));
}
return START_STICKY;
}
private void processMessage(String message) {
// currently we do not process any message
}
private void processCommand(int command, Object arg) {
try {
switch (command) {
case CommandId.START:
start();
break;
case CommandId.START_FOREGROUND_SERVICE:
startForegroundService();
break;
case CommandId.STOP:
stop();
break;
case CommandId.CONFIGURE:
configure((Config) arg);
break;
case CommandId.STOP_FOREGROUND:
stopForeground();
break;
case CommandId.START_FOREGROUND:
startForeground();
break;
case CommandId.REGISTER_HEADLESS_TASK:
registerHeadlessTask((String) arg);
break;
case CommandId.START_HEADLESS_TASK:
startHeadlessTask();
break;
case CommandId.STOP_HEADLESS_TASK:
stopHeadlessTask();
break;
}
} catch (Exception e) {
logger.error("processCommand: exception", e);
}
}
#Override
public synchronized void start() {
if (sIsRunning) {
return;
}
if (mConfig == null) {
logger.warn("Attempt to start unconfigured service. Will use stored or default.");
mConfig = getConfig();
// TODO: throw JSONException if config cannot be obtained from db
}
logger.debug("Will start service with: {}", mConfig.toString());
mPostLocationTask.setConfig(mConfig);
mPostLocationTask.clearQueue();
LocationProviderFactory spf = sLocationProviderFactory != null
? sLocationProviderFactory : new LocationProviderFactory(this);
mProvider = spf.getInstance(mConfig.getLocationProvider());
mProvider.setDelegate(this);
mProvider.onCreate();
mProvider.onConfigure(mConfig);
sIsRunning = true;
ThreadUtils.runOnUiThreadBlocking(new Runnable() {
#Override
public void run() {
mProvider.onStart();
if (mConfig.getStartForeground()) {
startForeground();
}
}
});
Bundle bundle = new Bundle();
bundle.putInt("action", MSG_ON_SERVICE_STARTED);
bundle.putLong("serviceId", mServiceId);
broadcastMessage(bundle);
}
#Override
public synchronized void startForegroundService() {
start();
startForeground();
}
#Override
public synchronized void stop() {
if (!sIsRunning) {
return;
}
if (mProvider != null) {
mProvider.onStop();
}
stopForeground(true);
stopSelf();
broadcastMessage(MSG_ON_SERVICE_STOPPED);
sIsRunning = false;
}
#Override
public void startForeground() {
if (sIsRunning && !mIsInForeground) {
Config config = getConfig();
Notification notification = new NotificationHelper.NotificationFactory(this).getNotification(
config.getNotificationTitle(),
config.getNotificationText(),
config.getLargeNotificationIcon(),
config.getSmallNotificationIcon(),
config.getNotificationIconColor());
if (mProvider != null) {
mProvider.onCommand(LocationProvider.CMD_SWITCH_MODE,
LocationProvider.FOREGROUND_MODE);
}
super.startForeground(NOTIFICATION_ID, notification);
mIsInForeground = true;
}
}
#Override
public synchronized void stopForeground() {
if (sIsRunning && mIsInForeground) {
stopForeground(true);
if (mProvider != null) {
mProvider.onCommand(LocationProvider.CMD_SWITCH_MODE,
LocationProvider.BACKGROUND_MODE);
}
mIsInForeground = false;
}
}
#Override
public synchronized void configure(Config config) {
if (mConfig == null) {
mConfig = config;
return;
}
final Config currentConfig = mConfig;
mConfig = config;
mPostLocationTask.setConfig(mConfig);
ThreadUtils.runOnUiThread(new Runnable() {
#Override
public void run() {
if (sIsRunning) {
if (currentConfig.getStartForeground() == true && mConfig.getStartForeground() == false) {
stopForeground(true);
}
if (mConfig.getStartForeground() == true) {
if (currentConfig.getStartForeground() == false) {
// was not running in foreground, so start in foreground
startForeground();
} else {
// was running in foreground, so just update existing notification
Notification notification = new NotificationHelper.NotificationFactory(LocationServiceImpl.this).getNotification(
mConfig.getNotificationTitle(),
mConfig.getNotificationText(),
mConfig.getLargeNotificationIcon(),
mConfig.getSmallNotificationIcon(),
mConfig.getNotificationIconColor());
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(NOTIFICATION_ID, notification);
}
}
}
if (currentConfig.getLocationProvider() != mConfig.getLocationProvider()) {
boolean shouldStart = mProvider.isStarted();
mProvider.onDestroy();
LocationProviderFactory spf = new LocationProviderFactory(LocationServiceImpl.this);
mProvider = spf.getInstance(mConfig.getLocationProvider());
mProvider.setDelegate(LocationServiceImpl.this);
mProvider.onCreate();
mProvider.onConfigure(mConfig);
if (shouldStart) {
mProvider.onStart();
}
} else {
mProvider.onConfigure(mConfig);
}
}
});
}
#Override
public synchronized void registerHeadlessTask(String taskRunnerClass) {
logger.debug("Registering headless task");
mHeadlessTaskRunnerClass = taskRunnerClass;
}
#Override
public synchronized void startHeadlessTask() {
if (mHeadlessTaskRunnerClass != null) {
TaskRunnerFactory trf = new TaskRunnerFactory();
try {
mHeadlessTaskRunner = trf.getTaskRunner(mHeadlessTaskRunnerClass);
((AbstractTaskRunner) mHeadlessTaskRunner).setContext(this);
} catch (Exception e) {
logger.error("Headless task start failed: {}", e.getMessage());
}
}
}
#Override
public synchronized void stopHeadlessTask() {
mHeadlessTaskRunner = null;
}
#Override
public synchronized void executeProviderCommand(final int command, final int arg1) {
if (mProvider == null) {
return;
}
ThreadUtils.runOnUiThread(new Runnable() {
#Override
public void run() {
mProvider.onCommand(command, arg1);
}
});
}
#Override
public void onLocation(BackgroundLocation location) {
logger.debug("New location {}", location.toString());
location = transformLocation(location);
if (location == null) {
logger.debug("Skipping location as requested by the locationTransform");
return;
}
Bundle bundle = new Bundle();
bundle.putInt("action", MSG_ON_LOCATION);
bundle.putParcelable("payload", location);
broadcastMessage(bundle);
runHeadlessTask(new LocationTask(location) {
#Override
public void onError(String errorMessage) {
logger.error("Location task error: {}", errorMessage);
}
#Override
public void onResult(String value) {
logger.debug("Location task result: {}", value);
}
});
postLocation(location);
}
#Override
public void onStationary(BackgroundLocation location) {
logger.debug("New stationary {}", location.toString());
location = transformLocation(location);
if (location == null) {
logger.debug("Skipping location as requested by the locationTransform");
return;
}
Bundle bundle = new Bundle();
bundle.putInt("action", MSG_ON_STATIONARY);
bundle.putParcelable("payload", location);
broadcastMessage(bundle);
runHeadlessTask(new StationaryTask(location){
#Override
public void onError(String errorMessage) {
logger.error("Stationary task error: {}", errorMessage);
}
#Override
public void onResult(String value) {
logger.debug("Stationary task result: {}", value);
}
});
postLocation(location);
}
#Override
public void onActivity(BackgroundActivity activity) {
logger.debug("New activity {}", activity.toString());
Bundle bundle = new Bundle();
bundle.putInt("action", MSG_ON_ACTIVITY);
bundle.putParcelable("payload", activity);
broadcastMessage(bundle);
runHeadlessTask(new ActivityTask(activity){
#Override
public void onError(String errorMessage) {
logger.error("Activity task error: {}", errorMessage);
}
#Override
public void onResult(String value) {
logger.debug("Activity task result: {}", value);
}
});
}
#Override
public void onError(PluginException error) {
Bundle bundle = new Bundle();
bundle.putInt("action", MSG_ON_ERROR);
bundle.putBundle("payload", error.toBundle());
broadcastMessage(bundle);
}
private void broadcastMessage(int msgId) {
Bundle bundle = new Bundle();
bundle.putInt("action", msgId);
broadcastMessage(bundle);
}
private void broadcastMessage(Bundle bundle) {
Intent intent = new Intent(ACTION_BROADCAST);
intent.putExtras(bundle);
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
}
#Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
return super.registerReceiver(receiver, filter, null, mServiceHandler);
}
#Override
public void unregisterReceiver(BroadcastReceiver receiver) {
try {
super.unregisterReceiver(receiver);
} catch (IllegalArgumentException ex) {
// if was not registered ignore exception
}
}
public Config getConfig() {
Config config = mConfig;
if (config == null) {
ConfigurationDAO dao = DAOFactory.createConfigurationDAO(this);
try {
config = dao.retrieveConfiguration();
} catch (JSONException e) {
logger.error("Config exception: {}", e.getMessage());
}
}
if (config == null) {
config = Config.getDefault();
}
mConfig = config;
return mConfig;
}
public static void setLocationProviderFactory(LocationProviderFactory factory) {
sLocationProviderFactory = factory;
}
private void runHeadlessTask(Task task) {
if (mHeadlessTaskRunner == null) {
return;
}
logger.debug("Running headless task: {}", task);
mHeadlessTaskRunner.runTask(task);
}
/**
* Class used for the client Binder. Since this service runs in the same process as its
* clients, we don't need to deal with IPC.
*/
public class LocalBinder extends Binder {
public LocationServiceImpl getService() {
return LocationServiceImpl.this;
}
}
private BackgroundLocation transformLocation(BackgroundLocation location) {
if (sLocationTransform != null) {
return sLocationTransform.transformLocationBeforeCommit(this, location);
}
return location;
}
private void postLocation(BackgroundLocation location) {
mPostLocationTask.add(location);
}
public void handleRequestedAbortUpdates() {
broadcastMessage(MSG_ON_ABORT_REQUESTED);
}
public void handleHttpAuthorizationUpdates() {
broadcastMessage(MSG_ON_HTTP_AUTHORIZATION);
}
/**
* Broadcast receiver which detects connectivity change condition
*/
private BroadcastReceiver connectivityChangeReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
boolean hasConnectivity = isNetworkAvailable();
mPostLocationTask.setHasConnectivity(hasConnectivity);
logger.info("Network condition changed has connectivity: {}", hasConnectivity);
}
};
private boolean isNetworkAvailable() {
ConnectivityManager cm =
(ConnectivityManager) this.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
return activeNetwork != null && activeNetwork.isConnectedOrConnecting();
}
public long getServiceId() {
return mServiceId;
}
public boolean isBound() {
LocationServiceInfo info = new LocationServiceInfoImpl(this);
return info.isBound();
}
public static boolean isRunning() {
return sIsRunning;
}
public static void setLocationTransform(#Nullable LocationTransform transform) {
sLocationTransform = transform;
}
public static #Nullable LocationTransform getLocationTransform() {
return sLocationTransform;
}
}
Add the following to your build.gradle(app) dependencies.
dependencies {
// For Java
implementation 'androidx.work:work-runtime:2.7.1'
// For Kotlin
implementation 'androidx.work:work-runtime-ktx:2.7.1'
}
Some of dependency targets 31+ but didn't updated their flag. If you are not using any pending intent with such flag, we need to add WorkManager dependency in our build.gradle(app) and that's it.
// Kotlin + coroutines
val work_version = "2.7.1"
implementation("androidx.work:work-runtime-ktx:$work_version")
Google Mobile Ads SDK also start managing their library after 20.4.0 release.
This release and all previous versions require an explicit dependency
on androidx.work:work-runtime:2.7.0 to fix a bug causing app crashes
on Android S with the following stack trace.
solved the problem by adding:
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.work:work-runtime-ktx:2.7.1'
implementation 'com.google.firebase:firebase-messaging:23.0.4'
implementation 'com.google.firebase:firebase-iid:21.1.0'
implementation platform('com.google.firebase:firebase-bom:30.0.0')
implementation 'androidx.work:work-runtime-ktx:2.7.1'
implementation 'com.firebaseui:firebase-ui-auth:8.0.0'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation("com.google.android.gms:play-services-auth:20.2.0")
implementation 'com.google.android.material:material:1.6.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
implementation 'com.google.firebase:firebase-database-ktx:20.0.5'
implementation 'com.google.firebase:firebase-auth-ktx:21.0.3'
implementation 'com.google.firebase:firebase-auth:21.0.3'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'

Sink.Success of EventChannel from Platform cannot send a stream of data to flutter

I am newbie to flutter and java.
I am trying to send a stream of data from speechRecognizer.recognized.addeventlistener function from java to flutter. speechRecognizer.recognized.addeventlistener is using Microsoft Azure speech to text API .But the sink.success, attachEvent.success in my case is not sending any string to flutter.
I have attached a main.dart, homepage.dart code as well as java code here.
Hope you guys will help me to find a solution.
For safety reasons, I have removed YourSubscriptionKey and YourServiceRegion.
main.dart
import 'package:flutter/material.dart';
import 'home_page.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Event Channel Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Demo page'),
);
}
}
home_page.dart
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
static const stream = EventChannel(
'com.chamelalaboratory.demo.flutter_event_channel/eventChannel');
late StreamSubscription _streamSubscription;
String _currentValue = "";
void _startListener() {
_streamSubscription = stream.receiveBroadcastStream().listen(_listenStream);
}
void _cancelListener() {
_streamSubscription.cancel();
setState(() {
_currentValue = "Start Recording...";
});
}
void _listenStream(value) {
debugPrint("Received From Native: $value\n");
setState(() {
_currentValue = value;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(_currentValue.toUpperCase(), textAlign: TextAlign.justify),
const SizedBox(
height: 50,
),
//Start Btn
ElevatedButton(
onPressed: () => _startListener(),
child: Text("Record".toUpperCase()),
),
const SizedBox(
height: 50,
),
//Cancel Btn
ElevatedButton(
onPressed: () => _cancelListener(),
child: Text("Stop".toUpperCase()),
),
],
),
),
);
}
}
MainActivity.java
package com.example.sounddiaryapp;
import androidx.annotation.NonNull;
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import com.microsoft.cognitiveservices.speech.*;
import com.microsoft.cognitiveservices.speech.audio.*;
import java.util.concurrent.ExecutionException;
import java.io.IOException;
import java.util.concurrent.Future;
import androidx.core.app.ActivityCompat;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import java.util.*;
import java.util.concurrent.Future;
import static android.Manifest.permission.*;
import java.util.concurrent.TimeUnit;
import android.os.Handler;
import java.util.Objects;
import io.flutter.plugin.common.EventChannel;
public class MainActivity extends FlutterActivity {
private static final String CHANNEL = "codecommon";
private static String YourSubscriptionKey = "";
private static String YourServiceRegion = "";
public static final String STREAM = "com.chamelalaboratory.demo.flutter_event_channel/eventChannel";
final String TAG_NAME = "From_Native";
private EventChannel.EventSink attachEvent;
private int count = 1;
public String receivedText = "";
List<String> textList=new ArrayList<String>();
private Handler handler;
SpeechConfig speechConfig = SpeechConfig.fromSubscription(YourSubscriptionKey,
YourServiceRegion);
AudioConfig audioConfig = AudioConfig.fromDefaultMicrophoneInput();
SpeechRecognizer speechRecognizer = new SpeechRecognizer(speechConfig, audioConfig);
private final Runnable runnable = new Runnable() {
#Override
public void run() {
try{
int requestCode = 5; // unique code for the permission request
ActivityCompat.requestPermissions(MainActivity.this,
new String[] { RECORD_AUDIO, INTERNET }, requestCode);
try {
speechRecognizer.recognized.addEventListener((s, e) -> {
if (e.getResult().getReason() == ResultReason.RecognizedSpeech) {
System.out.println("RECOGNIZED: Text=" + e.getResult().getText());
String receivedText = e.getResult().getText();
attachEvent.success(receivedText);
} else if (e.getResult().getReason() == ResultReason.NoMatch) {
System.out.println("NOMATCH: Speech could not be recognized.");
try {
speechRecognizer.stopContinuousRecognitionAsync().get();
// attachEvent.endOfStream();
}
catch (Exception ex ) {
System.out.println(ex.getMessage());
}
}
});
speechRecognizer.canceled.addEventListener((s, e) -> {
System.out.println("CANCELED: Reason=" + e.getReason());
if (e.getReason() == CancellationReason.Error) {
System.out.println("CANCELED: ErrorCode=" + e.getErrorCode());
System.out.println("CANCELED: ErrorDetails=" + e.getErrorDetails());
System.out.println(
"CANCELED: Did you set the speech resource key and region values?");
}
try {
speechRecognizer.stopContinuousRecognitionAsync().get();
attachEvent.endOfStream();
}
catch (Exception ex ) {
System.out.println(ex.getMessage());
}
});
speechRecognizer.sessionStopped.addEventListener((s, e) -> {
System.out.println("\n top Session stopped event.");
try {
speechRecognizer.stopContinuousRecognitionAsync().get();
// attachEvent.endOfStream();
}
catch (Exception ex ) {
System.out.println(ex.getMessage());
}
});
speechRecognizer.startContinuousRecognitionAsync().get();
} catch(Exception e) {
System.out.println("*****************************************************");
System.out.println(e.getMessage());
try {
speechRecognizer.stopContinuousRecognitionAsync().get();
// attachEvent.endOfStream();
}
catch (Exception ex ) {
System.out.println(ex.getMessage());
}
}
} catch (Exception ex) {
System.out.println("+++++++++++++++++++++++++++++ Unexpected exception: " + ex.getMessage());
// recognizer.stopContinuousRecognitionAsync().get();
assert (false);
System.exit(1);
}
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new EventChannel(Objects.requireNonNull(getFlutterEngine()).getDartExecutor(), STREAM).setStreamHandler(
new EventChannel.StreamHandler() {
#Override
public void onListen(Object args, final EventChannel.EventSink events) {
Log.w(TAG_NAME, "Adding listener");
attachEvent = events;
count = 1;
handler = new Handler();
runnable.run();
}
#Override
public void onCancel(Object args) {
Log.w(TAG_NAME, "Cancelling listener");
try{
stopSpeechToText();
}
catch (Exception ex) {
System.out.println("-------------------------------------");
System.out.println(ex);
}
handler.removeCallbacks(runnable);
handler = null;
count = 1;
attachEvent = null;
System.out.println("StreamHandler - onCancelled: ");
}
}
);
}
public void stopSpeechToText() throws InterruptedException, ExecutionException,IOException {
speechRecognizer .sessionStopped.addEventListener((s, e) -> {
System.out.println("\n top Session stopped event.");
try {
speechRecognizer .stopContinuousRecognitionAsync().get();
// attachEvent.endOfStream();
}
catch (Exception ex ) {
System.out.println(ex.getMessage());
}
});
}
#Override
protected void onDestroy() {
super.onDestroy();
handler.removeCallbacks(runnable);
handler = null;
attachEvent = null;
}
}

[android studio]having problem with handler.postdelayed [closed]

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 1 year ago.
Improve this question
hope you doing great.i'm very new to android programming and i have source of an app which i intend to modify and add PatternLockView library to it . i watched a youtube video tutorial but i can't use handler.postdelay option in the code (android studio returns with : Cannot resolve symbol 'postdelayed').the coder who made the tutorial didn't got any errors or so but android studio gives me lots of them . any help or hint is appreciated.
here is the code :
Handler handler = new Handler ();
handler.postdelayed {
#Override
public void run() {
SharedPreferences preferences = getSharedPreferences("PREFS", 0);
String password = preferences.getString("password","0");
if (password.equals("0")) {
Intent intent = new Intent (getApplicationContext(),CreatePasswordActivity.class);
startActivity(intent);
finish();
} else {
Intent intent = new Intent (getApplicationContext(),InputPasswordActivity.class);
startActivity(intent);
finish();
}
}
}2000
/*#######################################################
*
* Maintained by Gregor Santner, 2017-
* https://gsantner.net/
*
* License of this file: Apache 2.0 (Commercial upon request)
* https://www.apache.org/licenses/LICENSE-2.0
*
#########################################################*/
package net.gsantner.markor.activity;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.NonNull;
import android.support.design.widget.BottomNavigationView;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.content.ContextCompat;
import android.support.v4.view.ViewPager;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import com.pixplicity.generate.Rate;
import net.gsantner.markor.BuildConfig;
import net.gsantner.markor.R;
import net.gsantner.markor.format.TextFormat;
import net.gsantner.markor.ui.FilesystemViewerCreator;
import net.gsantner.markor.ui.NewFileDialog;
import net.gsantner.markor.util.ActivityUtils;
import net.gsantner.markor.util.AppSettings;
import net.gsantner.markor.util.PermissionChecker;
import net.gsantner.markor.util.ShareUtil;
import net.gsantner.opoc.activity.GsFragmentBase;
import net.gsantner.opoc.format.markdown.SimpleMarkdownParser;
import net.gsantner.opoc.ui.FilesystemViewerAdapter;
import net.gsantner.opoc.ui.FilesystemViewerData;
import net.gsantner.opoc.ui.FilesystemViewerFragment;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.concurrent.TimeUnit;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import butterknife.OnLongClick;
import butterknife.OnPageChange;
public class MainActivity extends AppActivityBase implements FilesystemViewerFragment.FilesystemFragmentOptionsListener, BottomNavigationView.OnNavigationItemSelectedListener {
//START
Handler handler = new Handler(Looper.getMainLooper());
handler.postDelayed( () -> {
SharedPreferences preferences = getSharedPreferences("PREFS", 0);
String password = preferences.getString("password","0");
if (password.equals("0")) {
Intent intent = new Intent (getApplicationContext(),CreatePasswordActivity.class);
startActivity(intent);
finish();
} else {
Intent intent = new Intent (getApplicationContext(),InputPasswordActivity.class);
startActivity(intent);
finish();
}
}, 2000);
// END
public static boolean IS_DEBUG_ENABLED = false;
#BindView(R.id.toolbar)
public Toolbar _toolbar;
#BindView(R.id.bottom_navigation_bar)
BottomNavigationView _bottomNav;
#BindView(R.id.fab_add_new_item)
FloatingActionButton _fab;
#BindView(R.id.main__view_pager_container)
ViewPager _viewPager;
private SectionsPagerAdapter _viewPagerAdapter;
private boolean _doubleBackToExitPressedOnce;
private MenuItem _lastBottomMenuItem;
private AppSettings _appSettings;
private ActivityUtils _contextUtils;
private ShareUtil _shareUtil;
#SuppressLint("SdCardPath")
#Override
protected void onCreate(Bundle savedInstanceState) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
getWindow().setExitTransition(null);
}
_appSettings = new AppSettings(this);
_contextUtils = new ActivityUtils(this);
_shareUtil = new ShareUtil(this);
_contextUtils.setAppLanguage(_appSettings.getLanguage());
if (_appSettings.isOverviewStatusBarHidden()) {
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
setTheme(_appSettings.isDarkThemeEnabled() ? R.style.AppTheme_Dark : R.style.AppTheme_Light);
super.onCreate(savedInstanceState);
setContentView(R.layout.main__activity);
ButterKnife.bind(this);
setSupportActionBar(_toolbar);
_toolbar.setOnClickListener(this::onToolbarTitleClicked);
optShowRate();
try {
if (_appSettings.isAppCurrentVersionFirstStart(true)) {
SimpleMarkdownParser smp = SimpleMarkdownParser.get().setDefaultSmpFilter(SimpleMarkdownParser.FILTER_ANDROID_TEXTVIEW);
String html = "";
html += smp.parse(getString(R.string.copyright_license_text_official).replace("\n", " \n"), "").getHtml();
html += "<br/><br/><br/><big><big>" + getString(R.string.changelog) + "</big></big><br/>" + smp.parse(getResources().openRawResource(R.raw.changelog), "", SimpleMarkdownParser.FILTER_ANDROID_TEXTVIEW);
html += "<br/><br/><br/><big><big>" + getString(R.string.licenses) + "</big></big><br/>" + smp.parse(getResources().openRawResource(R.raw.licenses_3rd_party), "").getHtml();
ActivityUtils _au = new ActivityUtils(this);
_au.showDialogWithHtmlTextView(0, html);
}
} catch (IOException e) {
e.printStackTrace();
}
IntroActivity.optStart(this);
// Setup viewpager
_viewPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
_viewPager.setAdapter(_viewPagerAdapter);
_viewPager.setOffscreenPageLimit(4);
_bottomNav.setOnNavigationItemSelectedListener(this);
// noinspection PointlessBooleanExpression - Send Test intent
if (BuildConfig.IS_TEST_BUILD && false) {
DocumentActivity.launch(this, new File("/sdcard/Documents/mordor/aa-beamer.md"), false, true, null, null);
}
(new ActivityUtils(this)).applySpecialLaunchersVisibility(_appSettings.isSpecialFileLaunchersEnabled());
_bottomNav.postDelayed(() -> {
if (_appSettings.getAppStartupTab() != R.id.nav_notebook) {
_bottomNav.setSelectedItemId(_appSettings.getAppStartupTab());
}
}, 1);
}
private void optShowRate() {
new Rate.Builder(this)
.setTriggerCount(4)
.setMinimumInstallTime((int) TimeUnit.MINUTES.toMillis(30))
.setFeedbackAction(() -> new ActivityUtils(this).showGooglePlayEntryForThisApp())
.build().count().showRequest();
}
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
PermissionChecker permc = new PermissionChecker(this);
permc.checkPermissionResult(requestCode, permissions, grantResults);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
super.onOptionsItemSelected(item);
AppSettings as = new AppSettings(this);
switch (item.getItemId()) {
case R.id.action_preview: {
File f = _bottomNav.getSelectedItemId() == R.id.nav_quicknote ? as.getQuickNoteFile() : as.getTodoFile();
DocumentActivity.launch(MainActivity.this, f, false, true, null, null);
return true;
}
case R.id.action_settings: {
new ActivityUtils(this).animateToActivity(SettingsActivity.class, false, null);
return true;
}
}
return false;
}
#Override
public boolean onCreateOptionsMenu(final Menu menu) {
getMenuInflater().inflate(R.menu.main__menu, menu);
menu.findItem(R.id.action_settings).setVisible(_appSettings.isShowSettingsOptionInMainToolbar());
_contextUtils.tintMenuItems(menu, true, Color.WHITE);
_contextUtils.setSubMenuIconsVisiblity(menu, true);
return true;
}
#Override
protected void onResume() {
//new AndroidSupportMeWrapper(this).mainOnResume();
super.onResume();
IS_DEBUG_ENABLED = BuildConfig.IS_TEST_BUILD;
if (_appSettings.isRecreateMainRequired()) {
// recreate(); // does not remake fragments
Intent intent = getIntent();
overridePendingTransition(0, 0);
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
finish();
overridePendingTransition(0, 0);
startActivity(intent);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && _appSettings.isMultiWindowEnabled()) {
setTaskDescription(new ActivityManager.TaskDescription(getString(R.string.app_name)));
}
int color = ContextCompat.getColor(this, _appSettings.isDarkThemeEnabled()
? R.color.dark__background : R.color.light__background);
_viewPager.getRootView().setBackgroundColor(color);
if (_appSettings.isKeepScreenOn()) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
} else {
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
}
#Override
#SuppressWarnings("unused")
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// Determine some results and forward using Local Broadcast
Object result = _shareUtil.extractResultFromActivityResult(requestCode, resultCode, data, this);
try {
FilesystemViewerFragment frag = (FilesystemViewerFragment) _viewPagerAdapter.getFragmentByTag(FilesystemViewerFragment.FRAGMENT_TAG);
frag.getAdapter().reconfigure();
} catch (Exception ignored) {
recreate();
}
}
#OnLongClick({R.id.fab_add_new_item})
public boolean onLongClickFab(View view) {
PermissionChecker permc = new PermissionChecker(this);
FilesystemViewerFragment fsFrag = (FilesystemViewerFragment) _viewPagerAdapter.getFragmentByTag(FilesystemViewerFragment.FRAGMENT_TAG);
if (fsFrag != null && permc.mkdirIfStoragePermissionGranted()) {
fsFrag.getAdapter().setCurrentFolder(fsFrag.getCurrentFolder().equals(FilesystemViewerAdapter.VIRTUAL_STORAGE_RECENTS)
? FilesystemViewerAdapter.VIRTUAL_STORAGE_FAVOURITE : FilesystemViewerAdapter.VIRTUAL_STORAGE_RECENTS
, true);
}
return true;
}
#SuppressWarnings("SwitchStatementWithTooFewBranches")
#OnClick({R.id.fab_add_new_item})
public void onClickFab(View view) {
PermissionChecker permc = new PermissionChecker(this);
FilesystemViewerFragment fsFrag = (FilesystemViewerFragment) _viewPagerAdapter.getFragmentByTag(FilesystemViewerFragment.FRAGMENT_TAG);
if (fsFrag == null) {
return;
}
if (fsFrag.getAdapter().isCurrentFolderVirtual()) {
fsFrag.getAdapter().loadFolder(_appSettings.getNotebookDirectory());
return;
}
if (permc.mkdirIfStoragePermissionGranted()) {
switch (view.getId()) {
case R.id.fab_add_new_item: {
if (_shareUtil.isUnderStorageAccessFolder(fsFrag.getCurrentFolder()) && _shareUtil.getStorageAccessFrameworkTreeUri() == null) {
_shareUtil.showMountSdDialog(this);
return;
}
if (!fsFrag.getAdapter().isCurrentFolderWriteable()) {
return;
}
NewFileDialog dialog = NewFileDialog.newInstance(fsFrag.getCurrentFolder(), true, (ok, f) -> {
if (ok) {
if (f.isFile()) {
DocumentActivity.launch(MainActivity.this, f, false, false, null, null);
} else if (f.isDirectory()) {
FilesystemViewerFragment wrFragment = (FilesystemViewerFragment) _viewPagerAdapter.getFragmentByTag(FilesystemViewerFragment.FRAGMENT_TAG);
if (wrFragment != null) {
wrFragment.reloadCurrentFolder();
}
}
}
});
dialog.show(getSupportFragmentManager(), NewFileDialog.FRAGMENT_TAG);
break;
}
}
}
}
#Override
public void onBackPressed() {
// Exit confirmed with 2xBack
if (_doubleBackToExitPressedOnce) {
super.onBackPressed();
_appSettings.setFileBrowserLastBrowsedFolder(_appSettings.getNotebookDirectory());
return;
}
// Check if fragment handled back press
GsFragmentBase frag = _viewPagerAdapter.getCachedFragments().get(_viewPager.getCurrentItem());
if (frag != null && frag.onBackPressed()) {
return;
}
// Confirm exit with back / snackbar
_doubleBackToExitPressedOnce = true;
new ActivityUtils(this).showSnackBar(R.string.press_back_again_to_exit, false, R.string.exit, view -> finish());
new Handler().postDelayed(() -> _doubleBackToExitPressedOnce = false, 2000);
}
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
updateFabVisibility(item.getItemId() == R.id.nav_notebook);
PermissionChecker permc = new PermissionChecker(this);
switch (item.getItemId()) {
case R.id.nav_notebook: {
_viewPager.setCurrentItem(0);
_toolbar.setTitle(getFileBrowserTitle());
return true;
}
case R.id.nav_todo: {
permc.doIfExtStoragePermissionGranted(); // cannot prevent bottom tab selection
restoreDefaultToolbar();
_viewPager.setCurrentItem(1);
_toolbar.setTitle(R.string.todo);
return true;
}
case R.id.nav_quicknote: {
permc.doIfExtStoragePermissionGranted(); // cannot prevent bottom tab selection
restoreDefaultToolbar();
_viewPager.setCurrentItem(2);
_toolbar.setTitle(R.string.quicknote);
return true;
}
case R.id.nav_more: {
restoreDefaultToolbar();
_viewPager.setCurrentItem(3);
_toolbar.setTitle(R.string.more);
return true;
}
}
return false;
}
public void updateFabVisibility(boolean visible) {
if (visible) {
_fab.show();
} else {
_fab.hide();
}
}
public String getFileBrowserTitle() {
final File file = _appSettings.getFileBrowserLastBrowsedFolder();
String title = getString(R.string.app_name);
if (!_appSettings.getNotebookDirectory().getAbsolutePath().equals(file.getAbsolutePath())) {
title = "> " + file.getName();
}
return title;
}
#OnPageChange(value = R.id.main__view_pager_container, callback = OnPageChange.Callback.PAGE_SELECTED)
public void onViewPagerPageSelected(int pos) {
Menu menu = _bottomNav.getMenu();
PermissionChecker permc = new PermissionChecker(this);
(_lastBottomMenuItem != null ? _lastBottomMenuItem : menu.getItem(0)).setChecked(false);
_lastBottomMenuItem = menu.getItem(pos).setChecked(true);
updateFabVisibility(pos == 0);
_toolbar.setTitle(new String[]{getFileBrowserTitle(), getString(R.string.todo), getString(R.string.quicknote), getString(R.string.more)}[pos]);
if (pos > 0 && pos < 3) {
permc.doIfExtStoragePermissionGranted(); // cannot prevent bottom tab selection
}
}
private FilesystemViewerData.Options _filesystemDialogOptions = null;
#Override
public FilesystemViewerData.Options getFilesystemFragmentOptions(FilesystemViewerData.Options existingOptions) {
if (_filesystemDialogOptions == null) {
_filesystemDialogOptions = FilesystemViewerCreator.prepareFsViewerOpts(getApplicationContext(), false, new FilesystemViewerData.SelectionListenerAdapter() {
#Override
public void onFsViewerConfig(FilesystemViewerData.Options dopt) {
dopt.descModtimeInsteadOfParent = true;
//opt.rootFolder = _appSettings.getNotebookDirectory();
dopt.rootFolder = _appSettings.getFolderToLoadByMenuId(_appSettings.getAppStartupFolderMenuId());
dopt.folderFirst = _appSettings.isFilesystemListFolderFirst();
dopt.doSelectMultiple = dopt.doSelectFolder = dopt.doSelectFile = true;
dopt.mountedStorageFolder = _shareUtil.getStorageAccessFolder();
dopt.showDotFiles = _appSettings.isShowDotFiles();
dopt.fileComparable = FilesystemViewerFragment.sortFolder(null);
}
#Override
public void onFsViewerDoUiUpdate(FilesystemViewerAdapter adapter) {
if (adapter != null && adapter.getCurrentFolder() != null && !TextUtils.isEmpty(adapter.getCurrentFolder().getName())) {
_appSettings.setFileBrowserLastBrowsedFolder(adapter.getCurrentFolder());
_toolbar.setTitle(adapter.areItemsSelected() ? "" : getFileBrowserTitle());
invalidateOptionsMenu();
if (adapter.getCurrentFolder().equals(FilesystemViewerAdapter.VIRTUAL_STORAGE_FAVOURITE)) {
adapter.getFsOptions().favouriteFiles = _appSettings.getFavouriteFiles();
}
}
}
#Override
public void onFsViewerSelected(String request, File file, final Integer lineNumber) {
if (TextFormat.isTextFile(file)) {
DocumentActivity.launch(MainActivity.this, file, false, null, null, lineNumber);
} else if (file.getName().toLowerCase().endsWith(".apk")) {
_shareUtil.requestApkInstallation(file);
} else {
DocumentActivity.askUserIfWantsToOpenFileInThisApp(MainActivity.this, file);
}
}
});
}
return _filesystemDialogOptions;
}
class SectionsPagerAdapter extends FragmentPagerAdapter {
private HashMap<Integer, GsFragmentBase> _fragCache = new LinkedHashMap<>();
SectionsPagerAdapter(FragmentManager fragMgr) {
super(fragMgr);
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
GsFragmentBase fragment = (GsFragmentBase) super.instantiateItem(container, position);
_fragCache.put(position, fragment);
return fragment;
}
#Override
public Fragment getItem(int pos) {
final Fragment existing = _fragCache.get(pos);
if (existing != null) {
return existing;
}
switch (_bottomNav.getMenu().getItem(pos).getItemId()) {
default:
case R.id.nav_notebook: {
return FilesystemViewerFragment.newInstance(getFilesystemFragmentOptions(null));
}
case R.id.nav_quicknote: {
return DocumentEditFragment.newInstance(_appSettings.getQuickNoteFile(), false, -1);
}
case R.id.nav_todo: {
return DocumentEditFragment.newInstance(_appSettings.getTodoFile(), false, -1);
}
case R.id.nav_more: {
return MoreFragment.newInstance();
}
}
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
super.destroyItem(container, position, object);
_fragCache.remove(position);
}
#Override
public int getCount() {
return _bottomNav.getMenu().size();
}
public GsFragmentBase getFragmentByTag(String fragmentTag) {
for (GsFragmentBase frag : _fragCache.values()) {
if (fragmentTag.equals(frag.getFragmentTag())) {
return frag;
}
}
return null;
}
public HashMap<Integer, GsFragmentBase> getCachedFragments() {
return _fragCache;
}
}
#Override
protected void onStop() {
super.onStop();
restoreDefaultToolbar();
}
/**
* Restores the default toolbar. Used when changing the tab or moving to another activity
* while {#link FilesystemViewerFragment} action mode is active (e.g. when renaming a file)
*/
private void restoreDefaultToolbar() {
FilesystemViewerFragment wrFragment = (FilesystemViewerFragment) _viewPagerAdapter.getFragmentByTag(FilesystemViewerFragment.FRAGMENT_TAG);
if (wrFragment != null) {
wrFragment.clearSelection();
}
}
private void onToolbarTitleClicked(View v) {
Fragment f = _viewPagerAdapter.getItem(_viewPager.getCurrentItem());
if (f instanceof DocumentEditFragment) {
DocumentEditFragment def = (DocumentEditFragment) f;
def.onToolbarTitleClicked(_toolbar);
}
}
}
Since the postDelayed is a method, you have to use () along with it. It takes 2 parameters, first one is a Runnable object and second one is a millisecond value to delay which is an int type. Also Handler() constructor is depracated, that's why you have to bind your handler to a looper object, commonly to the main looper. Here is the fixed code:
// Make sure you import the right Handler class
import android.os.Handler;
Handler handler = new Handler(Looper.getMainLooper());
handler.postDelayed( () -> {
SharedPreferences preferences = getSharedPreferences("PREFS", 0);
String password = preferences.getString("password","0");
if (password.equals("0")) {
Intent intent = new Intent (getApplicationContext(),CreatePasswordActivity.class);
startActivity(intent);
finish();
} else {
Intent intent = new Intent (getApplicationContext(),InputPasswordActivity.class);
startActivity(intent);
finish();
}
}, millisecondDelay);
UPDATE
I've noted that your postdelayed syntax isn't correct. It must be as postDelayed. I updated my answer chek the code out again.

Java - app crash when using in app purchase

So I have experienced some issue with in app purchase in my app.
I have started working on an old (8 months old) project I had earlier, but I am having some issue with app purchase. The app is already live on Play Store, so in app purchase is active.
In build.gradle (:app) I have changed from:
dependencies {
implementation 'com.android.billingclient:billing:2.2.1'
to this:
dependencies {
def billing_version = "3.0.0" // In App Purchase
implementation "com.android.billingclient:billing:$billing_version"
This is the full code of my UpgradeActivity.java:
package com.pinloop.testproj;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.android.billingclient.api.BillingClient;
import com.android.billingclient.api.BillingClientStateListener;
import com.android.billingclient.api.BillingFlowParams;
import com.android.billingclient.api.BillingResult;
import com.android.billingclient.api.Purchase;
import com.android.billingclient.api.PurchasesUpdatedListener;
import com.android.billingclient.api.SkuDetails;
import com.android.billingclient.api.SkuDetailsParams;
import com.android.billingclient.api.SkuDetailsResponseListener;
import java.util.ArrayList;
import java.util.List;
public class UpgradeActivity extends AppCompatActivity implements PurchasesUpdatedListener {
private Button upgradeButton;
private TextView restoreButton;
private BillingClient billingClient;
private List skuList = new ArrayList();
private String sku = "com.pinloop.testproj.pro";
private SkuDetails mSkuDetails;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_upgrade);
upgradeButton = (Button) findViewById(R.id.upgradeButton);
upgradeButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
upgradeAction();
}
});
// Check In App Purchase
upgradeButton.setEnabled(true);
skuList.add(sku);
Boolean pro = getBoolFromPref(this,"myPref", sku);
if (pro)
{
Toast.makeText(this, "you are a premium user", Toast.LENGTH_SHORT).show();
//upgradeButton.setVisibility(View.INVISIBLE);
upgradeButton.setText(R.string.you_are_premium);
}
else
{
Toast.makeText(this, "not pro", Toast.LENGTH_SHORT).show();
setupBillingClient();
}
}
private void upgradeAction() {
BillingFlowParams billingFlowParams = BillingFlowParams
.newBuilder()
.setSkuDetails(mSkuDetails)
.build();
billingClient.launchBillingFlow(UpgradeActivity.this, billingFlowParams);
}
// In App Handler:
private void setupBillingClient() {
billingClient = BillingClient.newBuilder(this).enablePendingPurchases().setListener(this).build();
billingClient.startConnection(new BillingClientStateListener(){
#Override
public void onBillingSetupFinished(BillingResult billingResult) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
// The BillingClient is setup successfully
loadAllSKUs();
}
}
#Override
public void onBillingServiceDisconnected() {
// Try to restart the connection on the next request to
// Google Play by calling the startConnection() method.
}
});
}
private void loadAllSKUs() {
Toast.makeText(this, "loadAllSKUs", Toast.LENGTH_SHORT).show();
if (billingClient.isReady())
{
Toast.makeText(this, "billingclient ready", Toast.LENGTH_SHORT).show();
SkuDetailsParams params = SkuDetailsParams.newBuilder()
.setSkusList(skuList)
.setType(BillingClient.SkuType.INAPP)
.build();
billingClient.querySkuDetailsAsync(params, new SkuDetailsResponseListener() {
#Override
public void onSkuDetailsResponse(BillingResult billingResult, List<SkuDetails> skuDetailsList) {
Toast.makeText(UpgradeActivity.this, "inside query" + billingResult.getResponseCode(), Toast.LENGTH_SHORT).show();
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK
&& !skuDetailsList.isEmpty())
{
for (Object skuDetailsObject : skuDetailsList) {
final SkuDetails skuDetails = (SkuDetails) skuDetailsObject;
Toast.makeText(UpgradeActivity.this, "" + skuDetails.getSku(), Toast.LENGTH_SHORT).show();
if (skuDetails.getSku() == sku)
mSkuDetails = skuDetails;
upgradeButton.setEnabled(true);
upgradeButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
BillingFlowParams billingFlowParams = BillingFlowParams
.newBuilder()
.setSkuDetails(skuDetails)
.build();
billingClient.launchBillingFlow(UpgradeActivity.this, billingFlowParams);
}
});
}
}
}
});
}
else
Toast.makeText(this, "billingclient not ready", Toast.LENGTH_SHORT).show();
}
#Override
public void onPurchasesUpdated(BillingResult billingResult, #Nullable List<Purchase> purchases) {
int responseCode = billingResult.getResponseCode();
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK
&& purchases != null) {
for (Purchase purchase : purchases) {
handlePurchase(purchase);
}
}
else
if (responseCode == BillingClient.BillingResponseCode.USER_CANCELED) {
// Handle an error caused by a user cancelling the purchase flow.
//Log.d(TAG, "User Canceled" + responseCode);
}
else if (responseCode == BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED) {
///mSharedPreferences.edit().putBoolean(getResources().getString(R.string.pref_remove_ads_key), true).commit();
///setAdFree(true);
setBoolInPref(this,"myPref",sku, true );
}
else {
//Log.d(TAG, "Other code" + responseCode);
// Handle any other error codes.
}
}
private void handlePurchase(Purchase purchase) {
if (purchase.getSku().equals(sku)) {
///mSharedPreferences.edit().putBoolean(getResources().getString(R.string.pref_remove_ads_key), true).commit();
///setAdFree(true);
setBoolInPref(this,"myPref",sku, true );
Toast.makeText(this, "Purchase done. you are now a premium member.", Toast.LENGTH_SHORT).show();
}
}
private Boolean getBoolFromPref(Context context, String prefName, String constantName) {
SharedPreferences pref = context.getSharedPreferences(prefName, 0); // 0 - for private mode
return pref.getBoolean(constantName, false);
}
private void setBoolInPref(Context context,String prefName, String constantName, Boolean val) {
SharedPreferences pref = context.getSharedPreferences(prefName, 0); // 0 - for private mode
SharedPreferences.Editor editor = pref.edit();
editor.putBoolean(constantName, val);
//editor.commit();
editor.apply();
// Update 2015: Android recommends the use of apply() now over commit(),
// because apply() operates on a background thread instead of storing the persistent data immediately,
// and possible blocking the main thread.
}
}
Basically what happens when pressing the upgradeButton is that the app crashes. I cannot figure out what I am doing wrong. I am using the correct SKU, and the app has been live on Play Store for over a year now.
This is the error log I get when pressing the upgradeButton:
I/zygote: Do partial code cache collection, code=124KB, data=72KB
After code cache collection, code=124KB, data=72KB
Increasing code cache capacity to 512KB
D/EGL_emulation: eglMakeCurrent: 0xdb928540: ver 3 0 (tinfo 0xdb92bbd0)
D/AndroidRuntime: Shutting down VM
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.pinloop.testproj, PID: 13352
java.lang.IllegalArgumentException: SKU cannot be null.
at com.android.billingclient.api.BillingFlowParams$Builder.build(com.android.billingclient:billing##3.0.0:23)
at com.pinloop.testproj.UpgradeActivity.upgradeAction(UpgradeActivity.java:130)
at com.pinloop.testproj.UpgradeActivity.access$000(UpgradeActivity.java:31)
at com.pinloop.testproj.UpgradeActivity$1.onClick(UpgradeActivity.java:56)
at android.view.View.performClick(View.java:6294)
at android.view.View$PerformClick.run(View.java:24770)
at android.os.Handler.handleCallback(Handler.java:790)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6494)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
Not sure what to look at here, but I can see that java.lang.IllegalArgumentException: SKU cannot be null., but sku has already been declared: private String sku = "com.pinloop.testproj.pro";. Any ideas?
Here is the update code
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.android.billingclient.api.BillingClient;
import com.android.billingclient.api.BillingClientStateListener;
import com.android.billingclient.api.BillingFlowParams;
import com.android.billingclient.api.BillingResult;
import com.android.billingclient.api.Purchase;
import com.android.billingclient.api.PurchasesUpdatedListener;
import com.android.billingclient.api.SkuDetails;
import com.android.billingclient.api.SkuDetailsParams;
import com.android.billingclient.api.SkuDetailsResponseListener;
import com.aumhum.aumhum.R;
import java.util.ArrayList;
import java.util.List;
public class UpgradeActivity extends AppCompatActivity implements PurchasesUpdatedListener {
private Button upgradeButton;
private TextView restoreButton;
private BillingClient billingClient;
private final List<String> skuList = new ArrayList();
private final String sku = "com.pinloop.testproj.pro";
private SkuDetails mSkuDetails;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_upgrade);
upgradeButton = (Button) findViewById(R.id.upgradeButton);
upgradeButton.setEnabled(false);
upgradeButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
upgradeAction();
}
});
// Check In App Purchase
skuList.add(sku);
setButtonStatus();
}
private void setButtonStatus(){
Boolean pro = getBoolFromPref(this,"myPref", sku);
if (pro)
{
Toast.makeText(this, "you are a premium user", Toast.LENGTH_SHORT).show();
//upgradeButton.setVisibility(View.INVISIBLE);
upgradeButton.setText(R.string.you_are_premium);
}
else
{
Toast.makeText(this, "not pro", Toast.LENGTH_SHORT).show();
setupBillingClient();
}
}
private void upgradeAction() {
if (mSkuDetails==null){
Toast.makeText(this,"Please wait while we get details",Toast.LENGTH_SHORT).show();
return;
}
BillingFlowParams billingFlowParams = BillingFlowParams
.newBuilder()
.setSkuDetails(mSkuDetails)
.build();
billingClient.launchBillingFlow(UpgradeActivity.this, billingFlowParams);
}
// In App Handler:
private void setupBillingClient() {
billingClient = BillingClient.newBuilder(this).enablePendingPurchases().setListener(this).build();
billingClient.startConnection(new BillingClientStateListener(){
#Override
public void onBillingSetupFinished(BillingResult billingResult) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
// The BillingClient is setup successfully
loadAllSKUs();
}
}
#Override
public void onBillingServiceDisconnected() {
// Try to restart the connection on the next request to
// Google Play by calling the startConnection() method.
}
});
}
private void loadAllSKUs() {
Toast.makeText(this, "loadAllSKUs", Toast.LENGTH_SHORT).show();
if (billingClient.isReady())
{
Toast.makeText(this, "billingclient ready", Toast.LENGTH_SHORT).show();
SkuDetailsParams params = SkuDetailsParams.newBuilder()
.setSkusList(skuList)
.setType(BillingClient.SkuType.INAPP)
.build();
billingClient.querySkuDetailsAsync(params, new SkuDetailsResponseListener() {
#Override
public void onSkuDetailsResponse(BillingResult billingResult, List<SkuDetails> skuDetailsList) {
Toast.makeText(UpgradeActivity.this, "inside query" + billingResult.getResponseCode(), Toast.LENGTH_SHORT).show();
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK
&& !skuDetailsList.isEmpty())
{
for (SkuDetails skuDetails : skuDetailsList) {
Toast.makeText(UpgradeActivity.this, "" + skuDetails.getSku(), Toast.LENGTH_SHORT).show();
if (skuDetails.getSku().equals(sku)) {
mSkuDetails = skuDetails;
upgradeButton.setEnabled(true);
}
}
}
}
});
}
else
Toast.makeText(this, "billingclient not ready", Toast.LENGTH_SHORT).show();
}
#Override
public void onPurchasesUpdated(BillingResult billingResult, #Nullable List<Purchase> purchases) {
int responseCode = billingResult.getResponseCode();
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK
&& purchases != null) {
for (Purchase purchase : purchases) {
handlePurchase(purchase);
}
}
else
if (responseCode == BillingClient.BillingResponseCode.USER_CANCELED) {
// Handle an error caused by a user cancelling the purchase flow.
//Log.d(TAG, "User Canceled" + responseCode);
}
else if (responseCode == BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED) {
///mSharedPreferences.edit().putBoolean(getResources().getString(R.string.pref_remove_ads_key), true).commit();
///setAdFree(true);
setBoolInPref(this,"myPref",sku, true );
}
else {
//Log.d(TAG, "Other code" + responseCode);
// Handle any other error codes.
}
}
private void handlePurchase(Purchase purchase) {
if (purchase.getSku().equals(sku)) {
///mSharedPreferences.edit().putBoolean(getResources().getString(R.string.pref_remove_ads_key), true).commit();
///setAdFree(true);
setBoolInPref(this,"myPref",sku, true );
Toast.makeText(this, "Purchase done. you are now a premium member.", Toast.LENGTH_SHORT).show();
}
}
private Boolean getBoolFromPref(Context context, String prefName, String constantName) {
SharedPreferences pref = context.getSharedPreferences(prefName, 0); // 0 - for private mode
return pref.getBoolean(constantName, false);
}
private void setBoolInPref(Context context,String prefName, String constantName, Boolean val) {
SharedPreferences pref = context.getSharedPreferences(prefName, 0); // 0 - for private mode
SharedPreferences.Editor editor = pref.edit();
editor.putBoolean(constantName, val);
//editor.commit();
editor.apply();
// Update 2015: Android recommends the use of apply() now over commit(),
// because apply() operates on a background thread instead of storing the persistent data immediately,
// and possible blocking the main thread.
}
}
3 problems
You have enabled upgrade button by default and don't have null check in upgradeAction method
You are comparing strings by == instead of .equals in java
You are setting upgrade onclick listener 2 times
the 'mSkuDetails' attribute?
if (skuDetails.getSku() == sku){
mSkuDetails = skuDetails;
upgradeButton.setEnabled(true);
upgradeButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
BillingFlowParams billingFlowParams = BillingFlowParams
.newBuilder()
.setSkuDetails(mSkuDetails)
.build();
billingClient.launchBillingFlow(UpgradeActivity.this, billingFlowParams);
}
});
}
a. Verify the sku is the same as product code set in Google Play In App Billing section.
b. Debug through the code to see that null is not passed as sku in the call to launch billing flow.
c. Refresh Play Store cache in the device your are testing

I need help in uploading large files in nodejs

I am creating realtime andoid chat app in java, backend in nodejs and mongodb.
I want to ask
How to save chat in mongodb?
How to upload lager files in nodejs or mongodb? I cann't upload file more than 100kb in this.
this is the code.
ChatActivity.java
package com.example.chat;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Base64;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.AdView;
import com.google.android.gms.ads.MobileAds;
import com.google.android.gms.ads.initialization.InitializationStatus;
import com.google.android.gms.ads.initialization.OnInitializationCompleteListener;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
public class ChatActivity extends AppCompatActivity implements TextWatcher {
AdView mAdView;
private String name;
private WebSocket webSocket;
private String SERVER_PATH = "ws://group2all.com:50000/";
private EditText messageEdit;
private View sendBtn, pickImgBtn;
private RecyclerView recyclerView;
private int IMAGE_REQUEST_ID = 1;
private MessageAdapter messageAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
MobileAds.initialize(this, new OnInitializationCompleteListener() {
#Override
public void onInitializationComplete(InitializationStatus initializationStatus) {
}
});
mAdView = findViewById(R.id.adView4);
AdRequest adRequest = new AdRequest.Builder().build();
mAdView.loadAd(adRequest);
name = getIntent().getStringExtra("name");
initiateSocketConnection();
}
private void initiateSocketConnection() {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(SERVER_PATH).build();
webSocket = client.newWebSocket(request, new SocketListener());
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
String string = s.toString().trim();
if (string.isEmpty()) {
resetMessageEdit();
} else {
sendBtn.setVisibility(View.VISIBLE);
pickImgBtn.setVisibility(View.INVISIBLE);
}
}
private void resetMessageEdit() {
messageEdit.removeTextChangedListener(this);
messageEdit.setText("");
sendBtn.setVisibility(View.INVISIBLE);
pickImgBtn.setVisibility(View.VISIBLE);
messageEdit.addTextChangedListener(this);
}
private class SocketListener extends WebSocketListener {
#Override
public void onOpen(WebSocket webSocket, Response response) {
super.onOpen(webSocket, response);
runOnUiThread(() -> {
Toast.makeText(ChatActivity.this,
"Socket Connection Successful!",
Toast.LENGTH_SHORT).show();
initializeView();
});
}
#Override
public void onMessage(WebSocket webSocket, String text) {
super.onMessage(webSocket, text);
runOnUiThread(() -> {
try {
JSONObject jsonObject = new JSONObject(text);
jsonObject.put("isSent", false);
messageAdapter.addItem(jsonObject);
recyclerView.smoothScrollToPosition(messageAdapter.getItemCount() - 1);
} catch (JSONException e) {
e.printStackTrace();
}
});
}
}
private void initializeView() {
messageEdit = findViewById(R.id.messageEdit);
sendBtn = findViewById(R.id.sendBtn);
pickImgBtn = findViewById(R.id.pickImgBtn);
recyclerView = findViewById(R.id.recyclerView);
messageAdapter = new MessageAdapter(getLayoutInflater());
recyclerView.setAdapter(messageAdapter);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
messageEdit.addTextChangedListener(this);
sendBtn.setOnClickListener(v -> {
JSONObject jsonObject = new JSONObject();
try {
jsonObject.put("name", name);
jsonObject.put("message", messageEdit.getText().toString());
webSocket.send(jsonObject.toString());
jsonObject.put("isSent", true);
messageAdapter.addItem(jsonObject);
recyclerView.smoothScrollToPosition(messageAdapter.getItemCount() - 1);
resetMessageEdit();
} catch (JSONException e) {
e.printStackTrace();
}
});
pickImgBtn.setOnClickListener(v -> {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
intent.setType("video/*");
startActivityForResult(Intent.createChooser(intent, "Pick image"),
IMAGE_REQUEST_ID);
});
}
#Override
protected void onActivityResult(int requestCode, int resultCode, #Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == IMAGE_REQUEST_ID && resultCode == RESULT_OK) {
try {
InputStream is = getContentResolver().openInputStream(data.getData());
Bitmap image = BitmapFactory.decodeStream(is);
sendImage(image);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
private void sendImage(Bitmap image) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
String base64String = Base64.encodeToString(outputStream.toByteArray(),
Base64.DEFAULT);
JSONObject jsonObject = new JSONObject();
try {
jsonObject.put("name", name);
jsonObject.put("image", base64String);
webSocket.send(jsonObject.toString());
jsonObject.put("isSent", true);
messageAdapter.addItem(jsonObject);
recyclerView.smoothScrollToPosition(messageAdapter.getItemCount() - 1);
} catch (JSONException e) {
e.printStackTrace();
}
}
}
This is server.js file
const SocketServer = require('websocket').server
const http = require('http')
const express = require('express')
const app = express()
const mongoClient = require('mongodb').MongoClient
const server = http.createServer((req, res) => {})
const url = "mongodb://localhost:27017"
server.listen(50000, ()=>{
console.log("Listening on port 50000...")
})
app.listen(60000, () => {
console.log("Listening on port 60000...")
})
app.use(express.json())
mongoClient.connect(url, (err, db) => {
if (err) {
console.log("Error while connecting mongo client")
} else {
const myDb = db.db('myDb')
const collection = myDb.collection('myTable')
app.post('/signup', (req, res) => {
const newUser = {
name: req.body.name,
email: req.body.email,
password: req.body.password
}
const query = { email: newUser.email }
collection.findOne(query, (err, result) => {
if (result == null) {
collection.insertOne(newUser, (err, result) => {
res.status(200).send()
})
} else {
res.status(400).send()
}
})
})
app.post('/login', (req, res) => {
const query = {
email: req.body.email,
password: req.body.password
}
collection.findOne(query, (err, result) => {
if (result != null) {
const objToSend = {
name: result.name,
email: result.email
}
res.status(200).send(JSON.stringify(objToSend))
} else {
res.status(404).send()
}
})
})
}
})
wsServer = new SocketServer({httpServer:server})
const connections = []
wsServer.on('request', (req) => {
const connection = req.accept()
console.log('new connection')
connections.push(connection)
connection.on('message', (mes) => {
connections.forEach(element => {
if (element != connection)
element.sendUTF(mes.utf8Data)
})
})
connection.on('close', (resCode, des) => {
console.log('connection closed')
connections.splice(connections.indexOf(connection), 1)
})
})
One more thing i want to ask in js file
Can i use one port in this at 60000?If yes how?
Please tell me code to better understanding.

Categories

Resources