I'm using this plugin to implement Google Play Games Services in an Ionic React/Capacitor project.
However, when I call GameServices.showLeaderboard("abcdefghijklmnopqr");, logcat show the following:
05-09 07:44:51.544 15437 15524 V Capacitor/Plugin: To native (Capacitor plugin): callbackId: 129018517, pluginId: GameServices, methodName: showLeaderboard
05-09 07:44:51.544 15437 15524 V Capacitor: callback: 129018517, pluginId: GameServices, methodName: showLeaderboard, methodData: {}
05-09 07:44:51.545 15437 15502 W GameServices: showLeaderboard called without providing leaderboardId
As you can see, the methodData is empty.
I have absolutely no idea of java (hence why I use Capacitor), but this is the only piece of code I could find that looks remotely related, in android\src\main\java\GameServices.java, in the GitHub linked.
#PluginMethod()
public void showLeaderboard(final PluginCall call) {
final String leaderboardId = call.getString("leaderboardId");
if (leaderboardId == null) {
Log.w(TAG, "showLeaderboard called without providing leaderboardId");
return;
}
Log.d(TAG, "showLeaderboard called with id: " + leaderboardId);
final GoogleSignInAccount lastSignedInAccount = GoogleSignIn.getLastSignedInAccount(getContext());
if (null == lastSignedInAccount) {
Log.w(TAG,
"cannot find last signed in account, either services are disabled or fingerprint doesn't match services account");
call.resolve();
return;
}
Games.getLeaderboardsClient(getContext(), lastSignedInAccount).getLeaderboardIntent(leaderboardId)
.addOnCompleteListener(task -> {
if (task.isSuccessful()) {
Log.d(TAG, "showLeaderboard:getIntent:success");
saveCall(call);
startActivityForResult(call, task.getResult(), RC_LEADERBOARD_UI);
} else {
Log.e(TAG, "showLeaderboard:getIntent:error");
call.reject("showLeaderboard:getIntent:error");
}
});
}
Could you please help me work around this issue? It seems to be the only Google Play Games Capacitor plugin that remotely works...
Edit 1:
I should note, that the GameServices.signIn(); method works just fine.
Edit2:
Trying to call GameServices.unlockAchievement("abcdef"); didn't work either, logging Achievement ID must not be null or empty. Apparently no method, that needs an argument, works.
After rifling through all of the plugins code, I realized the methods expect objects as parameters.
This works:
GameServices.showLeaderboard({leaderboardId: "abcdefghijklmnopqr"});
Related
I've integrated UnityAds on my Android app (that is not published yet).
I get app id and placement id from database on my server.
App id and placement id are correct, I've copied and pasted about 30 times for be sure of it.
So, when I try to get an ad in test mode, it give me the INVALID_ARGUMENT error.
Here an explaination of the error code by Unity, but as you can see it is a little generic.
I have an object that simply represents an ad service (like admob, FAN, inmobi etc)
In this case the object is called advert, and here it's how I show an ad with Unity:
protected void showUnity(){
UnityAds.initialize(this, advert.getApiKey(), true); //advert.getApiKey() returns the app id
UnityAds.addListener(new IUnityAdsListener() {
#Override
public void onUnityAdsReady(String s) {
Log.i(TAG, "onUnityAdsReady "+s);
if(s.equals(advert.getUnitId()) && !unityReady)
UnityAds.show(ActivityAd.this, advert.getUnitId()); //advert.getUnitId() returns the placement id
}
#Override
public void onUnityAdsStart(String s) {
Log.i(TAG, "onUnityAdsStart "+s);
unityReady = true;
}
#Override
public void onUnityAdsFinish(String s, UnityAds.FinishState finishState) {
if (finishState.compareTo(UnityAds.FinishState.COMPLETED) == 0) {
onAdReward(); //my callback for reward
} else if (finishState.compareTo(UnityAds.FinishState.SKIPPED) == 0) {
onAdClosed(); //my callback for ad close
} else if (finishState.compareTo(UnityAds.FinishState.ERROR) == 0) {
onAdError(finishState.toString()); //my callback for errors
}
}
#Override
public void onUnityAdsError(UnityAds.UnityAdsError unityAdsError, String s) {
onAdError(unityAdsError.toString()); //my callback for errors, here results INVALID_ARGUMENT error
}
});
}
Does anyone know what is wrong? Thanks in advance
If you check the callback closely the onUnityAdsError has 2 params, first provides the error code and the second param provides you information about what went wrong.
#Override
public void onUnityAdsError(UnityAds.UnityAdsError unityAdsError, String reason) {
onAdError(unityAdsError.toString()); //my callback for errors, here results INVALID_ARGUMENT error
}
So just check the reason and you should be able to find out what is going wrong in your integration.
Here are some methods which you can follow to solve this INVALID_ARGUMENT problem
1. Make sure you are implementing the right Initialization code in your app. There are 2 types of Initialization.
Only Unity ads Initialization
Mediation Initialization
and both methods have their own banner, interstitial, and rewarded ad code.
2. Make sure you enable test mode as Boolean. (i.e: private Boolean testMode = true;) (make sure to do false this before publish on store)
3. You can add your mobile phone as a test device to get test ads on your phone forcefully. for this, you have to first copy the Ad ID of your device. For that, go to your mobile settings > Google > Ads > This device's advertising ID. copy that ID and go to unity dashboard > Monetization > Testing > Add Test Device. Add your device Ads ID here with any name, and now you will be able to see test ads on the device.
{Novice Android Developer}
I am following this tutorial regarding adding OneSignal Push notifications to my application. One thing that I am having difficulty with is the adding of the MainActivity.java code found at the end of the tutorial. I receive multiple errors.
I would be much obliged if someone would be able to assist me with this last portion of OneSignal integration. The main issue I am having is the placement of this code in regards to what is already in the MainActivity and what the tutorial mentions.
When I insert the code where the tutorial says, I receive an error which says
#Override not applicable to type
GitHub Repo of Project
// This fires when a notification is opened by tapping on it or one is received while the app is running.
private class ExampleNotificationOpenedHandler implements OneSignal.NotificationOpenedHandler {
#Override
public void notificationOpened(String message, JSONObject additionalData, boolean isActive) {
try {
if (additionalData != null) {
if (additionalData.has("actionSelected"))
Log.d("OneSignalExample", "OneSignal notification button with id " + additionalData.getString("actionSelected") + " pressed");
Log.d("OneSignalExample", "Full additionalData:\n" + additionalData.toString());
}
} catch (Throwable t) {
t.printStackTrace();
}
This is the code I am referring to.
*Note
I omitted other parts of the tutorial in the code for the reason that I am aware of that portion of the tutorial
That means either the name of the function or the parameters are not correct. Or that function doesn't exist in the library at all. According to OneSignal's docs, the correct signature is
public void notificationOpened(OSNotificationOpenResult result)
So either that tutorial is for a different version or the tutorial was never working.
Android 7.0 allows users (via developer options) to choose the implementation of their WebView. The user can choose the standalone WebView or use the Chrome APK to render WebViews. Reference
Since this potentially means those who use WebViews now have two different code bases to worry about, it would be useful to know which implementation is currently selected.
Is there a way to determine what WebView implementation is selected in Android 7?
Looks like this now available in Android O Preview:
Link: https://developer.android.com/preview/features/managing-webview.html
Starting in Android 7.0 (API level 24), users can choose among several
different packages for displaying web content in a WebView object.
Android O includes an API for fetching information related to the
package that is displaying web content in your app. This API is
especially useful when analyzing errors that occur only when your app
tries to display web content using a particular package's
implementation of WebView.
To use this API, add the logic shown in the following code snippet:
PackageInfo webViewPackageInfo = WebView.getCurrentWebViewPackage();
Log.d(TAG, "WebView version: " + webViewPackageInfo.versionName);
WebView.getCurrentWebViewPackage Documentation: https://developer.android.com/reference/android/webkit/WebView.html#getCurrentWebViewPackage()
To get the current Android WebView implementation and version I've created this method which should be valid for every API level.
#SuppressLint("PrivateApi")
#SuppressWarnings({"unchecked", "JavaReflectionInvocation"})
public #Nullable PackageInfo getCurrentWebViewPackageInfo() {
PackageInfo pInfo = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//starting with Android O (API 26) they added a new method specific for this
pInfo = WebView.getCurrentWebViewPackage();
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
//with Android Lollipop (API 21) they started to update the WebView
//as a separate APK with the PlayStore and they added the
//getLoadedPackageInfo() method to the WebViewFactory class and this
//should handle the Android 7.0 behaviour changes too
try {
Class webViewFactory = Class.forName("android.webkit.WebViewFactory");
Method method = webViewFactory.getMethod("getLoadedPackageInfo");
pInfo = (PackageInfo) method.invoke(null);
} catch (Exception e) {
e.printStackTrace();
}
} else {
//before Lollipop the WebView was bundled with the
//OS, the fixed versions can be found online, for example:
//Android 4.4 has WebView version 30.0.0.0
//Android 4.4.3 has WebView version 33.0.0.0
//etc...
}
return pInfo;
}
Then you can evaluate the result
if (pInfo != null) {
Log.d("WEBVIEW VERSION", pInfo.packageName + ", " + pInfo.versionName);
}
Remember: Immediately after an app update of WebView, a crash
could appear as described here:
https://stackoverflow.com/a/29809338/2910520, at this moment, this line webViewFactory.getMethod("getLoadedPackageInfo") of the code above would return null.
Actually there is
nothing you can do to prevent this, (this should not happen if the WebView implementation is taken from Chrome app but is not confirmed).
As a supplementary info to the answer of DataDino, for APIs below 26 here's a chunk of code that would give the desired output:
Class webViewFactory = Class.forName("android.webkit.WebViewFactory");
Method method = webViewFactory.getMethod("getLoadedPackageInfo");
PackageInfo packageInfo = (PackageInfo) method.invoke(null, null);
if ("com.android.webview".equals(packageInfo.packageName)) {
// "Android System WebView" is selected
} else {
// something else selected
// in case of chrome it would be "com.android.chrome"
}
There is an appCompat version:
WebViewCompat.getCurrentWebViewPackage(context)
dependencies {
// ...
implementation 'androidx.webkit:webkit:1.4.0' // Add to your gradle
}
public static String WebViewPackageName(Context context) {
PackageInfo webViewPackageInfo = WebViewCompat.getCurrentWebViewPackage(context);
return webViewPackageInfo.packageName;
}
Google review team requires glassewares to:
Dim the screen if there isn't an expectation that a user is
looking at it.
This is consistent with the "in the here and now" experience of Glass.
Glassware should always dim the screen if there isn't an expectation
that a user is looking at it. Ideally it behaves like a timeline and
dims after 15s. A user can 'rewake' the screen by looking up.
Update to be made: If a user is not looking at the results set in the
card scroller, dim the screen.
This hints at using the EyeGesture, which doesn't seem to be mentioned anywhere on the Glass Develop Page.
After some searching I found this EyeGesture library (github) that from this stackoverflow post (Google Glass Eye Gesture Crashing (EyeGestureLib)) doesn't seem to work anymore (and hasn't been updated in 4 months+).
The accepted answer (from the stackoverflow post) proposed using this revised EyeGesture library (github)
It was also mentioned (in the stackoverflow post - as a comment ) that:
Basically, you're trying to expose classes that exist in the Glass
environment itself, but not through the official APIs. By declaring
these stub classes (none of the methods are implemented) and by
putting them into the com.google.android.glass.eye package, we're
allowing our code to compile with these unimplemented classes. At
runtime, the system has implementations of those classes and the
application will instead use the system's implementations.
Here are my following questions:
Will there be (and when) an offcial API for EyeGesture's any time soon?
I tried Implementing the revised EyeGesture library into my activity by following the guide proposed without any luck. What could I be doing wrong?
Is there something I'm missing for it to be detected? I know that with the GestureDetector I'm required to Override the onGenericMotionEvent(MotionEvent event), is there something similar for the EyeGesture?
Here is what I'm currently doing:
I have a package named com.google.android.glass and in this package I have the following:
EyeGesture enum that implements Parcelable
EyeGestureManager class
I have in the main package:
GestureIds class (This one is different the github in that it's a public class and not private)
In my activity I have:
private void createEyeGestureDetector(ResultActivity resultActivity) {
final GestureIds gestureIds = new GestureIds();
//The github guide didn't mention any class names for
//mEyeGestureManager and mEyeGestureListener .. so I added some..
EyeGestureManager mEyeGestureManager = EyeGestureManager.from(resultActivity);
EyeGestureManager.Listener mEyeGestureListener = new EyeGestureManager.Listener() {
#Override
public void onDetected(EyeGesture gesture) {
Log.i("EyeGestureListener", "Gesture: " + gesture.getId());
int id = gesture.getId();
if(id == gestureIds.WINK_ID || id == gestureIds.DOUBLE_WINK_ID) {
Log.d("EyeGesture", "Wink");
} else if (id == gestureIds.BLINK_ID || id == gestureIds.DOUBLE_BLINK_ID){
Log.d("EyeGesture", "Blink");
} else if (id == gestureIds.LOOK_AT_SCREEN_ID || id == gestureIds.LOOK_AWAY_FROM_SCREEN_ID) {
Log.d("EyeGesture", "Screen");
}
}
};
}
In my onCreate I have:
//..
super.onCreate(bundle);
createEyeGestureDetector(this);
//..
Update Logcat:
When I do:
for (EyeGesture eg : EyeGesture.values()) {
boolean supported = mEyeGestureManager.isSupported(eg);
Log.w("yupyup", eg.name() + ":" + supported);
}
I get:
12-10 18:40:51.252 2405-2405/com.google.android.glass.websurg.websurg W/yupyup﹕ WINK:true
12-10 18:40:51.252 2405-2405/com.google.android.glass.websurg.websurg W/yupyup﹕ DOUBLE_WINK:false
12-10 18:40:51.252 2405-2405/com.google.android.glass.websurg.websurg W/yupyup﹕ BLINK:false
12-10 18:40:51.252 2405-2405/com.google.android.glass.websurg.websurg W/yupyup﹕ DOUBLE_BLINK:true
12-10 18:40:51.260 2405-2405/com.google.android.glass.websurg.websurg W/yupyup﹕ DON:true
12-10 18:40:51.268 2405-2405/com.google.android.glass.websurg.websurg W/yupyup﹕ DOFF:true
12-10 18:40:51.268 2405-2405/com.google.android.glass.websurg.websurg W/yupyup﹕ LOOK_AT_SCREEN:true
12-10 18:40:51.268 2405-2405/com.google.android.glass.websurg.websurg W/yupyup﹕ LOOK_AWAY_FROM_SCREEN:false
I also added (from the first github link):
#Override
protected void onStart(){
super.onStart();
createEyeGestureDetector(this);
for (EyeGesture eg : EyeGesture.values()) {
boolean supported = mEyeGestureManager.isSupported(eg);
Log.w("yupyup", eg.name() + ":" + supported);
}
mEyeGestureManager.register(EyeGesture.LOOK_AT_SCREEN, mEyeGestureListener);
mEyeGestureManager.register(EyeGesture.LOOK_AWAY_FROM_SCREEN, mEyeGestureListener);
mEyeGestureManager.register(EyeGesture.WINK, mEyeGestureListener);
}
and
#Override
protected void onStop(){
mEyeGestureManager.unregister(EyeGesture.LOOK_AT_SCREEN, mEyeGestureListener);
mEyeGestureManager.unregister(EyeGesture.LOOK_AWAY_FROM_SCREEN, mEyeGestureListener);
mEyeGestureManager.unregister(EyeGesture.WINK, mEyeGestureListener);
super.onStop();
}
This gives me:
12-10 18:46:11.314 2553-2553/com.google.android.glass.websurg.websurg I/EyeGestureManager﹕ Removing listener: com.google.android.glass.websurg.websurg.ResultActivity$1#41b8b908 for eye gesture: LOOK_AT_SCREEN
12-10 18:46:11.314 2553-2553/com.google.android.glass.websurg.websurg I/EyeGestureManager﹕ Removing listener: com.google.android.glass.websurg.websurg.ResultActivity$1#41b8b908 for eye gesture: LOOK_AWAY_FROM_SCREEN
12-10 18:46:11.314 2553-2553/com.google.android.glass.websurg.websurg I/EyeGestureManager﹕ Removing listener: com.google.android.glass.websurg.websurg.ResultActivity$1#41b8b908 for eye gesture: WINK
However they do not get detected.. even the WINK since it seems to be supported.
Google team already answered some of these but I will go ahead and provide more details about their answer and also provide an alternate way of doing these stuff you requested.
Dim the screen if there isn't an expectation that a user is looking at
it.
This is consistent with the "in the here and now" experience of Glass.
Glassware should always dim the screen if there isn't an expectation
that a user is looking at it. Ideally it behaves like a timeline and
dims after 15s. A user can 're-wake' the screen by looking up.
Update to be made: If a user is not looking at the results set in the
card scroller, dim the screen.
Glass handles that itself but the problem is that if the user doesn't touch the Glass pad for about 10 seconds or more, Glass will go to sleep and your App will stop running.
Great way of fixing this is to make Glass screen always on and check when the user looks at the screen or when they remove the Glass.
If the user looks at the screen, increase the brightness of the screen, if they look away, decrease the brightness of the screen.
If they remove the Glass from their face, decrease the brightness to zero, turn off the screen and stop running all the big CPU intensive code you have.
If they put back the Glass on their face, increase the brightness of the screen,turn on the Screen and then enable all your CPU intensive code.
You could just have a boolean variable to determine when to start or stop running. This method is recommended if you don't want your app to stop running after no touch event for seconds. It also saves battery when running your app.
Code Examples for the things I said above are below:
To Get Screen Brightness:
//Get Screen Brightness
public float getScreenBrightness() {
WindowManager.LayoutParams wMLayout = getWindow().getAttributes();
return wMLayout.screenBrightness;
}
To Set Screen Brightness(0 to 1):
//Set Screen Brightness
public boolean setScreenBrightness(float sBrightness){
if(sBrightness>=0){
WindowManager.LayoutParams wMLayout = getWindow().getAttributes();
wMLayout.screenBrightness = sBrightness; //Modify Brightness
getWindow().setAttributes(wMLayout); //Apply changes
return true;
}else
{
return false;
}
}
To Keep the Screen On or Off:
//Turn Screen On/Off
public void keepScreenOn(boolean screenOn){
if(screenOn) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}else{
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
}
Don't forgeet to add permision in the Manifest:
<uses-permission android:name="android.permission.WAKE_LOCK" />
If you are just doing developing and don't want to worry about permision right now, you can use:
<uses-permission android:name="com.google.android.glass.permission.DEVELOPMENT" />
and avoid having to look up what permission to use. I suggest you use that for now as it will save you time during development and you don't have to worry about permission when coding.
[EYE GESTURE]
No Google official API available for detecting those. Anything available now is a little hack to access hidden Glass API for that. Glass team is working on it and they said the API will only be release when it is reliable. Right now, it is NOT perfect according to them.
NOTE
The answer I am about to post below SHOULD work but may NOT work on the next Glass update. When they do update, something magically changes and one function will STOP working. Glass API and Glass itself is on Beta Mode and therefore expect things to keep changing until official EYE Gesture API gets released.
There are two ways to detect Eye Gesture. One way is to use IntentFilter and wait for "gesture" message.Another way is to use Stub Library to Access the hidden Glass API. I will talk about both here as there are prons and cons for each method.
Method 1 (Stub Lib):
This is the way you are currently trying to do it.
Pros:
Can detect more gestures
Cons:
Wink CANNOT be stooped from taking pictures.
Yo are using different library than the one I used that is still working. I will try to fix your problem if that doesn't work, You should then do it the way I did mine with the library I used too.
You got Step 1 wrong.
Step 1: Create the stubs:
Create a package called com.google.android.glass. In this package
create two classes: EyeGesture and EyeGestureManager
It should be
com.google.android.glass.eye
NOT
com.google.android.glass
com.google.android.glass may have worked in the past but there were too many updates.
So, EyeGesture and EyeGestureManager must be placed in your package called com.google.android.glass.eye
If Eye gesture is still not detected, forget about that library and use the one I am currently using. Close your project and create a new one.
Steps:
1) Download the library from here. (Last update was 4 months ago). The one you are currently using was probably last updated 8 months ago or even a year ago.
https://github.com/prt2121/EyeGestureLib
The zip file will have a long name like "EyeGestureLib-fwenindioniwenubwudew".
Rename the Zip file to "EyeGestureLib".
Extract the folder inside with a long name like "EyeGestureLib-f8a9fef3bde4396f947106e78cd0be7c7ecdd5a6"
Rename that folder to "EyeGestureLib"
The "EyeGestureLib" folder should have two folders inside it called "EyeGestureStub" and "EyeGestureDemoApp" plus other useless files.
2) Open Eclipse and create a new project.
create a simple MainActivty class activity.
3) Inside your MainActivity class:
private EyeGestureManager mEyeGestureManager;
private EyeGestureListener mEyeGestureListener;
private EyeGesture target1 = EyeGesture.WINK;
private EyeGesture target2 = EyeGesture.DOUBLE_BLINK;
private EyeGesture target3 = EyeGesture.LOOK_AT_SCREEN;
Inside onCreate:
mEyeGestureManager = EyeGestureManager.from(this);
mEyeGestureListener = new EyeGestureListener();
Inside onStart:
mEyeGestureManager.register(target1, mEyeGestureListener);
mEyeGestureManager.register(target2, mEyeGestureListener);
mEyeGestureManager.register(target3, mEyeGestureListener);
Inside onStop:
mEyeGestureManager.unregister(target1, mEyeGestureListener);
mEyeGestureManager.unregister(target2, mEyeGestureListener);
mEyeGestureManager.unregister(target3, mEyeGestureListener);
Inside MainActivity (Not inside any function but just anywhere inside you MainActivity class):
private class EyeGestureListener implements Listener {
#Override
public void onEnableStateChange(EyeGesture eyeGesture, boolean paramBoolean) {
}
#Override
public void onDetected(final EyeGesture eyeGesture) {
//Show what we just detected
Log.i(eyeGesture.toString() , " is detected");
//Check which eye event occured
if (eyeGesture.name() == target1.name()) {
// Wink
Log.i("EyeGesture: ", " you just winked");
} else if (eyeGesture.name() == target2.name()) {
// Double blink
Log.i("EyeGesture: ", " you just double winked");
} else if (eyeGesture.name() == target3.name()) {
// Look at Screen
Log.i("EyeGesture: ", " you Looked at Screen");
}
}
}
4) You will get error.
Import the EyeGestureStub that is inside the EyeGestureLib folder to fix it.
To fix the error:
a) Go to File -> Import -> Android -> Existing Android Code into Workspace
Click Next, Browse and Browse the EyeGestureStub folder inside EyeGestureLib folder.
Make sure to exclude the "EyeGestureDemoApp" if it is there. You ONLY need EyeGestureLib folder which contains EyeGesture and EyeGestureManager.
b) Right click on "EyeGestureStub" -> Properties -> Android ->
On the right side,under Project Build Target make sure that "Glass Development Kit Preview" check-box is checked.
Under Library, make sure that the "Is Library" check-box is checked.
Click Apply and Ok to exit the window.
c) Open Android SDK Manger. check for the version of Android SDK Build-tools installed. I have 21.1.1.
d) Open the project.properties of EyeGestureStub and change sdk.buildtools=18.1.1 to sdk.buildtools=21.1.1
Finish.
Done. It should work if you followed the instruction.
Run it and choose MainActivity as the the Launch Activity.
<-------------------------------------------------------------------------------------------------------------------------------->
[STILL NOT WORKING? IMPORT EVERYTHING && work from there]
If you can't get it to Work, delete the current project and import the whole project downloaded then work from there up. This is the easiest way. You may need to fix some errors before you can compile.**
To import the project,
1) Go to File -> Other -> Android -> Android Project from Existing Code.
Next -> Browse
then choose the EyeGestureLib folder which contains both the EyeGestureStub and EyeGestureDemoApp.
Make sure under Project to Import that both EyeGestureStub and EyeGestureDemoApp are check-box are checked then click Finish.
2) Right click on "EyeGestureStub" -> Properties -> Android ->
On the right side,under Project Build Target make sure that "Glass Development Kit Preview" check-box is checked.
Under Library, make sure that the "Is Library" check-box is checked.
Click Apply and Ok to exit the window.
3) Right click on "MainActivity" -> Properties -> Android ->
On the right side,under Project Build Target make sure that "Glass Development Kit Preview" check-box is checked.
4) You will get invisible error that will not be showing.
To see it Go to Windows -> Show View -> Problems
There, you will see all the problems.
Next step to fix it, we have to match the Android SDK Build-tools version with the ones listed in the project.properties of both EyeGestureStub and MainActivity
a) Open Android SDK Manger. check for the version of Android SDK Build-tools installed. I have 21.1.1.
b) Open the project.properties of EyeGestureStub and change sdk.buildtools=18.1.1 to sdk.buildtools=21.1.1
c) Open the project.properties of MainActivity and change sdk.buildtools=18.1.1 to sdk.buildtools=21.1.1
Note: Changing the first project.properties may automatically change the second one.
Done. It should work if you followed the instruction.
Run it and choose MainActivity as the the Launch Activity.
<-------------------------------------------------------------------------------------------------------------------------------->
Method 2 (IntentFilter)
Pros:
Wink CAN be stopped from taking pictures.
Cons:
Detects WINK ONLY
The first method can receive four events (WINK,DOUBLE_WINK,DOUBLE_BLINK,LOOK_AT_SCREEN,) but this method can ONLY receive one event (WINK).
This method is useful if you just want to detect ONLY WINK without Glass taking a picture.
To listen to Intent, you have to extend BroadcastReceiver.
public class EyeGesture extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getStringExtra("gesture").equals("WINK")) {
//Disable Camera Snapshot
abortBroadcast();
Log.e("WINKED ","");
}else {
Log.e("SOMETHING", "is detected " + intent.getStringExtra("gesture"));
}
}
}
You must register the intent in the Manifest as below:
<receiver android:name="com.inno.inno.glassplugin.EyeGesture">
<intent-filter>
<action android:name="com.google.android.glass.action.EYE_GESTURE" />
</intent-filter>
</receiver>
The name specified in the Manifest must match the name of the class listening to the intent which is EyeGesture.
Simple as that. No library required but only WINK can be detected. It also stops Glass from taking picture when wink is detected. You can comment abortBroadcast(); if you want Glass to take picture when event is detected.
This is for any one looking to detect Eye Gesture from Glass at this moment. These are the only current solutions around until Google releases their official Eye Gesture API.
You should file for a new Glass API feature here. File it as Glass Eye Gesture API Request. If the Glass team receives too much of this feature request, they will make it their top priority and release it. I already filed for one.
Got a return from the Google Glass Review team. Their response to :
Dim the screen if there isn't an expectation that a user is looking at
it.
This is consistent with the "in the here and now" experience of Glass.
Glassware should always dim the screen if there isn't an expectation
that a user is looking at it. Ideally it behaves like a timeline and
dims after 15s. A user can 'rewake' the screen by looking up.
Update to be made: If a user is not looking at the results set in the
card scroller, dim the screen.
was this:
The platform handles this, are you overriding this in some way, are
you holding a wake-lock when the result is shown?
So it seems as of now it is not intended to actually work straight with the EyeGesture, since it apparantely does it automatically (need confirmation on this part). In any case there is no point in trying to handle the LOOK_AT_SCREEN since the LOOK_AWAY_FROM_SCREEN isn't handled.
12-10 18:40:51.268 2405-2405/com.google.android.glass.websurg.websurg W/yupyup﹕ LOOK_AWAY_FROM_SCREEN:false
For those interested in handling the Wink EyeGesture here is what apparantely works according to some information I've gathered (needs to be confirmed).
The idea is to use is to use an EyeGesture and EyeGestureManager stub. Pretty much they exist within the environment but not in the API. This means to access them you need to create Subs that durring runtime will work (atleast that's how I understood it).
Apparantely there is a known bug when handling the WINK EyeGesture. It will take a picture. This may be caused from within the Google Glass settings where Take a picture when a wink is detected is activated. (needs to be confirmed).
So how to actually handle it?
Step 1: Create the stubs:
Create a package called com.google.android.glass. In this package create two classes: EyeGesture and EyeGestureManager
EyeGesture:
package com.google.android.glass.eye;
import android.os.Parcel;
import android.os.Parcelable;
/**
* https://gist.github.com/victorkp/9094a6aea9db236a97f3E
*
*/
public enum EyeGesture implements Parcelable {
BLINK, DOFF, DON, DOUBLE_BLINK, DOUBLE_WINK, LOOK_AT_SCREEN, LOOK_AWAY_FROM_SCREEN, WINK;
public int getId(){
return -1;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
}
}
EyeGestureManager:
package com.google.android.glass.eye;
import android.content.Context;
/**
*
* If there are any updates required check: https://gist.github.com/victorkp/9094a6aea9db236a97f3
*
*/
public class EyeGestureManager {
public static final int INFINITE_TIMEOUT = -1;
public static final String SERVICE_NAME = "eye_gesture";
public interface Listener {
public void onDetected(EyeGesture gesture);
}
public static EyeGestureManager from(Context paramContext) {
return null;
}
public void activateGazeLogging(boolean paramBoolean) {
}
public boolean applyAndSaveCalibration(EyeGesture paramEyeGesture) {
return false;
}
public boolean clearCalibration(EyeGesture paramEyeGesture) {
return false;
}
public void enableGazeService(boolean paramBoolean) {
}
public boolean endCalibrationInterval(EyeGesture paramEyeGesture) {
return false;
}
public boolean isCalibrationComplete(EyeGesture paramEyeGesture) {
return false;
}
public boolean isGazeLogging() {
return false;
}
public boolean isRegistered() {
return false;
}
public boolean isSupported(EyeGesture paramEyeGesture) {
return false;
}
public boolean loadCalibration(EyeGesture paramEyeGesture) {
return false;
}
public boolean register(EyeGesture gesture, EyeGestureManager.Listener listener){
return false;
}
public boolean startCalibrationInterval(EyeGesture paramEyeGesture) {
return false;
}
public boolean unregister(EyeGesture gesture, EyeGestureManager.Listener listener) {
return false;
}
}
Great, you've got both stubs. First thing you should notice is that they aren't 100% like the one from the github link. Some of the functions have been deprecated, I've kept the ones that work. (I haven't tried every single one).
What is next? Well you need (not really but it's easier if you do) to create the GestureId class (yes you can put this anywhere you want to).
GestureId:
package com.google.android.glass.websurg.websurg;
import com.google.android.glass.eye.EyeGesture;
/**
*
*
* For updates check out: https://gist.github.com/victorkp/9094a6aea9db236a97f3
*
*
*
*/
public class GestureIds {
public int BLINK_ID;
public int WINK_ID;
public int DOUBLE_BLINK_ID;
public int DOUBLE_WINK_ID;
public int LOOK_AT_SCREEN_ID;
public int LOOK_AWAY_FROM_SCREEN_ID;
public GestureIds() {
BLINK_ID = EyeGesture.BLINK.getId();
WINK_ID = EyeGesture.WINK.getId();
DOUBLE_BLINK_ID = EyeGesture.DOUBLE_BLINK.getId();
DOUBLE_WINK_ID = EyeGesture.DOUBLE_WINK.getId();
LOOK_AT_SCREEN_ID = EyeGesture.LOOK_AT_SCREEN.getId();
LOOK_AWAY_FROM_SCREEN_ID = EyeGesture.LOOK_AWAY_FROM_SCREEN.getId();
}
}
Great now you have all the classes to actually get started. Keep in mind the stubs are the ones that will work correctly during runtime (atleast I think so since I don't get any errors?)
Say you have a MainActivity and you want to add the EyeGesture:
private void createEyeGestureDetector(Context context) {
mGestureIds = new GestureIds();
mEyeGestureManager = EyeGestureManager.from(context);
mEyeGestureListener = new EyeGestureManager.Listener() {
#Override
public void onDetected(EyeGesture gesture) {
Log.w("EyeGestureListener", "Gesture: " + gesture.getId());
int id = gesture.getId();
if (id == mGestureIds.WINK_ID || id == mGestureIds.DOUBLE_WINK_ID) {
Log.d("EyeGesture", "Wink");
} else if (id == mGestureIds.BLINK_ID || id == mGestureIds.DOUBLE_BLINK_ID) {
Log.d("EyeGesture", "Blink");
} else if (id == mGestureIds.LOOK_AT_SCREEN_ID || id == mGestureIds.LOOK_AWAY_FROM_SCREEN_ID) {
Log.d("EyeGesture", "Screen");
}
runOnUiThread(new Runnable() {
#Override
public void run() {
Log.w("detected", "omg detected");
}
});
}
};
}
Notice I have also a run(). I've added both since one github linked had it and the other didn't. Tried both and doesn't seem to get detected.
You call this (the one right above) function in your onCreate(); , then in your onResume();
mEyeGestureManager.register(EyeGesture.LOOK_AT_SCREEN, mEyeGestureListener);
mEyeGestureManager.register(EyeGesture.LOOK_AWAY_FROM_SCREEN, mEyeGestureListener);
mEyeGestureManager.register(EyeGesture.WINK, mEyeGestureListener);
The on your onPause():
mEyeGestureManager.unregister(EyeGesture.LOOK_AT_SCREEN, mEyeGestureListener);
mEyeGestureManager.unregister(EyeGesture.LOOK_AWAY_FROM_SCREEN, mEyeGestureListener);
mEyeGestureManager.unregister(EyeGesture.WINK, mEyeGestureListener);
Now all this seems to work (no errors when the functions are called, and logs are shown saying they were called (notice there are no logs in the stubs)).
However, my google glass doesn't seem to be detecting the EyeGesture. I'm pretty sure it's all good, or that I'm missing something minor. I won't accept it as an answer since, it only answers part of my question. Feel free to try this out yourself and let me know how it works.
Lets say my Android App version 0.1 is installed currently on the User's phone. Everytime they launch my App I want to check if there is a different version available in the Android Market let's say this version is 0.2. If there is a mismatch between these two version I want to show a dialog box prompting the user to Upgrade the App.
I totally understand there exists a notification procedure from Android Market itself to the users but as far as my Analytics data is concerned it is not very effective in reminding users to upgrade to the new version of the App.
Any insight would be very helpful. Thanks StackOverflowers, you guys rock!
As of 2019 the best way for updating your app is to use In-app updates provided by Play Core library (1.5.0+). It works for Lollipop and newer, but let's be fair, Kit-Kat is less than 7% as of today and soon will be gone forever. You can safely run this code on Kit-Kat without version checks, it won't crash.
Official documentation: https://developer.android.com/guide/app-bundle/in-app-updates
There are two types of In-app updates: Flexible and Immediate
Flexible will ask you nicely in a dialog window:
whereas Immediate will require you to update the app in order to continue using it with full-screen message (this page can be dismissed):
Important: for now, you can't choose which type of update to roll out in your App Release section on Developer Play Console. But apparently, they will give us that option soon.
From what I've tested, currently, we're getting both types available in onSuccessListener.
So let's implement both types in our code.
In module build.gradle add the following dependency:
dependencies {
implementation 'com.google.android.play:core:1.6.1'//for new version updater
}
In MainActivity.class:
private static final int REQ_CODE_VERSION_UPDATE = 530;
private AppUpdateManager appUpdateManager;
private InstallStateUpdatedListener installStateUpdatedListener;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
checkForAppUpdate();
}
#Override
protected void onResume() {
super.onResume();
checkNewAppVersionState();
}
#Override
public void onActivityResult(int requestCode, final int resultCode, Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
switch (requestCode) {
case REQ_CODE_VERSION_UPDATE:
if (resultCode != RESULT_OK) { //RESULT_OK / RESULT_CANCELED / RESULT_IN_APP_UPDATE_FAILED
L.d("Update flow failed! Result code: " + resultCode);
// If the update is cancelled or fails,
// you can request to start the update again.
unregisterInstallStateUpdListener();
}
break;
}
}
#Override
protected void onDestroy() {
unregisterInstallStateUpdListener();
super.onDestroy();
}
private void checkForAppUpdate() {
// Creates instance of the manager.
appUpdateManager = AppUpdateManagerFactory.create(AppCustom.getAppContext());
// Returns an intent object that you use to check for an update.
Task<AppUpdateInfo> appUpdateInfoTask = appUpdateManager.getAppUpdateInfo();
// Create a listener to track request state updates.
installStateUpdatedListener = new InstallStateUpdatedListener() {
#Override
public void onStateUpdate(InstallState installState) {
// Show module progress, log state, or install the update.
if (installState.installStatus() == InstallStatus.DOWNLOADED)
// After the update is downloaded, show a notification
// and request user confirmation to restart the app.
popupSnackbarForCompleteUpdateAndUnregister();
}
};
// Checks that the platform will allow the specified type of update.
appUpdateInfoTask.addOnSuccessListener(appUpdateInfo -> {
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE) {
// Request the update.
if (appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)) {
// Before starting an update, register a listener for updates.
appUpdateManager.registerListener(installStateUpdatedListener);
// Start an update.
startAppUpdateFlexible(appUpdateInfo);
} else if (appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) ) {
// Start an update.
startAppUpdateImmediate(appUpdateInfo);
}
}
});
}
private void startAppUpdateImmediate(AppUpdateInfo appUpdateInfo) {
try {
appUpdateManager.startUpdateFlowForResult(
appUpdateInfo,
AppUpdateType.IMMEDIATE,
// The current activity making the update request.
this,
// Include a request code to later monitor this update request.
MainActivity.REQ_CODE_VERSION_UPDATE);
} catch (IntentSender.SendIntentException e) {
e.printStackTrace();
}
}
private void startAppUpdateFlexible(AppUpdateInfo appUpdateInfo) {
try {
appUpdateManager.startUpdateFlowForResult(
appUpdateInfo,
AppUpdateType.FLEXIBLE,
// The current activity making the update request.
this,
// Include a request code to later monitor this update request.
MainActivity.REQ_CODE_VERSION_UPDATE);
} catch (IntentSender.SendIntentException e) {
e.printStackTrace();
unregisterInstallStateUpdListener();
}
}
/**
* Displays the snackbar notification and call to action.
* Needed only for Flexible app update
*/
private void popupSnackbarForCompleteUpdateAndUnregister() {
Snackbar snackbar =
Snackbar.make(drawerLayout, getString(R.string.update_downloaded), Snackbar.LENGTH_INDEFINITE);
snackbar.setAction(R.string.restart, new View.OnClickListener() {
#Override
public void onClick(View view) {
appUpdateManager.completeUpdate();
}
});
snackbar.setActionTextColor(getResources().getColor(R.color.action_color));
snackbar.show();
unregisterInstallStateUpdListener();
}
/**
* Checks that the update is not stalled during 'onResume()'.
* However, you should execute this check at all app entry points.
*/
private void checkNewAppVersionState() {
appUpdateManager
.getAppUpdateInfo()
.addOnSuccessListener(
appUpdateInfo -> {
//FLEXIBLE:
// If the update is downloaded but not installed,
// notify the user to complete the update.
if (appUpdateInfo.installStatus() == InstallStatus.DOWNLOADED) {
popupSnackbarForCompleteUpdateAndUnregister();
}
//IMMEDIATE:
if (appUpdateInfo.updateAvailability()
== UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) {
// If an in-app update is already running, resume the update.
startAppUpdateImmediate(appUpdateInfo);
}
});
}
/**
* Needed only for FLEXIBLE update
*/
private void unregisterInstallStateUpdListener() {
if (appUpdateManager != null && installStateUpdatedListener != null)
appUpdateManager.unregisterListener(installStateUpdatedListener);
}
And we're done!
Testing.
Please read the docs so you will know how to test it properly with test tracks on Google Play.
Long story short:
Sign your app with the release certificate and upload it to the one of publishing tracks in Developer Play Console under App Releases (alpha/beta/other custom closed track).
In your release track page in the Manage Testers section create and add a list of testers and make sure you checked the checkbox! - this step is optional since your developer account email is also a testers account and you can use it for testing.
Under the list of testers you will find "Opt-in URL" - copy this url and give it to your testers or open it yourself. Go to that page and accept proposition for testing. There will be a link to the app. (You won't be able to search for the app in Play Store so bookmark it)
Install the app on your device by that link.
In build.gradle increment the version of defaultConfig { versionCode k+1 } and build another signed apk Build > Generate Signed Bundle / APK... and upload it to your publishing track.
Wait for... 1 hour? 2 hours? or more before it will be published on the track.
CLEAR THE CACHE of Play Store app on your device. The problem is that Play app caches details about installed apps and their available updates so you need to clear the cache. In order to do that take two steps:
7.1. Go to Settings > App > Google PLay Store > Storage > Clear Cache.
7.2. Open the Play Store app > open main menu > My apps & games > and there you should see that your app has a new update.
If you don't see it make sure that your new update is already released on the track (go to your bookmarked page and use it to open your apps listing on the Play Store to see what version is shown there). Also, when your update will be live you'll see a notification on the top right of your Developer Play Console (a bell icon will have a red dot).
Hope it helps.
The Android Market is a closed system and has only an unofficial api that might break at any point of time.
Your best bet is simply to host a file(xml, json or simple text) on a web server of yours in which you just have to update the current version of your app when you post it on the Market.
Your app will then only have to fetch that file at startup, checks wether currently installed app has a lower version number and displays a dialog to warn the user he is lagging.
Another option you can use, if you want to avoid having your backend server to store your current app version like it's suggested in the accepted answer, is to use Google Tag Manager (GTM).
If you're already using the Google Analytics SDK, you have the GTM in it also.
In GTM you can define a value in the container for your app that specifies your latest released version. For example:
{
"latestAppVersion": 14,
...
}
Then you can query that value when your app starts and show the user update dialog reminder if there's a newer version.
Container container = TagManager.getInstance(context).openContainer(myContainerId);
long latestVersionCode = container.getLong("latestAppVersion");
// get currently running app version code
PackageInfo pInfo = getPackageManager().getPackageInfo(getPackageName(), 0);
long versionCode = pInfo.versionCode;
// check if update is needed
if(versionCode < latestVersionCode) {
// remind user to update his version
}
Take a look at this library that you can use to query the Android Market API
http://code.google.com/p/android-market-api/
You can use this Android Library: https://github.com/danielemaddaluno/Android-Update-Checker. It aims to provide a reusable instrument to check asynchronously if exists any newer released update of your app on the Store.
It is based on the use of Jsoup (http://jsoup.org/) to test if a new update really exists parsing the app page on the Google Play Store:
private boolean web_update(){
try {
String curVersion = applicationContext.getPackageManager().getPackageInfo(package_name, 0).versionName;
String newVersion = curVersion;
newVersion = Jsoup.connect("https://play.google.com/store/apps/details?id=" + package_name + "&hl=en")
.timeout(30000)
.userAgent("Mozilla/5.0 (Windows; U; WindowsNT 5.1; en-US; rv1.8.1.6) Gecko/20070725 Firefox/2.0.0.6")
.referrer("http://www.google.com")
.get()
.select("div[itemprop=softwareVersion]")
.first()
.ownText();
return (value(curVersion) < value(newVersion)) ? true : false;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
And as "value" function the following (works if values are beetween 0-99):
private long value(String string) {
string = string.trim();
if( string.contains( "." )){
final int index = string.lastIndexOf( "." );
return value( string.substring( 0, index ))* 100 + value( string.substring( index + 1 ));
}
else {
return Long.valueOf( string );
}
}
If you want only to verify a mismatch beetween versions, you can change:
"value(curVersion) < value(newVersion)" with "value(curVersion) != value(newVersion)"
For prompting Android App User to Update App if current version is not equal to market version, you should first check the app version on the market and compare it with the version of the app on the device. If they are different, it may be an update available. In this post I wrote down the code for getting the current version of market and current version on the device and compare them together. I also showed how to show the update dialog and redirect the user to the update page. Please visit this link: https://stackoverflow.com/a/33925032/5475941
My working Kotlin code for force App update:
const val FLEXIABLE_UPADTE: Int = 101
const val FORCE_UPDATE: Int = 102
const val APP_UPDATE_CODE: Int = 500
override fun onCreate {
// Get updateType from Webservice.
updateApp(updateType)
}
private fun updateApp(statusCode: Int) {
appUpdateManager = AppUpdateManagerFactory.create(this #MainActivity)
val appUpdateInfoTask = appUpdateManager ? .appUpdateInfo
appUpdateInfoTask ? .addOnSuccessListener {
appUpdateInfo - >
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE) {
if ((statusCode == Constants.FORCE_UPDATE))
appUpdateManager ? .startUpdateFlowForResult(
appUpdateInfo, AppUpdateType.IMMEDIATE, this, Constants.APP_UPDATE_CODE
)
else if (statusCode == Constants.FLEXIABLE_UPADTE)
appUpdateManager ? .startUpdateFlowForResult(
appUpdateInfo, AppUpdateType.FLEXIBLE, this, Constants.FLEXIABLE_UPADTE
)
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent ? ) {
try {
if (requestCode == Constants.APP_UPDATE_CODE && resultCode == Activity.RESULT_OK) {
if (resultCode != RESULT_OK) {
appUpdateCompleted()
}
}
} catch (e: java.lang.Exception) {
}
}
private fun appUpdateCompleted() {
Snackbar.make(
findViewById(R.id.activity_main_layout),
"An update has just been downloaded.",
Snackbar.LENGTH_INDEFINITE
).apply {
setAction("RESTART") {
appUpdateManager.completeUpdate()
}
setActionTextColor(resources.getColor(R.color.snackbar_action_text_color))
show()
}
}