I know In-App billing is new in Android and I would like to use it, but the version requirements make me think twice whether it's worth the effort. I would appreciate any input from those who have implemented or worked with In App Billing in detail.
I still have 10% 1.5 users. In app billing requires at least 1.6 to work. Does that mean 1.5 users will crash immediately? If not, at what point does it fail? I don't want to write a bunch of hacky code to stay compatible with 1.5 users.
If user reinstalls the app, are their app purchases remembered?
At what point does it fail if you don't have the required Market version?
Thanks.
Regarding version support, you'll have write some extra code to detect the device OS version (see android.os.Build.VERSION) so make sure it will run on 1.5 devices. I strongly suggest isolating that code in its own class, and only instantiate that class after your version check. That way your code stays clean (not "hacky") and you don't accidentally reference a 1.6+ class from a class field. In my code, I have version test classes that look like this:
public class Android8 {
private static final String TAG = "Android8";
// public test variables
public static final boolean IS_V8;
public static final boolean AT_LEAST_V8;
private static final Object pimpl;
static {
int sdk_int = 0;
try {
Field field = Build.VERSION.class.getField( "SDK" );
String sdk_str = (String)field.get( null );
sdk_int = Integer.parseInt( sdk_str );
} catch( Throwable e ) {
}
IS_V8 = (sdk_int==8);
AT_LEAST_V8 = (sdk_int>=8);
if( AT_LEAST_V8 ) {
pimpl = new Implementation();
} else {
pimpl = null;
}
}
// Version safe interface
public static void Camera_setDisplayOrientation( Camera camera, int degrees ) {
if( AT_LEAST_V8 )
((Implementation)pimpl).Camera_setDisplayOrientation( camera, degrees );
}
// Will cause a verify error if loaded in a pre Android8 environment
private static final class Implementation {
public static void Camera_setDisplayOrientation( Camera camera, int degrees ) {
camera.setDisplayOrientation( degrees );
}
}
}
Question 2: NO, if items are UNMANAGED. Yes if they are.
That's the point with managed items, let's the Google's servers manage (remenber) the purchased items for this sort of cases.
(The "manage by user account" purchase type is useful if you are selling items such as game levels or application features. These items are not transient and usually need to be restored whenever a user reinstalls your application, wipes the data on their device, or installs your application on a new device.)
from: http://developer.android.com/guide/market/billing/billing_admin.html#billing-purchase-type
Related
I'm trying to send data back and forth from Flutter to my native platform (in this case Android).
In order to keep some model consistency, I have generated the models for all platforms by using Protocol-Buffers.
When I try to pass data from Android to Flutter I'm not finding any way to do it without shenanigans like serializing to a handcrafted JSON.
There must be a way to use protobuf in order to do so, isn't it?
In order to give context, I have made a minimal app to try to solve this problem:
My Protocol Buffer
syntax = "proto3";
option java_package = "com.test.protobuf_test";
option java_outer_classname = "ProtoModel";
message SimplePerson {
int32 id= 1;
string name= 2;
}
From which I generate my model using:
protoc --java_out and protoc --dart_out
In Dart I get my class
class SimplePerson extends $pb.GeneratedMessage {...}
And in Java
public final class ProtoModel {
...
public static final class SimplePerson extends
com.google.protobuf.GeneratedMessageV3 implements
SimplePersonOrBuilder {...}
}
From Android inside my method channel, I am trying to pass one or many ProtoModel.SimplePerson objects back to Dart.
No success so far.
How would you actually do it?
I'd expect it to be something like
In Java:
ProtoModel.SimplePerson person = ProtoModel.SimplePerson.newBuilder().setId(3).setName("Person Name").build();
result(person);
And in Dart:
var result = await platform.invokeMethod("generatePerson");
if(result is SimplePerson) {
print("Success!");
} else {
print("Failure!");
}
So far I'm only getting Failures or Exceptions.
Thanks!
your very close your using result but i have it working with result.success
when (call.method) {
"getPlatformVersion" -> result.success(getPlatformVersion().toByteArray())
}
private fun getPlatformVersion(): Models.Version {
return Models.Version.newBuilder().setVersionName("Android ${android.os.Build.VERSION.RELEASE}").build()
}
great example here https://www.freecodecamp.org/news/flutter-platform-channels-with-protobuf-e895e533dfb7/
EDIT didnt see how old this post was
I have to use this as Pigeon is sill early access, and although pigeon was generally harder to set up i do prefer it
I have the following Reporting code:
public class Reporting {
private ExtentHtmlReporter extentHtmlReporter;
private static ThreadLocal<ExtentReports> extentReports = new ThreadLocal<>();
private static ThreadLocal<ExtentTest> extentTest = new ThreadLocal<>();
public synchronized ExtentTest createInstanceReport(String testCaseName) {
System.out.println(extentReports.get());
new File(Constants.userDir + "/Reports/").mkdirs();
// To generate report with name
extentHtmlReporter = new ExtentHtmlReporter(
Constants.userDir + "/Reports/" +
"ExecutionReport_" + new SimpleDateFormat(
Constants.date).format(new Date()) + ".html");
// Setting Document Title
extentHtmlReporter.config().setDocumentTitle("Demo");
// Setting Report Name
extentHtmlReporter.config().setReportName("Demo Automation");
// Setting Theme
extentHtmlReporter.config().setTheme(Theme.STANDARD);
// Setting Chart location
extentHtmlReporter.config().setTestViewChartLocation(ChartLocation.TOP);
// Setting Chart visibility
extentHtmlReporter.config().setChartVisibilityOnOpen(false);
// Setting Time stamp
extentHtmlReporter.config().setTimeStampFormat("yyyy-MM-dd HH:mm:ss");
// Setting append exist as true
extentHtmlReporter.setAppendExisting(true);
ExtentReports extentReports = new ExtentReports();
extentReports.attachReporter(extentHtmlReporter);
// Setting system info
extentReports.setSystemInfo("Name",
BaseTest.prop.getProperty(Constants.testerName));
extentReports.setSystemInfo("Environment",
BaseTest.prop.getProperty(Constants.environment));
extentReports.setSystemInfo("Browser",
BaseTest.prop.getProperty(Constants.browser));
Reporting.extentReports.set(extentReports); // Instead of using here extentReport thread like this, Can anyone suggest to use it directly
// Add test case name in report
ExtentTest extentTest = Reporting.extentTest.get();
extentTest = Reporting.extentReports.get().createTest(testCaseName);
Reporting.extentTest.set(extentTest);
// Assigning categories
extentTest.assignCategory(MultiFunction.getProp()
.getProperty(Constants.browser));
System.out.println(Reporting.extentReports.get());
System.out.println(Reporting.extentTest.get());
return extentTest;
}
public synchronized ExtentTest getExtentTest() {
return extentTest.get();
}
public synchronized ExtentReports getInstanceReport() {
return extentReports.get();
}
public synchronized void remove() {
extentReports.remove();
extentTest.remove();
}
}
I was trying parallel testing using TestNG (and will have to use Selenium grid and sauce in future). I execute 2 test cases then only one test case result is added in the report.
I have isolated the extentTest, extentReporter and WebDriver instances using threadPool.
Tried below with extentHtmlReporter instance:
1) Tried to make it static(no luck)
2) Tried to make it local (the same behaviour, getting only 1 test case result)
3) Tried as a non-static global variable ( no luck)
Could you suggest how to solve the above issue?
Please note: Only one report is generated. But when I tried to run parallel test cases in debug mode reports are generated for both the test case. I think because one test case gets over its killing some instance (when running in non-debug mode)
Also, I want to redesign the following place in my code:
For extentRpeort, I am using:
Reporting.extentReports.set(extentReports);
To add extentReport instance to my extentReport Thread.
Instead of adding like this I want to use it directly so as to reduce line of code.
If I understand correctly you have to generate Report from all executed TestNG cases.
However, from code which you shared, it is very visible that you will have some trouble with it. You are making a few critical mistakes and result are obvious:
For generating reports with TestNG I will suggest grabbing information about test execution from TestNG listener. Something like:
public final class TestNGListener extends TestListenerAdapter implements IInvokedMethodListener, ISuiteListener {
#Override
public void onStart(ITestContext context) {
Logger.info(buildMessage(Logger.PREFIX_TEST_STARTED, context.getName()));
}
#Override
public void onFinish(ITestContext context) {
Logger.info(buildMessage(Logger.PREFIX_TEST_FINISHED, context.getName()));
}
#Override
public void onTestStart(ITestResult result) {
Logger.info(buildMessage(Logger.PREFIX_METHOD_STARTED, getMethodName(result)));
}
#Override
public void onTestSuccess(ITestResult result) {
Logger.info(buildMessage(Logger.PREFIX_METHOD_SUCCESS, getMethodName(result)));
processTestResult(result);
}
#Override
public void onTestFailure(ITestResult result) {
Logger.info(buildMessage(Logger.PREFIX_METHOD_FAILED, getMethodName(result)));
}
You can't do everything in one method! You broke Single Responsibility Principle. Your createInstanceReport() is doing all jobs (setting report details, set system info, attach an executed test case to report) at one place. You have to redesign this logic to some logical separate operations. After redesigning your problem with the next line:
Reporting.extentReports.set(extentReports)
Could successfully disappear.
You have to consider a case, why you need to use exactly Extent, Reports Version 3. TestNG has test reports from the box. They are poor but they are presented out of the box. If you want just to improve it a little bit you could use ReportNG over TestNG.
It is quite easy to configure: Configuring ReportNG with TestNG for HTML Reports.
It isn't maintained, but it makes TestNG reports really eye candy and understandable.
Anyway, my suggestion is to use TestNGListener for getting info about test cases execution. And read more about good programming practice.
Work with TestNG/jUnit (or other runner framework that you are using) listener, here is a good example how to do it.
Do not put everything in a single class.
https://www.swtestacademy.com/extent-reports-version-3-reporting-testng/
The issue was with the flushing of extent report instance.
I was using ThreadLocal for storing extent report instance and was flushing the wrong instance.
I'm investigating a memory leak (or leaks) which seem to be core to our Forms app.
We're using FreshMvvm, which previously had a pretty bad leak due to Pages/PageModels not being garbage collected. Michael Ridland (FreshMvvm) author) fixed that, which is great.
However, we're still seeing the figure returned by GC.GetTotalMemory() steadily increase. It does decrease, but the general trend is upwards.
I've stripped things right back so that I've just got a plain Page which pushes another page and we can then pop back to the first.
Doing this, I'm seeing GC.GetTotalMemory() generally increase by 2K - 4K every cycle. This is after forcibly calling GC.Collect();
We're running Xamarin.Forms 2.3.4.270 - I know this is quite old, but we really don't want to upgrade as we don't want to introduce 'new Forms' bugs! However, I have tried experimentally upgrading to 2.5.0.121934, and I see the same behaviour (as well as aspects of the app stop working with this version).
Short of using Profiler, are there any investigative techniques I can use to find the culprit?
My next step will be to strip more and more out!
I've encountered a similar issue in the past with Xamarin.Forms memory leaks and to help figure what views/pages were the culprit this class was created:
public static class Refs
{
private static readonly List<WeakReference> _refs = new List<WeakReference>();
public static readonly BindableProperty IsWatchedProperty = BindableProperty.CreateAttached("IsWatched", typeof(bool), typeof(Refs), false, propertyChanged: OnIsWatchedChanged);
public static bool GetIsWatched(BindableObject obj)
{
return (bool)obj.GetValue(IsWatchedProperty);
}
public static void SetIsWatched(BindableObject obj, bool value)
{
obj.SetValue(IsWatchedProperty, value);
}
private static void OnIsWatchedChanged(BindableObject bindable, object oldValue, object newValue)
{
AddRef(bindable);
}
public async static void AddRef(object p)
{
GC.Collect();
await Task.Delay(100);
GC.Collect();
_refs.Add(new WeakReference(p));
foreach (var #ref in _refs)
{
if (#ref.IsAlive)
{
var obj = #ref.Target;
Debug.WriteLine("IsAlive: " + obj.GetType().Name);
}
else
{
Debug.WriteLine("IsAlive: False");
}
}
Debug.WriteLine("---------------");
}
}
Then to use it you can use the IsWatched property in your pages xaml or in code behind you can just do Refs.AddRef(this); after InitializeComponent(); and just navigate to and from the page several times. If it is being gc'ed the console should print IsAlive: False if not it will print out the type.
Most often the leaks we had were fixed by clearing the BindingContext and/or setting some things to null when leaving the page.
protected override void OnParentSet()
{
base.OnParentSet();
if (Parent == null)
{
BindingContext = null;
}
}
I want to use an android internal class, com.android.internal.widget.LockPatternUtils .
I searched for examples and I got this:
LockPatternUtils lpu = new LockPatternUtils(this.getContentResolver());
However, i cant import, autimport does not appear and manually doing
import com.android.internal.widget.LockPatternUtils
doesnt work.
How is this handled? I think there was something about creating an interface but not sure about it
EDIT: I got this:
boolean patternLocked = android.provider.Settings.System.getInt(
getContentResolver(),Settings.System.LOCK_PATTERN_ENABLED, 0)==1;
That works for the pattern lock, but i cant know about the pin or password lock
Use reflection to access the internal Android method getActivePasswordQuality (line 350 of LockPatternUtils.java) and compare the int it returns to the int for any of the DevicePolicyManager Constants:
protected boolean isSecure() {
String LOCK_PATTERN_UTILS = "com.android.internal.widget.LockPatternUtils";
try {
Class<?> lockPatternUtilsClass = Class.forName(LOCK_PATTERN_UTILS);
Object lockPatternUtils = lockPatternUtilsClass.getConstructor(Context.class).newInstance(this);
Method method = lockPatternUtilsClass.getMethod("getActivePasswordQuality");
int lockProtectionLevel = Integer.valueOf(String.valueOf(method.invoke(lockPatternUtils)));
// Then check if lockProtectionLevel == DevicePolicyManager.TheConstantForWhicheverLevelOfProtectionYouWantToEnforce, and return true if the check passes, false if it fails
}
catch (Exception ex) {
ex.printStackTrace();
}
return false;
}
How is this handled?
Those classes are only available as part of full firmware builds, not from Android SDK apps.
You can not use Android internal classes, they do not come with the public SDK.
You can build your own SDK exposing them more or less as they do here How do I build the Android SDK with hidden and internal APIs available?
This is not recommended because you will have no guarantees.
is it safe to write such compatible code on Android?
if (Build.os.SDK_INT >= 11) {
newClass instance = new newClass();
....
}
else {
oldClass instance = new oldClass();
....
}
one of my colleagues argue with me that ClassNotFoundException might be thrown up when running the above code since ClassLoader is attempting to load newClass on an android os device which is below android 11. But I've tried couple times, and didn't see this happen.
After googling around for couple hours, I didn't find any information on how and when android default classLoader loads a specific class.
You should check the compatability like the following... It gives you more accurate than the above:
private static int currentApi = 0;
public static int getApiLevel() {
if (currentApi > 0) {
return currentApi;
}
if (android.os.Build.VERSION.SDK.equalsIgnoreCase("3")) {
currentApi = 3;
} else {
try {
Field f = android.os.Build.VERSION.class.getDeclaredField("SDK_INT");
currentApi = (Integer) f.get(null);
} catch (Exception e) {
return 0;
}
}
return currentApi;
}
you can alway use reflection to check if the class exists:
try {
Class.forName("yourclass")
} catch (ClassNotFoundExecption) {
oldClass instance = new oldClass();
}
Yes, this is safe to do on recent versions of Android. I want to say froyo and above, but it may have been even earlier than that. I don't recall for sure.
What happens is that dalvik performs a verification pass on the dex file at install time. For any classes/methods/fields that it can't resolve, it replaces those accesses with an instruction that throws a VerifyError.
In your example, if that code got loaded on, e.g. api 10, newClass instance = new newClass() would conceptually be replaced with throw new VerifYError(). So as long as that branch never gets executed at runtime, everything is good.
Short answer - don't do it.
Most VMs only load a class when it is absolutely needed. However a class loader is allowed to cache binary representation of classes beforehand[1].
Class loaders are allowed to cache binary representations of types,
load types early in anticipation of eventual use, or load types
together in related groups.
[1] - http://www.artima.com/insidejvm/ed2/lifetype2.html
[2] - http://developer.android.com/tools/extras/support-library.html
Edit - Have you checked if the class you need is available as part of the android support package ? [2]