I'm trying to find the way to make USSD requests in Android. I found this - http://commandus.com/blog/?p=58 .
I added all needed files to my project.
USSDDumbExtendedNetworkService.java:
package com.android.ussdcodes;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.os.IBinder;
import android.os.PatternMatcher;
import android.os.RemoteException;
import android.util.Log;
import com.android.internal.telephony.IExtendedNetworkService;
import com.android.ussdcodes.R;
/**
* Service implements IExtendedNetworkService interface.
* USSDDumbExtendedNetworkService
* Service must have name "com.android.ussd.IExtendedNetworkService" of the intent declared
* in the Android manifest file so com.android.phone.PhoneUtils class bind
* to this service after system rebooted.
* Please note service is loaded after system reboot!
* Your application must check is system rebooted.
* #see Util#syslogHasLine(String, String, String, boolean)
*/
public class USSDDumbExtendedNetworkService extends Service {
public static final String TAG = "CommandusUSSDExtNetSvc";
public static final String LOG_STAMP = "*USSDTestExtendedNetworkService bind successfully*";
public static final String URI_SCHEME = "ussdcodes";
public static final String URI_AUTHORITY = "android.com";
public static final String URI_PATH = "/";
public static final String URI_PAR = "return";
public static final String URI_PARON = "on";
public static final String URI_PAROFF = "off";
public static final String MAGIC_ON = ":ON;)";
public static final String MAGIC_OFF = ":OFF;(";
public static final String MAGIC_RETVAL = ":RETVAL;(";
private static boolean mActive = false;
private static CharSequence mRetVal = null;
private Context mContext = null;
private String msgUssdRunning = "USSD running...";
final BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_INSERT.equals(intent.getAction())) {
mContext = context;
if (mContext != null) {
msgUssdRunning = mContext.getString(R.string.USSD_run);
mActive = true;
Log.d(TAG, "activate");
}
} else if (Intent.ACTION_DELETE.equals(intent.getAction())) {
mContext = null;
mActive = false;
Log.d(TAG, "deactivate");
}
}
};
private final IExtendedNetworkService.Stub mBinder = new IExtendedNetworkService.Stub() {
#Override
public void setMmiString(String number) throws RemoteException {
Log.d(TAG, "setMmiString: " + number);
}
#Override
public CharSequence getMmiRunningText() throws RemoteException {
Log.d(TAG, "getMmiRunningText: " + msgUssdRunning);
return msgUssdRunning;
}
#Override
public CharSequence getUserMessage(CharSequence text)
throws RemoteException {
if (MAGIC_ON.contentEquals(text)) {
mActive = true;
Log.d(TAG, "control: ON");
return text;
} else {
if (MAGIC_OFF.contentEquals(text)) {
mActive = false;
Log.d(TAG, "control: OFF");
return text;
} else {
if (MAGIC_RETVAL.contentEquals(text)) {
mActive = false;
Log.d(TAG, "control: return");
return mRetVal;
}
}
}
if (!mActive) {
Log.d(TAG, "getUserMessage deactivated: " + text);
return text;
}
String s = text.toString();
// store s to the !
Uri uri = new Uri.Builder()
.scheme(URI_SCHEME)
.authority(URI_AUTHORITY)
.path(URI_PATH)
.appendQueryParameter(URI_PAR, text.toString())
.build();
sendBroadcast(new Intent(Intent.ACTION_GET_CONTENT, uri));
mActive = false;
mRetVal = text;
Log.d(TAG, "getUserMessage: " + text + "=" + s);
return null;
}
#Override
public void clearMmiString() throws RemoteException {
Log.d(TAG, "clearMmiString");
}
};
/**
* Put stamp to the system log when PhoneUtils bind to the service
* after Android has rebooted. Application must call {#link Util#syslogHasLine(String, String, String, boolean)} to
* check is phone rebooted or no. Without reboot phone application does not bind tom this service!
*/
#Override
public IBinder onBind(Intent intent) {
// Do not localize!
Log.i(TAG, LOG_STAMP);
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_INSERT);
filter.addAction(Intent.ACTION_DELETE);
filter.addDataScheme(URI_SCHEME);
filter.addDataAuthority(URI_AUTHORITY, null);
filter.addDataPath(URI_PATH, PatternMatcher.PATTERN_LITERAL);
registerReceiver(mReceiver, filter);
return mBinder;
}
public IBinder asBinder() {
Log.d(TAG, "asBinder");
return mBinder;
}
}
Manifest:
<receiver android:name="com.android.ussdcodes.BootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<service
android:name=".USSDDumbExtendedNetworkService" >
<intent-filter android:icon="#drawable/ic_launcher">
<action android:name="com.android.ussd.IExtendedNetworkService" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
BootReceiver.java:
package com.android.ussdcodes;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public class BootReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
Log.d("USSDService", context.getString(R.string.service_started));
context.startService(new Intent(context,USSDDumbExtendedNetworkService.class));
}
}
So now the service starts after boot complete.
And my question is how I have to send USSD request and get responce with service?
Thanks!)
Well, I have found the answer.
I just put the link on my gist.
Deactivate messages
USSDDumbExtendedNetworkService.mActive = false;
Send USSD:
Intent launchCall = new Intent(Intent.ACTION_CALL,
Uri.parse("tel:" + Uri.encode(ussdcode)));
launchCall.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
launchCall.addFlags(Intent.FLAG_FROM_BACKGROUND);
startActivity(launchCall);
Activate messages again
USSDDumbExtendedNetworkService.mActive = true;
USSDDumbExtendedNetworkService.mRetVal = null;
Related
I'm developing a Unity app which will be able to interface with the Android app Termux via the Intent described here:
https://github.com/termux/termux-app/wiki/RUN_COMMAND-Intent#advance-examples
In my Unity-Android plugin I have two classes:
PluginInstance.java:
package com.saricden.exd2termux;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
import com.termux.shared.termux.TermuxConstants.TERMUX_APP.RUN_COMMAND_SERVICE;
import com.unity3d.player.UnityPlayer;
import java.util.Arrays;
public class PluginInstance {
private static Activity unityActivity;
public static void receiveUnityActivity(Activity tActivity) {
unityActivity = tActivity;
}
private String unityGameObjectName = null;
public void debugLog(String msg) {
UnityPlayer.UnitySendMessage("DebugParent/DebugListener", "LogMessage", "BIGTEST (" + msg + ")");
}
public void loginToTermux() {
Intent intent = new Intent();
intent.setClassName("com.termux", "com.termux.app.RunCommandService");
intent.setAction("com.termux.RUN_COMMAND");
intent.putExtra("com.termux.RUN_COMMAND_PATH", "/data/data/com.termux/files/usr/bin/login");
intent.putExtra("com.termux.RUN_COMMAND_WORKDIR", "/data/data/com.termux/files/home");
intent.putExtra("com.termux.RUN_COMMAND_BACKGROUND", true);
intent.putExtra("com.termux.RUN_COMMAND_SESSION_ACTION", "0");
unityActivity.startForegroundService(intent);
}
public void sendToUnity(String msg) {
UnityPlayer.UnitySendMessage(unityGameObjectName, "ReceiveTermuxOutput", msg);
}
public void execTermuxCMD(String cmd) {
debugLog("Started command...");
String[] cmdParts = cmd.split(" ");
String cmdRoot = cmdParts[0];
String[] cmdArgs = Arrays.copyOfRange(cmdParts, 1, cmdParts.length);
Intent intent = new Intent();
intent.setClassName("com.termux", "com.termux.app.RunCommandService");
intent.setAction("com.termux.RUN_COMMAND");
intent.putExtra("com.termux.RUN_COMMAND_PATH", "/data/data/com.termux/files/usr/bin/" + cmdRoot);
intent.putExtra("com.termux.RUN_COMMAND_ARGUMENTS", cmdArgs);
intent.putExtra("com.termux.RUN_COMMAND_WORKDIR", "/data/data/com.termux/files/home");
intent.putExtra("com.termux.RUN_COMMAND_BACKGROUND", true);
intent.putExtra("com.termux.RUN_COMMAND_SESSION_ACTION", "0");
Intent pluginResultsServiceIntent = new Intent(unityActivity, PluginResultsService.class);
int executionId = PluginResultsService.getNextExecutionId();
pluginResultsServiceIntent.putExtra(PluginResultsService.EXTRA_EXECUTION_ID, executionId);
PendingIntent pendingIntent = PendingIntent.getService(unityActivity, executionId, pluginResultsServiceIntent, PendingIntent.FLAG_ONE_SHOT);
intent.putExtra(RUN_COMMAND_SERVICE.EXTRA_PENDING_INTENT, pendingIntent);
unityActivity.startForegroundService(intent);
debugLog("Started foreground service, with run_background 'true'");
}
public void setUnityGameObjectName(String name) {
unityGameObjectName = name;
}
}
PluginResultsService.java:
package com.saricden.exd2termux;
import android.app.IntentService;
import android.content.Intent;
import androidx.annotation.Nullable;
import com.unity3d.player.UnityPlayer;
public class PluginResultsService extends IntentService {
public static final String EXTRA_EXECUTION_ID = "execution_id";
private static int EXECUTION_ID = 1000;
public static final String PLUGIN_SERVICE_LABEL = "PluginResultsService";
private static final String LOG_TAG = "PluginResultsService";
public PluginResultsService(){
super(PLUGIN_SERVICE_LABEL);
}
#Override
protected void onHandleIntent(#Nullable Intent intent) {
UnityPlayer.UnitySendMessage("DebugParent/DebugListener", "LogMessage", "onHandleIntent!!!");
}
public static synchronized int getNextExecutionId() {
EXECUTION_ID++;
UnityPlayer.UnitySendMessage("DebugParent/DebugListener", "LogMessage", "Get next execution_id (" + EXECUTION_ID + ")");
return EXECUTION_ID;
}
}
Then finally, my Unity MonoBehaviour class that kicks it all off:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Vuplex.WebView;
public class TerminalManager : MonoBehaviour
{
private AndroidJavaClass unityClass;
private AndroidJavaObject unityActivity;
private AndroidJavaObject _pluginInstance;
private WebViewPrefab webViewPrefab;
void InitializePlugin(string pluginName) {
unityClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
unityActivity = unityClass.GetStatic<AndroidJavaObject>("currentActivity");
_pluginInstance = new AndroidJavaObject(pluginName);
if (_pluginInstance == null) {
Debug.Log("!!!!! >> PLUGIN INSTANCE ERROR << !!!!!");
}
_pluginInstance.CallStatic("receiveUnityActivity", unityActivity);
_pluginInstance.Call("loginToTermux");
}
async void Start() {
webViewPrefab = GetComponent<WebViewPrefab>();
InitializePlugin("com.saricden.exd2termux.PluginInstance");
_pluginInstance.Call("setUnityGameObjectName", transform.root.name + "/" + transform.name);
await webViewPrefab.WaitUntilInitialized();
webViewPrefab.WebView.MessageEmitted += (sender, eArgs) => {
Debug.Log("Dispatching Termux command...");
Debug.Log(eArgs.Value);
_pluginInstance.Call("execTermuxCMD", eArgs.Value);
};
}
public void ReceiveTermuxOutput(string res) {
Debug.Log("Receiving Termux response...");
webViewPrefab.WebView.ExecuteJavaScript("appendResponse(`" + res + "`)");
}
}
I can confirm the Termux commands are running. I have tested by running mkdir testFolder, then opening Termux and checking the home directory. The "testFolder" was successfully created.
The problem: PluginResultsService -> onHandleIntent never gets called!
How can I go about further debugging this? And/or does anyone have any suggestions as to what the problem might be?
I figured out the solution.
I was defining:
<service android:name=".PluginResultsService" android:exported="false" />
Inside my Unity app's top-level AndroidManifest.xml, but really it needed to be defined in the plugin-level AndroidManifest.xml!
Let's goo!
In my app, I am relying on onUserLeaveHint() method when the user presses the home button, but this method is also being called when you are starting multi window mode in android 7.0 by long pressing "recents button" (which I don't want to perform same thing that I do when home button been pressed). So I want to know if there is a way to detect which is which. Cheers!
Note: onMultiWindowModeChanged() being called after onUserLeaveHint()
I think this is what you're looking for.
HomeWatcher mHomeWatcher = new HomeWatcher(this);
mHomeWatcher.setOnHomePressedListener(new OnHomePressedListener() {
#Override
public void onHomePressed() {
// do something here...
}
#Override
public void onHomeLongPressed() {
}
});
mHomeWatcher.startWatch();
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.util.Log;
public class HomeWatcher {
static final String TAG = "hg";
private Context mContext;
private IntentFilter mFilter;
private OnHomePressedListener mListener;
private InnerRecevier mRecevier;
public HomeWatcher(Context context) {
mContext = context;
mFilter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
}
public void setOnHomePressedListener(OnHomePressedListener listener) {
mListener = listener;
mRecevier = new InnerRecevier();
}
public void startWatch() {
if (mRecevier != null) {
mContext.registerReceiver(mRecevier, mFilter);
}
}
public void stopWatch() {
if (mRecevier != null) {
mContext.unregisterReceiver(mRecevier);
}
}
class InnerRecevier extends BroadcastReceiver {
final String SYSTEM_DIALOG_REASON_KEY = "reason";
final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions";
final String SYSTEM_DIALOG_REASON_LONG_PRESS = "assist";
final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) {
String reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY);
if (reason != null) {
Log.e(TAG, "action:" + action + ",reason:" + reason);
if (mListener != null) {
if (reason.equals(SYSTEM_DIALOG_REASON_HOME_KEY)) {
mListener.onHomePressed();
} else if (reason.equals(SYSTEM_DIALOG_REASON_LONG_PRESS)) {
mListener.onHomeLongPressed();
}
}
}
}
}
}
}
public interface OnHomePressedListener {
public void onHomePressed();
public void onHomeLongPressed();
}
I'm using the Google-API to get the count of unread E-Mails and some Events from the Google-Calender. I used the Quick-Start Sample Code from Google and it worked fine(asks for scope-permissions if not set and so on). So I formed it to a Service and a Auth-Activity for the user to change their Accounts. My problem is that the User don't get asked for Authorization anymore. The App still uses the right Account and if I select an Account which is authorized for my App it still work but if I select a new Account no Authorization-form is being shown.
Google AuthUtil Error:
09-19 13:13:54.114: D/DEBUG_TEST(7072): ERROR: UserRecoverableErrorNeedPermission
Here is my Activity that changes the Account. Every time a user selects an Google-Account the Static-Variable "gAccountDaten"(GoogleAccountCredential Object) got updated.
import android.accounts.AccountManager;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.util.Log;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.Toast;
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential;
import com.google.api.client.util.ExponentialBackOff;
import com.google.api.services.calendar.CalendarScopes;
import com.google.api.services.gmail.GmailScopes;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import pub.devrel.easypermissions.AfterPermissionGranted;
import pub.devrel.easypermissions.EasyPermissions;
public class GoogleAuthActivity extends Activity implements EasyPermissions.PermissionCallbacks {
static GoogleAccountCredential gAccountDaten;
static final int REQUEST_GOOGLE_PLAY_SERVICES = 1002;
static final int REQUEST_ACCOUNT_PICKER = 1000;
static final int REQUEST_AUTHORIZATION = 1001;
static final int REQUEST_PERMISSION_GET_ACCOUNTS = 1003;
private static final String PREF_ACCOUNT_NAME = "accountName";
private static final String[] SCOPES = { GmailScopes.GMAIL_LABELS, GmailScopes.GMAIL_READONLY , GmailScopes.MAIL_GOOGLE_COM, CalendarScopes.CALENDAR_READONLY};
private Boolean verbunden = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.googleauthactivitylayout);
gAccountDaten = GoogleAccountCredential.usingOAuth2(getApplicationContext(), Arrays.asList(SCOPES)).setBackOff(new ExponentialBackOff());
chooseAccount();
}
private String getGoogleAccountNameSettings(){
String toReturn = getPreferences(Context.MODE_PRIVATE)
.getString(PREF_ACCOUNT_NAME, null);
Log.d("ACCOUNT_DEBUG", "FUNC: getGoogleAccountNameSettings --> " + toReturn);
return toReturn;
}
private boolean setGoogleAccountNameSettings(String accountName){
try{
SharedPreferences settings = getPreferences(Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
editor.putString(PREF_ACCOUNT_NAME, accountName);
editor.apply();
Log.d("ACCOUNT_DEBUG", "FUNC: setGoogleAccountNameSettings --> " + accountName);
}catch (Exception exc){
Log.e("ACCOUNT_DEBUG", "FUNC: setGoogleAccountNameSettings --> ERROR");
return false;
}finally {
Log.e("ACCOUNT_DEBUG", "FUNC: setGoogleAccountNameSettings --> ERROR");
return true;
}
}
private boolean removeGoogleAccountNameSettings(){
try{
SharedPreferences settings = getPreferences(Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
editor.remove(PREF_ACCOUNT_NAME);
editor.apply();
GoogleService.EMAIL_COUNT = 0;
GoogleService.lstKalender.clear();
Log.d("ACCOUNT_DEBUG", "FUNC: removeGoogleAccountNameSettings --> " + "rennt");
}catch (Exception exc){
Log.e("ACCOUNT_DEBUG", "FUNC: removeGoogleAccountNameSettings --> ERROR");
return false;
}finally {
Log.e("ACCOUNT_DEBUG", "FUNC: removeGoogleAccountNameSettings --> ERROR");
return true;
}
}
#AfterPermissionGranted(REQUEST_PERMISSION_GET_ACCOUNTS)
private void chooseAccount( ) {
removeGoogleAccountNameSettings();
if (EasyPermissions.hasPermissions(
this, android.Manifest.permission.GET_ACCOUNTS)) {
String accountName = getGoogleAccountNameSettings();
if (accountName != null) {
gAccountDaten.setSelectedAccountName(accountName);
this.startService(new Intent(this, GoogleService.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK));
Log.d("AUTH_DEBUG", "Already authed" + " | " + gAccountDaten.getScope());
//finish();
} else {
// Start a dialog from which the user can choose an account
startActivityForResult(
gAccountDaten.newChooseAccountIntent(),
REQUEST_ACCOUNT_PICKER);
}
} else {
// Request the GET_ACCOUNTS permission via a user dialog
EasyPermissions.requestPermissions(
this,
"This app needs to access your Google account .",
REQUEST_PERMISSION_GET_ACCOUNTS,
android.Manifest.permission.GET_ACCOUNTS);
}
}
#Override
protected void onActivityResult(
int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch(requestCode) {
case REQUEST_GOOGLE_PLAY_SERVICES:
if (resultCode != RESULT_OK) {
//mOutputText.setText(
// "This app requires Google Play Services. Please install " +
// "Google Play Services on your device and relaunch this app.");
} else {
//getResultsFromApi();
}
break;
case REQUEST_ACCOUNT_PICKER:
if (resultCode == RESULT_OK && data != null && data.getExtras() != null) {
String accountName =
data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
if (accountName != null) {
setGoogleAccountNameSettings(accountName);
gAccountDaten.setSelectedAccountName(accountName);
Log.d("AUTH_DEBUG", "Authed after Picker -->" + accountName + " | " + gAccountDaten.getScope());
this.startService(new Intent(this, GoogleService.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK));
finish();
}
}
break;
case REQUEST_AUTHORIZATION:
if (resultCode == RESULT_OK) {
Log.d("AUTH_DEBUG", "started aufter authorization -->" + resultCode + " | " + gAccountDaten.getScope());
this.startService(new Intent(this, GoogleService.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK));
finish();
}
break;
}
}
/////PERMISSION CALLBACKS
#Override
public void onRequestPermissionsResult(int requestCode,
#NonNull String[] permissions,
#NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
EasyPermissions.onRequestPermissionsResult(
requestCode, permissions, grantResults, this);
}
/**
* Callback for when a permission is granted using the EasyPermissions
* library.
* #param requestCode The request code associated with the requested
* permission
* #param list The requested permission list. Never null.
*/
#Override
public void onPermissionsGranted(int requestCode, List<String> list) {
// Do nothing.
}
/**
* Callback for when a permission is denied using the EasyPermissions
* library.
* #param requestCode The request code associated with the requested
* permission
* #param list The requested permission list. Never null.
*/
#Override
public void onPermissionsDenied(int requestCode, List<String> list) {
Log.d("DEBUG_TEST", "NO PERM");
}
}
And here is my Google-Service Code. It will gather the information from the google api every 60 seconds and write it to static variables.
import android.*;
import android.app.Dialog;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import android.widget.RemoteViews;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.api.client.extensions.android.http.AndroidHttp;
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential;
import com.google.api.client.googleapis.extensions.android.gms.auth.GooglePlayServicesAvailabilityIOException;
import com.google.api.client.googleapis.extensions.android.gms.auth.UserRecoverableAuthIOException;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.client.util.ExponentialBackOff;
import com.google.api.services.calendar.Calendar;
import com.google.api.services.calendar.CalendarScopes;
import com.google.api.services.calendar.model.CalendarList;
import com.google.api.services.calendar.model.CalendarListEntry;
import com.google.api.services.calendar.model.Event;
import com.google.api.services.calendar.model.EventDateTime;
import com.google.api.services.calendar.model.Events;
import com.google.api.services.gmail.GmailScopes;
import com.google.api.services.gmail.model.Label;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import pub.devrel.easypermissions.AfterPermissionGranted;
import pub.devrel.easypermissions.EasyPermissions;
public class GoogleService extends Service implements EasyPermissions.PermissionCallbacks{
GoogleAccountCredential gAccountDaten;
static final int REQUEST_ACCOUNT_PICKER = 1000;
static final int REQUEST_AUTHORIZATION = 1001;
static final int REQUEST_GOOGLE_PLAY_SERVICES = 1002;
static final int REQUEST_PERMISSION_GET_ACCOUNTS = 1003;
private static final String PREF_ACCOUNT_NAME = "accountName";
static String testj = "";
static ArrayList<Kalender> lstKalender = new ArrayList<>();
static int EMAIL_COUNT = 0;
//GOOGLE BERECHTIGUNGEN
private static final String[] SCOPES = { GmailScopes.GMAIL_LABELS, GmailScopes.GMAIL_READONLY , GmailScopes.MAIL_GOOGLE_COM, CalendarScopes.CALENDAR_READONLY};
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("DEBUG_TEST", "onStartCommand");
gAccountDaten = GoogleAuthActivity.gAccountDaten;
if(gAccountDaten == null){
Log.d("DEBUG_TEST", "gAccountDaten == NULL!");
return 0;
}
getResultsFromApi();
final Handler handler = new Handler();
Runnable runnable = new Runnable() {
#Override
public void run() {
getResultsFromApi();
handler.postDelayed(this, 60000);
}
};
handler.postDelayed(runnable, 60000);
return START_STICKY;
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onPermissionsGranted(int requestCode, List<String> perms) {
}
#Override
public void onPermissionsDenied(int requestCode, List<String> perms) {
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
}
//HELFER FUNKTIONEN
private void getResultsFromApi() {
if (! isGooglePlayServicesAvailable()) {
acquireGooglePlayServices();
} else if (gAccountDaten.getSelectedAccountName() == null) {
Log.d("DEBUG_TEST", "ChooseAccount");
} else if (! isDeviceOnline()) {
//Gerät Offline
} else {
Log.d("DEBUG_TEST", "MakeRequestTask -->" + gAccountDaten.getSelectedAccountName());
new MakeRequestTask(gAccountDaten).execute();
}
}
//CHECK: GERÄT ONLINE ?
private boolean isDeviceOnline() {
ConnectivityManager connMgr =
(ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
return (networkInfo != null && networkInfo.isConnected());
}
//CHECK: GOOGLE SERVICE ONLINE ?
private boolean isGooglePlayServicesAvailable() {
GoogleApiAvailability apiAvailability =
GoogleApiAvailability.getInstance();
final int connectionStatusCode =
apiAvailability.isGooglePlayServicesAvailable(this);
return connectionStatusCode == ConnectionResult.SUCCESS;
}
private void acquireGooglePlayServices() {
GoogleApiAvailability apiAvailability =
GoogleApiAvailability.getInstance();
final int connectionStatusCode =
apiAvailability.isGooglePlayServicesAvailable(this);
}
/////////////////////////MAKE REQUEST KLASSE////////////////////////////////////
private class MakeRequestTask extends AsyncTask<Void, Void, List<String>> {
private com.google.api.services.gmail.Gmail mService = null;
private com.google.api.services.calendar.Calendar mServiceKalender = null;
private Exception mLastError = null;
public MakeRequestTask(GoogleAccountCredential credential) {
HttpTransport transport = AndroidHttp.newCompatibleTransport();
JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
HttpTransport transport2 = AndroidHttp.newCompatibleTransport();
JsonFactory jsonFactory2 = JacksonFactory.getDefaultInstance();
mService = new com.google.api.services.gmail.Gmail.Builder(
transport, jsonFactory, credential)
.setApplicationName("Gmail API Android Quickstart")
.build();
mServiceKalender = new com.google.api.services.calendar.Calendar.Builder(
transport2, jsonFactory2, credential)
.setApplicationName("Google Calendar API Android Quickstart")
.build();
Log.d("DEBUG_TEST", "mService und mServiceKalender Objecte instanzieren --> " + credential.getSelectedAccountName());
}
/**
* Background task to call Gmail API.
* #param params no parameters needed for this task.
*/
#Override
protected List<String> doInBackground(Void... params) {
try {
return getDataFromApi();
} catch (Exception e) {
mLastError = e;
cancel(true);
return null;
}
}
/**
* Fetch a list of Gmail labels attached to the specified account.
* #return List of Strings labels.
* #throws IOException
*/
private List<String> getDataFromApi() throws IOException {
// Get the labels in the user's account.
String user = "me";
List<String> labels = new ArrayList<String>();
Label label = mService.users().labels().get(user, "INBOX").execute();
Log.d("DEBUG_TEST", "getDataFromApi() --> " + user );
//EMAIL_COUNT = label.getMessagesUnread();
EMAIL_COUNT = label.getMessagesTotal();
labels.add("E-Mails: " + EMAIL_COUNT);
lstKalender.clear();
String pageToken = null;
do {
CalendarList calendarList = mServiceKalender.calendarList().list().setPageToken(pageToken).execute();
List<CalendarListEntry> items = calendarList.getItems();
for (CalendarListEntry calendarListEntry : items) {
String KALENDERNAME = calendarListEntry.getSummary();
String KALENDER_ID = calendarListEntry.getId();
Kalender neuerKalender = new Kalender();
neuerKalender.setKalenderName(KALENDERNAME);
neuerKalender.setKalenderId(KALENDER_ID);
Log.d("KALENDER_DEBUG", "Kalender: " + KALENDERNAME + "wurde angelegt! ID: " + KALENDER_ID);
String pageToken2 = null;
do {
Events events = mServiceKalender.events().list(KALENDER_ID).setPageToken(pageToken2).execute();
List<Event> items2 = events.getItems();
for (Event event : items2) {
String calName = event.getSummary();
EventDateTime calStart = event.getStart();
EventDateTime calEnde = event.getEnd();
String calLocation = event.getLocation();
String calID = event.getId();
if(neuerKalender.addEvent(calName,calStart,calEnde,calLocation,calID)){
//Log.d("KALENDER_DEBUG", "Event: " + calName + " wurde zum Kalender " + KALENDERNAME + " hinzugefügt!");
}
}
pageToken2 = events.getNextPageToken();
} while (pageToken2 != null);
lstKalender.add(neuerKalender);
Log.d("KALENDER_DEBUG", "Kalender: " + neuerKalender.getKalenderName() + " durchgelaufen und zur Globalen Liste hinzugefügt!");
}
pageToken = calendarList.getNextPageToken();
} while (pageToken != null);
DaWidgetProvider.updateEventList(getBaseContext());
UpdateService updateService = new UpdateService();
updateService.buildUpdate(getBaseContext());
DaWidgetProvider.updateAllWidgets(getBaseContext());
return labels;
}
#Override
protected void onPreExecute() {
}
#Override
protected void onPostExecute(List<String> output) {
if (output == null || output.size() == 0) {
// mOutputText.setText("No results returned.");
} else {
//output.add(0, "Data retrieved using the Gmail API:");
//mOutputText.setText(TextUtils.join("\n", output));
//mProgress.dismiss();
Context mContext = getApplicationContext();
}
}
#Override
protected void onCancelled() {
if (mLastError != null) {
if (mLastError instanceof GooglePlayServicesAvailabilityIOException) {
Log.d("DEBUG_TEST", "ERROR: " + mLastError.getMessage());
} else if (mLastError instanceof UserRecoverableAuthIOException) {
Log.d("DEBUG_TEST", "ERROR: UserRecoverableError" + mLastError.getMessage());
} else {
Log.d("DEBUG_TEST", "ERROR: " + mLastError.getMessage());
}
} else {
Log.d("DEBUG_TEST", "abgebrochen");
}
}
}
}
Thanks,
J. Doe ;)
Nice that seems to be the problem. But ive got a problem to fix it, because my Google-Service and my Google-Account Picker Activity are differented. I tried to pass the mLastError to my Activity in an Intent and start the Authentication-Request there but i got the following Error:
09-19 13:44:18.508: E/AndroidRuntime(7959): java.lang.RuntimeException: Parcelable encountered IOException writing serializable object (name = com.google.api.client.googleapis.extensions.android.gms.auth.UserRecoverableAuthIOException)
And my Code changes in Google-Service and GoogleAuthActivity:
Google-Service:
protected void onCancelled() {
if (mLastError != null) {
if (mLastError instanceof GooglePlayServicesAvailabilityIOException) {
Log.d("DEBUG_TEST", "ERROR: " + mLastError.getMessage());
} else if (mLastError instanceof UserRecoverableAuthIOException) {
Log.d("DEBUG_TEST", "ERROR: UserRecoverableError" + mLastError.getCause().getMessage());
//KEINE BERECHTIGUNG GESETZT
[CODE CHANGES]
Intent startIntent = new Intent(getApplicationContext(), GoogleAuthActivity.class);
startIntent.putExtra("auththis", mLastError);
startIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(startIntent);
[/CODE CHANGES]
} else {
Log.d("DEBUG_TEST", "ERROR: " + mLastError.getMessage());
}
} else {
Log.d("DEBUG_TEST", "abgebrochen");
}
}
GoogleAuthActivity:
//EXTRA DATA???
if(getIntent().hasExtra("auththis")){
Intent edIntent = getIntent();
Bundle extras = edIntent.getExtras();
UserRecoverableAuthIOException mLastError = null;
mLastError = (UserRecoverableAuthIOException) extras.get("auththis");
startActivityForResult(
mLastError.getIntent(),
GoogleAuthActivity.REQUEST_AUTHORIZATION);
}else{
chooseAccount();
}
I am trying to develop an app that automatically reads a One Time Password from an incoming SMS. I have a class called SMS where I'm storing the currently read messages and a class called SMSRule that contains information about the possible OTP Senders.
The SMSRule also contains a Pattern variable that should be matched with the message to see if it really is the correct sender of the OTP.
Everything is going smooth except for the OTP extraction from the message. My SMS is containing the message, but when I try to match the Pattern against the message, my match.find() returns false.
Following are my files:
OTPAutoRead.java
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.telephony.SmsMessage;
import android.util.Log;
import android.widget.Toast;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import auro.widget.OTP.SMS.SMS;
import auro.widget.OTP.SMS.SMSRule;
/**
* Created on 26/8/16.
* #author Auro
* #since 1.0
*/
public class OTPAutoRead extends BroadcastReceiver {
private static final String LOG_TAG = OTPAutoRead.class.getCanonicalName();
private static final String mReadPermission = "android.permission.READ_SMS";
private static final String mReceivePermission = "android.permission.RECEIVE_SMS";
private List<SMSRule> smsRules;
private Context context;
private String OTP;
public OTPAutoRead() {throw new InstantiationError("Empty Constructor");}
public OTPAutoRead(#NonNull Context context, final List<SMSRule> smsRules) {
if (smsRules == null || smsRules.size() == 0) {
throw new AuroMissingRulesException("No SMS Rules Found");
}
this.smsRules = smsRules;
this.context = context;
CheckSMSReadPermission();
IntentFilter itf = new IntentFilter();
itf.addAction("android.provider.Telephony.SMS_RECEIVED");
itf.setPriority(999);
context.registerReceiver(this,itf);
}
private void CheckSMSReadPermission() {
PackageManager packageManager = context.getPackageManager();
String packageName = context.getPackageName();
int readPermission = packageManager.checkPermission(mReadPermission,packageName);
int receivePermission = packageManager.checkPermission(mReceivePermission, packageName);
boolean canRead = (readPermission == PackageManager.PERMISSION_GRANTED);
if (!canRead)
Toast.makeText(context,"Please enable SMS read permission for auto OTP read", Toast.LENGTH_SHORT).show();
}
#Override
public void onReceive(Context context, Intent intent) {
try {
if (intent.getAction().equals("android.provider.Telephony.SMS_RECEIVED"))
tryReceiveMessage(intent);
} catch (Exception e) {
Log.i(LOG_TAG, "Failed to read SMS", e);
}
}
private void tryReceiveMessage(Intent intent) {
Bundle bundle = intent.getExtras();
SmsMessage[] messages = null;
if (bundle != null) {
Object[] pdus = (Object[]) bundle.get("pdus");
if (pdus != null) {
messages = new SmsMessage[pdus.length];
for (int i = 0; i < messages.length; i++) {
messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);
String messageFrom = messages[i].getOriginatingAddress().toUpperCase();
String messageBody = messages[i].getMessageBody().toUpperCase();
Log.d(LOG_TAG, "Message is from: " + messageFrom + " and its content is: " + messageBody);
SMS sms = new SMS();
sms.setMessage(messageBody).setAddress(messageFrom);
processOTP(sms);
}
}
}
}
private void processOTP(SMS sms) {
int i;
for (i = 0; i < smsRules.size(); i ++) {
if (sms.getAddress().toUpperCase().contains(smsRules.get(i).getSender().toUpperCase())) {
Pattern pattern = smsRules.get(i).getOTPPattern();
System.out.println(pattern.pattern());
System.out.println(sms.getMessage());
Matcher matcher = pattern.matcher(sms.getMessage().toUpperCase());
if (matcher.find()) {
OTP = matcher.group(1);
Log.i(LOG_TAG,"Extracted OTP is: " + OTP);
Toast.makeText(context,"OTP RECEIVED IS: " + OTP,Toast.LENGTH_SHORT).show();
return;
} else
Log.d(LOG_TAG,"Failed to extract OTP");
}
}
}
public String getOTP() {
return OTP;
}
}
SMS.java
import android.support.annotation.NonNull;
/**
* Created on 26/8/16.
* #author Auro
* #since 1.0
*/
public class SMS {
private String mID;
private String mAddress;
private String mMessage;
private boolean isRead = false;
private String mTime;
public String getID() {
return mID;
}
public SMS setID(#NonNull final String ID) {
mID = ID;
return this;
}
public String getAddress() {
return mAddress;
}
public SMS setAddress(#NonNull final String Address) {
mAddress = Address;
return this;
}
public String getMessage() {
return mMessage;
}
public SMS setMessage(#NonNull final String Message) {
mMessage = Message;
return this;
}
public boolean isRead() {
return isRead;
}
public SMS setReadState(boolean ReadState) {
isRead = ReadState;
return this;
}
public String getTime() {
return mTime;
}
public SMS setTime(#NonNull String Time) {
mTime = Time;
return this;
}
}
SMSRule.java
import java.util.regex.Pattern;
/**
* Created on 26/8/16.
* #author Auro
* #since 1.0
*/
public class SMSRule {
private String mSender;
private String mGroupID;
private Pattern mOTPPattern;
private SMS sms;
public String getSender() {
return mSender;
}
public SMSRule setSender(final String sender) {
mSender = sender;
return this;
}
public String getGroupID() {
return mGroupID;
}
public SMSRule setGroupID(final String groupID) {
mGroupID = groupID;
return this;
}
public Pattern getOTPPattern() {
return mOTPPattern;
}
public SMSRule setOTPPattern(final Pattern otpPattern) {
mOTPPattern = otpPattern;
return this;
}
}
MainActivity.java
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.EditText;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import auro.widget.OTP.OTPAutoRead;
import auro.juspay.widget.OTP.SMS.SMSRule;
public class MainActivity extends AppCompatActivity {
private EditText editText;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
editText = (EditText) findViewById(R.id.editText);
SMSRule rule = new SMSRule();
Pattern pattern = Pattern.compile("One Time Password is (\\d{6})".toUpperCase());
rule.setSender("BANK-XYZ");
rule.setOTPPattern(pattern);
List<SMSRule> smsRules = new ArrayList<>();
smsRules.add(rule);
OTPAutoRead otpAutoRead = new OTPAutoRead(this,smsRules);
String OTP = otpAutoRead.getOTP();
if (OTP != null)
{
editText.setText(OTP);
}
}
}
Problem is, when I run this in Debug mode, I get matchFound = false. I also tried changing the regex to
Pattern pattern = Pattern.compile("One Time Password is \d{6}".toUpperCase());
It just wouldn't work
Following is a screenshot: -
When you convert the pattern string to uppercase in Pattern.compile("One Time Password is (\\d{6})".toUpperCase()), \d (matching a digit) turns into \D (matching a non-digit).
Use [0-9] instead of the \d so that the pattern means the same, or just remove toUpperCase() and write the exact pattern, or use a pattern case insensitive flag.
I know there are many questions like this on here, and I have looked at many of them, like this one and all of the questions it references, but I still have no solution. The problem is that I have followed the Android Developer guide on using the search widget, but when I open my app and hit the search widget, the onNewIntent method never gets called. I put a Log.e into the handleIntent method so I could check if everything was connecting before I moved on to the next part of the function, but it never gets called. I thought the issue might be in the MainActivity in the onCreateOptionsMenu, if maybe I'm not calling something right there. This is my first time trying to do this, and I'd really appreciate any kind of help on this issue, thanks.
Here is my SearchableActivity:
package com.gmd.referenceapplication;
import android.app.ListActivity;
import android.app.SearchManager;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ListView;
import android.widget.Toast;
public class SearchableActivity extends ListActivity {
DatabaseTable db= new DatabaseTable(this);
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_searchable);
// get the intent sent when user searches from search widget, verify the action and extract what is typed in
Intent intent = getIntent();
handleIntent(intent);
}
public void onNewIntent(Intent intent) {
setIntent(intent);
handleIntent(intent);
}
private void handleIntent(Intent intent) {
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
String query = intent.getStringExtra(SearchManager.QUERY);
Cursor c = db.getWordMatches(query, null);
Log.e("Search Operation", "Database searched");
System.out.println(R.string.app_name);
//still need to process Cursor and display results
}
}
public void onListItemClick(ListView l,
View v, int position, long id) {
// call detail activity for clicked entry
}
private void doSearch(String queryStr) {
// get a Cursor, prepare the ListAdapter
// and set it
}
}
Android Manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
package="com.gmd.referenceapplication">
<application
android:allowBackup="true"
android:icon="#mipmap/nist_logo"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/Theme.AppCompat.Light.NoActionBar">
<meta-data
android:name="android.app.default_searchable"
android:value="com.example.SearchActivity" />
<!-- main activity below, will be a home screen -->
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- //the physical constants overview page, will hopefully split into smaller categories -->
<activity
android:name=".FundamentalPhysicalConstants"
android:label="#string/app_name" />
<!-- searchable activity, performs searches and presents results -->
<activity android:name=".SearchableActivity">
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.searchable"
android:resource="#xml/searchable" />
</activity>
<provider
android:name=".ConstantSuggestionProvider"
android:authorities="query"
android:enabled="true"
android:exported="true" />
<activity android:name=".ExampleListView" />
<activity android:name=".CommonConstants" />
<activity android:name=".ElectromagneticConstants" />
<activity android:name=".AtomicNuclearConstants" />
<activity android:name=".PhysicoChemicalConstants" />
<activity android:name=".ReferenceLinks" />
<activity android:name=".SIBaseUnits"/>
</application>
</manifest>
Searchable xml:
<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:label="#string/app_name"
android:hint="#string/search_hint">
</searchable>
Database Table:
package com.gmd.referenceapplication;
import android.content.ContentValues;
import android.content.Context;
import android.content.res.Resources;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.text.TextUtils;
import android.util.Log;
import com.gmd.referenceapplication.R;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
/**
* Created by gmd on 6/8/2016.
*/
public class DatabaseTable {
public static final String TAG = "ConstantDatabase";
//the columns included in the table
public static final String COL_QUANTITY = "QUANTIY";
public static final String COL_VALUE = "VALUE";
public static final String COL_UNCERTAINTY = "UNCERTAINTY";
public static final String COL_UNIT = "UNIT";
private static final String DATABASE_NAME = "CONSTANTS";
private static final String FTS_VIRTUAL_TABLE = "FTS";
private static final int DATABASE_VERSION = 1;
private final DatabaseOpenHelper mDatabaseOpenHelper;
public DatabaseTable(Context context){
mDatabaseOpenHelper = new DatabaseOpenHelper(context);
}
private static class DatabaseOpenHelper extends SQLiteOpenHelper {
private final Context mHelperContext;
private SQLiteDatabase mDatabase;
private static final String FTS_TABLE_CREATE =
"CREATE VIRTUAL TABLE " + FTS_VIRTUAL_TABLE +
" USING fts3 (" +
COL_QUANTITY + ", " +
COL_VALUE + "," +
COL_UNCERTAINTY + "," +
COL_UNIT + ")";
public DatabaseOpenHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
Log.e("Database Operation", "Database Created / Opened...");
mHelperContext = context;
}
#Override
public void onCreate(SQLiteDatabase db) {
mDatabase = db;
mDatabase.execSQL(FTS_TABLE_CREATE);
Log.e("Database Operation", "Constants Table Created ...");
loadConstants();
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
+ newVersion + ", which will destroy all old data");
db.execSQL("DROP TABLE IF EXISTS " + FTS_VIRTUAL_TABLE);
onCreate(db);
}
// populating the virtual table with a string reading code
private void loadConstants() {
new Thread(new Runnable() {
public void run() {
try {
loadConstantss();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}).start();
Log.e("Loading", "Constants Table Populated ...");
}
private void loadConstantss() throws IOException {
final Resources resources = mHelperContext.getResources();
InputStream inputStream = resources.openRawResource(R.raw.txt);
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
try {
String line;
while ((line = reader.readLine()) != null) {
String[] strings = TextUtils.split(line, ",");
if (strings.length < 4) continue;
long id = addConstant(strings[0].trim(), strings[1].trim(), strings[2].trim(), strings[3].trim());
if (id < 0) {
Log.e(TAG, "unable to add word: " + strings[0].trim());
}
}
} finally {
reader.close();
}
}
public long addConstant(String quantity, String value, String uncertainty, String unit) {
ContentValues initialValues = new ContentValues();
initialValues.put(COL_QUANTITY, quantity);
initialValues.put(COL_VALUE, value);
initialValues.put(COL_UNCERTAINTY, uncertainty);
initialValues.put(COL_UNIT, unit);
return mDatabase.insert(FTS_VIRTUAL_TABLE, null, initialValues);
}
}
public Cursor getWordMatches(String query, String[] columns) {
String selection = COL_QUANTITY + " MATCH ?";
String[] selectionArgs = new String[] {query+"*"};
return query(selection, selectionArgs, columns);
}
public Cursor query(String selection, String[] selectionArgs, String[] columns) {
SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
builder.setTables(FTS_VIRTUAL_TABLE);
Cursor cursor = builder.query(mDatabaseOpenHelper.getReadableDatabase(),
columns, selection, selectionArgs, null, null, null);
if (cursor == null) {
return null;
} else if (!cursor.moveToFirst()) {
cursor.close();
return null;
}
return cursor;
}
}
MainActivity:
package com.gmd.referenceapplication;
import android.app.SearchManager;
import android.app.SearchableInfo;
import android.content.Context;
import android.content.Intent;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.SearchView.OnQueryTextListener;
import android.support.v7.widget.SearchView;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.support.v7.widget.SearchView;
public class MainActivity extends AppCompatActivity {
DatabaseTable dbHelper = new DatabaseTable(this);
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar myToolbar = (Toolbar) findViewById(R.id.my_toolbar);
setSupportActionBar(myToolbar);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.overflow, menu);
SearchView searchView = (SearchView) menu.findItem(R.id.search).getActionView();
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String query) {
//search button is pressed
dbHelper.getWordMatches(query,null);
return true;
}
#Override
public boolean onQueryTextChange(String newText) {
// User changed the text
return true;
}
});
SearchManager searchManager =
(SearchManager) getSystemService(Context.SEARCH_SERVICE);
searchView =
(SearchView) menu.findItem(R.id.search).getActionView();
searchView.setSearchableInfo(
searchManager.getSearchableInfo(getComponentName()));
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.constants:
startActivity(new Intent(MainActivity.this, FundamentalPhysicalConstants.class));
return true;
case R.id.joes_rules:
//go to rules
//startActivity(new Intent(MainActivity.this, ExampleListView.class));
return true;
case R.id.home:
//Go back to the home screen
return true;
case R.id.search:
//open search
return true;
case R.id.links:
//go to referencelinks
startActivity(new Intent(this, ReferenceLinks.class));
return true;
case R.id.base_units:
//go to baseunits
startActivity(new Intent(this, SIBaseUnits.class));
return true;
default:
// If we got here, the user's action was not recognized.
// Invoke the superclass to handle it.
return super.onOptionsItemSelected(item);
}
}
}