Application crash with android 2.3 - java

I use this code to turn off the wifi connection and data connection
public static class LowBatteryReceiver extends BroadcastReceiver{
public void onReceive(Context context, Intent intent){
wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
wifiManager.setWifiEnabled(false);
wifi = (WifiManager)context.getSystemService(Context.WIFI_SERVICE);
try {
ConnectivityManager conman = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
Class<?> conmanClass = Class.forName(conman.getClass().getName());
Field iConnectivityManagerField = conmanClass.getDeclaredField("mService");
iConnectivityManagerField.setAccessible(true);
Object iConnectivityManager = iConnectivityManagerField.get(conman);
Class<?> iConnectivityManagerClass = Class.forName(iConnectivityManager.getClass().getName());
Method setMobileDataEnabledMethod = iConnectivityManagerClass.getDeclaredMethod("setMobileDataEnabled", Boolean.TYPE);
setMobileDataEnabledMethod.setAccessible(true);
setMobileDataEnabledMethod.invoke(iConnectivityManager, false);
} catch (Exception e) {
e.printStackTrace();
}
}
}
The class will be called in a method to work but this is not important.. I have a nexus 4 with android 4.3 and the code works. Also in android 4.0.3/4, 4.1, 4.1.2, 4.2.1 and .4.2.2.. I use an ActionbarSherlock library so i can use an holo.light anctionbar also with previews android versions. A friend of mine with android 2.3.6 tryied the application and tells me has a crash.. I can't see any logcat for now but i think the problem is the code above. I know that with android 2.3 there was another way to turn the 3g off but i don't know which. How can i detect the android version and make something like : If android >= 4 use the code i posted and if android <=4 use another code (if someone can tell me which is is better thanks).
EDIT:
I found a code for data with android 2.3.. Have i to do something like
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.GINGERBREAD) {
Method dataConnSwitchmethod;
Class telephonyManagerClass;
Object ITelephonyStub;
Class ITelephonyClass;
TelephonyManager telephonyManager = (TelephonyManager) context
.getSystemService(Context.TELEPHONY_SERVICE);
if(telephonyManager.getDataState() == TelephonyManager.DATA_CONNECTED){
isEnabled = true;
}else{
isEnabled = false;
}
telephonyManagerClass = Class.forName(telephonyManager.getClass().getName());
Method getITelephonyMethod = telephonyManagerClass.getDeclaredMethod("getITelephony");
getITelephonyMethod.setAccessible(true);
ITelephonyStub = getITelephonyMethod.invoke(telephonyManager);
ITelephonyClass = Class.forName(ITelephonyStub.getClass().getName());
if (isEnabled) {
dataConnSwitchmethod = ITelephonyClass
.getDeclaredMethod("disableDataConnectivity");
} else {
dataConnSwitchmethod = ITelephonyClass
.getDeclaredMethod("enableDataConnectivity");
}
dataConnSwitchmethod.setAccessible(true);
dataConnSwitchmethod.invoke(ITelephonyStub);
}
so if the version is >= gingerbread this will be the code right?

Example how to use it:
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.GINGERBREAD) {
// only for gingerbread and newer versions
}
Also you can check as below for the version 4>= or <=4 android.
if(Build.VERSION.SDK_INT >= 4.0){
//this code will be executed on devices running on DONUT (NOT ICS) or later
}
since constant 4 represents donut: public static final int DONUT = 4;
You can find out the Android version looking at Build.VERSION.

Related

TelephonyManager call state still set at CALL_STATE_RINGING after call has been ended

I'm currently building a spam-call blocking app as a fun project to get better with Android development, and am running into this odd bug.
I have a Broadcast Receiver setup to listen for changes in the call state, and logic to check if the call is incoming or not, and basically end the call if the number is not in the user's contact list or a "whitelist" of approved numbers held in a database. However, even after the call has been ended, the receiver continues to be called multiple times (sometimes 8 or more times) and will often be called with Call State set as ringing (again, despite the fact that the call has been ended).
Basically, I'm trying to keep track of how many times each unknown number calls the phone, but for each single call it records it as anywhere from 2 - 6 calls.
Here is the onReceive function I have set up:
public void onReceive(final Context con, Intent intent) {
System.out.println(intent);
final TelephonyManager telephony = (TelephonyManager) con.getSystemService(Context.TELEPHONY_SERVICE);
telephony.listen(new PhoneStateListener() {
#RequiresApi(api = Build.VERSION_CODES.P)
#Override
public void onCallStateChanged(int state, String incomingNumber) {
super.onCallStateChanged(state, incomingNumber);
System.out.print(state);
//Toast.makeText(con, incomingNumber, Toast.LENGTH_LONG).show();
if (TelephonyManager.CALL_STATE_RINGING == state) {
checkIfNumberInContacts(con, incomingNumber, telephony, state);
}
}
}, PhoneStateListener.LISTEN_CALL_STATE);
}
and here is the custom function that checks if the incoming number is in contacts/whitelisted and ends the call:
#RequiresApi(api = Build.VERSION_CODES.P)
public void checkIfNumberInContacts(Context con, String incomingNumber, TelephonyManager telephony,
int state) {
db = Room.databaseBuilder(con, SpamBlockerDB.class,
"spamblocker.db").createFromAsset("databases/spamblocker.db").allowMainThreadQueries().build();
FilteredCalls call = new FilteredCalls();
call.calltime = Calendar.getInstance().getTime().toString();
call.deleted = 0;
call.number = incomingNumber;
call.whitelisted = 0;
call.callcount = 1;
List<FilteredCalls> allCalls = db.callsDao().getAll();
boolean callerExistsInDB = false;
for (FilteredCalls item : allCalls) {
if (incomingNumber.equals(item.number)) {
callerExistsInDB = true;
String updatedTime = Calendar.getInstance().getTime().toString();
db.callsDao().updateCallTime(updatedTime, item.recID);
}
if (TelephonyManager.CALL_STATE_RINGING == state) {
int currentCount = db.callsDao().getCallCount(item.recID);
db.callsDao().updateCallCount(currentCount + 1, item.recID);
}
}
ContentResolver contentResolver = con.getContentResolver();
Uri uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(incomingNumber));
String contact = "";
Cursor c = contentResolver.query(uri, new String[]{ContactsContract.PhoneLookup.HAS_PHONE_NUMBER}, null, null);
if (c != null) {
if (c.moveToFirst()) {
do {
contact = c.getString(c.getColumnIndex("NUMBER"));
} while (c.moveToNext());
}
}
if (contact == "" && !checkIfNumberIsWhitelisted(con, incomingNumber)) {
TelecomManager telecomManager = (TelecomManager) con.getSystemService(Context.TELECOM_SERVICE);
telecomManager.endCall();
if (!callerExistsInDB) {
db.callsDao().insert(call);
}
}
}
Any ideas why the BroadCast receiver is called more than twice (once for incoming, once for hanging up), and why when it's called the subsequent times it still thinks the phone is ringing?
Appreciate the help in advance.
There are two ways to get callbacks when the phone state changes: PhoneStateListener and a BroadcastReceiver that listens to android.intent.action.PHONE_STATE.
It looks like you're mixing the two methods together, which means that whenever your BroadcastReceiver is called, you're registering another new PhoneStateListener, so you keep adding more and more listeners that do the same thing.
I would recommend not using PhoneStateListener at all, and go with the BroadcastReceiver approach only, which can be setup via AndroidManifest which allows it to be called even when the app is asleep.
See this tutorial: https://medium.com/#saishaddai/how-to-know-when-a-device-is-ringing-in-android-57e516d0ab42

Eddystone beacons are not working when use Beacons-Android library

I tried to Implements an Eddystone beacon on android device using beacon-android library https://github.com/adriancretu/beacons-android#features as follows.
public class MainActivity extends AppCompatActivity {
EddystoneURL beacon = new EddystoneURL("www.github.com");
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Beacons.initialize(this);
beacon.init(5, AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY,AdvertiseSettings.ADVERTISE_TX_POWER_HIGH,0,"Eddie");
beacon.start();
UUID uuid = beacon.getUUID();
String name = beacon.getName();
Log.i("Log","name is "+name);
Log.i("Log",uuid.toString());
String url = beacon.getURL();
Log.i("Log",url);
Log.i("Log","App started");
int k = beacon.getActiveState();
Log.i("Log","active state : "+k);
int powerlvl = beacon.getTxPowerLevel();
Log.i("Log","Power Lvl : "+ powerlvl);
}
}
and I get logs as follows
2020-04-09 20:27:42.771 14922-14922/? I/Log: name is Eddie
2020-04-09 20:27:42.772 14922-14922/? I/Log: 21ac707d-2ef0-4578-aa52-f8b8020d97c3
2020-04-09 20:27:42.772 14922-14922/? I/Log: www.github.com
2020-04-09 20:27:42.772 14922-14922/? I/Log: App started
2020-04-09 20:27:42.772 14922-14922/? I/Log: active state : 0
2020-04-09 20:27:42.772 14922-14922/? I/Log: Power Lvl : 3
the problem is the emulated beacon is was not identified by beacon scanners. I really appreciate the help of yours. Thank you.
I realize this question is about building a transmitter with the beacons-android library, so a better answer would be to show how to make this work properly with that library.
However, if no solution is found and the OP is open to using the Android Beacon Library (similarly named but completely different) to accomplish this, the code below will do it:
try {
byte[] urlBytes = UrlBeaconUrlCompressor.compress("https://www.github.com"");
Identifier encodedUrlIdentifier = Identifier.fromBytes(urlBytes, 0, urlBytes.length, false);
ArrayList<Identifier> identifiers = new ArrayList<Identifier>();
identifiers.add(encodedUrlIdentifier);
beacon = new Beacon.Builder()
.setIdentifiers(identifiers)
.setTxPower(-59)
.build();
BeaconParser beaconParser = new BeaconParser()
.setBeaconLayout(BeaconParser.EDDYSTONE_URL_LAYOUT);
BeaconTransmitter beaconTransmitter = new BeaconTransmitter(getApplicationContext(),
beaconParser);
beaconTransmitter.startAdvertising(beacon);
} catch (MalformedURLException e) {
Log.d(TAG, "That URL cannot be parsed");
}
Full Disclosure: I am the lead developer on the Android Beacon Library open source project.

Why android hides bluetooth mac address?

i'm trying to get bluetooth mac address in this way:
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
String address = mBluetoothAdapter.getAddress();
But it always returns:
02:00:00:00:00:00
Why? Is it a kind of security policy?
Thanks.
Giacomo
Maybe you should check out this answer.
Snippet from answer:
private String getBluetoothMac(final Context context) {
String result = null;
if (context.checkCallingOrSelfPermission(Manifest.permission.BLUETOOTH)
== PackageManager.PERMISSION_GRANTED) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// Hardware ID are restricted in Android 6+
// https://developer.android.com/about/versions/marshmallow/android-6.0-changes.html#behavior-hardware-id
// Getting bluetooth mac via reflection for devices with Android 6+
result = android.provider.Settings.Secure.getString(context.getContentResolver(),
"bluetooth_address");
} else {
BluetoothAdapter bta = BluetoothAdapter.getDefaultAdapter();
result = bta != null ? bta.getAddress() : "";
}
}
return result;
}

Android vibrate is deprecated. How to use VibrationEffect in Android>= API 26?

I am using Android's VIBRATOR_SERVICE to give a haptic feedback for a button touch.
((Vibrator) getSystemService(VIBRATOR_SERVICE)).vibrate(300);
Android Studio give me warning that method vibrate(interval) is deprecated I should use VibrationEffect for API>23.
So I usedVibrationEffect's method createOneShot which takes 2 params: interval, and amplitude.
I tried searching for it but got no clue about what to pass as amplitude, anybody got any idea about how to use it?
Update Added code
// Vibrate for 150 milliseconds
private void shakeItBaby() {
if (Build.VERSION.SDK_INT >= 26) {
((Vibrator) getSystemService(VIBRATOR_SERVICE)).vibrate(VibrationEffect.createOneShot(150,10));
} else {
((Vibrator) getSystemService(VIBRATOR_SERVICE)).vibrate(150);
}
}
with kotlin
private fun vibrate(){
val vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
vibrator.vibrate(VibrationEffect.createOneShot(200, VibrationEffect.DEFAULT_AMPLITUDE))
} else {
vibrator.vibrate(200)
}
}
Amplitude is an int value. Its The strength of the vibration. This must be a value between 1 and 255, or DEFAULT_AMPLITUDE which is -1.
You can use it as VibrationEffect.DEFAULT_AMPLITUDE
More details here
You can use this for haptic feedback (vibration):
view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
There are other constants available in HapticFeedbackConstants like VIRTUAL_KEY, KEYBOARD_TAP ...
Updated for Kotlin
// Vibrates the device for 100 milliseconds.
fun vibrateDevice(context: Context) {
val vibrator = getSystemService(context, Vibrator::class.java)
vibrator?.let {
if (Build.VERSION.SDK_INT >= 26) {
it.vibrate(VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE))
} else {
#Suppress("DEPRECATION")
it.vibrate(100)
}
}
}
Call the function as following:
vibrateDevice(requireContext())
Make sure you add permission to AndroidManifest.xml as following:
<uses-permission android:name="android.permission.VIBRATE"/>
Note that you don't need to ask for permission at runtime for using vibration.
You need to suppress deprecation in else clause, because the warning is from newer SDK.
I just stumbled across this and found out that VibrationEffect.createWaveform() basically uses the same long[]-pattern as the old vibrate().
Because of this, you can reuse your existing pattern like so (this is a Kotlin extension-function):
fun Context.vibrate(pattern: LongArray) {
val vibrator =
applicationContext.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator? ?: return
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
vibrator.vibrate(
VibrationEffect.createWaveform(pattern, VibrationEffect.DEFAULT_AMPLITUDE)
)
} else {
#Suppress("DEPRECATION")
vibrator.vibrate(pattern, -1)
}
}
And instead of VibrationEffect.createOneShot() you can use a pattern as well (e.g. longArrayOf(0, 150)) so no need to use different functions.
This library may help you out:
https://github.com/josephrubin/Rumble-4-Android
All you would need is
Rumble.once(150);
It handles the API versions for you.
working for me kotlin ext fun
for the haptic effect, vibro has 5 milliseconds!! (SHORT_HAPTIC_FEEDBACK_DURATION)
fun Context.performHapticFeedback() {
val vibrator = getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val vibrationEffect = VibrationEffect.createOneShot(HAPTIC_FEEDBACK_DURATION, VibrationEffect.DEFAULT_AMPLITUDE)
vibrator.vibrate(vibrationEffect)
} else {
vibrator.vibrate(TimeUnit.MILLISECONDS.toMillis(SHORT_HAPTIC_FEEDBACK_DURATION))
}
}
private const val SHORT_HAPTIC_FEEDBACK_DURATION = 5L
usage
addOnItemTouchListener(ItemTouchListener { position, event ->
if (event.action == MotionEvent.ACTION_DOWN) {
context.performHapticFeedback()
}
})
permission
<uses-permission android:name="android.permission.VIBRATE"/>
good luck ✌ :))
Open Manage NuGet Packages
Search and Install Xamarin.Essentials
try {
var duration = TimeSpan.FromMilliseconds(300);
Vibration.Vibrate(duration);
}
catch (FeatureNotSupportedException ex){}
catch (Exception ex){}

Android dual SIM -> change network

Starting SDK 22, Android officially support Dual-SIM and provide some documentation.
I would like to know if it is possible for an app to change wich SIM should access the network. For example I have a One Plus One 3T phone running Nougat and I use two SIM cards. Going into settings, I can manually change / select which SIM card to use to access the network.
Is it possible to achieve this ?
Thanks !
The solution I found for Android 7.1.1 API 25 is using SubscriptionManager and reflection api.
Note that you have to install your app in /system/priv-app/
You can do this through adb root.
Make sure you have in your AndroidManifest.xml
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.MODIFY_PHONE_STATE"/>
<uses-permission android:name="android.permission.WRITE_APN_SETTINGS"/>
The code is the following
The only thing this code does is exchange the sim data
private Integer subActual = null;
private Integer sim1 = null;
private Integer sim2 = null;
private SubscriptionInfo simInfo1;
private SubscriptionInfo simInfo2;
private Boolean dualSim;
SubscriptionManager subscriptionManager = SubscriptionManager.from(this);
#SuppressLint("MissingPermission")
List smList = subscriptionManager.getActiveSubscriptionInfoList();
Method[] smMethods = subscriptionManager.getClass().getMethods();
dualSim = smList.size() == 2;
if (dualSim) {
simInfo1 = (SubscriptionInfo) smList.get(0);
simInfo2 = (SubscriptionInfo) smList.get(1);
sim1 = simInfo1.getSubscriptionId();
sim2 = simInfo2.getSubscriptionId();
}
for (Method m : smMethods) {
if (m.getName().equals(("getDefaultDataSubscriptionId"))) {
try {
subActual = (int) m.invoke(subscriptionManager);
} catch (Exception e) {
}
}
}
for (Method m : smMethods) {
if (m.getName().equals("setDefaultDataSubId") && dualSim) {
try {
if (subActual == sim1) {
m.invoke(subscriptionManager, sim2);
}
if (subActual == sim2) {
m.invoke(subscriptionManager, sim1);
}
} catch (Exception e) {
}
}
}

Categories