Android studio updated and code too large now - java

I have a system that has been produced for 2 years now. It is an EMM system for controlling corporate devices.
It uses FireBase to send the functionality executed on the device from the server app to the device.
There are around 400 possible commands you can send to a device and all these commands are handled in one class initially, which overrides the onMessageReceived() from the FireBaseMessagingService class.
The older version of Android studio built the apk which is now in production. I have started to work on version 2 of my system after about a year off. so I updated my Android studio to the latest (4).
The Problem:
when I try to build the project and push onto a device, I get
error: code too large public void onMessageReceived(RemoteMessage remoteMessage) {
As stated before this onMessageReceived method can handle 400 different types of push notifications from the server app, so there are a lot of if/else statements in the method body.
Is there any reason why since the AS upgrade this will not work?
is there any setting I can change in AS to get past this?
What I have tried:
I thought about putting half of the if/else in another service class, to cut down on the method code. This would involve passing the remoteMessageMap to another class to carry on with the if/else processing.
remoteMessageMap from FireBase is a Map and Maps are not serializable as they extend the interface, so can't pass it.
public class MyAndroidFirebaseMsgService extends FirebaseMessagingService {
private static final String TAG = "MyAndroidFCMService";
AppObj appObj;
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Log.e(TAG, "remoteMessage.getData() = " + remoteMessage.getData());
Map remoteMessageMap = remoteMessage.getData();
String message = (String)remoteMessageMap.get("message");
thanks
[edit1]
else if(message.trim().equalsIgnoreCase("CLEARCACHE_REMOVE_APP_WL")){
Log.e(TAG, "received CLEARCACHE_REMOVE_APP_WL");
String pushGuid = (String)remoteMessageMap.get("pushguid");
Log.e(TAG, "pushGuid = " + pushGuid);
String clearCacheRemoveWhitelist = (String)remoteMessageMap.get("clear_cache_app_names");
Intent intentExecutePushCommand = new Intent( getApplicationContext(), ExecutePushCommandIntentService.class);
intentExecutePushCommand.putExtra("compID", MenuActivity.companyID);
intentExecutePushCommand.putExtra("command", message);
intentExecutePushCommand.putExtra("pushguid", pushGuid);
intentExecutePushCommand.putExtra("clear_cache_app_names", clearCacheRemoveWhitelist);
startService(intentExecutePushCommand);
}else if(message.trim().equalsIgnoreCase("CLEARCACHE_GET_PACKAGENAMES_WL")){
Log.e(TAG, "received CLEARCACHE_GET_PACKAGENAMES_WL");
String pushGuid = (String)remoteMessageMap.get("pushguid");
Log.e(TAG, "pushGuid = " + pushGuid);
Intent intentExecutePushCommand = new Intent( getApplicationContext(), ExecutePushCommandIntentService.class);
intentExecutePushCommand.putExtra("compID", MenuActivity.companyID);
intentExecutePushCommand.putExtra("command", message);
intentExecutePushCommand.putExtra("pushguid", pushGuid);
startService(intentExecutePushCommand);
}else if(message.trim().equalsIgnoreCase("CLEARCACHE_ADD_PACKAGENAME_WL")){
Log.e(TAG, "received CLEARCACHE_ADD_PACKAGENAME_WL");
String pushGuid = (String)remoteMessageMap.get("pushguid");
Log.e(TAG, "pushGuid = " + pushGuid);
String packageName = (String)remoteMessageMap.get("package_name");
Intent intentExecutePushCommand = new Intent( getApplicationContext(), ExecutePushCommandIntentService.class);
intentExecutePushCommand.putExtra("compID", MenuActivity.companyID);
intentExecutePushCommand.putExtra("command", message);
intentExecutePushCommand.putExtra("pushguid", pushGuid);
intentExecutePushCommand.putExtra("package_name", packageName);
startService(intentExecutePushCommand);
}

There is no need to pass the remoteMessageMap to another class. The source of the problem is the limitation in the java method size. Here is a piece of the official documentation of oracle which is related to this problem:
code_length
The value of the code_length item gives the number of bytes in the code array for this method.
The value of code_length must be greater than zero (as the code array must not be empty) and less than 65536.
The point is that your onMessageReceived method is too long, which is bigger than 64KB of compiled code. It is weird why it was compiled fine in previous versions of Android Studio :)
Anyway, the solution is to break the method into smaller fragments. My suggestion is fragmentation by some message types. For example:
private static final String COMMAND_1 = "COMMAND_1";
private static final String COMMAND_2 = "COMMAND_2";
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Log.e(TAG, "remoteMessage.getData() = " + remoteMessage.getData());
Map remoteMessageMap = remoteMessage.getData();
String message = (String) remoteMessageMap.get("message");
String type = extrated_from_received_message;
switch (type) {
case COMMAND_1:
handleCommand1(remoteMessageMap);
break;
case COMMAND_2:
handleCommand2(remoteMessageMap);
break;
// more commands ...
default:
// ...
}
}
private void handleCommand1(Map remoteMessageMap){
// do whatever related to command 1
}
private void handleCommand2(Map remoteMessageMap){
// do whatever related to command 2
}
In this way, the method size would be optimized and the performance of calling it will be far improved.

It seems that you are repeating the same lines of code a lot of times, just put these lines of code and maybe a few more in a separate method that is called on each else if and this will reduce the size of onMessageReceived()
Intent intentExecutePushCommand = new Intent( getApplicationContext(), ExecutePushCommandIntentService.class);
intentExecutePushCommand.putExtra("compID", MenuActivity.companyID);
intentExecutePushCommand.putExtra("command", message);
intentExecutePushCommand.putExtra("pushguid", pushGuid);

Related

TelecomManager.getLine1Number returning null for a valid PhoneAccountHandle

Google told me that in order for me to be able to use either TelephonyManager or TelcomManager to read device identifiers I need one of the requirements stated on the documentation for the respective device identifiers such as GetImei or GetLine1Number. I decided to meet the requirement that states The caller needs to be the default SMS Role holder on that device. So I created an app that is listed as an alternative SMS app on my device and then I check if my app holds that role and then try to get the device identifiers. My code reads the IMEI of both sim cards successfully but fails to read the phone number of the device as the method returns null but I did not expect this as the default sms app should be able to access that number for texting purposes. I am using the following code, help me make this work.
try
{
if (roleManager.IsRoleHeld(RoleManager.RoleSms))
{
var manager = (TelephonyManager)GetSystemService(Context.TelephonyService);
new Android.App.AlertDialog.Builder(this).SetTitle("Device Identifiers")
.SetMessage("phone no " + manager.Line1Number + "\n" + "line 1 imei: " + manager.GetImei(0) + "\n" +
"line 2 imei: " + manager.GetImei(1) + "\n" +
"serial no: " + manager.SimSerialNumber
).Show();
//telephonyManager.GetLine1 fails so
//try and use the telecom service to read the number
var telcom = (TelecomManager)GetSystemService(Context.TelecomService);
//get a list of all capable calling accounts
IList<PhoneAccountHandle> handles = telcom.CallCapablePhoneAccounts;
if (handles != null)
{
//Toast the phone capable calling accounts count
Toast.MakeText(this, handles.Count.ToString(), ToastLength.Short).Show();
//get the phone account handle in index 0
PhoneAccountHandle handle1 = handles[0];
//get the phone number associated with that acount
string phone1 = telcom.GetLine1Number(handle1);
if (phone1 != null)
{
new Android.App.AlertDialog.Builder(this).SetTitle("Phone Number")
.SetMessage(phone1).Show();
}
}
string phone = telcom.GetLine1Number(handles[1]);
if (phone != null)
{
Toast.MakeText(this,phone,ToastLength.Short).Show();
}
else
{
Toast.MakeText(this, "GetLine1 is returning null", ToastLength.Short).Show();
}
}
} catch (Java.Lang.SecurityException exc)
{
Toast.MakeText(this, exc.Message, ToastLength.Short).Show();
}
An answer in Android Java is also acceptable, Thank You.
At first, please check the AndroidManifest.xml if you added the <uses-permission android:name="android.permission.READ_PHONE_STATE"/> in it or not.
In addition, there is no reliable way to get the phone number from the SIM card because some telecom operators don't add this information in SIM card and someone did. So sometimes you can get the number and sometimes you will get a null even a phone number you used before.
The most used solution on the SO is using the Google Play Service to get the phone number. You can check this case. And in the Xamarin, you can use the package named Xamarin.GooglePlayServices.Auth to do that.

Android ACTION_SEND with special unicode characters

I am trying to send text using an intent. I thought it was straight-forward:
public Intent getIntent() {
final Intent intent = new Intent();
intent.setComponent(new ComponentName(packageName, ri.activityInfo.name));
intent.setAction(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_TEXT, getText());
return intent;
}
private String getText(){
// the emoji can be one of many. I removed all that for brevity
final int unicode = 0x1F3C5;
final String emoji = String.valueOf(Character.toChars(unicode));
// final String emoji = "\n1F3C5"; //didn't work either
// I also tried using HTML here but I think I have some wires crossed, so I am not ruling that out yet
return textPartOne + " " + emoji + " " + textPartTwo;
}
As you can see, the EXTRA_TEXT has a special character in it (an emoji). When the text sends from device A the emoji appears like I expect in the message. But on device B (the receiver) the text shows some madness (usually in two different messages):
#.£¡ù¿ ¡ ¡ | | < | | | \ < | [ ¡ { ¡ [ ¡
#H£(ù#æ#a#¤¥¡
¡9#ü#Hù#Θ#=#ø¥p
¡6#Ö#Δì¿ ¡ |
It doesn't matter if I send the message from device A to device B or B to A. I get the same results either way, so I believe it isn't an issue with the emoji not being supported.
Now, if I remove all but the emoji code:
Xäx&
If I remove the emoji code altogether it works like a charm.
But the client has gotta have those emojis...
😤
Some other things that may be of note:
I am using this to manage what intents I am presenting to the user. They could pick MMS, email, Twitter, Facebook or whatever really. I need to support all of these
I am getting the special characters from Emojipedia
I am not displaying the emojis anywhere in the app. They will be shown in Text message, Email, or Facebook/Twitter feed.
There is already an iOS variant of this app who is successfully doing this.
I have tried nearly a dozen different ways with no avail.
Is there anyone that may have some insight on how to properly send special characters?
Edit:
As you can see, I am sending the emoji I desire. But the result is far from what I expect. This is a screenshot of an emulator sending a text to itself.
Here are some of the ways I have tried
String attempt1 = "--";
try {
attempt1 = Html.fromHtml(new String("&#x1F3C5".getBytes("UTF-8"))).toString();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
final int codePoint = 0x26F7;
final String attempt2 = new String(new int[]{codePoint}, 0, 1);
final String attempt3 = new String(Character.toChars(codePoint));
final String attempt4 = "\u26F7";
All result in:
&n
If I add text to these:
final String attempt4 = "COME ON: \u26F7";
I get this:
#Σ£(ù#æ#s##£ Å¡7#X£¿ù¿
Which makes total sense he said sarcastically
You can use intents, which are messages sent between activities. In a intent you can put all sort of data, String, int, etc.
In your case, in activity2, before going to activity1, you will store a String message this way :
Intent intent = new Intent(activity2.this, activity1.class);
intent.putExtra("message", getText());
startActivity(intent);
In activity1, in onCreate(), you can get the String message by retrieving a Bundle (which contains all the messages sent by the calling activity) and call getString() on it :
Bundle bundle = getIntent().getExtras();
String message = bundle.getString("message");
Then you can set the text in the TextView:
TextView txtView = (TextView) findViewById(R.id.your_resource_textview);
txtView.setText(message);
Hope this helps !

Azure MobileServices on Android returns "The id must be numeric" on updates

I am programming an android program where links to my Azure with MobileService on Android.'
During execution of a similar update statement which can be found here, it returns as error "java.lang.IllegalArgumentException: The id must be numeric".
I am using the follow codes:
mTable.update(item, new TableOperationCallback<EntityTest>() {
public void onCompleted(EntityTest result,
Exception exception, ServiceFilterResponse response) {
if (exception == null) {
Toast.makeText(getApplicationContext(), "Successfully updated!", Toast.LENGTH_SHORT).show();
}
else{
Toast.makeText(getApplicationContext(), exception+"///"+response, Toast.LENGTH_SHORT).show();
}
}
});
Any help is gladly and hugely appreciated!
I'm not completely positive but Mobile Services recently changed over to a new model for the ID column on tables (it use to be integer based but is now String based). I would guess you're using an old version of the SDK which assumes it's going to get back integers for the ID column when now it will return strings. I would suggest downloading the latest version of the SDK and making sure your data model class (EntityTest from the looks of your code) is expecting a String ID and not an int.

How can I programmatically open a user specified calling/sms/mms Android app?

I've created an address book app, and am trying to add some features to it. What I'm trying to do right now is add the ability to longclick a phone number, and then either call/text/mms the number. This all works fine with phones, but I was wondering how to do this on tablets, since they will not have that same ability. The device I'm debugging on is a tablet, and I use HeyWire for texting. I know there are apps out there for calling via WiFi as well. Here's what I have so far for the texting section of my switch statement:
case 1: //SMS
if(CanCallAndText)
{
CustomSMSDialog SendSMSDialog = new CustomSMSDialog(BrowseListActivity.this, ParsedPhoneNum);
SendSMSDialog.setTitle("Sending text to " + PhoneNum);
SendSMSDialog.setCancelable(false);
SendSMSDialog.show();
}
else
{
try
{
Intent WiFiSMS = new Intent(Intent.ACTION_VIEW);
WiFiSMS.setData(Uri.parse("sms:" + PhoneNum));
WiFiSMS.setType("vnd.android-dir/mms-sms");
startActivityForResult(Intent.createChooser(WiFiSMS, ""), 0);
}//endtry
catch(Exception e)
{
Toast.makeText(getApplicationContext(), "Error: " + e.getMessage(), Toast.LENGTH_LONG).show();
}//endcatch
}//endelse
break;
I don't know if I'm doing the create chooser incorrectly or not, but it simply tells me that no apps can handle it. Thank you!
EDIT: Ooh, spotted a slight error that I should fix tomorrow. The URI should include ParsedPhoneNum, instead of PhoneNum. Anything other than a number, like a -, would be included in PhoneNum.

Get Android Device Name

How to get Android device name? I am using HTC desire. When I connected it via HTC Sync the software is displaying the Name 'HTC Smith' . I would like to fetch this name via code.
How is this possible in Android?
In order to get Android device name you have to add only a single line of code:
android.os.Build.MODEL;
Found here: getting-android-device-name
You can see answers at here Get Android Phone Model Programmatically
public String getDeviceName() {
String manufacturer = Build.MANUFACTURER;
String model = Build.MODEL;
if (model.startsWith(manufacturer)) {
return capitalize(model);
} else {
return capitalize(manufacturer) + " " + model;
}
}
private String capitalize(String s) {
if (s == null || s.length() == 0) {
return "";
}
char first = s.charAt(0);
if (Character.isUpperCase(first)) {
return s;
} else {
return Character.toUpperCase(first) + s.substring(1);
}
}
I solved this by getting the Bluetooth name, but not from the BluetoothAdapter (that needs Bluetooth permission).
Here's the code:
Settings.Secure.getString(getContentResolver(), "bluetooth_name");
No extra permissions needed.
On many popular devices the market name of the device is not available. For example, on the Samsung Galaxy S6 the value of Build.MODEL could be "SM-G920F", "SM-G920I", or "SM-G920W8".
I created a small library that gets the market (consumer friendly) name of a device. It gets the correct name for over 10,000 devices and is constantly updated. If you wish to use my library click the link below:
AndroidDeviceNames Library on Github
If you do not want to use the library above, then this is the best solution for getting a consumer friendly device name:
/** Returns the consumer friendly device name */
public static String getDeviceName() {
String manufacturer = Build.MANUFACTURER;
String model = Build.MODEL;
if (model.startsWith(manufacturer)) {
return capitalize(model);
}
return capitalize(manufacturer) + " " + model;
}
private static String capitalize(String str) {
if (TextUtils.isEmpty(str)) {
return str;
}
char[] arr = str.toCharArray();
boolean capitalizeNext = true;
String phrase = "";
for (char c : arr) {
if (capitalizeNext && Character.isLetter(c)) {
phrase += Character.toUpperCase(c);
capitalizeNext = false;
continue;
} else if (Character.isWhitespace(c)) {
capitalizeNext = true;
}
phrase += c;
}
return phrase;
}
Example from my Verizon HTC One M8:
// using method from above
System.out.println(getDeviceName());
// Using https://github.com/jaredrummler/AndroidDeviceNames
System.out.println(DeviceName.getDeviceName());
Result:
HTC6525LVW
HTC One (M8)
Try it. You can get Device Name through Bluetooth.
Hope it will help you
public String getPhoneName() {
BluetoothAdapter myDevice = BluetoothAdapter.getDefaultAdapter();
String deviceName = myDevice.getName();
return deviceName;
}
You can use:
From android doc:
MANUFACTURER:
String MANUFACTURER
The manufacturer of the product/hardware.
MODEL:
String MODEL
The end-user-visible name for the end product.
DEVICE:
String DEVICE
The name of the industrial design.
As a example:
String deviceName = android.os.Build.MANUFACTURER + " " + android.os.Build.MODEL;
//to add to textview
TextView textView = (TextView) findViewById(R.id.text_view);
textView.setText(deviceName);
Furthermore, their is lot of attribute in Build class that you can use, like:
os.android.Build.BOARD
os.android.Build.BRAND
os.android.Build.BOOTLOADER
os.android.Build.DISPLAY
os.android.Build.CPU_ABI
os.android.Build.PRODUCT
os.android.Build.HARDWARE
os.android.Build.ID
Also their is other ways you can get device name without using Build class(through the bluetooth).
Following works for me.
String deviceName = Settings.Global.getString(.getContentResolver(), Settings.Global.DEVICE_NAME);
I don't think so its duplicate answer. The above ppl are talking about Setting Secure, for me setting secure is giving null, if i use setting global it works. Thanks anyways.
universal way to get user defined DeviceName working for almost all devices and not requiring any permissions
String userDeviceName = Settings.Global.getString(getContentResolver(), Settings.Global.DEVICE_NAME);
if(userDeviceName == null)
userDeviceName = Settings.Secure.getString(getContentResolver(), "bluetooth_name");
Try this code. You get android device name.
public static String getDeviceName() {
String manufacturer = Build.MANUFACTURER;
String model = Build.MODEL;
if (model.startsWith(manufacturer)) {
return model;
}
return manufacturer + " " + model;
}
#hbhakhra's answer will do.
If you're interested in detailed explanation, it is useful to look into Android Compatibility Definition Document. (3.2.2 Build Parameters)
You will find:
DEVICE - A value chosen by the device implementer containing the
development name or code name identifying the configuration of the
hardware features and industrial design of the device. The value of
this field MUST be encodable as 7-bit ASCII and match the regular
expression “^[a-zA-Z0-9_-]+$”.
MODEL - A value chosen by the device implementer containing the name
of the device as known to the end user. This SHOULD be the same name
under which the device is marketed and sold to end users. There are no
requirements on the specific format of this field, except that it MUST
NOT be null or the empty string ("").
MANUFACTURER - The trade name of the Original Equipment Manufacturer
(OEM) of the product. There are no requirements on the specific format
of this field, except that it MUST NOT be null or the empty string
("").
UPDATE
You could retrieve the device from buildprop easitly.
static String GetDeviceName() {
Process p;
String propvalue = "";
try {
p = new ProcessBuilder("/system/bin/getprop", "ro.semc.product.name").redirectErrorStream(true).start();
BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
while ((line = br.readLine()) != null) {
propvalue = line;
}
p.destroy();
} catch (IOException e) {
e.printStackTrace();
}
return propvalue;
}
But keep in mind, this doesn't work on some devices.
Simply use
BluetoothAdapter.getDefaultAdapter().getName()
static String getDeviceName() {
try {
Class systemPropertiesClass = Class.forName("android.os.SystemProperties");
Method getMethod = systemPropertiesClass.getMethod("get", String.class);
Object object = new Object();
Object obj = getMethod.invoke(object, "ro.product.device");
return (obj == null ? "" : (String) obj);
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
you can get 'idol3' by this way.
Tried These libraries but nothing worked according to my expectation and was giving absolutely wrong names.
So i created this library myself using the same data.
Here is the link
AndroidPhoneNamesFinder
To use this library just add this for implementation
implementation 'com.github.aishik212:AndroidPhoneNamesFinder:v1.0.2'
Then use the following kotlin code
DeviceNameFinder.getPhoneValues(this, object : DeviceDetailsListener
{
override fun details(doQuery: DeviceDetailsModel?)
{
super.details(doQuery)
Log.d(TAG, "details: "+doQuery?.calculatedName)
}
})
These are the values you will get from DeviceDetailsModel
val brand: String? #This is the brandName of the Device
val commonName: String?, #This is the most common Name of the Device
val codeName: String?, #This is the codeName of the Device
val modelName: String?, #This is the another uncommon Name of the Device
val calculatedName: String?, #This is the special name that this library tries to create from the above data.
Example of Android Emulator -
brand=Google
commonName=Google Android Emulator
codeName=generic_x86_arm
modelName=sdk_gphone_x86
calculatedName=Google Android Emulator
Within the GNU/Linux environment of Android, e.g., via Termux UNIX shell on a non-root device, it's available through the /system/bin/getprop command, whereas the meaning of each value is explained in Build.java within Android (also at googlesource):
% /system/bin/getprop | fgrep ro.product | tail -3
[ro.product.manufacturer]: [Google]
[ro.product.model]: [Pixel 2 XL]
[ro.product.name]: [taimen]
% /system/bin/getprop ro.product.model
Pixel 2 XL
% /system/bin/getprop ro.product.model | tr ' ' _
Pixel_2_XL
For example, it can be set as the pane_title for the status-right within tmux like so:
tmux select-pane -T "$(getprop ro.product.model)"
Gets an Android system property, or lists them all
adb shell getprop >prop_list.txt
Find your device name in prop_list.txt to get the prop name
e.g. my device name is ro.oppo.market.name
Get oppo.market Operator
adb shell getprop ro.oppo.market.name
My case on windows as follows
D:\winusr\adbl
λ *adb shell getprop ro.oppo.market.name*
OPPO R17

Categories

Resources