I'm developing an app based on the BluetoothChat example where I have the main activity with 2 buttons:
1. Find devices
2. Configuration
With the first button I search for devices with the same code as in the mentioned example.
With the second button, I enter to another activity where i have 3 seekbars with textviews under each one showing the current seekbar's value.
What I need is to modify BluetoothChat example's code to send an array with this 3 values instead of text.
This is the code of this activity where I'm trying to develop this.
How can I do this?
/*********************
*
* ONCREATE
*
********************/
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.configuration);
bar1 = (SeekBar)findViewById(R.id.fastbar);
bar1.setOnSeekBarChangeListener(this);
progress1 = (TextView)findViewById(R.id.out1);
bar2 = (SeekBar)findViewById(R.id.climbbar);
bar2.setOnSeekBarChangeListener(this);
progress2 = (TextView)findViewById(R.id.out2);
bar3 = (SeekBar)findViewById(R.id.platformbar);
bar3.setOnSeekBarChangeListener(this);
progress3 = (TextView)findViewById(R.id.out3);
mPreferences = PreferenceManager.getDefaultSharedPreferences(this);
/**Initialize the buffer for outgoing messages*/
GlobalVar.mOutStringBuffer = new StringBuffer("");
}
/*********************
*
* SEEKBAR
*
********************/
/**Method to get each seekbar's value and save it into a textview*/
#Override
public void onProgressChanged(SeekBar bar, int progress, boolean fromUser) {
switch (bar.getId()) {
case R.id.fastbar:
progress1.setText(progress + "%");
break;
case R.id.climbbar:
progress2.setText(progress + "%");
break;
case R.id.platformbar:
progress3.setText(progress + "%");
break;
}
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
seekBar.setSecondaryProgress(seekBar.getProgress());
}
/*********************
*
* ONRESUME
*
********************/
/**Load seekbar's values from sharedpreferences*/
#Override
protected void onResume() {
super.onResume();
loadedProgress1 = mPreferences.getInt(Key_PROGRESS_1, 50);
bar1.setProgress(loadedProgress1);
loadedProgress2 = mPreferences.getInt(Key_PROGRESS_2, 90);
bar2.setProgress(loadedProgress2);
loadedProgress3 = mPreferences.getInt(Key_PROGRESS_3, 70);
bar3.setProgress(loadedProgress3);
}
/*********************
*
* ONPAUSE
*
********************/
/**Save seekbar's values on sharedfreferences*/
#Override
protected void onPause() {
super.onPause();
SharedPreferences.Editor editor = mPreferences.edit();
savedProgress1 = bar1.getProgress();
editor.putInt(Key_PROGRESS_1, savedProgress1);
savedProgress2 = bar2.getProgress();
editor.putInt(Key_PROGRESS_2, savedProgress2);
savedProgress3 = bar3.getProgress();
editor.putInt(Key_PROGRESS_3, savedProgress3);
editor.commit();
}
/*********************
*
* OPTIONS
*
********************/
#Override
public boolean onCreateOptionsMenu(Menu menu) {
/**Inflate the menu; this adds items to the action bar if it is present.*/
getMenuInflater().inflate(R.menu.menu_conf, menu);
return true;
}
/**Options menu where I call the send function*/
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.send:
sendValues();
break;
case R.id.reset:
resetValues(null);
break;
}
return true;
}
/*********************
*
* SEND & RESET
*
********************/
public void sendValues() {
/**Seekbars values*/
String message1 = progress1.getText().toString();
String message2 = progress2.getText().toString();
String message3 = progress3.getText().toString();
/**Check that we're actually connected before trying anything*/
if (GlobalVar.mTransmission.getState() != GlobalVar.STATE_CONNECTED) {
Toast.makeText(this, R.string.not_connected, Toast.LENGTH_SHORT).show();
return;
}
/**
*
*HERE IS WHERE I'M TRYING TO CREATE A FUNCTION TO SEND THE 3 VALUES IN A STRING TYPE MESSAGE
*THIS CODE IS FROM THE SETUPCHAT() FUNCTION, SO IS WRONG IN THIS CASE
*/
/**Get the message bytes and tell the to write*/
byte[] send = message.getBytes();
GlobalVar.mTransmission.write(send);
/**Reset out string buffer to zero*/
GlobalVar.mOutStringBuffer.setLength(0);
}
public void resetValues() {
defaultValue1 = 50;
bar1.setProgress(defaultValue1);
defaultValue2 = 90;
bar2.setProgress(defaultValue1);
defaultValue3 = 70;
bar3.setProgress(defaultValue1);
}
This is the new function created:
public void sendValues() {
/**Send the seekbars values*/
String message = progress1.getText().toString()+":"+progress2.getText().toString()+":"+progress3.getText().toString();
String[] values = message.split(":");
for (String value : values) {
int number = Integer.valueOf(value);
}
/**Check that we're actually connected before trying anything*/
if (GlobalVar.mTransmission.getState() != GlobalVar.STATE_CONNECTED) {
Toast.makeText(this, R.string.not_connected, Toast.LENGTH_SHORT).show();
return;
}
/**Get the message bytes and tell the Transmission to write*/
byte[] send = message.getBytes();
GlobalVar.mTransmission.write(send);
/**Reset out string buffer to zero*/
GlobalVar.mOutStringBuffer.setLength(0);
}
You can send the values as a string with a regularExpression ex. "5:15:33" and then split it when you receive the string on ":" Like this.
String str = bar1.getText().toString()+":"+bar2.getText().toString()+":"+bar3.getText().toString();
Send str String to the reciver and, you can then get the int's out of the String array like this
String[] values = str.split(":");
for (String value : values) {
int number = Integer.valueOf(value);
}
Related
on line 370 i need a way to look for a string of 'f's' in the advertisement data from a TI CC2650. I found this template online but I'm looking for specific advertising data. Please let me know what string array I need to look at to find this.
package net.jmodwyer.beacon.beaconPoC;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentSender;
import android.content.SharedPreferences;
import android.location.Location;
import android.os.Bundle;
import android.os.RemoteException;
import android.preference.PreferenceManager;
import android.text.util.Linkify;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ScrollView;
import android.widget.Toast;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesClient;
import com.google.android.gms.location.LocationClient;
import net.jmodwyer.ibeacon.ibeaconPoC.R;
import org.altbeacon.beacon.Beacon;
import org.altbeacon.beacon.BeaconConsumer;
import org.altbeacon.beacon.BeaconManager;
import org.altbeacon.beacon.RangeNotifier;
import org.altbeacon.beacon.Region;
import org.altbeacon.beacon.utils.UrlBeaconUrlCompressor;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
/**
* Adapted from original code written by D Young of Radius Networks.
* #author dyoung, jodwyer
*
*/
public class ScanActivity extends Activity implements BeaconConsumer,
GooglePlayServicesClient.ConnectionCallbacks,
GooglePlayServicesClient.OnConnectionFailedListener {
// Constant Declaration
private static final String PREFERENCE_SCANINTERVAL = "scanInterval";
private static final String PREFERENCE_TIMESTAMP = "timestamp";
private static final String PREFERENCE_POWER = "power";
private static final String PREFERENCE_PROXIMITY = "proximity";
private static final String PREFERENCE_RSSI = "rssi";
private static final String PREFERENCE_MAJORMINOR = "majorMinor";
private static final String PREFERENCE_UUID = "uuid";
private static final String PREFERENCE_INDEX = "index";
private static final String PREFERENCE_LOCATION = "location";
private static final String PREFERENCE_REALTIME = "realTimeLog";
private static final String MODE_SCANNING = "Stop Scanning";
private static final String MODE_STOPPED = "Start Scanning";
protected static final String TAG = "ScanActivity";
/*
* Define a request code to send to Google Play services
* This code is returned in Activity.onActivityResult
*/
private final static int
CONNECTION_FAILURE_RESOLUTION_REQUEST = 9000;
private FileHelper fileHelper;
private BeaconManager beaconManager;
private Region region;
private int eventNum = 1;
// This StringBuffer will hold the scan data for any given scan.
private StringBuffer logString;
// Preferences - will actually have a boolean value when loaded.
private Boolean index;
private Boolean location;
private Boolean uuid;
private Boolean majorMinor;
private Boolean rssi;
private Boolean proximity;
private Boolean power;
private Boolean timestamp;
private String scanInterval;
// Added following a feature request from D.Schmid.
private Boolean realTimeLog;
// LocationClient for Google Play Location Services
LocationClient locationClient;
private ScrollView scroller;
private EditText editText;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_scan);
verifyBluetooth();
PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
BeaconScannerApp app = (BeaconScannerApp)this.getApplication();
beaconManager = app.getBeaconManager();
//beaconManager.setForegroundScanPeriod(10);
region = app.getRegion();
beaconManager.bind(this);
locationClient = new LocationClient(this, this, this);
fileHelper = app.getFileHelper();
scroller = (ScrollView)ScanActivity.this.findViewById(R.id.scanScrollView);
editText = (EditText)ScanActivity.this.findViewById(R.id.scanText);
// Initialise scan button.
getScanButton().setText(MODE_STOPPED);
}
#Override
public void onResume() {
super.onResume();
beaconManager.bind(this);
}
#Override
public void onPause() {
super.onPause();
// Uncommenting the following leak prevents a ServiceConnection leak when using the back
// arrow in the Action Bar to come out of the file list screen. Unfortunately it also kills
// background scanning, and as I have no workaround right now I'm settling for the lesser of
// two evils.
// beaconManager.unbind(this);
}
public String getCurrentLocation() {
/** Default "error" value is set for location, will be overwritten with the correct lat and
* long values if we're ble to connect to location services and get a reading.
*/
String location = "Unavailable";
if (locationClient.isConnected()) {
Location currentLocation = locationClient.getLastLocation();
if (currentLocation != null) {
location = Double.toString(currentLocation.getLatitude()) + "," +
Double.toString(currentLocation.getLongitude());
}
}
return location;
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.main_activity_actions, menu);
return super.onCreateOptionsMenu(menu);
}
#Override
public void onBeaconServiceConnect() {}
/**
*
* #param view
*/
public void onScanButtonClicked(View view) {
toggleScanState();
}
// Handle the user selecting "Settings" from the action bar.
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.Settings:
// Show settings
Intent api = new Intent(this, AppPreferenceActivity.class);
startActivityForResult(api, 0);
return true;
case R.id.action_listfiles:
// Launch list files activity
Intent fhi = new Intent(this, FileHandlerActivity.class);
startActivity(fhi);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
/**
* Start and stop scanning, and toggle button label appropriately.
*/
private void toggleScanState() {
Button scanButton = getScanButton();
String currentState = scanButton.getText().toString();
if (currentState.equals(MODE_SCANNING)) {
stopScanning(scanButton);
} else {
startScanning(scanButton);
}
}
/**
* start looking for beacons.
*/
private void startScanning(Button scanButton) {
// Set UI elements to the correct state.
scanButton.setText(MODE_SCANNING);
((EditText)findViewById(R.id.scanText)).setText("");
// Reset event counter
eventNum = 1;
// Get current values for logging preferences
SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
HashMap <String, Object> prefs = new HashMap<String, Object>();
prefs.putAll(sharedPrefs.getAll());
index = (Boolean)prefs.get(PREFERENCE_INDEX);
location = (Boolean)prefs.get(PREFERENCE_LOCATION);
uuid = (Boolean)prefs.get(PREFERENCE_UUID);
majorMinor = (Boolean)prefs.get(PREFERENCE_MAJORMINOR);
rssi = (Boolean)prefs.get(PREFERENCE_RSSI);
proximity = (Boolean)prefs.get(PREFERENCE_PROXIMITY);
power = (Boolean)prefs.get(PREFERENCE_POWER);
timestamp = (Boolean)prefs.get(PREFERENCE_TIMESTAMP);
scanInterval = (String)prefs.get(PREFERENCE_SCANINTERVAL);
realTimeLog = (Boolean)prefs.get(PREFERENCE_REALTIME);
// Get current background scan interval (if specified)
if (prefs.get(PREFERENCE_SCANINTERVAL) != null) {
beaconManager.setBackgroundBetweenScanPeriod(Long.parseLong(scanInterval));
}
logToDisplay("Scanning...");
// Initialise scan log
logString = new StringBuffer();
//Start scanning again.
beaconManager.setRangeNotifier(new RangeNotifier() {
#Override
public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
if (beacons.size() > 0) {
Iterator <Beacon> beaconIterator = beacons.iterator();
while (beaconIterator.hasNext()) {
Beacon beacon = beaconIterator.next();
// Debug - logging a beacon - checking background logging is working.
System.out.println("Logging another beacon.");
logBeaconData(beacon);
}
}
}
});
try {
beaconManager.startRangingBeaconsInRegion(region);
} catch (RemoteException e) {
// TODO - OK, what now then?
}
}
/**
* Stop looking for beacons.
*/
private void stopScanning(Button scanButton) {
try {
beaconManager.stopRangingBeaconsInRegion(region);
} catch (RemoteException e) {
// TODO - OK, what now then?
}
String scanData = logString.toString();
if (scanData.length() > 0) {
// Write file
fileHelper.createFile(scanData);
// Display file created message.
Toast.makeText(getBaseContext(),
"File saved to:" + getFilesDir().getAbsolutePath(),
Toast.LENGTH_SHORT).show();
scanButton.setText(MODE_STOPPED);
} else {
// We didn't get any data, so there's no point writing an empty file.
Toast.makeText(getBaseContext(),
"No data captured during scan, output file will not be created.",
Toast.LENGTH_SHORT).show();
scanButton.setText(MODE_STOPPED);
}
}
/**
*
* #return reference to the start/stop scanning button
*/
private Button getScanButton() {
return (Button)findViewById(R.id.scanButton);
}
/**
*
* #param beacon The detected beacon
*/
private void logBeaconData(Beacon beacon) {
StringBuilder scanString = new StringBuilder();
if (index) {
scanString.append(eventNum++);
}
if (beacon.getServiceUuid() == 0xfeaa) {
if (beacon.getBeaconTypeCode() == 0x00) {
scanString.append(" Eddystone-UID -> ");
scanString.append(" Namespace : ").append(beacon.getId1());
scanString.append(" Identifier : ").append(beacon.getId2());
logEddystoneTelemetry(scanString, beacon);
} else if (beacon.getBeaconTypeCode() == 0x10) {
String url = UrlBeaconUrlCompressor.uncompress(beacon.getId1().toByteArray());
scanString.append(" Eddystone-URL -> " + url);
} else if (beacon.getBeaconTypeCode() == 0x20) {
scanString.append(" Eddystone-TLM -> ");
logEddystoneTelemetry(scanString, beacon);
}
} else {
// Just an old fashioned iBeacon or AltBeacon...
logGenericBeacon(scanString, beacon);
}
logToDisplay(scanString.toString());
scanString.append("\n");
// Code added following a feature request by D.Schmid - writes a single entry to a file
// every time a beacon is detected, the file will only ever have one entry as it will be
// recreated on each call to this method.
// Get current background scan interval (if specified)
if (realTimeLog) {
// We're in realtime logging mode, create a new log file containing only this entry.
fileHelper.createFile(scanString.toString(), "realtimelog.txt");
}
logString.append(scanString.toString());
}
/**
* Logs iBeacon & AltBeacon data.
*/
private void logGenericBeacon(StringBuilder scanString, Beacon beacon) {
// Comment stuff out for whatever reason
/*
if (location) {
scanString.append(" Location: ").append(getCurrentLocation()).append(" ");
}
`
*/
if (uuid) {
scanString.append(" UUID: ").append(beacon.getId1());
if (beacon.getId1().equals("ffffffff-ffff-ffff-ffff-ffffffffffff ")){
scanString.append("WE DID IT!!!!!!!!!!!");
}else{
scanString.append(" WE DID NOT DO IT =( ");
}
/*
if ((beacon.getId1()).equals ("f")){
scanString.append("WE DID IT!!!!!!!!!!!");
}else{
scanString.append(" WE DID NOT DO IT!!!!!!!!!!! ");
}
*/
}
// Making if statements to test for advertising data
/*
if (majorMinor) {
scanString.append(" Maj. Mnr.: ");
if (beacon.getId2() != null) {
scanString.append(beacon.getId2());
}
scanString.append("-");
if (beacon.getId3() != null) {
scanString.append(beacon.getId3());
}
}
if (rssi) {
scanString.append(" RSSI: ").append(beacon.getRssi());
}
if (proximity) {
scanString.append(" Proximity: ").append(BeaconHelper.getProximityString(beacon.getDistance()));
}
if (power) {
scanString.append(" Power: ").append(beacon.getTxPower());
}
if (timestamp) {
scanString.append(" Timestamp: ").append(BeaconHelper.getCurrentTimeStamp());
} */
}
private void logEddystoneTelemetry(StringBuilder scanString, Beacon beacon) {
// Do we have telemetry data?
if (beacon.getExtraDataFields().size() > 0) {
long telemetryVersion = beacon.getExtraDataFields().get(0);
long batteryMilliVolts = beacon.getExtraDataFields().get(1);
long pduCount = beacon.getExtraDataFields().get(3);
long uptime = beacon.getExtraDataFields().get(4);
scanString.append(" Telemetry version : " + telemetryVersion);
scanString.append(" Uptime (sec) : " + uptime);
scanString.append(" Battery level (mv) " + batteryMilliVolts);
scanString.append(" Tx count: " + pduCount);
}
}
/**
*
* #param line
*/
private void logToDisplay(final String line) {
runOnUiThread(new Runnable() {
public void run() {
editText.append(line + "\n");
// Temp code - don't really want to do this for every line logged, will look for a
// workaround.
Linkify.addLinks(editText, Linkify.WEB_URLS);
scroller.fullScroll(View.FOCUS_DOWN);
}
});
}
private void verifyBluetooth() {
try {
if (!BeaconManager.getInstanceForApplication(this).checkAvailability()) {
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Bluetooth not enabled");
builder.setMessage("Please enable bluetooth in settings and restart this application.");
builder.setPositiveButton(android.R.string.ok, null);
builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
#Override
public void onDismiss(DialogInterface dialog) {
finish();
System.exit(0);
}
});
builder.show();
}
}
catch (RuntimeException e) {
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Bluetooth LE not available");
builder.setMessage("Sorry, this device does not support Bluetooth LE.");
builder.setPositiveButton(android.R.string.ok, null);
builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
#Override
public void onDismiss(DialogInterface dialog) {
finish();
System.exit(0);
}
});
builder.show();
}
}
/* Location services code follows */
#Override
protected void onStart() {
super.onStart();
// Connect the client.
locationClient.connect();
}
#Override
protected void onStop() {
// Disconnect the client.
locationClient.disconnect();
super.onStop();
}
#Override
public void onConnected(Bundle dataBundle) {
// Uncomment the following line to display the connection status.
// Toast.makeText(this, "Connected", Toast.LENGTH_SHORT).show();
}
#Override
public void onDisconnected() {
// Display the connection status
Toast.makeText(this, "Disconnected. Please re-connect.",
Toast.LENGTH_SHORT).show();
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
/* Google Play services can resolve some errors it detects.
* If the error has a resolution, try sending an Intent to
* start a Google Play services activity that can resolve
* error.
*/
if (connectionResult.hasResolution()) {
try {
// Start an Activity that tries to resolve the error
connectionResult.startResolutionForResult(
this,
CONNECTION_FAILURE_RESOLUTION_REQUEST);
/*
* Thrown if Google Play services canceled the original
* PendingIntent
*/
} catch (IntentSender.SendIntentException e) {
// Log the error
e.printStackTrace();
}
} else {
/*
* If no resolution is available, display a dialog to the
* user with the error.
*/
Toast.makeText(getBaseContext(),
"Location services not available, cannot track device location.",
Toast.LENGTH_SHORT).show();
}
}
// Define a DialogFragment that displays the error dialog
public static class ErrorDialogFragment extends DialogFragment {
// Global field to contain the error dialog
private Dialog mDialog;
// Default constructor. Sets the dialog field to null
public ErrorDialogFragment() {
super();
mDialog = null;
}
// Set the dialog to display
public void setDialog(Dialog dialog) {
mDialog = dialog;
}
// Return a Dialog to the DialogFragment.
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return mDialog;
}
}
/*
* Handle results returned to the FragmentActivity
* by Google Play services
*/
#Override
protected void onActivityResult(
int requestCode, int resultCode, Intent data) {
// Decide what to do based on the original request code
switch (requestCode) {
case CONNECTION_FAILURE_RESOLUTION_REQUEST :
/*
* If the result code is Activity.RESULT_OK, try
* to connect again
*/
switch (resultCode) {
case Activity.RESULT_OK :
/*
* TODO - Try the request again
*/
break;
}
}
}
}
Need to cast to string first .toString
if (uuid) {
scanString.append(" UUID: ").append(beacon.getId1());
// Making if statements to look for all f's in advertising data
if (beacon.getId1().toString().equals(Str1)){
scanString.append("\nAlarm ACTIVATED\n");
}else{
scanString.append("\n Alarm NOT active\n");
}
}
I have successfully created a speech to text app which is able to open activities based on recognising keywords. At the moment for me to start the Speech to text feature I need to click a button. I now do not want to manually press the button and want to use a continuous listener to trigger the button.
I have been looking into using pocketsphinx and have added a keyphrase "listen to command" once this keyphrase is heard I want the button to be automatically pressed which is then followed by a sequence of commands I can add into the code. I do not need offline speech to text etc and hence I am using Google's speech to text option but am planning to use pocketsphinx to trigger the google's speech to text feature.
Below is most of the code that I partly modified from pocketsphinx:
public class PocketSphinxActivity extends Activity implements RecognitionListener {
/* Named searches allow to quickly reconfigure the decoder */
private static final String KWS_SEARCH = "wakeup";
private static final String FORECAST_SEARCH = "forecast";
private static final String DIGITS_SEARCH = "digits";
private static final String PHONE_SEARCH = "phones";
private static final String MENU_SEARCH = "menu";
/* Keyword we are looking for to activate menu */
private static final String KEYPHRASE = "listen to command"; //adjust this keyphrase!
private SpeechRecognizer recognizer;
private HashMap < String, Integer > captions;
#Override
public void onCreate(Bundle state) {
super.onCreate(state);
// Prepare the data for UI
captions = new HashMap < String, Integer > ();
captions.put(KWS_SEARCH, R.string.kws_caption);
captions.put(MENU_SEARCH, R.string.menu_caption);
captions.put(DIGITS_SEARCH, R.string.digits_caption);
captions.put(PHONE_SEARCH, R.string.phone_caption);
captions.put(FORECAST_SEARCH, R.string.forecast_caption);
setContentView(R.layout.main);
((TextView) findViewById(R.id.caption_text))
.setText("Preparing the recognizer");
// Recognizer initialization is a time-consuming and it involves IO,
// so we execute it in async task
new AsyncTask < Void, Void, Exception > () {
#Override
protected Exception doInBackground(Void...params) {
try {
Assets assets = new Assets(PocketSphinxActivity.this);
File assetDir = assets.syncAssets();
setupRecognizer(assetDir);
} catch (IOException e) {
return e;
}
return null;
}
#Override
protected void onPostExecute(Exception result) {
if (result != null) {
((TextView) findViewById(R.id.caption_text))
.setText("Failed to init recognizer " + result);
} else {
switchSearch(KWS_SEARCH);
}
}
}.execute();
}
#Override
public void onDestroy() {
super.onDestroy();
recognizer.cancel();
recognizer.shutdown();
}
/**
* In partial result we get quick updates about current hypothesis. In
* keyword spotting mode we can react here, in other modes we need to wait
* for final result in onResult.
*/
#Override
public void onPartialResult(Hypothesis hypothesis) {
if (hypothesis == null)
return;
String text = hypothesis.getHypstr();
if (text.equals(KEYPHRASE))
switchSearch(MENU_SEARCH);
else if (text.equals(DIGITS_SEARCH))
switchSearch(DIGITS_SEARCH);
else if (text.equals(PHONE_SEARCH))
switchSearch(PHONE_SEARCH);
else if (text.equals(FORECAST_SEARCH))
switchSearch(FORECAST_SEARCH);
else
((TextView) findViewById(R.id.result_text)).setText(text);
}
/**
* This callback is called when we stop the recognizer.
*/
#Override
public void onResult(Hypothesis hypothesis) {
((TextView) findViewById(R.id.result_text)).setText("");
if (hypothesis != null) {
String text = hypothesis.getHypstr();
makeText(getApplicationContext(), text, Toast.LENGTH_SHORT).show();
}
}
#Override
public void onBeginningOfSpeech() {}
/**
* We stop recognizer here to get a final result
*/
#Override
public void onEndOfSpeech() {
if (!recognizer.getSearchName().equals(KWS_SEARCH))
switchSearch(KWS_SEARCH);
}
private void switchSearch(String searchName) {
recognizer.stop();
// If we are not spotting, start listening with timeout (10000 ms or 10 seconds).
if (searchName.equals(KWS_SEARCH))
recognizer.startListening(searchName);
else
recognizer.startListening(searchName, 10000);
String caption = getResources().getString(captions.get(searchName));
((TextView) findViewById(R.id.caption_text)).setText(caption);
}
private void setupRecognizer(File assetsDir) throws IOException {
// The recognizer can be configured to perform multiple searches
// of different kind and switch between them
recognizer = defaultSetup()
.setAcousticModel(new File(assetsDir, "en-us-ptm"))
.setDictionary(new File(assetsDir, "cmudict-en-us.dict"))
// To disable logging of raw audio comment out this call (takes a lot of space on the device)
.setRawLogDir(assetsDir)
// Threshold to tune for keyphrase to balance between false alarms and misses
.setKeywordThreshold(1e-45 f)
// Use context-independent phonetic search, context-dependent is too slow for mobile
.setBoolean("-allphone_ci", true)
.getRecognizer();
recognizer.addListener(this);
/** In your application you might not need to add all those searches.
* They are added here for demonstration. You can leave just one.
*/
// Create keyword-activation search.
recognizer.addKeyphraseSearch(KWS_SEARCH, KEYPHRASE);
// Create grammar-based search for selection between demos
File menuGrammar = new File(assetsDir, "menu.gram");
recognizer.addGrammarSearch(MENU_SEARCH, menuGrammar);
// Create grammar-based search for digit recognition
File digitsGrammar = new File(assetsDir, "digits.gram");
recognizer.addGrammarSearch(DIGITS_SEARCH, digitsGrammar);
// Create language model search
File languageModel = new File(assetsDir, "weather.dmp");
recognizer.addNgramSearch(FORECAST_SEARCH, languageModel);
// Phonetic search
File phoneticModel = new File(assetsDir, "en-phone.dmp");
recognizer.addAllphoneSearch(PHONE_SEARCH, phoneticModel);
}
#Override
public void onError(Exception error) {
((TextView) findViewById(R.id.caption_text)).setText(error.getMessage());
}
#Override
public void onTimeout() {
switchSearch(KWS_SEARCH);
}
I am not interested in most of the commands, I just simply want the app to listen as soon as it is opened (which it currently does) and as soon as a person says Listen to command it then presses a button which I have set an id of bVoice
If someone can kindly help me modify the above code so that I know what to delete and what to add that will be greatly appreciated. Also note that if there is a much easier method of doing this please feel free to share (having a custom key phrase isn't also necessary).
Something like this, and you do not need to "push the button", you can directly call button callback instead and perform actual steps you are interested in.
public class PocketSphinxActivity extends Activity implements RecognitionListener {
private static final String KWS_SEARCH = "wakeup";
private static final String KEYPHRASE = "listen to command"; //adjust this keyphrase!
private SpeechRecognizer recognizer;
#Override
public void onCreate(Bundle state) {
super.onCreate(state);
setContentView(R.layout.main);
((TextView) findViewById(R.id.caption_text))
.setText("Preparing the recognizer");
new AsyncTask < Void, Void, Exception > () {
#Override
protected Exception doInBackground(Void...params) {
try {
Assets assets = new Assets(PocketSphinxActivity.this);
File assetDir = assets.syncAssets();
setupRecognizer(assetDir);
} catch (IOException e) {
return e;
}
return null;
}
#Override
protected void onPostExecute(Exception result) {
if (result != null) {
((TextView) findViewById(R.id.caption_text))
.setText("Failed to init recognizer " + result);
} else {
recognizer.startListening(KWS_SEARCH);
}
}
}.execute();
}
#Override
public void onDestroy() {
super.onDestroy();
recognizer.cancel();
recognizer.shutdown();
}
/**
* In partial result we get quick updates about current hypothesis. In
* keyword spotting mode we can react here, in other modes we need to wait
* for final result in onResult.
*/
#Override
public void onPartialResult(Hypothesis hypothesis) {
if (hypothesis == null)
return;
String text = hypothesis.getHypstr();
if (text.equals(KEYPHRASE)) {
recognizer.cancel();
performAction(); // <- You have to implement this
recognizer.startListening(KWS_SEARCH);
}
}
#Override
public void onResult(Hypothesis hypothesis) {}
#Override
public void onBeginningOfSpeech() {}
#Override
public void onEndOfSpeech() {}
#Override
public void onTimeout() {}
private void setupRecognizer(File assetsDir) throws IOException {
// The recognizer can be configured to perform multiple searches
// of different kind and switch between them
recognizer = defaultSetup()
.setAcousticModel(new File(assetsDir, "en-us-ptm"))
.setDictionary(new File(assetsDir, "cmudict-en-us.dict"))
.getRecognizer();
recognizer.addListener(this);
// Create keyword-activation search.
recognizer.addKeyphraseSearch(KWS_SEARCH, KEYPHRASE);
}
#Override
public void onError(Exception error) {
((TextView) findViewById(R.id.caption_text)).setText(error.getMessage());
}
public void peformAction() {
// do here whatever you want
}
I am facing a problem with an expansion file process, I do not know what the problem is. I hope if someone could help.
I have followed the steps by Google; yet I cannot assure I have done it correctly 100%.
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*/
import com.android.vending.expansion.zipfile.ZipResourceFile;
import com.android.vending.expansion.zipfile.ZipResourceFile.ZipEntryRO;
import com.google.android.vending.expansion.downloader.Constants;
import com.google.android.vending.expansion.downloader.DownloadProgressInfo;
import com.google.android.vending.expansion.downloader.DownloaderClientMarshaller;
import com.google.android.vending.expansion.downloader.DownloaderServiceMarshaller;
import com.google.android.vending.expansion.downloader.Helpers;
import com.google.android.vending.expansion.downloader.IDownloaderClient;
import com.google.android.vending.expansion.downloader.IDownloaderService;
import com.google.android.vending.expansion.downloader.IStub;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Messenger;
import android.os.SystemClock;
import android.provider.Settings;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.zip.CRC32;
/**
* This is sample code for a project built against the downloader library. It
* implements the IDownloaderClient that the client marshaler will talk to as
* messages are delivered from the DownloaderService.
*/
public class ExpansionFileDownloaderActivity extends Activity implements IDownloaderClient {
private static final String LOG_TAG = "LVLDownloader";
private ProgressBar mPB;
private TextView mStatusText;
private TextView mProgressFraction;
private TextView mProgressPercent;
private TextView mAverageSpeed;
private TextView mTimeRemaining;
private View mDashboard;
private View mCellMessage;
private Button mPauseButton;
private Button mWiFiSettingsButton;
private boolean mStatePaused;
private int mState;
private IDownloaderService mRemoteService;
private IStub mDownloaderClientStub;
private void setState(int newState) {
if (mState != newState) {
mState = newState;
mStatusText.setText(Helpers.getDownloaderStringResourceIDFromState(newState));
}
}
private void setButtonPausedState(boolean paused) {
mStatePaused = paused;
int stringResourceID = paused ? R.string.text_button_resume :
R.string.text_button_pause;
mPauseButton.setText(stringResourceID);
}
/**
* This is a little helper class that demonstrates simple testing of an
* Expansion APK file delivered by Market. You may not wish to hard-code
* things such as file lengths into your executable... and you may wish to
* turn this code off during application development.
*/
private static class XAPKFile {
public final boolean mIsMain;
public final int mFileVersion;
public final long mFileSize;
XAPKFile(boolean isMain, int fileVersion, long fileSize) {
mIsMain = isMain;
mFileVersion = fileVersion;
mFileSize = fileSize;
}
}
/**
* Here is where you place the data that the validator will use to determine
* if the file was delivered correctly. This is encoded in the source code
* so the application can easily determine whether the file has been
* properly delivered without having to talk to the server. If the
* application is using LVL for licensing, it may make sense to eliminate
* these checks and to just rely on the server.
*/
private static final XAPKFile[] xAPKS = {
new XAPKFile(
true, // true signifies a main file
1, // the version of the APK that the file was uploaded
// against
102459993L // the length of the file in bytes
)
};
/**
* Go through each of the APK Expansion files defined in the structure above
* and determine if the files are present and match the required size. Free
* applications should definitely consider doing this, as this allows the
* application to be launched for the first time without having a network
* connection present. Paid applications that use LVL should probably do at
* least one LVL check that requires the network to be present, so this is
* not as necessary.
*
* #return true if they are present.
*/
boolean expansionFilesDelivered() {
for (XAPKFile xf : xAPKS) {
String fileName = Helpers.getExpansionAPKFileName(this, xf.mIsMain, xf.mFileVersion);
if (!Helpers.doesFileExist(this, fileName, xf.mFileSize, false))
return false;
}
return true;
}
public static boolean expansionFilesDelivered(Context ctx) {
for (XAPKFile xf : xAPKS) {
String fileName = Helpers.getExpansionAPKFileName(ctx, xf.mIsMain, xf.mFileVersion);
if (!Helpers.doesFileExist(ctx, fileName, xf.mFileSize, false))
return false;
}
return true;
}
/**
* Calculating a moving average for the validation speed so we don't get
* jumpy calculations for time etc.
*/
static private final float SMOOTHING_FACTOR = 0.005f;
/**
* Used by the async task
*/
private boolean mCancelValidation;
/**
* Go through each of the Expansion APK files and open each as a zip file.
* Calculate the CRC for each file and return false if any fail to match.
*
* #return true if XAPKZipFile is successful
*/
void validateXAPKZipFiles() {
AsyncTask<Object, DownloadProgressInfo, Boolean> validationTask = new AsyncTask<Object, DownloadProgressInfo, Boolean>() {
#Override
protected void onPreExecute() {
mDashboard.setVisibility(View.VISIBLE);
mCellMessage.setVisibility(View.GONE);
mStatusText.setText(R.string.text_verifying_download);
mPauseButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mCancelValidation = true;
}
});
mPauseButton.setText(R.string.text_button_cancel_verify);
super.onPreExecute();
}
#Override
protected Boolean doInBackground(Object... params) {
for (XAPKFile xf : xAPKS) {
String fileName = Helpers.getExpansionAPKFileName(
ExpansionFileDownloaderActivity.this,
xf.mIsMain, xf.mFileVersion);
if (!Helpers.doesFileExist(ExpansionFileDownloaderActivity.this, fileName,
xf.mFileSize, false))
return false;
fileName = Helpers
.generateSaveFileName(ExpansionFileDownloaderActivity.this, fileName);
ZipResourceFile zrf;
byte[] buf = new byte[1024 * 256];
try {
zrf = new ZipResourceFile(fileName);
ZipEntryRO[] entries = zrf.getAllEntries();
/**
* First calculate the total compressed length
*/
long totalCompressedLength = 0;
for (ZipEntryRO entry : entries) {
totalCompressedLength += entry.mCompressedLength;
}
float averageVerifySpeed = 0;
long totalBytesRemaining = totalCompressedLength;
long timeRemaining;
/**
* Then calculate a CRC for every file in the Zip file,
* comparing it to what is stored in the Zip directory.
* Note that for compressed Zip files we must extract
* the contents to do this comparison.
*/
for (ZipEntryRO entry : entries) {
if (-1 != entry.mCRC32) {
long length = entry.mUncompressedLength;
CRC32 crc = new CRC32();
DataInputStream dis = null;
try {
dis = new DataInputStream(
zrf.getInputStream(entry.mFileName));
long startTime = SystemClock.uptimeMillis();
while (length > 0) {
int seek = (int) (length > buf.length ? buf.length
: length);
dis.readFully(buf, 0, seek);
crc.update(buf, 0, seek);
length -= seek;
long currentTime = SystemClock.uptimeMillis();
long timePassed = currentTime - startTime;
if (timePassed > 0) {
float currentSpeedSample = (float) seek
/ (float) timePassed;
if (0 != averageVerifySpeed) {
averageVerifySpeed = SMOOTHING_FACTOR
* currentSpeedSample
+ (1 - SMOOTHING_FACTOR)
* averageVerifySpeed;
} else {
averageVerifySpeed = currentSpeedSample;
}
totalBytesRemaining -= seek;
timeRemaining = (long) (totalBytesRemaining / averageVerifySpeed);
this.publishProgress(
new DownloadProgressInfo(
totalCompressedLength,
totalCompressedLength
- totalBytesRemaining,
timeRemaining,
averageVerifySpeed
)
);
}
startTime = currentTime;
if (mCancelValidation)
return true;
}
if (crc.getValue() != entry.mCRC32) {
Log.e(Constants.TAG,
"CRC does not match for entry: "
+ entry.mFileName
);
Log.e(Constants.TAG,
"In file: " + entry.getZipFileName());
return false;
}
} finally {
if (null != dis) {
dis.close();
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
return true;
}
#Override
protected void onProgressUpdate(DownloadProgressInfo... values) {
onDownloadProgress(values[0]);
super.onProgressUpdate(values);
}
#Override
protected void onPostExecute(Boolean result) {
if (result) {
mDashboard.setVisibility(View.VISIBLE);
mCellMessage.setVisibility(View.GONE);
mStatusText.setText(R.string.text_validation_complete);
mPauseButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
finish();
}
});
mPauseButton.setText(android.R.string.ok);
} else {
mDashboard.setVisibility(View.VISIBLE);
mCellMessage.setVisibility(View.GONE);
mStatusText.setText(R.string.text_validation_failed);
mPauseButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
finish();
}
});
mPauseButton.setText(android.R.string.cancel);
}
super.onPostExecute(result);
}
};
validationTask.execute(new Object());
}
/**
* If the download isn't present, we initialize the download UI. This ties
* all of the controls into the remote service calls.
*/
private void initializeDownloadUI() {
mDownloaderClientStub = DownloaderClientMarshaller.CreateStub
(this, SampleDownloaderService.class);
setContentView(R.layout.downloader_ui);
mPB = (ProgressBar) findViewById(R.id.progressBar);
mStatusText = (TextView) findViewById(R.id.statusText);
mProgressFraction = (TextView) findViewById(R.id.progressAsFraction);
mProgressPercent = (TextView) findViewById(R.id.progressAsPercentage);
mAverageSpeed = (TextView) findViewById(R.id.progressAverageSpeed);
mTimeRemaining = (TextView) findViewById(R.id.progressTimeRemaining);
mDashboard = findViewById(R.id.downloaderDashboard);
mCellMessage = findViewById(R.id.approveCellular);
mPauseButton = (Button) findViewById(R.id.pauseButton);
mWiFiSettingsButton = (Button) findViewById(R.id.wifiSettingsButton);
mPauseButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (mStatePaused) {
mRemoteService.requestContinueDownload();
} else {
mRemoteService.requestPauseDownload();
}
setButtonPausedState(!mStatePaused);
}
});
mWiFiSettingsButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startActivity(new Intent(Settings.ACTION_WIFI_SETTINGS));
}
});
Button resumeOnCell = (Button) findViewById(R.id.resumeOverCellular);
resumeOnCell.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mRemoteService.setDownloadFlags(IDownloaderService.FLAGS_DOWNLOAD_OVER_CELLULAR);
mRemoteService.requestContinueDownload();
mCellMessage.setVisibility(View.GONE);
}
});
}
/**
* Called when the activity is first create; we wouldn't create a layout in
* the case where we have the file and are moving to another activity
* without downloading.
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/**
* Both downloading and validation make use of the "download" UI
*/
initializeDownloadUI();
/**
* Before we do anything, are the files we expect already here and
* delivered (presumably by Market) For free titles, this is probably
* worth doing. (so no Market request is necessary)
*/
if (!expansionFilesDelivered()) {
try {
Intent launchIntent = ExpansionFileDownloaderActivity.this
.getIntent();
Intent intentToLaunchThisActivityFromNotification = new Intent(
ExpansionFileDownloaderActivity
.this, ((Object) this).getClass() //..... this.getClass()
);
intentToLaunchThisActivityFromNotification.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_CLEAR_TOP);
intentToLaunchThisActivityFromNotification.setAction(launchIntent.getAction());
if (launchIntent.getCategories() != null) {
for (String category : launchIntent.getCategories()) {
intentToLaunchThisActivityFromNotification.addCategory(category);
}
}
// Build PendingIntent used to open this activity from
// Notification
PendingIntent pendingIntent = PendingIntent.getActivity(
ExpansionFileDownloaderActivity.this,
0, intentToLaunchThisActivityFromNotification,
PendingIntent.FLAG_UPDATE_CURRENT);
// Request to start the download
int startResult = DownloaderClientMarshaller.startDownloadServiceIfRequired(this,
pendingIntent, SampleDownloaderService.class);
if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) {
// The DownloaderService has started downloading the files,
// show progress
initializeDownloadUI();
return;
} // otherwise, download not needed so we fall through to
// starting the movie
} catch (NameNotFoundException e) {
Log.e(LOG_TAG, "Cannot find own package! MAYDAY!");
e.printStackTrace();
}
} else {
validateXAPKZipFiles();
}
}
/**
* Connect the stub to our service on start.
*/
#Override
protected void onStart() {
if (null != mDownloaderClientStub) {
mDownloaderClientStub.connect(this);
}
super.onStart();
}
/**
* Disconnect the stub from our service on stop
*/
#Override
protected void onStop() {
if (null != mDownloaderClientStub) {
mDownloaderClientStub.disconnect(this);
}
super.onStop();
}
/**
* Critical implementation detail. In onServiceConnected we create the
* remote service and marshaler. This is how we pass the client information
* back to the service so the client can be properly notified of changes. We
* must do this every time we reconnect to the service.
*/
#Override
public void onServiceConnected(Messenger m) {
mRemoteService = DownloaderServiceMarshaller.CreateProxy(m);
mRemoteService.onClientUpdated(mDownloaderClientStub.getMessenger());
}
/**
* The download state should trigger changes in the UI --- it may be useful
* to show the state as being indeterminate at times. This sample can be
* considered a guideline.
*/
#Override
public void onDownloadStateChanged(int newState) {
setState(newState);
boolean showDashboard = true;
boolean showCellMessage = false;
boolean paused;
boolean indeterminate;
switch (newState) {
case IDownloaderClient.STATE_IDLE:
// STATE_IDLE means the service is listening, so it's
// safe to start making calls via mRemoteService.
paused = false;
indeterminate = true;
break;
case IDownloaderClient.STATE_CONNECTING:
case IDownloaderClient.STATE_FETCHING_URL:
showDashboard = true;
paused = false;
indeterminate = true;
break;
case IDownloaderClient.STATE_DOWNLOADING:
paused = false;
showDashboard = true;
indeterminate = false;
break;
case IDownloaderClient.STATE_FAILED_CANCELED:
case IDownloaderClient.STATE_FAILED:
case IDownloaderClient.STATE_FAILED_FETCHING_URL:
case IDownloaderClient.STATE_FAILED_UNLICENSED:
paused = true;
showDashboard = false;
indeterminate = false;
break;
case IDownloaderClient.STATE_PAUSED_NEED_CELLULAR_PERMISSION:
case IDownloaderClient.STATE_PAUSED_WIFI_DISABLED_NEED_CELLULAR_PERMISSION:
showDashboard = false;
paused = true;
indeterminate = false;
showCellMessage = true;
break;
case IDownloaderClient.STATE_PAUSED_BY_REQUEST:
paused = true;
indeterminate = false;
break;
case IDownloaderClient.STATE_PAUSED_ROAMING:
case IDownloaderClient.STATE_PAUSED_SDCARD_UNAVAILABLE:
paused = true;
indeterminate = false;
break;
case IDownloaderClient.STATE_COMPLETED:
showDashboard = false;
paused = false;
indeterminate = false;
validateXAPKZipFiles();
return;
default:
paused = true;
indeterminate = true;
showDashboard = true;
}
int newDashboardVisibility = showDashboard ? View.VISIBLE : View.GONE;
if (mDashboard.getVisibility() != newDashboardVisibility) {
mDashboard.setVisibility(newDashboardVisibility);
}
int cellMessageVisibility = showCellMessage ? View.VISIBLE : View.GONE;
if (mCellMessage.getVisibility() != cellMessageVisibility) {
mCellMessage.setVisibility(cellMessageVisibility);
}
mPB.setIndeterminate(indeterminate);
setButtonPausedState(paused);
}
/**
* Sets the state of the various controls based on the progressinfo object
* sent from the downloader service.
*/
#Override
public void onDownloadProgress(DownloadProgressInfo progress) {
mAverageSpeed.setText(getString(R.string.kilobytes_per_second,
Helpers.getSpeedString(progress.mCurrentSpeed)));
mTimeRemaining.setText(getString(R.string.time_remaining,
Helpers.getTimeRemaining(progress.mTimeRemaining)));
progress.mOverallTotal = progress.mOverallTotal;
mPB.setMax((int) (progress.mOverallTotal >> 8));
mPB.setProgress((int) (progress.mOverallProgress >> 8));
mProgressPercent.setText(Long.toString(progress.mOverallProgress
* 100 /
progress.mOverallTotal) + "%");
mProgressFraction.setText(Helpers.getDownloadProgressString
(progress.mOverallProgress,
progress.mOverallTotal));
}
#Override
protected void onDestroy() {
this.mCancelValidation = true;
super.onDestroy();
}
}
this is the SampleDownloaderService class,
import com.google.android.vending.expansion.downloader.impl.DownloaderService;
/**
* Created by Abdulkarim Kanaan on 22-Mar-14.
*/
public class SampleDownloaderService extends DownloaderService {
// You must use the public key belonging to your publisher account
public static final String BASE64_PUBLIC_KEY = "<Place Key provided by Google Play Publisher>";
// You should also modify this salt
public static final byte[] SALT = new byte[] { 1, 42, -12, -1, 54, 98,
-100, -12, 43, 2, -8, -4, 9, 5, -106, -107, -33, 45, -1, 84
};
#Override
public String getPublicKey() {
return BASE64_PUBLIC_KEY;
}
#Override
public byte[] getSALT() {
return SALT;
}
#Override
public String getAlarmReceiverClassName() {
return SampleAlarmReceiver.class.getName();
}
}
Finally, this is the Main Activity creation method, where the check is carried out to see whether the expansion file exists or not.
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String.valueOf(ExpansionFileDownloaderActivity.expansionFilesDelivered(this)));
if (!ExpansionFileDownloaderActivity.expansionFilesDelivered(this)){
startActivity(new Intent(this, ExpansionFileDownloaderActivity.class));
}
//startApp(); // Expansion files are available, start the app
}
the problem is that after uploading the expansion file to Google Play, when the program starts, it shows that downloading process is going to be carried out. After a short period, it stops saying that "Download failed because the resources could not be found". When I place the file (obb file) in the SD Card, the application works as expected. However, I am trying now to download the file from Google Play.
What is the problem?
Thank you in advance,
I was having the same problem and no matter what I tried it still didnt work.
Finally I managed to download the apk expansion file. What I did was the following.
*Make sure your file size is correct (file size not disk size).
*Make sure versionCode matches with your manifest's versionCode.
*Make sure you have changed the SALT bytes.
Step by step:
1- Create a new app in the developer console.
2- Obtain the LVL Licence Key and repace it in your code.
3- Sign and export your application and create a zip with your expansion file without compression (compression: "store" in win rar).
4-Upload your app to google play but upload it in the Beta Testing Section. (without a production version listed, this is very important you must not have a production version saved in draft).
5- Create a google group with the people you want to allow beta acess and under the Beta Testing tab you must add that group.
5- Now its the scary part, you must click on publish. Your app will not show in google play, it will only be shown to your testing groups, TRUST ME. It should look like this:
*Note: Make sure you don't promote the beta to production or it will be shown, also notice I don't have a production version listed.
6- Under Beta Testing tab there is a link that a you must click in order to use the beta.
After that just download the app from google play, you can just type your package name for example:
https://play.google.com/store/apps/details?id=com.mydomain.myapp
and the app will be installed along with the expansion package.
If you want to test the files download, install the app and then delete the obb file downloaded. This will start the obb download process.
Hope this helps, as it did for me!
Cheers.
I am building an app that detects when the car is moving or parking by detecting its speed.
I have some code that detects the current speed, but I don't know how to write that code so if the speed is over 20 km/h so do anything, and if the speed is 0 km/h so do anything.
Here is my code:
public class DeviceSpeedDemoActivity extends Activity implements GPSCallback{
private GPSManager gpsManager = null;
private double speed = 0.0;
private int measurement_index = Constants.INDEX_KM;
private AbsoluteSizeSpan sizeSpanLarge = null;
private AbsoluteSizeSpan sizeSpanSmall = null;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
gpsManager = new GPSManager();
gpsManager.startListening(getApplicationContext());
gpsManager.setGPSCallback(this);
((TextView)findViewById(R.id.info_message)).setText(getString(R.string.info));
measurement_index = AppSettings.getMeasureUnit(this);
}
#Override
public void onGPSUpdate(Location location)
{
location.getLatitude();
location.getLongitude();
speed = location.getSpeed();
String speedString = "" + roundDecimal(convertSpeed(speed),2);
String unitString = measurementUnitString(measurement_index);
setSpeedText(R.id.info_message,speedString + " " + unitString);
}
#Override
protected void onDestroy() {
gpsManager.stopListening();
gpsManager.setGPSCallback(null);
gpsManager = null;
super.onDestroy();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
boolean result = true;
switch(item.getItemId())
{
case R.id.menu_about:
{
displayAboutDialog();
break;
}
case R.id.unit_km:
{
measurement_index = 0;
AppSettings.setMeasureUnit(this, 0);
break;
}
case R.id.unit_miles:
{
measurement_index = 1;
AppSettings.setMeasureUnit(this, 1);
break;
}
default:
{
result = super.onOptionsItemSelected(item);
break;
}
}
return result;
}
private double convertSpeed(double speed){
return ((speed * Constants.HOUR_MULTIPLIER) * Constants.UNIT_MULTIPLIERS[measurement_index]);
}
private String measurementUnitString(int unitIndex){
String string = "";
switch(unitIndex)
{
case Constants.INDEX_KM: string = "km/h"; break;
case Constants.INDEX_MILES: string = "mi/h"; break;
}
return string;
}
private double roundDecimal(double value, final int decimalPlace)
{
BigDecimal bd = new BigDecimal(value);
bd = bd.setScale(decimalPlace, RoundingMode.HALF_UP);
value = bd.doubleValue();
return value;
}
private void setSpeedText(int textid,String text)
{
Spannable span = new SpannableString(text);
int firstPos = text.indexOf(32);
span.setSpan(sizeSpanLarge, 0, firstPos,Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
span.setSpan(sizeSpanSmall, firstPos + 1, text.length(),Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
TextView tv = ((TextView)findViewById(textid));
tv.setText(span);
}
private void displayAboutDialog()
{
final LayoutInflater inflator = LayoutInflater.from(this);
final View settingsview = inflator.inflate(R.layout.about, null);
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(getString(R.string.app_name));
builder.setView(settingsview);
builder.setPositiveButton(android.R.string.ok, new OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
}
});
builder.create().show();
}
}
You can use a simple if-elseif block. When you are getting your speed, i.e. onGPSUpdate, just add the following:
if(convertSpeed(speed) > 20.0){
//Code for Speed Over 20
}
else{
//code for speed less than 20
}
By the way when using location.getLongitude() use a try-catch block, as they don't work well always, and you don't want to annoy the user with a force-close.
I solved a similar problem for a flight logging application. I wanted to detect the begin of taxiing to the runway, the take-off, landing, and reaching the parking position, plus some more complex things as touch-and-goes.
A useful pattern here is a state machine (or the state pattern): Your app maintains a state (e.g. stopped, or running; in my example parking, taxi-for-take-off, in-flight, and so on). If in a certain state you look for different events: E.g. if in state "stopped" you look if the speed exceeds a certain threshold, e.g. 5 km/h or if the location changes and certain distance threshold is exceeded. If in state "driving" you check for the speed dropping below a certain threshold and if that happens you look if it stays below that thereshold for some time (if you want to avoid detecting a short stop like taking a turn). And so on.
Thus your app is in a certain state which determines the interpretation of sensor events. Your apps evaluates the sensor events wrt. to the current state and switches state if the evaluations supports a hypothesis that the state should be changed.
In your method onGPSUpdate(), you should be able to determine this criteria.
#Override
public void onGPSUpdate(Location location)
{
location.getLatitude();
location.getLongitude();
speed = location.getSpeed();
double speedKPH = convertSpeed(speed);
if(speedKPH > 20.0)
{
//Do something, Speed Over 20
}else if(speedKPH == 0)
{
//Do something else, speed 0
}
String speedString = "" + roundDecimal(convertSpeed(speed),2);
String unitString = measurementUnitString(measurement_index);
setSpeedText(R.id.info_message,speedString + " " + unitString);
}
I have a Form with edittexts and a button to call the camera with an intent (return a bitmap that is put into the imageview)...From the portrait mode i enter all edittext filed and then click the camera button which forwards me to the camera - in the camera i take a picture after what I get returned to Activity 1 (staying in portrait orientation - and all editext fields are restore in onRestoreInstanceState()) - and the last callback method of Activity 1 is onResume() (what is ok) - But the problem comes when I make an orientation change from this portrait to landscape mode - the callback methods are following
So the last callback orientation change is onPause(). I do not understand why? The problem is that onSaveInstanceState is called prior of onPause - so when I turn back to portrait mode everything will be empty (editexts, imageview..) - this strange behavior continues on every orientation change (the onPause() is called last).
I am sure this problem has to do something with the taking an image (startInentforResult....) because everything (editext fields) works fine on orientation change prior to taking an image...sometimes I can also take an image and it works fine, but in most cases not...
So my question is what is it that "drives" my Activity up to the onPause() method instead up to the onResume()?
Thanks, I would really appreciate if somebody knows the solution because I am struggling with this already a few days and could not find the solution.
The project has many classes but this is the activity code (Important to note is that the problem arises only when I take an image from camera app, after that the activity lifecycle goes crazy - also this activity is called from the main activity with 'startIntentforResult()'. I do not use 'android:configChanges="orientation|keyboardHidden"' to stop the recreatioin ):
public class NewCounterActivity extends Activity {
Button btnCreate;
Button btnCancel;
Button btnTakeImg;
ImageView counterImgView;
CheckBox existsDamage;
EditText inputNameFirst;
EditText inputNameLast;
EditText inputAdresse;
EditText inputCounterID;
EditText inputCounterValue;
EditText inputDescription;
TextView registerErrorMsg;
DatabaseHandler db;
//Data to be submitted
String nameFirst;
String nameLast;
String adresse;
String counterID;
String counterValue;
String countDescript;
String existsDmg;
Bitmap counterBitmap;
Bitmap recievedBitmap;
String longitude;
String latitude;
LocationTracker gps;
// JSON Response node names
private static String KEY_SUCCESS = "success";
private static String KEY_ERROR = "error";
private static String KEY_ERROR_MSG = "error_msg";
//The dimensions of the ImageView
int targetW;
int targetH;
// Some lifecycle callbacks so that the image can survive orientation change
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
Log.e("onSaveInstanceState", "fadsfass");
outState.putParcelable("bitmap", counterBitmap);
outState.putString("fname", inputNameFirst.getText().toString());
outState.putString("lname", inputNameLast.getText().toString());
outState.putString("adrese", inputAdresse.getText().toString());
outState.putString("cID", inputCounterID.getText().toString());
outState.putString("cValue", inputCounterValue.getText().toString());
outState.putString("Descript", inputDescription.getText().toString());
outState.putString("ErrorMsg", registerErrorMsg.getText().toString());
outState.putBoolean("damageCheck", existsDamage.isChecked());
((MyApplicationClass) getApplication()).detach(this);
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
Log.e("onRestoreInstanceState", "fadsfass");
counterBitmap = savedInstanceState.getParcelable("bitmap");
counterImgView.setImageBitmap(counterBitmap);
inputNameFirst.setText(savedInstanceState.getString("fname"));
inputNameLast.setText(savedInstanceState.getString("lname"));
inputAdresse.setText(savedInstanceState.getString("adrese"));
inputCounterID.setText(savedInstanceState.getString("cID"));
inputCounterValue.setText(savedInstanceState.getString("cValue"));
inputDescription.setText(savedInstanceState.getString("Descript"));
registerErrorMsg.setText(savedInstanceState.getString("ErrorMsg"));
existsDamage.setChecked(savedInstanceState.getBoolean("damageCheck"));
((MyApplicationClass) getApplication()).attach(this);
}
#Override
public void onContentChanged() {
// TODO Auto-generated method stub
super.onContentChanged();
Log.e("onContetnChanged", "fadsfass");
}
#Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
Log.e("onDestroy", "fadsfass");
}
#Override
public void onDetachedFromWindow() {
// TODO Auto-generated method stub
super.onDetachedFromWindow();
Log.e("onDetachedFromWindow", "fadsfass");
}
#Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
Log.e("onPause", "fadsfass");
}
#Override
protected void onRestart() {
// TODO Auto-generated method stub
super.onRestart();
Log.e("onRestart", "fadsfass");
}
#Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
Log.e("onResume", "fadsfass");
}
#Override
protected void onStart() {
// TODO Auto-generated method stub
super.onStart();
Log.e("onStart", "fadsfass");
}
#Override
protected void onStop() {
// TODO Auto-generated method stub
super.onStop();
Log.e("onStop", "fadsfass");
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.newcounteractivity_layout);
Log.e("onCreate", "mActivity equals NULL");
inputNameFirst = (EditText) findViewById(R.id.createFirstName);
inputNameLast = (EditText) findViewById(R.id.createLastName);
inputAdresse = (EditText) findViewById(R.id.createAdresse);
inputCounterID = (EditText) findViewById(R.id.createCounterID);
inputCounterValue = (EditText) findViewById(R.id.createCounterValue);
inputDescription = (EditText) findViewById(R.id.createDescription);
registerErrorMsg = (TextView) findViewById(R.id.create_error);
btnCreate = (Button) findViewById(R.id.btnCreate);
btnCancel = (Button) findViewById(R.id.btnCancel);
btnTakeImg = (Button) findViewById(R.id.btnImage);
counterImgView = (ImageView) findViewById(R.id.counterImgView);
existsDamage = (CheckBox) findViewById(R.id.createDamageExists);
//REGISTER BUTTON CLICK EVENTS
btnCreate.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
//new DoBackgroundTask(NewCounterActivity.this).execute();
//CounterUser data to submit
nameFirst = inputNameFirst.getText().toString().trim();
nameLast = inputNameLast.getText().toString().trim();
adresse = inputAdresse.getText().toString().trim();
counterID = inputCounterID.getText().toString().trim();
counterValue = inputCounterValue.getText().toString().trim();
countDescript = inputDescription.getText().toString().trim();
existsDmg = Integer.toString((existsDamage.isChecked()) ? 1 : 0);
// create LocationTracker class object
gps = new LocationTracker(NewCounterActivity.this);
if(!gps.canGetLocation()){
gps.stopUsingGPS();
gps.showSettingsAlert();
//Ovo se mozda treba i izbaciti
gps.getLocation();
}
else{
processInput();
}
}
});
btnCancel.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Intent returnIntent = new Intent();
setResult(RESULT_CANCELED, returnIntent);
finish();
}
});
btnTakeImg.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if(isIntentAvailable(NewCounterActivity.this, MediaStore.ACTION_IMAGE_CAPTURE)){
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(takePictureIntent,2);
}
else {
Toast.makeText(NewCounterActivity.this, "No Camera Available", Toast.LENGTH_SHORT).show();
}
}
});
}
/************************************************************************************************
* Methods used in this class
* */
public void processInput(){
//Get current Longitude and Latitude
longitude = Double.toString(gps.getLongitude());
latitude = Double.toString(gps.getLatitude());
//Na kraju iskljuci location updatese - ne moze na emulatru jer ja emit coordinate preko DDMS... a kad emit on mora biti ukljucen da bi primio
//gps.stopUsingGPS();
Toast.makeText(getApplicationContext(), "Your Location is - \nLat: " + longitude + "\nLong: " + latitude, Toast.LENGTH_LONG).show();
if (!nameFirst.equals("") && !nameLast.equals("") && !adresse.equals("") && !counterID.equals("") && !counterValue.equals("")
&& counterBitmap != null ){
new DoBackgroundTask(NewCounterActivity.this).execute();
}
else{
// Not all fields are filled
registerErrorMsg.setText("Not all fields are filled");
}
}
//Method to check whether an app can handle your intent
public boolean isIntentAvailable(Context context, String action) {
final PackageManager packageManager = context.getPackageManager();
final Intent intent = new Intent(action);
List<ResolveInfo> list = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
return list.size() > 0;
}
/**************************************************************************************************
*
* When the calling activity, Activity #1, resumes after having called another activity, Activity #2, using startActivityForResult,
* the method onActivityResult in Activity #1 is called BEFORE onResume.
* This is important to know if you are instantiating your SQLite Database objects from within onResume in Activity #1. If so, you will also need to instantiate the object from within onActivityResult,
* when returning from Activity #2.
*
* startActivityForResult() is asynchronous. It can feel synchronous to the user since the UI will change and your calling activity will be paused
* (your onPause() method will be called).
*/
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// TODO Auto-generated method stub
super.onActivityResult(requestCode, resultCode, data);
Log.e("onActivityResult", "fadsfass");
if (requestCode == 2) {
if(resultCode == RESULT_OK){
Bundle extras = data.getExtras();
recievedBitmap = (Bitmap) extras.get("data");
}
if (resultCode == RESULT_CANCELED) {
Toast.makeText(NewCounterActivity.this, "No Image Taken", Toast.LENGTH_SHORT).show();
}
}
}
/**
* Koristim onWindowFocusChanged jer kad se vratim na Activity 1 onda dodje do potpunog recreate Activitija i getWidth()/height() ne mogu dobiti
* ni u jednom od lifecicle methoda - naime ide start onCreate,...onActivityResult(), onResume() - u onactivityResult izvadim bitmap i pohranim ga u receivedBitmap
* te kad getWidth() postane dostupan system invoke ovu dole methodu. :D
*/
#Override
public void onWindowFocusChanged(boolean hasFocus){
if(recievedBitmap != null){
targetW=counterImgView.getWidth();
targetH=counterImgView.getHeight();
Log.e("onWindowFocusChanged", "fadsfass" + " " + targetW + " " + targetH);
// http://stackoverflow.com/questions/4837715/how-to-resize-a-bitmap-in-android
// http://sunil-android.blogspot.com/2013/03/resize-bitmap-bitmapcreatescaledbitmap.html
// Scale or resize Bitmap to ImageView dimensions
counterBitmap = Bitmap.createScaledBitmap(recievedBitmap, targetW, targetH, false);
/**
* Canvas: trying to use a recycled bitmap android.graphics - This exception occurs when you try to recycle a bitmap which is already recycled.
* http://androdevvision.blogspot.com/2011/10/solution-for-out-of-memory-error-and.html
*/
if(recievedBitmap != null && !recievedBitmap.isRecycled()){
recievedBitmap.recycle();
recievedBitmap = null;
}
counterImgView.setImageBitmap(counterBitmap);
}
}
/************************************************************************************************
* Background AsyncTask to create new counterUser - https://github.com/callorico/CustomAsyncTask - najbolje radi
* new DoBackgroundTask(NewCounterActivity.this).execute();
* */
private static class DoBackgroundTask extends CustomAsyncTask<Void, Integer, JSONObject> {
private static final String TAG = "DoBackgroundTask";
private ProgressDialog mProgress;
private int mCurrProgress;
private NewCounterActivity myActivity = null;
public DoBackgroundTask(NewCounterActivity activity) {
super(activity);
}
#Override
protected void onPreExecute() {
super.onPreExecute();
showProgressDialog();
}
#Override
protected void onActivityDetached() {
if (mProgress != null) {
mProgress.dismiss();
mProgress = null;
}
}
#Override
protected void onActivityAttached() {
showProgressDialog();
}
private void showProgressDialog() {
mProgress = new ProgressDialog(mActivity);
mProgress.setProgressStyle(ProgressDialog.STYLE_SPINNER);
mProgress.setIndeterminate(true);
mProgress.setMessage(" Saljem na server... ");
mProgress.setCancelable(true);
mProgress.setOnCancelListener(new OnCancelListener() {
#Override
public void onCancel(DialogInterface dialog) {
cancel(true);
}
});
mProgress.show();
mProgress.setProgress(mCurrProgress);
}
#Override
protected JSONObject doInBackground(Void... params) {
//so you need to either pass an instance of the outer class to the inner class method (or its constructor) as a parameter,
//or create it inside the method.
JSONObject json = null;
if(mActivity != null){
myActivity = (NewCounterActivity) mActivity;
//Prepare counterBitmap as String
ByteArrayOutputStream stream = new ByteArrayOutputStream();
//Write a compressed version of the bitmap to the specified output stream.
myActivity.counterBitmap.compress(Bitmap.CompressFormat.JPEG, 90, stream);
byte [] b_array = stream.toByteArray();
String bitmapString = Base64.encodeBytes(b_array);
//Get workerId from logged worker
Functions workerFunction = new Functions();
DatabaseHandler db = new DatabaseHandler(mActivity);
String workerID = db.retrieveWorker().get("workerId");
if(myActivity != null){
//Get JsonObject from Functions.java
json = workerFunction.newCounterUser(myActivity.counterID, myActivity.counterValue, myActivity.adresse, myActivity.nameFirst, myActivity.nameLast, bitmapString, myActivity.existsDmg, myActivity.countDescript, workerID, myActivity.longitude, myActivity.latitude);
}
}
return json;
}
#Override
protected void onPostExecute(JSONObject jsonObject) {
super.onPostExecute(jsonObject);
if (mActivity != null) {
mProgress.dismiss();
try {
if (jsonObject.getString(KEY_SUCCESS) != null) {
myActivity.registerErrorMsg.setText("");
String res = jsonObject.getString(KEY_SUCCESS);
if(Integer.parseInt(res) == 1){
// counterUser successfully registered
Toast.makeText(mActivity, "New counterUser is created", Toast.LENGTH_LONG).show();
// Return back to MainActivity
Intent returnIntent = new Intent();
returnIntent.putExtra("result",jsonObject.toString());
mActivity.setResult(RESULT_OK,returnIntent);
// Close all views before launching MainActivity
returnIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
mActivity.finish();
}else{
// Error in registration
myActivity.registerErrorMsg.setText("Error occured in registration");
}
}
} catch (JSONException e) {
Log.e("Error","NO Json at all");
e.printStackTrace();
}
} else {
Log.d(TAG, "AsyncTask finished while no Activity was attached.");
}
}
}
Same issues on call to recreate(), when activity has been updated it get onPause after onResume. Tested on emulator, bug exists on Marshallow and below.
This is my fix
private static boolean isRecreate = false;
private void reCreateActivity() {
isRecreate = true;
recreate();
}
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if(isRecreate) {
isRecreate = false;
runOnUiThread(new Runnable() {
#Override
public void run() {
onResume();
}
});
}
}
I don't know if that's correct, but it works.
EDIT
Best solution to avoid this issues, call recreate in postDelayed with 0 delayMillis
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
recreate();
}
}, 0);
It is only normal, that strange code produces strange behavior ...
replace this line:
((MyApplicationClass) getApplication()).detach(this);
with this line:
super.onSaveInstanceState(outState);