How to make Android app automatically configure w/ debug vs. release values? - java

I'm working on an Android app, specifically one that uses the Facebook Android SDK. In development mode, I'm working with a test Facebook app that goes by one ID. However, in release mode, the app will be working with second Facebook app with a different ID.
I'm wondering how most Android (or Java might be a suitable enough realm of knowledge) developers here go about having their app automatically build with debug vs. release values. An ideal solution does not involve a manual switch (e.g.: switching public static final DEBUG = false; to true) before building.

It's been a while since you asked but I thought I'd share how I'm doing it.
Like Sebastian hinted, an Ant script can handle that change for you and generate the static final constants that you're looking for. You can configure IntelliJ or Eclipse to make it almost seamless.
I tried to detail the different steps I took over here, let me know if it helps. I know I never have to make any manual changes before releasing, and it's a nice relief!

In eclipse ADT 17.0 and above there is a new feature for this. Check the BuildConfig.DEBUG that is automatically built with your code.
For more information see http://developer.android.com/sdk/eclipse-adt.html

I can't recommend the IMEI method... the main problem with it is that not all Android devices will have IMEIs. A better way is to examine the signature used to sign the .apk.
// See if we're a debug or a release build
try {
PackageInfo packageInfo = getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES);
if (packageInfo.signatures.length>0) {
String signature = new String(packageInfo.signatures[0].toByteArray());
isReleaseBuild = !signature.contains("Android Debug");
}
} catch (NameNotFoundException e1) {
e1.printStackTrace();
}

I use a slightly more mundane method (in case you're still interested in solutions).
At application launch my application checks for the existence of a text file stored in /sdcard/. Each application I have looks for a specific file like "applicationdebug.txt". If the file exists, then the application goes into debug mode and starts being verbose with log statements and using my debug Facebook key, etc.
Then I simply remove (or rename) the file to on the device to see how the application performs in release mode.

Usually you will use 1 or 2 devices for debugging only. So you can set the DEBUG switch based on the Devices? So you can simply use the IMEI.
add a new Application class to your project and have it initialize the field (suspect to put it in a Const class).
In your Applications onCreate method, call Const.setupDebug(getApplicationContext());
Implement the setupDebug like this
public class Const {
private static boolean debug = false;
public static boolean isDebug() {
return debug;
}
private static void setDebug(boolean debug) {
Const.debug = debug;
}
private static String [] DEBUG_DEVICES = new String[] {
"000000000000000", "gfjdhsgfhjsdg" // add ur devices
};
public static void setupDebug(Context context) {
Arrays.sort(DEBUG_DEVICES);
TelephonyManager mTelephonyMgr = (TelephonyManager)
context.getSystemService(Context.TELEPHONY_SERVICE);
String imei = mTelephonyMgr.getDeviceId();
if (imei == null) imei = "000000000000000";
if(Arrays.binarySearch(DEBUG_DEVICES, imei) > -1) {
setDebug(true);
}
}
}
Switch from constant field to constant Method.
Const.isDebug()

With Eclipse I create 3 projects in the workspace :
ApplicationProject
It is a library project
Contain all source code
In values/refs.xml I add
<bool name="debug_mode">true</bool>
ApplicationProjectDEBUG
Use ApplicationProject
Overide AndroidManifest and other xml file with
developement specific config
In values/refs.xml I add
<bool name="debug_mode">true</bool>
ApplicationProjectPROD
Use ApplicationProject
Overide AndroidManifest and other xml file with
production specific config
In values/refs.xml I add
<bool name="debug_mode">false</bool>
I signe APK from this project to put on the store

Related

How to delete a folder on Android 11?

I have a doubt, how I can delete a folder on Android 11 (or 10)?
Have much answers here on Stack how to do it, but nothing of worked.
This code worked for me on Android 5:
public static boolean deleteDir(File dir) {
if (dir.isDirectory()) {
String[] children = dir.list();
for (int i=0; i<children.length; i++) {
boolean success = deleteDir(new File(dir, children[i]));
if (!success) {
return false;
}
}
}
// The directory is now empty so delete it
return dir.delete();
}
On newest versions of Android it not work. I noticed that there are applications that can to do this, so I imagine it is possible. Any answer about it?
Android has done much to change its permission models around file access since Android 10 and 11. The preferred approach is to use Scoped Storage APIs.
Many of the old file read/write permissions are on life support and will no longer work. If they do continue to work, then must justify to Google why you need them, and then go through a security approval by Google. You will only be granted approval if you app is classified as a file browser application, or the like.
Basically, the new model is to use Android's file browsers to gain access to read/write/update a particular file that the user selects, and defer the rest of the file management to Google's first-party applications. The access you get is based on what the user selected in the first-party file browser. You are then handed a URI back to your application with the proper permissions to perform the intended action, such as read/write/etc...
You may find this video useful:
https://www.youtube.com/watch?v=RjyYCUW-9tY
Have you tried Context.deleteFile() ?
getApplicationContext().deleteFile(filename);

How do I specify and add a custom printer in an Android app?

I'm creating an app for Android. Part of the desired app functionality is that the user can select a special printer (let's just call it Transfer Printer) which will pass on the document-to-be-printed to a process running on an external server.
What steps do I need to take to add a custom printer to the list of printers in the Android print panel, accessible from the Print option of the Overflow menu?
It is desirable to use the existing Android print panel functionality rather than, for example, an additional Share option in the App Selector because of user experience considerations; it won't be intuitive to the user to click Share rather than Print for the desired functionality.
Prior research
There is an existing similar question which has gathered little interest since it was posted some time ago. The asker has identified the PrintManager class as a lead but I believe that the PrintService class is likely to be more fruitful:
A print service is responsible for discovering printers, adding discovered printers, removing added printers, and updating added printers.
The same page details Declaration and Configuration of the print service. I've done so as below.
Attempted Execution
Declaration and Configuration
In AndroidManifest.xml:
...
<application
... >
...
<service
android:name=".TransferPrintService"
android:permission="android.permission.BIND_PRINT_SERVICE"
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="android.printservice.PrintService" />
</intent-filter>
<meta-data
android:name="android.printservice"
android:resource="#xml/transfer_print_service" />
</service>
</application>
Meta-data
It's unclear to me exactly where the meta-data is supposed to be specified. From SERVICE_META_DATA section of the PrintService page:
This meta-data must reference a XML resource containing a print-service tag.
In res/xml/transfer_print_service.xml:
<print-service
android:label="TransferPrintService"
android:vendor="Company Ltd." />
TransferPrintService Class
This creates a custom PrinterDiscoverySession. My goal at this stage is to just get a printer appearing in the print panel and work from there.
public class TransferPrintService extends PrintService {
public TransferPrintService() {
}
#Override
public void onPrintJobQueued(PrintJob printJob) {
printJob.start();
printJob.complete();
}
#Override
public PrinterDiscoverySession onCreatePrinterDiscoverySession() {
return new TransferPrinterDiscoverySession(this);
}
#Override
public void onRequestCancelPrintJob(PrintJob printJob) {
}
}
The service is started in a BroadcastReceiver on an ACTION_BOOT_COMPLETED intent.
TransferPrinterDiscoverySession Class
This actually creates the custom printer.
public class TransferPrinterDiscoverySession extends PrinterDiscoverySession {
private transferPrintService printService;
private static final String PRINTER = "Transfer Printer";
public transferPrinterDiscoverySession(TransferPrintService printService) {
this.printService = printService;
}
#Override
public void onStartPrinterDiscovery(List<PrinterId> printerList) {
PrinterId id = printService.generatePrinterId(PRINTER);
PrinterInfo.Builder builder =
new PrinterInfo.Builder(id, PRINTER, PrinterInfo.STATUS_IDLE);
PrinterInfo info = builder.build();
List<PrinterInfo> infos = new ArrayList<>();
infos.add(info);
addPrinters(infos);
}
#Override
public void onStopPrinterDiscovery() {
}
#Override
public void onValidatePrinters(List<PrinterId> printerIds) {
}
#Override
public void onStartPrinterStateTracking(PrinterId printerId) {
PrinterInfo.Builder builder = new PrinterInfo.Builder(printerId,
PRINTER, PrinterInfo.STATUS_IDLE);
PrinterCapabilitiesInfo.Builder capBuilder =
new PrinterCapabilitiesInfo.Builder(printerId);
capBuilder.addMediaSize(PrintAttributes.MediaSize.ISO_A4, true);
capBuilder.addResolution(new PrintAttributes.Resolution(
"Default", "Default", 360, 360), true);
capBuilder.setColorModes(PrintAttributes.COLOR_MODE_COLOR
+ PrintAttributes.COLOR_MODE_MONOCHROME,
PrintAttributes.COLOR_MODE_COLOR);
capBuilder.setMinMargins(PrintAttributes.Margins.NO_MARGINS);
PrinterCapabilitiesInfo caps = capBuilder.build();
builder.setCapabilities(caps);
PrinterInfo info = builder.build();
List<PrinterInfo> infos = new ArrayList<PrinterInfo>();
infos.add(info);
addPrinters(infos);
}
#Override
public void onStopPrinterStateTracking(PrinterId printerId) {
}
#Override
public void onDestroy() {
}
}
Major Concerns
This doesn't produce an additional printer option.
Is the arrangement of documents correct? Specifically, having the <print-service> tag in a separate XML document under res? Trying to place the tag anywhere in the AndroidManfiest.xml document produces IDE errors.
How do I call into the TransferPrintService? As an example, suppose I'm in Chrome, I open the Overflow menu, and select Print... Which PrintService is invoked? How do I make sure it's mine?
Am I on completely the wrong track here?
The trick I was missing was actually enabling the Print Service via the Android Settings menu. Actually doing this wasn't as straightforward as I would have hoped as the device manufacturer had removed the setting from the menu. It should be right under Accessibility in the System section of the menu.
I ended up installing the Cloud Print app by Google, which gave me access to the Print Service settings temporarily (to enable the Cloud Print service). Once in here I noticed that my own service was, in fact, present.
For posterity: To avoid un-installing and re-installing Cloud Print every time you want to change the Print Service settings, use the following SQLite3 commands, either with adb shell or from Terminal Emulator (or similar):
sqlite3 data/data/com.android.providers.settings/databases/settings.db
You should now have access to the Settings database and be using the SQLite3 command line shell. The settings of interest are located in the secure table and are enabled_print_services and enabled_on_first_boot_system_print_services. You can check if these settings already exist by using:
.dump secure
If they don't, then use the following commands:
INSERT INTO secure VALUES(<id>, 'enabled_on_first_boot_system_print_services', 'com.companyname.appservice/com.companyname.appservice.TransferPrintService');
INSERT INTO secure VALUES(<id>, 'enabled_print_services', 'com.companyname.appservice/com.companyname.appservice.TransferPrintService');
You should, of course, replace 'com.companyname.appservice' with your own package and 'TransferPrintService' with your own print service. If these setting names do already exist, and your print service isn't listed, then you'll need to UPDATE instead of INSERT INTO:
UPDATE secure SET value = '<existing print services>:<new print service>' WHERE name = 'enabled_on_first_boot_system_print_services';
UPDATE secure SET value = '<existing print services>:<new print service>' WHERE name = 'enabled_print_services';
You'll need to make sure to include any existing print services as part of the UPDATE command; listed print services are separated by a colon ":".
Reboot the device to apply the updates to the settings database.

App crashing after update FragmentList from loader using support library

I have a loader that uses the Support Library v4, it is being used to carry the loading of a ListView in an Activity that contains two fragments, one containing the ListView (being it an extension of ListFragment) and the other carrying a button (which can be clicked while the loader is doing the work).
The implementation is pretty similar to the one available at the Android documentation of the AsyncTaskLoader, which is also creating a ListView trough the loader, with the exception of the monitoring part, where my implementation does not need the monitoring of changes: http://developer.android.com/reference/android/content/AsyncTaskLoader.html
As the app has support to API level 8, I am using the FragmentActivity::getSupportLoaderManager method to start the loader as advised in the documentation in order to keep the support.
http://developer.android.com/reference/android/support/v4/app/FragmentActivity.html
When using this class as opposed to new platform's built-in fragment and loader support, you must use the getSupportFragmentManager() and getSupportLoaderManager() methods respectively to access those features.
Being the loader started from the Fragment, I had to use the ListFragment::getActivity method to call the method FragmentActivity::getSupportLoaderManager, resulting in the following code being used to start the loader:
getActivity().getSupportLoaderManager().initLoader(0, null, this).forceLoad();
The app is running fine with APIs higher than 8, but on level 8 it is crashing when the loader tries to renders the list on UI after loading (Loader::onLoadFinished method).
Debugging I found that it is crashing at the moment that the ArrayAdapter<>::addAll method is being called on the adapter, which confirms that the problem is on the rendering of the UI. At that moment, the app is thrown to the SamplingProfilerIntegration class where something related to a snapshot is trying to be done at a static part of the class:
/** Whether or not a snapshot is being persisted. */
private static final AtomicBoolean pending = new AtomicBoolean(false);
static {
samplingProfilerMilliseconds = SystemProperties.getInt("persist.sys.profiler_ms", 0);
samplingProfilerDepth = SystemProperties.getInt("persist.sys.profiler_depth", 4);
if (samplingProfilerMilliseconds > 0) {
File dir = new File(SNAPSHOT_DIR);
dir.mkdirs();
// the directory needs to be writable to anybody to allow file writing
dir.setWritable(true, false);
// the directory needs to be executable to anybody to allow file creation
dir.setExecutable(true, false);
if (dir.isDirectory()) {
snapshotWriter = Executors.newSingleThreadExecutor(new ThreadFactory() {
public Thread newThread(Runnable r) {
return new Thread(r, TAG);
}
});
enabled = true;
Log.i(TAG, "Profiling enabled. Sampling interval ms: "
+ samplingProfilerMilliseconds);
} else {
snapshotWriter = null;
enabled = true;
Log.w(TAG, "Profiling setup failed. Could not create " + SNAPSHOT_DIR);
}
} else {
snapshotWriter = null;
enabled = false;
Log.i(TAG, "Profiling disabled.");
}
}
It could be related to the specific behavior of the UI rendering on prior to Honeycomb versions as stated in the documentation, but I can not think of what.
http://developer.android.com/reference/android/support/v4/app/FragmentActivity.html
Prior to Honeycomb (3.0), an activity's state was saved before pausing. Fragments are a significant amount of new state, and dynamic enough that one often wants them to change between pausing and stopping. These classes throw an exception if you try to change the fragment state after it has been saved, to avoid accidental loss of UI state. However this is too restrictive prior to Honeycomb, where the state is saved before pausing. To address this, when running on platforms prior to Honeycomb an exception will not be thrown if you change fragments between the state save and the activity being stopped. This means that in some cases if the activity is restored from its last saved state, this may be a snapshot slightly before what the user last saw.
I found that the support libraries v4 and v7, which are the being imported to the project, does not support the method ArrayAdapter<>::addAll() and this was making the app crash.
This question is related to this issue and the solution presented was suitable to address my problem:
ListViews - how to use ArrayAdapter.addAll() function before API 11?
So, the solution was to implement my own version of the ArrayAdapter class, so that the prior to Honeycomb versions of Android could use it.

Open a prespective on eclipse startup - programmatically

I am developing a eclipse plugin. I need to open my prespective when we open the eclipse at first time. Any ways to achieve this? i guess some listener must be available but could not trace out.
We can open a prespective after eclipse start using PlatformUI.getWorkbench().showPrespective(<prespective id>)
Similarly is there a way to open the prespective on eclipse startup, so that our desired prespective gets opened when starting the eclipse.
You can use the org.eclipse.ui.startup extension point in your plugin. When the plugin is activated, check/set a preference to decide if you want to switch perspectives and then schedule a UIJob do do it.
Implement the extension point. Some class in the plugin needs implements org.eclipse.ui.IStartup. The activator class is fine in this case. Particularly, since you don't need anything in the earlyStartup method.
In the start method, make the decision to switch and schedule it:
public void start(BundleContext context) throws Exception {
super.start(context);
plugin = this;
final boolean switchPerpective = processPluginUpgrading();
if (switchPerpective) {
final IWorkbench workbench = PlatformUI.getWorkbench();
new UIJob("Switching perspectives"){
#Override
public IStatus runInUIThread(IProgressMonitor monitor) {
try {
workbench.showPerspective(perspectiveId, workbench.getActiveWorkbenchWindow());
} catch (WorkbenchException e) {
return new Status(IStatus.ERROR,PLUGIN_ID,"Error while switching perspectives", e);
}
return Status.OK_STATUS;
}}
.run(new NullProgressMonitor());
}
}
Use the preference store to keep data for your decision logic. In this implementation, the perspective is switched once per workspace whenever the plugin is upgraded. The data recorded in the preference store will allow a future version to have a difference policy. It uses the getPreferenceStore from AbstractUIPlugin so it is scoped per workspace. If you want to use other scopes, see the FAQ.
private Boolean processPluginUpgrading() {
final Version version = getDefault().getBundle().getVersion();
final IPreferenceStore preferenceStore = getDefault().getPreferenceStore();
final String preferenceName = "lastVersionActivated";
final String lastVersionActivated = preferenceStore.getString(preferenceName);
final boolean upgraded =
"".equals(lastVersionActivated)
|| (version.compareTo(new Version(lastVersionActivated)) > 0);
preferenceStore.setValue(preferenceName, version.toString());
return upgraded;
}
One thing I am doing to open my custom perspective in my plugin is to configure it in config.ini in eclipe's installation folder as below:
-perspective <my perspective id>
and it is working fine. I got this information from Lars Vogel's tutorial, which you can find here. Hope this helps.
Other way:
org.eclipse.ui.IPerspectiveRegistry.setDefaultPerspective(id) this sets default perspective to the given id. API Docs for the same.
Go to
D:\{MyTestSpace}\eclipse\features\myCustom.plugin.feature_3.1.0.201607220552
you can see feature.xml under plugin tag you get the id.
Use this id in config.ini which you can find under
D:\{MyTestSpace}\eclipse\configuration
As
-perspective <myCustum.plugin>

Reliable way of detecting whether an Android app is running in 'BlueStacks'

I would like to ascertain at run-time inside an Android app whether it is running within the BlueStacks Android emulator. This is so I can modify the way the app runs when running inside BlueStacks.
BlueStacks does not support multi-touch so I want to implement an alternative to the standard pinch-to-zoom functionality my current app has.
E.g.
If (appIsRunningInBlueStacks){
mySurfaceView.enableMultiTouchAlternatives();
} else{
mySurfaceView.enableMultiTouchFeatures();
}
What is a reliable way of ascertaining the value of appIsRunningInBlueStacks?
EDIT Summary of answers to comments on question:
Ben, Taras, thanks for the suggestions. The Build.MODEL etc. values for BlueStacks are:
Model: "GT-I9100"
Manufacturer: "samsung"
Device: "GT-I9100"
Product: "GT-I9100"
This is the same model number as the Samsung Galaxy SII so it would not be ideal to use this for fear of treating all users with SIIs the same as those on BlueStacks.
CommonsWare, the app continues to run in BlueStacks even with the < uses-feature> for multitouch in the manifest. In fact (also answering iagreen's question)...
packageManager.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT);
... returns true! This is to be expected I suppose as the emulator is convinced it is a Samsung Galaxy SII!
Therefore we are still without a way of reliably detecting whether an app is running on BlueStacks without also throwing all Samsung Galaxy SII users in the same bucket. Any other ideas?
All the above methods are not working on BlueStacks 5. The correct way to do is checking if the path of /mnt/windows/BstSharedFolder exists. It is working fine on both BlueStacks 4 and 5.
fun checkFilesExist(files: Array<String>): Boolean {
files.forEach {
val file = File(it)
if (file.exists()) {
return true
}
}
return false
}
fun isBlueStacks(): Boolean {
val BLUE_STACKS_FILES = arrayOf(
"/mnt/windows/BstSharedFolder"
)
return checkFilesExist(BLUE_STACKS_FILES)
}
You can check that the Bluestacks shared folder exist
/sdcard/windows/BstSharedFolder
Boolean onBlueStacks()
{
File sharedFolder = new File(Environment
.getExternalStorageDirectory().toString()
+ File.separatorChar
+ "windows"
+ File.separatorChar
+ "BstSharedFolder");
if (sharedFolder.exists())
{
return true;
}
return false;
}
After trying all the suggested solutions available online we found the Google's SafetyNet Attestation API is the only solution for detecting VMs like BlueStack(any version) and NoxPlayer.
Apps that care about content piracy (and other security issues) can filter their availability on the Google Play like Netflix filters devices on the PlayStore.
The new “device catalog” section of the console includes an option
called “SafetyNet exclusion,” which can be used to prevent “devices
that fail integrity tests or those that are uncertified by Google,”
from downloading a specific app: among these would be rooted devices
and those running custom ROMs.
But there is a catch user will still find the APK from cross-sharing or other distribution systems, so the client must implement SafetyNet Attestation API on the app level.
How does it work?
SafetyNet examines software and hardware information on the device
where your app is installed to create a profile of that device. The
service then attempts to find this same profile within a list of
device models that have passed Android compatibility testing. The API
also uses this software and hardware information to help you assess
the basic integrity of the device, as well as the APK information of
the calling app. This attestation helps you to determine whether or
not the particular device has been tampered with or otherwise
modified.
It's an (easy to implement) paid API from the Google which allows 10,000 free hits per day as of now :\
If anyone is interested in detecting VMs by them self, these are the good papers available suggesting heuristic approaches :
Evading Android Runtime Analysis via Sandbox Detection
Rage Against the Virtual Machine:
Hindering Dynamic Analysis of Android Malware
My version of BlueStacks is reporting my Build.Model as GT-N7100.
Using: android.opengl.GLES20.glGetString(android.opengl.GLES20.GL_RENDERER) I get Bluestacks.
It maybe too late but for the sake of others who have the same problem :
public boolean isRunningOnEmulator() {
return Build.FINGERPRINT.startsWith("generic")
|| Build.FINGERPRINT.startsWith("unknown")
|| Build.MODEL.contains("google_sdk")
|| Build.MODEL.contains("Emulator")
|| Build.MODEL.contains("Android SDK built for x86")
|| Build.MANUFACTURER.contains("Genymotion")
|| (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic"))
|| "google_sdk".equals(Build.PRODUCT)
|| Build.PRODUCT.contains("vbox86p")
|| Build.DEVICE.contains("vbox86p")
|| Build.HARDWARE.contains("vbox86");
}
Based on Mr. Regis' answer, you can detect it when the shared folder is present. However in Bluestacks 4, using file.exists() will only return false. This is because the shared folder has no permissions (000 or ----------). But listing files in the directory will detect the folder.
String path = Environment.getExternalStorageDirectory().toString();
Log.d("FILES", "Path: " + path);
File directory = new File(path);
File[] files = directory.listFiles();
for (File file : files) {
if (file.getName().contains("windows")) {
Log.d("FILES", "windows file exists, it's a bluestacks emu");
}
}
This Will be unique.
There is no bluetooth device in Bluestack.
So try to get The Bluetooth Address string which is always 'null' on Bluestack or Any emulator.Make sure you are adding Bluetooth permission on your project manifest.
BluetoothAdapter m_BluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
String m_bluetoothAdd = m_BluetoothAdapter.getAddress();

Categories

Resources