Android: run application in dev/prod environment? - android

I want to make development easier by implementing a configuration parameter that determines whether the app should be run in 'DEV' mode or 'PROD' mode.
I want this parameter to be accessible from any file (based on this parameter different chunks of code will be executed).
What's the most practical way to store this parameter (which isn't accessible or changeable by the user)?
How can I access it from within the application?

Starting with ADT 17 (IIRC), you have this automatically as part of the auto generated BuildConfig class.
The DEBUG field is always true when developing, but when you export a signed or unsigned apk, it is set to false. You can use it as:
if(BuildConfig.DEBUG) {
//Debug mode
}
Or the other way around:
if(!BuildConfig.DEBUG) {
//Release mode
}

You can use an enum:
public enum BuildType {
Release, Pilot, Debug;
}
And assign it to a global variable:
public static final BuildType BUILD_TYPE = BuildType.Debug;
You can even create some methods in the enum that allow you
switch over very specific parts of your application.
Now you can do stuff like this:
if (MyApplication.BUILD_TYPE != BuildType.Release) {
// some code that does not go in the release
}

A static field in one of your Activity ? Or I am missing something ?
public static boolean isDev = true;
You can set it in the onCreate of your main activity.

You can create a new class and add your variable there.
Also you can add some methods which will display some details about your app in dev mode.
public class Console{
public final static boolean isDev = true;
public static error(String tag, String msg){
if(isDev){
Log.e(TAG, msg);
}
}
}

Related

Extent Report Issue Parallel testing

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.

How to use DEBUG flags in AOSP classes?

Sometimes in AOSP sources I see private static final boolean debug flag with false as ïts value. And there are debug output if this flag is true. Something like this:
private static final boolean DEBUG_DRAW = false;
private static final Paint DEBUG_DRAW_PAINT;
static {
DEBUG_DRAW_PAINT = DEBUG_DRAW ? new Paint() : null;
if (DEBUG_DRAW_PAINT != null) {
DEBUG_DRAW_PAINT.setAntiAlias(true);
DEBUG_DRAW_PAINT.setColor(Color.MAGENTA);
}
}
Who and how uses it? Is it possible to switch this flag somehow and take debug output of AOSP classes?
Everything is possible with Java and Reflection
Pros:
I don't think there is anything more powerfull than that
Cons:
This technique will be executed at runtime so any code executed before this (e.g. classes loading)... well, is already executed. So you won't see the effects
Slow at runtime
Dangerous. Use it carefully
You can modify any value using this:
Class<?> klass = ...
Field field = klass.getDeclaredField("DEBUG_DRAW");
field.setAccesible(true);
field.set(
null, // as this is an static attribute we don't need anything here...
true // set true as new value
);
As I stated before reflection is a dangerous technique so that snipped will throw several exceptions if used wrong so you will have to handle them

Android what's the best way to hide logs? [duplicate]

This question already has answers here:
How to remove all debug logging calls before building the release version of an Android app?
(31 answers)
Closed 7 years ago.
so.. I'm using Log.d("", ""); in many places in my app for debuging.
but I don't want those logs in the store.
right now in order to hide them in the store version, I created a Constant.class and put a boolean there called debugMode, and wrapped every log i have in an if statement like this :
if (Constant.debugMode) {
Log.d(TAG, "check123");
}
and then when I build a google store apk I change that boolean to true instead of false.
that's kind of clumsy in my opinion, is there any more efficient way to do that?
Make a simple logger class that has public static methods and a swich to enable logs only for debug versions of your app. Here is a sample code.
public class Logger {
public static final boolean D = BuildConfig.DEBUG;
public static void LogInfo(String TAG, String msg) {
if (D) {
Log.i(TAG, msg);
}
}
public static void LogWarrning(String TAG, String msg) {
if (D) {
Log.w(TAG, msg);
}
}
public static void LogDebug(String TAG, String msg) {
if (D) {
Log.d(TAG, msg);
}
}
public static void LogError(String TAG, String msg) {
if (D) {
Log.e(TAG, msg);
}
}
}
Usage
Logger.LogDebug("Test", "Example");
This way you can keep the if clauses in one place and don't have to worry about them. Also I don't think it clumsy this way.
I find a far easier solution is to forget all the if checks all over
the place and just use ProGuard to strip out any Log.d() or Log.v()
method calls when we call our Ant release target.
That way, we always have the debug info being output for regular
builds and don't have to make any code changes for release builds.
ProGuard can also do multiple passes over the bytecode to remove other
undesired statements, empty blocks and can automatically inline short
methods where appropriate.
For example, here's a very basic ProGuard config for Android:
-dontskipnonpubliclibraryclasses
-dontobfuscate
-forceprocessing
-optimizationpasses 5
-keep class * extends android.app.Activity
-assumenosideeffects class android.util.Log {
public static * d(...);
public static * v(...); } So you would save that to a file, then call ProGuard from Ant, passing in your just-compiled JAR and the
Android platform JAR you're using.
#REF:Remove all debug logging calls before publishing: are there tools to do this?

Keeping a session in all application life

I need to create a session and change it at times. In a specific activity should recover it and compare it to a different variable and modify the value of this session. I tried to create a class for this, but the change of activity, the value back to null. I need it to remain until the application is closed.
below:
import android.app.Application;
public class Util extends Application {
private static String idCorrente;
public void onCreate() {
super.onCreate();
idCorrente="0";
}
public static String getIdCorrente() {
return idCorrente;
}
public static void setIdCorrente(String id) {
Util.idCorrente = id;
}
}
I do not know exactly the right way to do it.
You need to store the data on the device somehow. I would recommend reading the Storage Options page of the Android Developers Guide.
Specifically, I think you will find SharedPreferences well-suited for your application.

How can a code in a library project call code in the application project?

In an effort to reduce duplication, I have my app's workspace split into 3 projects:
Main (A library project, where all of the common code lives)
Free (To make the free version)
Paid (To make the paid version)
Behavior in the free version and the paid version must differ sometimes. How can I "call into" the final projects from the library project?
Here is some sample psuedo-code to illustrate my question:
In the Main project:
private void makeADecision() {
if (AppCode.isPaid()) {
// do one thing
} else {
// do something else
}
}
In the Free project:
public class AppCode {
public static bool isPaid() {
return false;
}
}
In the Paid project:
public class AppCode {
public static bool isPaid() {
return true;
}
}
That is basically what I have tried, but it won't compile because the Main project doesn't know about the AppCode class.
Bear in mind that this is only an example, so try not to focus on how an app can tell if it is the paid version or not. :) So far the best solution I have found is to put a string in the resources of all three projects and then make a decision based on its value but I don't like that method. Besides being ugly, I would prefer to keep functionality where it belongs. That way I can prevent "paid-only" functionality from being compiled into the free version at all and I can avoid having to include any "free-only" code in the paid version.
Step #1: Define an interface in the library, so it is available to all three parties, whose methods are whatever operations you want the library to perform on the app
Step #2: Have the app supply an implementation of the interface to the library via some library-supplied API
Step #3: Have the library call methods on the supplied interface as needed
I don't think it's a good idea to call the main App from the library, even if it's possible.
Instead I'd be adding a public static boolean to the library and set it from within your application once it starts for the first time.
public class MyLibrary {
public static boolean IS_PAID = false;
public void makeADecision() {
if(IS_PAID) {
// do one thing
} else {
// do something else
}
}
}
and in your main application you could do something like
com.yourname.yourlib.MyLibrary.IS_PAID = true;
to set it. Since it's not final, you can change it's state at any time. If it's more complicated behavior, you could use a public static listener or callback which you could assign from your full/free app and then call it from your library
You could use reflection to achieve that - take care that it is usually not a very good idea.
For example:
static private boolean isAppPaid;
static {
try {
Class c = Class.forName("your.package.AppCode");
Method m = c.getMethod("isPaid");
isAppPaid = (boolean) m.invoke(null);
}
catch (Exception e) {
isAppPaid = false;
}
}
There probably are mistakes in my code - I have never used Java reflection much.
Edit: I agree with Tseng that making a library invoke application code is debatable at best. (Except if said library is a framework that takes over the client application.)
You could also make the free and paid versions make subclasses of whatever class makeADecision is in and implement the separate behavior that way.
so in main
public class BaseClass {
...
public void makeADecision() {}
...
}
in free
public class FreeClass extends BaseClass {
...
public void makeADecision() {
//free implementation here
}
...
}
in paid
public class PaidClass extends BaseClass {
...
public void makeADecision() {
//paid implementation here
}
...
}
Tseng Solution is an easy and straightforward solution. Thanks for that, it is the one i have used. Also, I fell upon that article which could help you implement the solution :
http://www.firstlightassociates.co.uk/blog/2011/software/android-software/managing-free-and-paid-android-applications-2/
Hope it will help some of you !
Cheers

Categories

Resources