I know this is very lame but I'm totally new to android development. I'm writing a sensor based app which will change wallpaper each time the phone is shaked. Once the app is minimized it runs in background.
It works wonderfully when I run it for the first time.But When I minimize it and re-open it looks like 2 instances of the app are running. So it goes on. Every time I minimize and open the app, looks like one more instance is started in parallel.
Problem it causes:
1: Multiple instances of same app listening to "shake"
2: Multiple instances of same app trying to change wallpaper
3: Wallpaper change made by the the last instance of the app is noticeable
I tried setting below:
android:clearTaskOnLaunch="true"
android:launchMode="singleInstance"
Nothing works.
Kindly help.
Below is my activity class & Manifext file
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="abhijit.android.sensor"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="14"
android:targetSdkVersion="19" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.SET_WALLPAPER" />
<application
android:name="abhijit.android.sensor.GlobalVarible"
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="abhijit.android.sensor.SensorTestActivity"
android:label="#string/app_name"
android:clearTaskOnLaunch="true"
android:launchMode="singleInstance" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
SensorTestActivity.java
package abhijit.android.sensor;
import java.io.IOException;
import java.util.Date;
import java.util.Random;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.WallpaperManager;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Color;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.os.Vibrator;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
public class SensorTestActivity extends Activity implements SensorEventListener {
private SensorManager sensorManager;
private boolean color = false;
private View view;
TextView text;
private long lastUpdate;
Random rnd = new Random();
int colors;
private Vibrator vibrate;
GlobalVarible globalVariable;
WallpaperManager myWallpaperManager;
Button b1,b2,b3;
private static String AppVersion ="WALL-e v0.7 (Beta)";
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
// requestWindowFeature(Window.FEATURE_NO_TITLE);
// getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sensor_test);
view = findViewById(R.id.textView);
text = (TextView) findViewById(R.id.textView);
view.setBackgroundColor(Color.GREEN);
b1 = (Button) findViewById(R.id.Enable_button);
b2 = (Button) findViewById(R.id.Dis_button);
b3 = (Button) findViewById(R.id.Exit_button);
sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
vibrate = (Vibrator) getSystemService(VIBRATOR_SERVICE);
myWallpaperManager = WallpaperManager.getInstance(getApplicationContext());
lastUpdate = new Date().getTime();
// Calling Application class (see application tag in AndroidManifest.xml)
globalVariable = (GlobalVarible) getApplicationContext();
//Set name and email in global/application context
globalVariable.setCounter(0);
globalVariable.setWall(1);
System.out.println("Counter set to :" + globalVariable.getCounter());
}
#Override
public void onSensorChanged(SensorEvent event)
{
if ((event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) & globalVariable.appEnabled) {getAccelerometer(event);}
}
private void getAccelerometer(SensorEvent event) {
float[] values = event.values;
// Movement
float x = values[0];
float y = values[1];
float z = values[2];
float accelationSquareRoot = (x * x + y * y + z * z) / (SensorManager.GRAVITY_EARTH * SensorManager.GRAVITY_EARTH);
// long actualTime = event.timestamp;
long actualTime = (new Date()).getTime() + (event.timestamp - System.nanoTime()) / 1000000L;
if (accelationSquareRoot >= 3) //
{
long timediff = actualTime - lastUpdate;
if (timediff < 1000) {
return;
}
// Toast.makeText(this, "Detected device movement & working !!! ", Toast.LENGTH_SHORT).show();
globalVariable.setCounter(globalVariable.getCounter()+1);
vibrate.vibrate(300);
if (color)
{
colors= Color.argb(255, rnd.nextInt(256), rnd.nextInt(256), rnd.nextInt(256));
view.setBackgroundColor(colors);
} else {
view.setBackgroundColor(colors);
}
color = !color;
System.out.println("Counter # " + globalVariable.getCounter());
if(globalVariable.getCounter()==1)
{
globalVariable.setCounter(0);
try
{
switch (globalVariable.getWall())
{
case 1:myWallpaperManager.setResource(R.drawable.wall1);break;
case 2:myWallpaperManager.setResource(R.drawable.wall2);break;
case 3:myWallpaperManager.setResource(R.drawable.wall3);break;
case 4:myWallpaperManager.setResource(R.drawable.wall4);break;
case 5:myWallpaperManager.setResource(R.drawable.wall5);break;
case 6:myWallpaperManager.setResource(R.drawable.wall6);break;
case 7:myWallpaperManager.setResource(R.drawable.wall7);break;
case 8:myWallpaperManager.setResource(R.drawable.wall8);break;
case 9:myWallpaperManager.setResource(R.drawable.wall9);break;
case 10:myWallpaperManager.setResource(R.drawable.wall10);break;
case 11:myWallpaperManager.setResource(R.drawable.wall11);break;
case 12:myWallpaperManager.setResource(R.drawable.wall12);break;
case 13:myWallpaperManager.setResource(R.drawable.wall13);break;
case 14:myWallpaperManager.setResource(R.drawable.wall14);break;
case 15:myWallpaperManager.setResource(R.drawable.wall15);break;
case 16:myWallpaperManager.setResource(R.drawable.wall16);break;
case 17:myWallpaperManager.setResource(R.drawable.wall17);break;
case 18:myWallpaperManager.setResource(R.drawable.wall18);break;
case 19:myWallpaperManager.setResource(R.drawable.wall19);break;
case 20:myWallpaperManager.setResource(R.drawable.wall20);break;
case 21:myWallpaperManager.setResource(R.drawable.wall21);break;
case 22:myWallpaperManager.setResource(R.drawable.wall22);break;
case 23:myWallpaperManager.setResource(R.drawable.wall23);break;
case 24:myWallpaperManager.setResource(R.drawable.wall24);break;
case 25:myWallpaperManager.setResource(R.drawable.wall25);break;
case 26:myWallpaperManager.setResource(R.drawable.wall26);break;
case 27:myWallpaperManager.setResource(R.drawable.wall27);break;
default:break;
}
Toast.makeText(this, "Successfully set as wallpaper to :"+globalVariable.getWall(), Toast.LENGTH_SHORT).show();
System.out.println("Counter : Wallerpaper Set to #"+globalVariable.getWall());
globalVariable.setWall(globalVariable.getWall()+1);
if(globalVariable.getWall()>27) globalVariable.setWall(1);
lastUpdate = actualTime;
} catch (IOException e)
{Toast.makeText(this, "Error set as wallpaper :", Toast.LENGTH_SHORT).show();}
}
}
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
#Override
protected void onResume() {
super.onResume();
// register this class as a listener for the orientation and
// accelerometer sensors
sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),SensorManager.SENSOR_DELAY_NORMAL);
Toast.makeText(this, "Sensor detection resumed !!!", Toast.LENGTH_SHORT).show();
}
#Override
protected void onPause() {
// unregister listener
super.onPause();
// sensorManager.unregisterListener(this);
Toast.makeText(this, "Sensor detection running in background !!!", Toast.LENGTH_SHORT).show();
// System.exit(0);
}
public void b1_Onclick(View v)
{
System.out.println("new Enabled!!!");
globalVariable.appEnabled=true;
sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),SensorManager.SENSOR_DELAY_NORMAL);
text.setText("Application is [ ENABLED ]");
}
public void b2_Onclick(View v)
{
System.out.println("new Disbled!!!");
globalVariable.appEnabled=false;
sensorManager.unregisterListener(this);
text.setText("Application is [ DISABLED ]");
}
public void b3_Onclick(View v)
{
System.out.println("new exit!!!");
sensorManager.unregisterListener(this);
this.onPause();
this.finish();
}
public void b4_Onclick(View view)
{
AlertDialog.Builder dlgAlert = new AlertDialog.Builder(this);
dlgAlert.setMessage(AppVersion + " \n \nCopyright \u00a9 2014, \nAbhijit Mishra");
dlgAlert.setTitle(AppVersion);
dlgAlert.setPositiveButton("OK", null);
dlgAlert.setCancelable(true);
dlgAlert.create().show();
dlgAlert.setPositiveButton("Ok", new DialogInterface.OnClickListener(){public void onClick(DialogInterface dialog, int which){}});
}
}
GlobalVariable.Java
package abhijit.android.sensor;
import android.app.Application;
public class GlobalVarible extends Application{
public int counter,wall;
boolean appEnabled=true;
public int getWall() {
return wall;
}
public void setWall(int wall) {
this.wall = wall;
}
public int getCounter() {
return counter;
}
public void setCounter(int counter) {
this.counter = counter;
}
}
Please let me know what should I do next so that Only 1 thread of the the application runs even if I minimize and bring it back up.
Regards,
Abhijit
The problem is here:
#Override
protected void onResume() {
super.onResume();
// register this class as a listener for the orientation and
// accelerometer sensors
sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),SensorManager.SENSOR_DELAY_NORMAL);
}
#Override
protected void onPause() {
// unregister listener
// sensorManager.unregisterListener(this);
}
You're not unregistering the listener, so it is registered multiple times.
I understand that what you want to do is to keep listening even if the activity is paused, and you could use a boolean flag to only register once. But it's probably not a good idea. Activites that are not visible can be finished by the system at any time.
For these kinds of use cases a Service would be far more appropriate (as a bonus, you could set it up to start on BOOT_COMPLETED, so that you don't need to re-run the app to set up this listener when you restart the device).
So, in short, I would recommend:
Create a Service. In onCreate(), register the service as a listener to SensorManager (very much as you do here).
When your activity runs, send an Intent to start the service (see docs).
Optionally, specify a listener for ACTION_BOOT_COMPLETED so that the service is restarted when the device restarts. See this answer.
Related
Is there a way to create and save text files outside of the Android/data folder in external storage? I am creating a music app and I added a preset manager so users can save, load, and edit preset files (which worked great before Android 11), I don’t want these files to be automatically removed if the app is uninstalled. That would be like if Photoshop deleted all of your Photoshop documents when Photoshop is uninstalled, that’s terrible! These are files that the user saves and they can be deleted separately if the user wants to.
There has to be a way around this but I haven’t been able to find anything that works. ACTION_OPEN_DOCUMENT_TREE looked very promising, until I saw that Android has removed this option too.
https://developer.android.com/about/versions/11/privacy/storage
You can no longer use the ACTION_OPEN_DOCUMENT_TREE intent action to
request access to the following directories:
The root directory of the internal storage volume.
The root directory of each SD card volume that the device manufacturer considers to be reliable, regardless of whether the card
is emulated or removable. A reliable volume is one that an app can
successfully access most of the time.
The Download directory.
I’ve read Google Play only allows MANAGE_EXTERNAL_STORAGE in apps that need it (like file browsers, or anti-virus, etc) which likely will not work in my case. I don’t want to rely on only targeting older API’s so requestLegacyExternalStorage won’t work either.
Everything I’ve looked into appears to be a dead end. Is there anything else I can do?
Here is a short test program (I’m using LibGDX), which at the moment can only save to the root location:
Android/data/com.mygdx.filetest/files/
[core] FileTest.java
package com.mygdx.filetest;
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Files;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.utils.ScreenUtils;
public class FileTest implements ApplicationListener {
private NativePermissions permissions;
private Files.FileType fileType;
private String directory = "TestDir/";
private String name = "text.txt";
public FileTest(final NativePermissions permissions){
this.permissions = permissions;
}
#Override
public void create(){
fileType = getFileType();
if (permissions != null){
permissions.checkExternalStoragePermission();
} else {
permissionGranted();
}
}
private Files.FileType getFileType(){
switch(Gdx.app.getType()) {
case Android:
return Files.FileType.External;
default:
return Files.FileType.Local;
}
}
#Override public void render(){ ScreenUtils.clear(0.4f, 0.4f, 0.4f, 1); }
#Override public void resize(int width, int height) {}
#Override public void pause(){}
#Override public void resume(){}
#Override public void dispose (){}
public void permissionGranted() {
FileHandle fileHandle = Gdx.files.getFileHandle(directory+name, fileType);
if (fileHandle!=null) fileHandle.writeString("test", false);
}
}
[android] AndroidLauncher.java
package com.mygdx.filetest;
import android.Manifest;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.widget.Toast;
import androidx.core.app.ActivityCompat;
import com.badlogic.gdx.backends.android.AndroidApplication;
import com.badlogic.gdx.backends.android.AndroidApplicationConfiguration;
public class AndroidLauncher extends AndroidApplication {
private final FileTest application;
private final int STORAGE_PERMISSION_CODE = 1;
private boolean dialogBoxShowing = false;
public AndroidLauncher(){
final AndroidPermissions permissions = new AndroidPermissions(this);
application = new FileTest(permissions);
}
#Override
protected void onCreate (Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AndroidApplicationConfiguration config = new AndroidApplicationConfiguration();
initialize(application, config);
}
#Override
public void onRequestPermissionsResult(final int requestCode, final String permissions[], final int[] grantResults) {
dialogBoxShowing = false;
if (requestCode == STORAGE_PERMISSION_CODE) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this, "Permission GRANTED", Toast.LENGTH_SHORT).show();
permissionGranted();
} else {
Toast.makeText(this, "Permission DENIED", Toast.LENGTH_SHORT).show();
}
}
}
public void promptExternalStoragePermission() {
if (dialogBoxShowing) return;
dialogBoxShowing = true;
this.runOnUiThread(new Runnable() {
#Override
public void run() {
final AlertDialog.Builder builder = new AlertDialog.Builder(AndroidLauncher.this);
builder.setMessage("To save user presets and custom settings, allow access to your phone’s storage.");
builder.setCancelable(false);
// reverse these buttons to put "NO" on left and "YES" on right
builder.setPositiveButton("NOT NOW", new DialogInterface.OnClickListener(){
#Override
public void onClick(DialogInterface dialog, int which) {
dialogBoxShowing = false;
dialog.dismiss();
}
});
builder.setNegativeButton("CONTINUE", new DialogInterface.OnClickListener(){
#Override
public void onClick(DialogInterface dialog, int which) {
ActivityCompat.requestPermissions(AndroidLauncher.this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, STORAGE_PERMISSION_CODE);
}
});
builder.create().show();
}
});
}
public void permissionGranted(){ application.permissionGranted(); }
}
[core] NativePermissions.java
package com.mygdx.filetest;
public interface NativePermissions {
public void checkExternalStoragePermission();
}
[android] AndroidPermissions.java
package com.mygdx.filetest;
import android.Manifest;
import android.content.pm.PackageManager;
import androidx.core.content.ContextCompat;
public class AndroidPermissions implements NativePermissions {
private final AndroidLauncher context;
public AndroidPermissions(final AndroidLauncher context){
this.context = context;
}
#Override
public void checkExternalStoragePermission() {
if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED){
context.permissionGranted();
} else {
context.promptExternalStoragePermission();
}
}
}
[android] AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mygdx.filetest">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:isGame="true"
android:appCategory="game"
android:label="#string/app_name"
android:theme="#style/GdxTheme" >
<activity
android:name="com.mygdx.filetest.AndroidLauncher"
android:label="#string/app_name"
android:screenOrientation="fullUser"
android:configChanges="keyboard|keyboardHidden|navigation|orientation|screenSize|screenLayout">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
A lott of fuss.
You can in the classic way save your files to the public Documents directory.
Or use SAF with ACTION_OPEN_DOCUMENT_TREE for that directory.
Both dont need 'all files access'.
There is no error when gradle. When it is install on the virtual machine, the program cannot run and the following error is displayed. This is the code. This is Android development.
MainActivity.java
package com.example.app;
import java.util.List;
import android.annotation.SuppressLint;
import android.graphics.Color;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.content.Intent;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import com.esri.arcgisruntime.data.ArcGISFeature;
import com.esri.arcgisruntime.data.FeatureEditResult;
import com.esri.arcgisruntime.loadable.LoadStatus;
import com.esri.arcgisruntime.mapping.Basemap;
import com.esri.arcgisruntime.concurrent.ListenableFuture;
import com.esri.arcgisruntime.layers.ArcGISTiledLayer;
import com.esri.arcgisruntime.data.ServiceFeatureTable;
import com.esri.arcgisruntime.layers.FeatureLayer;
import com.esri.arcgisruntime.mapping.ArcGISMap;
import com.esri.arcgisruntime.mapping.GeoElement;
import com.esri.arcgisruntime.mapping.Viewpoint;
import com.esri.arcgisruntime.mapping.view.Callout;
import com.esri.arcgisruntime.mapping.view.DefaultMapViewOnTouchListener;
import com.esri.arcgisruntime.mapping.view.IdentifyLayerResult;
import com.esri.arcgisruntime.mapping.view.MapView;
import com.google.android.material.snackbar.Snackbar;
import android.app.ProgressDialog;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private MapView mMapView;
private Callout mCallout;
private ArcGISFeature mSelectedArcGISFeature;
private static final String TAG = MainActivity.class.getSimpleName();
private android.graphics.Point mClickPoint;
private Snackbar mSnackbarSuccess;
private Snackbar mSnackbarFailure;
private String mSelectedArcGISFeatureAttributeValue;
private boolean mFeatureUpdated;
private View mCoordinatorLayout;
private ProgressDialog mProgressDialog;
private ServiceFeatureTable mServiceFeatureTable;
private FeatureLayer mFeatureLayer;
#SuppressLint("ClickableViewAccessibility")
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mMapView = findViewById(R.id.mapView);
String url = "http://map.geoq.cn/arcgis/rest/services/ChinaOnlineCommunity/MapServer";
ArcGISTiledLayer arcGISTiledLayer = new ArcGISTiledLayer(url);
Basemap basemap = new Basemap(arcGISTiledLayer);
ArcGISMap arcGISMap = new ArcGISMap(basemap);
mCallout = mMapView.getCallout();
ServiceFeatureTable mServiceFeatureTable = new ServiceFeatureTable("http://192.168.1.103:6080/arcgis/rest/services/huda1/MapServer/0");
FeatureLayer featureLayer = new FeatureLayer(mServiceFeatureTable);
arcGISMap.getOperationalLayers().add(featureLayer);
mFeatureLayer.setOpacity(0.8f);*/
mMapView.setMap(arcGISMap);
mMapView.setViewpoint(new Viewpoint(30.577421, 114.331955, 10000));
//set an on touch listener to listen for click events
// set an on touch listener to listen for click events
mMapView.setOnTouchListener(new DefaultMapViewOnTouchListener(this, mMapView) {
#Override
public boolean onSingleTapConfirmed(MotionEvent e) {
// get the point that was clicked and convert it to a point in map coordinates
mClickPoint = new android.graphics.Point((int) e.getX(), (int) e.getY());
// clear any previous selection
mFeatureLayer.clearSelection();
mSelectedArcGISFeature = null;
mCallout.dismiss();
// identify the GeoElements in the given layer
final ListenableFuture<IdentifyLayerResult> identifyFuture = mMapView
.identifyLayerAsync(mFeatureLayer, mClickPoint, 5, false, 1);
// add done loading listener to fire when the selection returns
identifyFuture.addDoneListener(() -> {
try {
// call get on the future to get the result
IdentifyLayerResult layerResult = identifyFuture.get();
List<GeoElement> resultGeoElements = layerResult.getElements();
if (!resultGeoElements.isEmpty()) {
if (resultGeoElements.get(0) instanceof ArcGISFeature) {
mSelectedArcGISFeature = (ArcGISFeature) resultGeoElements.get(0);
// highlight the selected feature
mFeatureLayer.selectFeature(mSelectedArcGISFeature);
// show callout with the value for the attribute "typdamage" of the selected feature
mSelectedArcGISFeatureAttributeValue = (String) mSelectedArcGISFeature.getAttributes()
.get("typdamage");
showCallout(mSelectedArcGISFeatureAttributeValue);
Toast.makeText(MainActivity.this, "Tap on the info button to change attribute value",
Toast.LENGTH_SHORT).show();
}
} else {
// none of the features on the map were selected
mCallout.dismiss();
}
} catch (Exception e1) {
Log.e(TAG, "Select feature failed: " + e1.getMessage());
}
});
return super.onSingleTapConfirmed(e);
}
});
mSnackbarSuccess = Snackbar
.make(mCoordinatorLayout, "Feature successfully updated", Snackbar.LENGTH_LONG)
.setAction("UNDO", view -> {
String snackBarText = updateAttributes(mSelectedArcGISFeatureAttributeValue) ?
"Feature is restored!" :
"Feature restore failed!";
Snackbar snackbar1 = Snackbar.make(mCoordinatorLayout, snackBarText, Snackbar.LENGTH_SHORT);
snackbar1.show();
});
mSnackbarFailure = Snackbar.make(mCoordinatorLayout, "Feature update failed", Snackbar.LENGTH_LONG);
}
/**
* Function to read the result from newly created activity
*/
#Override
protected void onActivityResult(int requestCode,
int resultCode, final Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == 100) {
// display progress dialog while updating attribute callout
mProgressDialog.show();
updateAttributes(data.getStringExtra("typdamage"));
}
}
/**
* Applies changes to the feature, Service Feature Table, and server.
*/
private boolean updateAttributes(final String typeDamage) {
// load the selected feature
mSelectedArcGISFeature.loadAsync();
// update the selected feature
mSelectedArcGISFeature.addDoneLoadingListener(() -> {
if (mSelectedArcGISFeature.getLoadStatus() == LoadStatus.FAILED_TO_LOAD) {
Log.e(TAG, "Error while loading feature");
}
// update the Attributes map with the new selected value for "typdamage"
mSelectedArcGISFeature.getAttributes().put("typdamage", typeDamage);
try {
// update feature in the feature table
ListenableFuture<Void> mapViewResult = mServiceFeatureTable.updateFeatureAsync(mSelectedArcGISFeature);
/*mServiceFeatureTable.updateFeatureAsync(mSelectedArcGISFeature).addDoneListener(new Runnable() {*/
mapViewResult.addDoneListener(() -> {
// apply change to the server
final ListenableFuture<List<FeatureEditResult>> serverResult = mServiceFeatureTable.applyEditsAsync();
serverResult.addDoneListener(() -> {
try {
// check if server result successful
List<FeatureEditResult> edits = serverResult.get();
if (!edits.isEmpty()) {
if (!edits.get(0).hasCompletedWithErrors()) {
Log.e(TAG, "Feature successfully updated");
mSnackbarSuccess.show();
mFeatureUpdated = true;
}
} else {
Log.e(TAG, "The attribute type was not changed");
mSnackbarFailure.show();
mFeatureUpdated = false;
}
if (mProgressDialog.isShowing()) {
mProgressDialog.dismiss();
// display the callout with the updated value
showCallout((String) mSelectedArcGISFeature.getAttributes().get("typdamage"));
}
} catch (Exception e) {
Log.e(TAG, "applying changes to the server failed: " + e.getMessage());
}
});
});
} catch (Exception e) {
Log.e(TAG, "updating feature in the feature table failed: " + e.getMessage());
}
});
return mFeatureUpdated;
}
/**
* Displays Callout
*
* #param title the text to show in the Callout
*/
private void showCallout(String title) {
// create a text view for the callout
RelativeLayout calloutLayout = new RelativeLayout(getApplicationContext());
TextView calloutContent = new TextView(getApplicationContext());
calloutContent.setId(R.id.textview);
calloutContent.setTextColor(Color.BLACK);
calloutContent.setTextSize(18);
calloutContent.setPadding(0, 10, 10, 0);
calloutContent.setText(title);
RelativeLayout.LayoutParams relativeParams = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
relativeParams.addRule(RelativeLayout.RIGHT_OF, calloutContent.getId());
// create image view for the callout
ImageView imageView = new ImageView(getApplicationContext());
imageView
.setImageDrawable(ContextCompat.getDrawable(getApplicationContext(), R.drawable.ic_info_outline_black_18dp));
imageView.setLayoutParams(relativeParams);
imageView.setOnClickListener(new ImageViewOnclickListener());
calloutLayout.addView(calloutContent);
calloutLayout.addView(imageView);
mCallout.setGeoElement(mSelectedArcGISFeature, null);
mCallout.setContent(calloutLayout);
mCallout.show();
}
#Override
protected void onPause() {
mMapView.pause();
super.onPause();
}
#Override
protected void onResume() {
super.onResume();
mMapView.resume();
}
#Override
protected void onDestroy() {
mMapView.dispose();
super.onDestroy();
}
/**
* Defines the listener for the ImageView clicks
*/
private class ImageViewOnclickListener implements View.OnClickListener {
#Override public void onClick(View v) {
Intent myIntent = new Intent(MainActivity.this, DamageTypesListActivity.class);
startActivityForResult(myIntent, 100);
}
}
}
DamageTypesListActivity.java
package com.example.app;
import android.content.Intent;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
/**
* Displays the Damage type options in a ListView.
*/
public class DamageTypesListActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.damage_types_listview);
final String[] damageTypes = getResources().getStringArray(R.array.damage_types);
ListView listView = (ListView) findViewById(R.id.listview);
listView.setAdapter(new ArrayAdapter<>(this, R.layout.damage_types, damageTypes));
listView.setTextFilterEnabled(true);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
Intent myIntent = new Intent();
myIntent.putExtra("typdamage", damageTypes[position]); //Optional parameters
setResult(100, myIntent);
finish();
}
});
}
#Override
public void onBackPressed() {
}
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.app">
<uses-permission android:name="android.permission.INTERNET" />
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
<application
android:allowBackup="false"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/Theme.DisplayAMapJava">
<activity
android:name=".MainActivity"
android:label="#string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".DamageTypesListActivity"
android:label="#string/select_damage_Type">
</activity>
</application>
</manifest>
strings.xml
<resources>
<string name="app_name">Display a map java</string>
<string name="select_damage_Type">"Select new damage type"</string>
<!-- Progress dialog messages -->
<string name="progress_title">Updating attribute type</string>
<string name="progress_message">Please wait…</string>
<!-- Sample Strings -->
<string name="sample_service_url">https://sampleserver6.arcgisonline.com/arcgis/rest/services/DamageAssessment/FeatureServer/0</string>
<string-array name="damage_types">
<item>Destroyed</item>
<item>Major</item>
<item>Minor</item>
<item>Affected</item>
<item>Inaccessible</item>
</string-array>
</resources>
below is logcat message
AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.app, PID: 21571
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.app/com.example.app.MainActivity}: java.lang.IllegalArgumentException: No suitable parent found from the given view. Please provide a valid view.
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3270)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3409)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2016)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
Caused by: java.lang.IllegalArgumentException: No suitable parent found from the given view. Please provide a valid view.
at com.google.android.material.snackbar.Snackbar.make(Snackbar.java:158)
at com.example.app.MainActivity.onCreate(MainActivity.java:137)
at android.app.Activity.performCreate(Activity.java:7802)
at android.app.Activity.performCreate(Activity.java:7791)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1299)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3245)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3409)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2016)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
Initialize the CoordinatorLayout (mCoordinatorLayout) before using it in Snackbar.make()
Use findViewById to initialize mCoordinatorLayout
mCoordinatorLayout = findViewById(layout_id)
So I've been playing around with Android M style permissions and I have a working demo so long as I do everything from within the MainActivity. I felt that it looked to cumbersome and wanted to split that in to multiple files so it looked a little cleaner and could be more easily maintained.
The problem is by moving the permissions checking into its own file, I am having trouble now getting my permissions checker to go back to MainActivity and launch the demo() method. Where I need to call back to main activities demo method is on line 73 and line 114 of the PermissionsChecker.java
NOTE: I followed this tutorial on Android Permissions
I have found this question as well as these questions here & here. I realize this is dangerously close to a duplicate of the last two, they got me close but I haven't quite been able to get it. If someone can break it down a little more for me, I am still fairly new to Java and Android.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest package="me.johnweland.androidrtp"
xmlns:android="http://schemas.android.com/apk/res/android">
<!-- // Permissions -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<!-- // Features -->
<uses-feature android:name="android.hardware.camera" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
MainActivity.java
package me.johnweland.androidrtp;
import android.Manifest;
import android.annotation.TargetApi;
import android.content.pm.PackageManager;
import android.os.Build;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Toast;
import java.util.HashMap;
import java.util.Map;
public class MainActivity extends AppCompatActivity {
final private int REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (Build.VERSION.SDK_INT >= 23) {
// Marshmallow+
PermissionChecker permissions = PermissionChecker.getInstance(this);
permissions.permissionsCheck();
} else {
// Pre-Marshmallow
demo();
}
}
#TargetApi(Build.VERSION_CODES.M)
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode) {
case REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS:
{
Map<String, Integer> perms = new HashMap<String, Integer>();
// Initial
perms.put(Manifest.permission.RECORD_AUDIO, PackageManager.PERMISSION_GRANTED);
perms.put(Manifest.permission.CAMERA, PackageManager.PERMISSION_GRANTED);
// Fill with results
for (int i = 0; i < permissions.length; i++)
perms.put(permissions[i], grantResults[i]);
// Check for RECORD_AUDIO
if (perms.get(Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED
&& perms.get(Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
// All Permissions Granted
demo();
}
else {
// Permission Denied
Toast.makeText(this, R.string.permission_denied_message, Toast.LENGTH_SHORT).show();
}
}
break;
default:
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
protected void demo() {
Toast.makeText(this, "Demo toast", Toast.LENGTH_LONG).show();
}
}
PermissionsChecker.java
package me.johnweland.androidrtp;
import android.Manifest;
import android.annotation.TargetApi;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.os.Build;
import java.util.ArrayList;
import java.util.List;
/**
* Created by jweland on 12/11/2015.
*/
public class PermissionChecker {
private static final String TAG = PermissionChecker.class.getSimpleName();
final private int REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS = 0;
private MainActivity mainActivity;
private static PermissionChecker instance = null;
private PermissionChecker(MainActivity activity) {
mainActivity = activity;
}
static public PermissionChecker getInstance(MainActivity activity) {
if (instance == null) {
instance = new PermissionChecker(activity);
return instance;
} else {
return instance;
}
}
#TargetApi(Build.VERSION_CODES.M)
protected void permissionsCheck(){
List<String> permissionsNeeded = new ArrayList<String>();
final List<String> permissionsList = new ArrayList<String>();
// Add permission check for any permission that is not NORMAL_PERMISSIONS
if(!addPermission(permissionsList, Manifest.permission.RECORD_AUDIO))
permissionsNeeded.add(mainActivity.getString(R.string.permission_microphone));
if(!addPermission(permissionsList, Manifest.permission.CAMERA))
permissionsNeeded.add(mainActivity.getString(R.string.permission_camera));
if(permissionsList.size() > 0) {
if(permissionsNeeded.size() > 0) {
// Need Rationale
String message = mainActivity.getString(R.string.permission_grant_message) + permissionsNeeded.get(0);
for (int i = 1; i < permissionsNeeded.size(); i++)
message = message + "\n" +permissionsNeeded.get(i);
showMessageOKCancel(message,
new DialogInterface.OnClickListener() {
#TargetApi(Build.VERSION_CODES.M)
#Override
public void onClick(DialogInterface dialog, int which) {
mainActivity.requestPermissions(permissionsList.toArray(new String[permissionsList.size()]),
REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
}
});
return;
}
mainActivity.requestPermissions(permissionsList.toArray(new String[permissionsList.size()]),
REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
return;
}
mainActivity.demo();
}
private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) {
new AlertDialog.Builder(mainActivity)
.setMessage(message)
.setPositiveButton(R.string.dialog_ok_button_text, okListener)
.setNegativeButton(R.string.dialog_cancel_button_text, null)
.create()
.show();
}
#TargetApi(Build.VERSION_CODES.M)
private boolean addPermission(List<String> permissionsList, String permission) {
if (mainActivity.checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
permissionsList.add(permission);
// Check for Rationale Option
if (!mainActivity.shouldShowRequestPermissionRationale(permission))
return false;
}
return true;
}
}
Initially in the last code block on lines 73 and 114 I had tried something to the effect of "MainActivity.demo();" where "demo();" is the method I wish to call.
You can make your class ask for your activity as a parameter, save it on a variable and call it's method whenever you want (assuming that your class and activity run on the same thread).
public class MyActivity extends AppCompatActivity
{
public void myFunction() {/* ... */}
}
public class MyClass
{
MyActivity activity;
//This could be the constructor
public void someFunction(MyActivity gActivity)
{
activity = gActivity;
}
public void anotherFunction()
{
//From activity you can get everything you want like context
//resources and anything else
activity.myFunction();
}
}
Another way you could do it is by creating an interface which saves a function (on your activity) and passes that interface as argument to your class and calls it whenever you want, but that's slightly more complicated, the first method is better in my opinion.
I am currently working with ActivityRecognitionClient , but unfortunately that Google has announced that the class had been deprecated and to use GoogleApiClient instead.
Not sure if I am doing it wrong or not, I am getting confused with the new API file. I have imported the Google Play Libraries, setup the API v2 key. I followed an online source on coding up the ActivityRecognitionClient version.
Below are the codes of the different files, whenever I switch tab to the actRecog it crashes and points the error to this line with a null pointer exception.
mActivityRecognitionPendingIntent = PendingIntent.getService(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
Full source code below : (API v2 keys are intentionally hidden for privacy purposes.)
MainActivity.java
package com.example.healthgps;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.app.Activity;
import android.app.TabActivity;
import android.content.Context;
import android.content.Intent;
import android.widget.TabHost;
import android.widget.TabHost.TabSpec;
public class MainActivity extends TabActivity {
TabHost mTabHost;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTabHost = getTabHost();
TabSpec firstSpec = mTabHost.newTabSpec("Stats");
firstSpec.setIndicator("Stats");
Intent firstIntent = new Intent(this, FirstActivity.class);
firstSpec.setContent(firstIntent);
TabSpec thirdSpec = mTabHost.newTabSpec("ActRecog");
thirdSpec.setIndicator("ActRecog");
Intent thirdIntent = new Intent(this, activityrecignition.class);
thirdSpec.setContent(thirdIntent);
mTabHost.addTab(firstSpec);
mTabHost.addTab(thirdSpec);
}
}
Manifest XML File :
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.healthgps"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="11"
android:targetSdkVersion="17" />
<permission
android:name="com.example.healthgps.permission.MAPS_RECEIVE"
android:protectionLevel="signature" />
<uses-feature
android:glEsVersion="0x00020000"
android:required="true" />
<uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION"/>
<uses-permission android:name="com.example.healthgps.permission.MAPS_RECEIVE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<meta-data
android:name="com.google.android.maps.v2.API_KEY"
android:value="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" />
<activity
android:name="com.example.healthgps.MainActivity"
android:label="Health Kit"
android:screenOrientation="portrait" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".FirstActivity"
android:label="Health Kit" >
</activity>
<activity
android:name=".activityrecignition"
android:label="Health Kit" >
</activity>
<service
android:name="com.example.healthgps.ActivityRecognitionIntentService"
android:label="#string/app_name"
android:exported="false">
</service>
<meta-data
android:name="com.google.android.gms.version"
android:value="#integer/google_play_services_version" />
</application>
</manifest>
activityrecignition.java (purposely typo for the name)
package com.example.healthgps;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Dialog;
import android.app.DialogFragment;
import android.app.IntentService;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender.SendIntentException;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks;
import com.google.android.gms.common.GooglePlayServicesClient.OnConnectionFailedListener;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.ActivityRecognition;
import com.google.android.gms.location.ActivityRecognitionResult;
import com.google.android.gms.location.DetectedActivity;
public class activityrecignition extends FragmentActivity implements ConnectionCallbacks,OnConnectionFailedListener {
public static final int MILLISECONDS_PER_SECOND = 1000;
public static final int DETECTION_INTERVAL_SECONDS = 20;
public static final int DETECTION_INTERVAL_MILLISECONDS =
MILLISECONDS_PER_SECOND * DETECTION_INTERVAL_SECONDS;
private final static int
CONNECTION_FAILURE_RESOLUTION_REQUEST = 9000;
private PendingIntent mActivityRecognitionPendingIntent;
// Store the current activity recognition client
private GoogleApiClient mGoogleApiClient;
private Context mContext;
private Intent intent;
TextView tv;
ActivityRecognitionIntentService ar;
private boolean mInProgress;
public enum REQUEST_TYPE {START, STOP}
private REQUEST_TYPE mRequestType;
Intent i;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.third);
mActivityRecognitionPendingIntent = PendingIntent.getService(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
tv=(TextView) findViewById(R.id.activityname);
mInProgress=false;
ar.onHandleIntent(i);
}
#SuppressLint("NewApi") public static class ErrorDialogFragment extends DialogFragment {
private Dialog mDialog;
#SuppressLint("NewApi") public ErrorDialogFragment() {
super();
mDialog = null;
}
public void setDialog(Dialog dialog) {
mDialog = dialog;
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return mDialog;
}
public void show(FragmentManager supportFragmentManager, String tag) {
}
}
#Override
protected void onActivityResult(
int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case CONNECTION_FAILURE_RESOLUTION_REQUEST :
switch (resultCode) {
case Activity.RESULT_OK :
break;
}
}
}
private boolean servicesConnected() {
int resultCode =
GooglePlayServicesUtil.
isGooglePlayServicesAvailable(this);
if (ConnectionResult.SUCCESS == resultCode) {
Log.d("Activity Recognition", "Google Play services is available.");
return true;
} else {
Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(
resultCode,
this,
CONNECTION_FAILURE_RESOLUTION_REQUEST);
if (errorDialog != null) {
// Create a new DialogFragment for the error dialog
ErrorDialogFragment errorFragment = new ErrorDialogFragment();
// Set the dialog in the DialogFragment
errorFragment.setDialog(errorDialog);
// Show the error dialog in the DialogFragment
errorFragment.show(
getSupportFragmentManager(),
"Activity Recognition");
}
return false;
}
}
public class ActivityRecognitionIntentService extends IntentService {
public ActivityRecognitionIntentService(String name) {
super(name);
// TODO Auto-generated constructor stub
}
private String getNameFromType(int activityType) {
switch(activityType) {
case DetectedActivity.IN_VEHICLE:
return "in_vehicle";
case DetectedActivity.ON_BICYCLE:
return "on_bicycle";
case DetectedActivity.ON_FOOT:
return "on_foot";
case DetectedActivity.STILL:
return "still";
case DetectedActivity.UNKNOWN:
return "unknown";
case DetectedActivity.TILTING:
return "tilting";
}
return "unknown";
}
#Override
protected void onHandleIntent(Intent intent) {
// If the incoming intent contains an update
if (ActivityRecognitionResult.hasResult(intent)) {
// Get the update
ActivityRecognitionResult result =
ActivityRecognitionResult.extractResult(intent);
// Get the most probable activity
DetectedActivity mostProbableActivity =
result.getMostProbableActivity();
/*
* Get the probability that this activity is the
* the user's actual activity
*/
int confidence = mostProbableActivity.getConfidence();
/*
* Get an integer describing the type of activity
*/
int activityType = mostProbableActivity.getType();
String activityName = getNameFromType(activityType);
tv.setText(activityName);
/*
* At this point, you have retrieved all the information
* for the current update. You can display this
* information to the user in a notification, or
* send it to an Activity or Service in a broadcast
* Intent.
*/
} else {
/*
* This implementation ignores intents that don't contain
* an activity update. If you wish, you can report them as
* errors.
*/
tv.setText("There are no updates!!!");
}
}
}
public void onClick(View v){
if(v.getId()==R.id.Start){
startUpdates();
}
if(v.getId()==R.id.Stop){
stopUpdates();
}
}
public void startUpdates() {
// Check for Google Play services
mRequestType = REQUEST_TYPE.START;
if (!servicesConnected()) {
return;
}
// If a request is not already underway
if (!mInProgress) {
// Indicate that a request is in progress
mInProgress = true;
// Request a connection to Location Services
mGoogleApiClient.connect();
//
} else {
/*
* A request is already underway. You can handle
* this situation by disconnecting the client,
* re-setting the flag, and then re-trying the
* request.
*/
mInProgress = true;
mGoogleApiClient.disconnect();
ActivityRecognition.ActivityRecognitionApi.requestActivityUpdates(mGoogleApiClient, DETECTION_INTERVAL_MILLISECONDS, mActivityRecognitionPendingIntent);
}
}
#Override
public void onConnected(Bundle dataBundle) {
// TODO Auto-generated method stub
ActivityRecognition.ActivityRecognitionApi.requestActivityUpdates(mGoogleApiClient, DETECTION_INTERVAL_MILLISECONDS, mActivityRecognitionPendingIntent);
/*
* Since the preceding call is synchronous, turn off the
* in progress flag and disconnect the client
*/
mInProgress = false;
mGoogleApiClient.disconnect();
switch (mRequestType) {
case START :
/*
* Request activity recognition updates using the
* preset detection interval and PendingIntent.
* This call is synchronous.
*/
ActivityRecognition.ActivityRecognitionApi.requestActivityUpdates(mGoogleApiClient, DETECTION_INTERVAL_MILLISECONDS, mActivityRecognitionPendingIntent);
break;
case STOP :
ActivityRecognition.ActivityRecognitionApi.removeActivityUpdates(mGoogleApiClient, mActivityRecognitionPendingIntent);
/*
* An enum was added to the definition of REQUEST_TYPE,
* but it doesn't match a known case. Throw an exception.
*/
default :
try {
throw new Exception("Unknown request type in onConnected().");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
}
}
#Override
public void onDisconnected() {
// TODO Auto-generated method stub
mInProgress = false;
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
// Turn off the request flag
mInProgress = false;
/*
* If the error has a resolution, start a Google Play services
* activity to resolve it.
*/
if (connectionResult.hasResolution()) {
try {
connectionResult.startResolutionForResult(
this,
CONNECTION_FAILURE_RESOLUTION_REQUEST);
} catch (SendIntentException e) {
// Log the error
e.printStackTrace();
}
// If no resolution is available, display an error dialog
} else {
// Get the error code
int errorCode = connectionResult.getErrorCode();
// Get the error dialog from Google Play services
Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(
errorCode,
this,
CONNECTION_FAILURE_RESOLUTION_REQUEST);
// If Google Play services can provide an error dialog
if (errorDialog != null) {
// Create a new DialogFragment for the error dialog
ErrorDialogFragment errorFragment =
new ErrorDialogFragment();
// Set the dialog in the DialogFragment
errorFragment.setDialog(errorDialog);
// Show the error dialog in the DialogFragment
errorFragment.show(
getSupportFragmentManager(),
"Activity Recognition");
}
}
}
public void stopUpdates() {
// Set the request type to STOP
mRequestType = REQUEST_TYPE.STOP;
/*
* Test for Google Play services after setting the request type.
* If Google Play services isn't present, the request can be
* restarted.
*/
if (!servicesConnected()) {
return;
}
// If a request is not already underway
if (!mInProgress) {
// Indicate that a request is in progress
mInProgress = true;
// Request a connection to Location Services
mGoogleApiClient.connect();
//
} else {
/*
* A request is already underway. You can handle
* this situation by disconnecting the client,
* re-setting the flag, and then re-trying the
* request.
*/
}
}
}
In the old code, it contains a part where the ActivityRecognitionClient requires an instantiation, but GoogleApiClient doesn't have.
Is there anyone who manage to switch over to the new API already ? I need some guide to get it there.
Thanks.
you must use GoogleApiClient.Builder in the following way
GoogleApiClient.Builder builder = new GoogleApiClient.Builder(<context>)
.addApi(<some api, i.e LocationServices.API>)
.addConnectionCallbacks(new ConnectionCallbacks() {
#Override
public void onConnectionSuspended(int arg) {}
#Override
public void onConnected(Bundle arg0) {
Intent intent = new Intent(getApplicationContext(), ActivityRecognitionService.class); // your custom ARS class
mPendingIntent = PendingIntent.getService(getApplicationContext(), 0, intent,PendingIntent.FLAG_UPDATE_CURRENT);
ActivityRecognition.ActivityRecognitionApi
.requestActivityUpdates(mGoogleApiClient, ACTIVITY_RECOGNITION_INTERVAL, mPendingIntent);}
}
.addOnConnectionFailedListener(new OnConnectionFailedListener() {
#Override
public void onConnectionFailed(ConnectionResult arg0) {
}
});
mGoogleApiClient = builder.build();
mGoogleApiClient.connect();
I have an app that alot of users have helped me on so far and I really appreciate it. Now I have a force close issue when the case is exicuted below. When I click the button 2 it will load the Ship.Class and everything works like I want it too. When I click button1 It will not load Ocean.Class and it force closes. The Ocean.Class is a mirror of the Ship.Class except for the variable names have been updated to reflect the different class. Is using the variable (a) like I am in the switch not something that is allowable?
package com.androidsleepmachine.gamble;
import android.app.Activity;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.Spinner;
public class Ocean extends Activity implements View.OnClickListener {
public static final Integer[] TIME_IN_MINUTES = { 30, 45, 60, 180, 360 };
public MediaPlayer mediaPlayer;
public Handler handler = new Handler();
public Button button1;
public Spinner spinner1;
// Initialize the activity
#Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.ocean);
button1 = (Button) findViewById(R.id.btn1);
button1.setOnClickListener(this);
spinner1 = (Spinner) findViewById(R.id.spinner1);
ArrayAdapter<Integer> adapter = new ArrayAdapter<Integer>(this,
android.R.layout.simple_spinner_item, TIME_IN_MINUTES);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner1.setAdapter(adapter);
}
// Play the sound and start the timer
private void playSound(int resourceId) {
// Cleanup any previous sound files
cleanup();
// Create a new media player instance and start it
mediaPlayer = MediaPlayer.create(this, resourceId);
mediaPlayer.start();
// Create the timer to stop the sound after x number of milliseconds
int selectedTime = TIME_IN_MINUTES[spinner1.getSelectedItemPosition()];
handler.postDelayed(runnable, selectedTime * 60 * 1000);
}
// Handle button callback
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn1:
playSound(R.raw.ocean_birds);
break;
}
}
// Stop the sound and cleanup the media player
public void cleanup() {
if (mediaPlayer != null) {
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer = null;
}
// Cancel any previously running tasks
handler.removeCallbacks(runnable);
}
// Runnable task used by the handler to stop the sound
public Runnable runnable = new Runnable() {
public void run() {
cleanup();
}
};
}
This is what I gathered from the Logcat
09-17 11:11:15.704: I/dalvikvm(1634): | sysTid=1634 nice=0 sched=0/0 cgrp=[fopen- error:2] handle=-1207757760
09-17 11:11:58.690: E/Trace(1665): error opening trace file: No such file or directory (2)
09-17 11:18:47.446: I/dalvikvm(1735): | sysTid=1735 nice=0 sched=0/0 cgrp=[fopen-error:2] handle=-1207757760
09-17 11:39:46.139: E/Trace(1758): error opening trace file: No such file or directory (2)
09-17 11:39:53.705: I/dalvikvm(1758): | sysTid=1758 nice=0 sched=0/0 cgrp=[fopen-error:2] handle=-1207757760
09-17 11:40:00.356: E/Trace(1774): error opening trace file: No such file or directory (2)
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.androidsleepmachine.gamble"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="16"
android:targetSdkVersion="18" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="com.androidsleepmachine.gamble.Home"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="com.androidsleepmachine.gamble.Ship" />
<activity android:name="com.androidsleepmachine.gamble.Ocean" />
</application>
</manifest>
Your Code works perfectly fine. I did try to run it even on the emulator and it's working perfectly fine:
public class Ocean extends Activity implements View.OnClickListener {
public static final Integer[] TIME_IN_MINUTES = { 30, 45, 60, 180, 360 };
public MediaPlayer mediaPlayer;
public Handler handler = new Handler();
public Button button1;
public Spinner spinner1;
// Initialize the activity
#Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.ocean);
button1 = (Button) findViewById(R.id.btn1);
button1.setOnClickListener(this);
spinner1 = (Spinner) findViewById(R.id.spinner1);
ArrayAdapter<Integer> adapter = new ArrayAdapter<Integer>(this,
android.R.layout.simple_spinner_item, TIME_IN_MINUTES);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner1.setAdapter(adapter);
}
// Play the sound and start the timer
private void playSound(int resourceId) {
// Cleanup any previous sound files
cleanup();
// Create a new media player instance and start it
mediaPlayer = MediaPlayer.create(this, resourceId);
mediaPlayer.start();
// Create the timer to stop the sound after x number of milliseconds
int selectedTime = TIME_IN_MINUTES[spinner1.getSelectedItemPosition()];
handler.postDelayed(runnable, selectedTime * 60 * 1000);
}
// Handle button callback
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn1:
playSound(R.raw.test);
/*
* getResources().openRawResource(R.raw.test);
*/break;
}
}
// Stop the sound and cleanup the media player
public void cleanup() {
if (mediaPlayer != null) {
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer = null;
}
// Cancel any previously running tasks
handler.removeCallbacks(runnable);
}
// Runnable task used by the handler to stop the sound
public Runnable runnable = new Runnable() {
public void run() {
cleanup();
}
};
}
The music keeps playing even when the app is closed.
I don't think there's anything wrong with the code.
Check whether you have added Ocean class (activity) in android manifest file .
Check whether you have id same as button1 in the layout .