#My app is working till I try to save the Message on the Database, see the image here:
#[App_Working][1]
#[1]: https://i.stack.imgur.com/G26MS.jpg
#I tried to find the content://com.courses.applicationcontentprovider.provider/notes file but i'm new on Developing world and didn't find
#Can someone show me how to fix that?
#I also checked some similar question's but this isn't work.
#Here is my AndroidManiFest.xml
<?xml version="1.0" encoding="utf-8"?>
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/Theme.ApplicationContentProvider.">
<provider
android:name=".database.NotesProvider"
android:authorities="com.example.applicationcontentprovider.provider"
android:enabled="true"
android:exported="true" />
<activity
android:name=".database.MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
#And the logcat in pointing to the NotesDetailFragments.kt
Process: com.example.contentprovider, PID: 787
java.lang.IllegalArgumentException: Unknown URL content://com.courses.applicationcontentprovider.provider/notes
at android.content.ContentResolver.insert(ContentResolver.java:2145)
at android.content.ContentResolver.insert(ContentResolver.java:2111)
at com.example.contentprovider.database.NotesDetailFragments.onClick(NotesDetailFragments.kt:78)
at com.android.internal.app.AlertController$ButtonHandler.handleMessage(AlertController.java:174)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
#tHANKS
class NotesDetailFragments: DialogFragment(), DialogInterface.OnClickListener {
private lateinit var noteEdiTitle: EditText
private lateinit var noteEditDescription: EditText
private var id: Long = 0
companion object {
private const val EXTRA_ID = "id"
fun newInstance(id: Long): NotesDetailFragments {
val bundle = Bundle()
bundle.putLong(EXTRA_ID, id)
val notesFragment = NotesDetailFragments()
notesFragment.arguments = bundle
return notesFragment
}
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val view: View? = activity?.layoutInflater?.inflate(R.layout.note_detail, null)
noteEdiTitle = view?.findViewById(R.id.note_edt_title) as EditText
noteEditDescription = view.findViewById(R.id.note_edt_description) as EditText
var newNote = true
if (arguments != null && arguments?.getLong(EXTRA_ID) != 0L){
id = arguments?.getLong(EXTRA_ID) as Long
val uri = Uri.withAppendedPath(URI_NOTES, id.toString())
val cursor =
activity?.contentResolver?.query(uri, null, null, null, null)
if (cursor?.moveToNext() as Boolean ) {
newNote = false
noteEdiTitle.setText(cursor.getString(cursor.getColumnIndex(TITTLE_NOTES)))
noteEditDescription.setText(cursor.getString(cursor.getColumnIndex(DESCRIPTION_NOTES)))
}
cursor.close()
}
return AlertDialog.Builder(activity as Activity)
.setTitle(if (newNote) "Nova mensagem" else "Editar Mensagem")
.setView(view)
.setPositiveButton("Salvar", this)
.setNegativeButton("Cancelar", this)
.create()
}
override fun onClick(dialog: DialogInterface?, which: Int) {
val values = ContentValues()
values.put(TITTLE_NOTES, noteEdiTitle.text.toString())
values.put(DESCRIPTION_NOTES, noteEditDescription.text.toString())
if(id != 0L){
val uri = Uri.withAppendedPath(URI_NOTES, id.toString())
context?.contentResolver?.update(uri, values, null, null)
}else {
context?.contentResolver?.insert(URI_NOTES, values)
}
}
Related
I have an NFC TAG scanner application which works fine and scans the tag and i can also read and write into tags without any issues , the only issue is that when i change the app package name and start the code , the nfc no longer works and scans tags , but when i revert to the other package name it works fine , any reason why and what could be wrong , Thank you in advance
class ActivateActivity : AppCompatActivity() {
private lateinit var binding : ActivityActivateScreenBinding
lateinit var writeTagFilters: Array<IntentFilter>
var nfcAdapter: NfcAdapter? = null
var pendingIntent: PendingIntent? = null
var writeMode = false
var myTag: Tag? = null
var actualMsg = ""
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityActivateScreenBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.btnActivate.setOnClickListener {
val organizationId = binding.organizationIdEdit.text.toString()
val nfcMsg = binding.txtNFCMessage.text.toString()
actualMsg = DOMAIN + organizationId + ucync + nfcMsg
try {
if(myTag == null){
Toast.makeText(this, ERROR_DETECTED,Toast.LENGTH_LONG).show()
} else {
write(actualMsg,myTag)
Toast.makeText(this, WRITE_SUCCESS,Toast.LENGTH_LONG).show()
}
}
catch (e : IOException){
Toast.makeText(this, WRITE_ERROR, Toast.LENGTH_LONG).show()
e.printStackTrace()
}
catch (e: FormatException){
Toast.makeText(this, WRITE_ERROR, Toast.LENGTH_LONG).show()
e.printStackTrace()
}
}
nfcAdapter = NfcAdapter.getDefaultAdapter(this)
if (nfcAdapter == null) {
// Stop here, we definitely need NFC
Toast.makeText(this, "This device doesn't support NFC.", Toast.LENGTH_LONG).show()
finish()
}
//For when the activity is launched by the intent-filter for android.nfc.action.NDEF_DISCOVERE
readFromIntent(intent)
pendingIntent = PendingIntent.getActivity(this, 0, Intent(this, javaClass)
.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP),
PendingIntent.FLAG_IMMUTABLE)
val tagDetected = IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED)
tagDetected.addCategory(Intent.CATEGORY_DEFAULT)
writeTagFilters = arrayOf(tagDetected)
}
/******************************************************************************
* Read From NFC Tag
****************************************************************************/
private fun readFromIntent(intent: Intent) {
val action = intent.action
if (NfcAdapter.ACTION_TAG_DISCOVERED == action || NfcAdapter.ACTION_TECH_DISCOVERED == action || NfcAdapter.ACTION_NDEF_DISCOVERED == action) {
myTag = intent.getParcelableExtra<Parcelable>(NfcAdapter.EXTRA_TAG) as Tag?
val rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)
val msgs = mutableListOf<NdefMessage>()
if (rawMsgs != null) {
for (i in rawMsgs.indices) {
msgs.add(i, rawMsgs[i] as NdefMessage)
}
buildTagViews(msgs.toTypedArray())
}
}
}
private fun buildTagViews(msgs: Array<NdefMessage>) {
if (msgs.isEmpty()) return
val text: String
val payload = msgs[0].records[0].payload
val textEncoding: Charset = if ((payload[0] and 128.toByte()).toInt() == 0) Charsets.UTF_8 else Charsets.UTF_16 // Get the Text Encoding
val languageCodeLength: Int = (payload[0] and 51).toInt() // Get the Language Code, e.g. "en"
try {
// Get the Text
text = String(payload, languageCodeLength + 1, payload.size - languageCodeLength - 1, textEncoding)
Log.d("VALUE","VALUE IS " + text)
} catch (e: UnsupportedEncodingException) {
Log.e("UnsupportedEncoding", e.toString())
}
}
/******************************************************************************
* Write to NFC Tag
****************************************************************************/
private fun write(msg : String , tag : Tag?){
val records = arrayOf(createRecord(msg))
val message = NdefMessage(records)
// Get an instance of Ndef for the tag.
val ndef = Ndef.get(tag)
// Enable I/O
ndef.connect()
// Write the message
ndef.writeNdefMessage(message)
// Close the connection
ndef.close()
}
#Throws(UnsupportedEncodingException::class)
private fun createRecord(text: String): NdefRecord {
val lang = "en"
val textBytes = text.toByteArray()
val langBytes = lang.toByteArray(charset("US-ASCII"))
val langLength = langBytes.size
val textLength = textBytes.size
val payload = ByteArray(1 + langLength + textLength)
// set status byte (see NDEF spec for actual bits)
payload[0] = langLength.toByte()
// copy langbytes and textbytes into payload
System.arraycopy(langBytes, 0, payload, 1, langLength)
System.arraycopy(textBytes, 0, payload, 1 + langLength, textLength)
return NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, ByteArray(0), payload)
}
/**
* For reading the NFC when the app is already launched
*/
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
setIntent(intent)
readFromIntent(intent)
if (NfcAdapter.ACTION_TAG_DISCOVERED == intent.action) {
myTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG)
}
}
public override fun onPause() {
super.onPause()
writeModeOff()
}
public override fun onResume() {
super.onResume()
writeModeOn()
}
/******************************************************************************
* Enable Write and foreground dispatch to prevent intent-filter to launch the app again
****************************************************************************/
private fun writeModeOn() {
writeMode = true
nfcAdapter!!.enableForegroundDispatch(this, pendingIntent, writeTagFilters, null)
}
/******************************************************************************
* Disable Write and foreground dispatch to allow intent-filter to launch the app
****************************************************************************/
private fun writeModeOff() {
writeMode = false
nfcAdapter!!.disableForegroundDispatch(this)
}
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.NFC"/>
<uses-feature android:name="android.hardware.nfc"
android:required="true"/>
<application
android:name=".BaseApplication"
android:allowBackup="true"
android:dataExtractionRules="#xml/data_extraction_rules"
android:fullBackupContent="#xml/backup_rules"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/Theme.UcyncBusiness"
tools:targetApi="31">
<activity
android:name=".ui.ActivateActivity"
android:exported="false">
<meta-data
android:name="android.app.lib_name"
android:value="" />
</activity>
<activity
android:name=".ui.HomeActivity"
android:exported="false">
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
<meta-data
android:name="android.nfc.action.TECH_DISCOVERED"
android:resource="#xml/nfc_tech_filter" />
<meta-data
android:name="android.app.lib_name"
android:value="" />
</activity>
<activity
android:name=".ui.SplashActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.lib_name"
android:value="" />
</activity>
<activity android:name=".MainActivity" />
</application>
I want to implement Call Recording for one of my client application but above Android 10 I am unable to do is successfully. here is my code classes. Your suggestions will be highly appreciated.
I know due to security reasons Google stopped working around Call Recording above Android 10(Q), But I needs this feature for CRM application.
This is my Receiver Class
public abstract class PhonecallReceiver extends BroadcastReceiver {
// The receiver will be recreated whenever android feels like it.
// We need a static variable to remember data between instantiations
private static int lastState = TelephonyManager.CALL_STATE_IDLE;
private static Date callStartTime;
private static boolean isIncoming;
private static String savedNumber; // because the passed incoming is only valid in ringing
#Override
public void onReceive(final Context context, Intent intent) {
// We listen to two intents. The new outgoing call only tells us of an outgoing call.
// We use it to get the number.
if (intent.getAction().equals("android.intent.action.NEW_OUTGOING_CALL")) {
savedNumber = intent.getExtras().getString("android.intent.extra.PHONE_NUMBER");
} else {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
String stateStr = intent.getExtras().getString(TelephonyManager.EXTRA_STATE);
String number = intent.getExtras().getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
int state = 0;
if (stateStr.equals(TelephonyManager.EXTRA_STATE_IDLE)) {
state = TelephonyManager.CALL_STATE_IDLE;
} else if (stateStr.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)) {
state = TelephonyManager.CALL_STATE_OFFHOOK;
} else if (stateStr.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
state = TelephonyManager.CALL_STATE_RINGING;
}
onCustomCallStateChanged(context, state, number);
} else {
// Android 9+
TelephonyManager telephony = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
telephony.listen(new PhoneStateListener() {
#Override
public void onCallStateChanged(int state, String number) {
onCustomCallStateChanged(context, state, number);
}
}, PhoneStateListener.LISTEN_CALL_STATE);
}
}
}
// Derived classes should override these to respond to specific events of interest
protected abstract void onIncomingCallStarted(Context ctx, String number, Date start);
protected abstract void onOutgoingCallStarted(Context ctx, String number, Date start);
protected abstract void onIncomingCallEnded(Context ctx, String number, Date start, Date end);
protected abstract void onOutgoingCallEnded(Context ctx, String number, Date start, Date end);
protected abstract void onMissedCall(Context ctx, String number, Date missed);
// Deals with actual events
// Incoming call - goes from IDLE to RINGING when it rings, to OFFHOOK when it's answered, to IDLE when its hung up
// Outgoing call - goes from IDLE to OFFHOOK when it dials out, to IDLE when hung up
public void onCustomCallStateChanged(Context context, int state, String number) {
if (lastState == state) {
// No change, debounce extras
return;
}
switch (state) {
case TelephonyManager.CALL_STATE_RINGING:
isIncoming = true;
callStartTime = new Date();
savedNumber = number;
onIncomingCallStarted(context, number, callStartTime);
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
// Transition of ringing->offhook are pickups of incoming calls. Nothing done on them
if (lastState != TelephonyManager.CALL_STATE_RINGING) {
isIncoming = false;
callStartTime = new Date();
onOutgoingCallStarted(context, savedNumber, callStartTime);
}
break;
case TelephonyManager.CALL_STATE_IDLE:
// Went to idle - this is the end of a call. What type depends on previous state(s)
if (lastState == TelephonyManager.CALL_STATE_RINGING) {
// Ring but no pickup - a miss
onMissedCall(context, savedNumber, callStartTime);
} else if (isIncoming) {
onIncomingCallEnded(context, savedNumber, callStartTime, new Date());
} else{
onOutgoingCallEnded(context, savedNumber, callStartTime, new Date());
}
break;
}
lastState = state;
}
}
This is my service that will work in background to record Audio Calls.
class CallRecorderNew : Service(), onCallingListner {
private lateinit var recorder: MediaRecorder
private var recordStarted = false
private var savedNumber: String? = null
private var lastState = TelephonyManager.CALL_STATE_IDLE
private var isIncoming = false
var fileName //Obtained by intent
: String? = null
var audiouri: Uri? = null
var file: ParcelFileDescriptor? = null
private val CHANNEL_ID = "NOTIFICATION_CHANNEL"
private lateinit var callReceiver: CallReceiver
private var viewmodal: ViewModal? = null
override fun onBind(arg0: Intent): IBinder? {
// TODO Auto-generated method stub
return null
}
override fun onDestroy() {
this.unregisterReceiver(callReceiver)
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
val intentFilter = IntentFilter()
intentFilter.addAction(ACTION_IN)
intentFilter.addAction(ACTION_OUT)
callReceiver = CallReceiver(this)
this.registerReceiver(callReceiver, intentFilter)
val notificationIntent = Intent(this, MainActivity::class.java)
val pendingIntent = PendingIntent.getActivity(
this,
0, notificationIntent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
)
val notification = NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle(R.string.app_name.toString() + " is running")
.setContentText("Please don not use battery optimizations")
.setSmallIcon(R.drawable.meeting_ic)
.setContentIntent(pendingIntent)
.setColor(resources.getColor(R.color.colorPrimary))
.build()
startForeground(1, notification)
// return super.onStartCommand(intent, flags, startId);
return START_STICKY
}
private fun stopRecording() {
if (recordStarted) {
try {
recorder.stop()
} catch (e: Exception) {
e.printStackTrace()
}
recordStarted = false
}
}
override fun onCreate() {
super.onCreate()
createNotificationChannel()
isServiceRunning = true
}
private fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val appName = getString(R.string.app_name)
val serviceChannel = NotificationChannel(
CHANNEL_ID,
appName,
NotificationManager.IMPORTANCE_DEFAULT
)
val manager = getSystemService(
NotificationManager::class.java
)
manager.createNotificationChannel(serviceChannel)
}
}
private fun saveAudioToExternalStorage(displayName: String): Boolean {
val recordingCollection = sdk29AndUp {
MediaStore.Audio.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
} ?: MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
val contentValue = ContentValues().apply {
put(MediaStore.Audio.Media.DISPLAY_NAME, displayName)
put(MediaStore.Audio.Media.MIME_TYPE, "audio/mpeg")
put(MediaStore.Audio.Media.TITLE, displayName)
put(MediaStore.Audio.Media.DATE_ADDED, (System.currentTimeMillis() / 1000).toInt())
}
return try {
contentResolver.insert(recordingCollection, contentValue)?.also { uri ->
contentResolver.openOutputStream(uri).use { outputStream ->
file = contentResolver.openFileDescriptor(uri, "w")
if (file != null) {
recorder = MediaRecorder()
recorder.setAudioSource(MediaRecorder.AudioSource.MIC)
recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP)
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB)
recorder.setOutputFile(file!!.fileDescriptor)
recorder.prepare()
recorder.start()
MainActivity.history[MainActivity.history.size - 1].recordingUrl =
uri.toString()
}
}
} ?: throw IOException("Couldn't create Media Store Entry")
true
} catch (e: Exception) {
e.printStackTrace()
false
}
}
companion object {
const val ACTION_IN = "android.intent.action.PHONE_STATE"
const val ACTION_OUT = "android.intent.action.NEW_OUTGOING_CALL"
const val EXTRA_PHONE_NUMBER = "android.intent.extra.PHONE_NUMBER"
var isServiceRunning = false
}
override fun onIncomingCallStarted(ctx: Context?, number: String?, date: String) {
isIncoming = true
savedNumber = number
saveAudioToExternalStorage("Incoming $savedNumber $date")
recordStarted = true
}
override fun onIncomingCallEnded(ctx: Context?, number: String?, date: String) {
stopRecording()
}
override fun onOutgoingCallStarted(ctx: Context?, number: String?, date: String) {
isIncoming = true
savedNumber = number
saveAudioToExternalStorage("Outgoing $savedNumber $date")
recordStarted = true
}
override fun onOutgoingCallEnded(ctx: Context?, number: String?, date: String) {
stopRecording()
}
override fun onMissedCall(ctx: Context?, number: String?, date: String) {
stopRecording()
}
internal class CallReceiver(private val callingListner: onCallingListner) :
PhonecallReceiver() {
override fun onOutgoingCallStarted(ctx: Context, number: String, start: Date) {
val msg = "start outgoing call: $number at $start"
Log.d("CallReceiverChecker", msg)
Toast.makeText(ctx.applicationContext, msg, Toast.LENGTH_SHORT).show()
callingListner.onOutgoingCallStarted(ctx, number, setDateTime())
}
override fun onOutgoingCallEnded(ctx: Context, number: String, start: Date?, end: Date) {
val msg = "end outgoing call: $number at $end"
Log.d("CallReceiverChecker", msg)
Toast.makeText(ctx.applicationContext, msg, Toast.LENGTH_SHORT).show()
callingListner.onOutgoingCallEnded(ctx, number, setDateTime())
}
override fun onIncomingCallStarted(ctx: Context, number: String, start: Date) {
val msg = "start incoming call: $number at $start"
Log.d("CallReceiverChecker", msg)
Toast.makeText(ctx.applicationContext, msg, Toast.LENGTH_SHORT).show()
callingListner.onIncomingCallStarted(ctx, number, setDateTime())
}
override fun onIncomingCallEnded(ctx: Context, number: String, start: Date?, end: Date) {
val msg = "end incoming call: $number at $end"
Log.d("CallReceiverChecker", msg)
Toast.makeText(ctx.applicationContext, msg, Toast.LENGTH_SHORT).show()
callingListner.onIncomingCallEnded(ctx, number, setDateTime())
}
override fun onMissedCall(ctx: Context, number: String, missed: Date) {
// callingListner.onMissedCall(ctx, number, setDateTime())
val msg = "missed call: $number at $missed"
Log.d("CallReceiverChecker", msg)
Toast.makeText(ctx.applicationContext, msg, Toast.LENGTH_SHORT).show()
callingListner.onMissedCall(ctx, number, setDateTime())
}
}
}
This is manifest file
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="app.sten.wit">
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.PROCESS_INCOMING_CALLS" />
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<uses-permission android:name="android.permission.ANSWER_PHONE_CALLS" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="com.android.vending.BILLING"/>
<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY"/>
<application
android:allowBackup="true"
android:fullBackupContent="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme"
android:usesCleartextTraffic="true"
tools:ignore="GoogleAppIndexingWarning">
<!-- <activity-->
<!-- android:name=".Views.MainActivity"-->
<!-- android:exported="true">-->
<!-- <intent-filter>-->
<!-- <action android:name="android.intent.action.MAIN" />-->
<!-- <category android:name="android.intent.category.LAUNCHER" />-->
<!-- </intent-filter>-->
<!-- </activity>-->
<service android:name=".CallRecorderNew" />
<activity
android:name=".Views.HistoryActivity"
android:exported="true" />
<activity
android:name=".Views.AddMeetingsActivity"
android:exported="true" />
<activity
android:name=".Views.AddClientActivity"
android:exported="false" />
<activity
android:name=".Views.ScheduleMeetingsActivity"
android:exported="false" /> <!-- <activity -->
<!-- android:name=".Views.MeetingsActivity" -->
<!-- android:exported="false" /> -->
<activity
android:name=".Views.DetailsActivity"
android:exported="false" />
<activity
android:name=".Views.MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- <receiver-->
<!-- android:name=".BootCompleteReceiver"-->
<!-- android:exported="true">-->
<!-- <intent-filter android:priority="999">-->
<!-- <action android:name="android.intent.action.BOOT_COMPLETED" />-->
<!-- <action android:name="android.intent.action.QUICKBOOT_POWERON" />-->
<!-- <action android:name="com.htc.intent.action.QUICKBOOT_POWERON" />-->
<!-- </intent-filter>-->
<!-- </receiver>-->
<service android:name=".CallRecorderNew" />
</application>
</manifest>
I have also added permission for MediaProjectionManager but I don't think this is working.
Google is actively disallowing call recording by 3rd-party apps. Here's a good write-up for it, but the short answer is that you simply can't record calls any longer
I have some issues developing the nfc plugin in Unity.
When nfc tagging, onNewIntent is not called, the app moves to the background and the default nfc tagviewer opens.
It worked on Android 9 and below, but not android 10.
I found a suspicious logs using Logcat.
NfcService: setForegroundDispatch: Caller not in foreground.
2020-04-05 15:33:45.857 32411-32411/? E/class com.package.product.NFCPlugin: call Activity
2020-04-05 15:33:45.857 32411-32411/? E/class com.package.product.NFCPlugin: intent:Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp= com.package.product/.NFCPlugin bnds=[1131,670][1405,1123] (has extras) }
2020-04-05 15:33:45.857 32411-32411/? E/class com.package.product.NFCPlugin: Skill Launcher Cat: LAUNCHER
2020-04-05 15:33:45.858 32411-32411/? E/class com.package.product.NFCPlugin: onResume Activity
2020-04-05 15:33:45.858 32411-32411/? E/class com.package.product.NFCPlugin: Call Check ForegroundDispatch
2020-04-05 15:33:45.858 7904-7904/? I/[LGHome6]Launcher: rebindModel: rebind = false, flag = 0, currentPage = 4, isLandscape = true, mChangedProfileByMultiWindow = false, mOrientationOfCurrentLayout = 0, mWorkspaceLoading = false, mChangedProfile = true, mIsMirrorMode = false
2020-04-05 15:33:45.858 7506-7542/? E/NfcService: setForegroundDispatch: Caller not in foreground.
2020-04-05 15:33:45.859 32411-32735/? W/System.err: javax.net.ssl.SSLException: Write error: ssl=0x7c105b0c48: I/O error during system call, Broken pipe
2020-04-05 15:33:45.859 32411-32411/? E/class com.package.product.NFCPlugin: Equal Result:true
2020-04-05 15:33:45.860 2437-2462/? V/DesktopModeManager: Notification is not exist.
2020-04-05 15:33:45.860 5005-5005/? I/OpaLayout: Setting opa enabled to true
2020-04-05 15:33:45.863 2437-8224/? D/InputDispatcher: Window went away: Window{f88f9f9 u0 com.lge.launcher3/com.lge.launcher3.LauncherExtension}
I have found several places to print the log. As a result, I found the part that outputs the same log in Android Code Search.
https://cs.android.com/android/platform/superproject/+/master:packages/apps/Nfc/src/com/android/nfc/NfcService.java;l=1053;bpv=1;bpt=1?q=NfcService:%20setForegroundDispatch%20enable&ss=android%2Fplatform%2Fsuperproject
#Override
public void setForegroundDispatch(PendingIntent intent,
IntentFilter[] filters, TechListParcel techListsParcel)
{
NfcPermissions.enforceUserPermissions(mContext);
if (!mForegroundUtils.isInForeground(Binder.getCallingUid()))
{
Log.e(TAG, "setForegroundDispatch: Caller not in foreground.");
return;
}
// Short-cut the disable path
if (intent == null && filters == null && techListsParcel == null)
{
mNfcDispatcher.setForegroundDispatch(null, null, null);
return;
}
// Validate the IntentFilters
if (filters != null) {
if (filters.length == 0) {
filters = null;
} else {
for (IntentFilter filter : filters) {
if (filter == null) {
throw new IllegalArgumentException("null IntentFilter");
}
}
}
}
// Validate the tech lists
String[][] techLists = null;
if (techListsParcel != null) {
techLists = techListsParcel.getTechLists();
}
mNfcDispatcher.setForegroundDispatch(intent, filters, techLists);
}
According to the code, if a log occurs, processing is stopped without returning any results. So it seems that the foregroundDispatch does not proceed and goes over.
I've been trying for a long time to solve this issue, but couldn't find the same case, so I think it's a new issue in android 10.
Does anyone know how to fix this?
Lastly, I'm sorry that it was difficult to understand because I used a translation site to write this question.
Below is the code for the plugin.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
m_instance=this;
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
if (mNfcAdapter == null)
{
finish();
return;
}
pendingIntent = PendingIntent.getActivity(NFCPlugin.this, 0, new Intent(NFCPlugin.this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
IntentFilter _ndfFilter = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
try
{
_ndfFilter.addDataType("*/*");
}
catch (MalformedMimeTypeException e) {
throw new RuntimeException("Filter fail:", e);
}
IntentFilter _ndfFilter_NDEF = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
try {
_ndfFilter_NDEF.addDataType("*/*");
}
catch (MalformedMimeTypeException e) {
throw new RuntimeException("Filter fail:", e);
}
IntentFilter _ndfFilter_Tech = new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED);
try {
_ndfFilter_Tech.addDataType("*/*");
}
catch (MalformedMimeTypeException e) {
throw new RuntimeException("Filter fail:", e);
}
mIntentFilter = new IntentFilter[] { _ndfFilter ,_ndfFilter_NDEF,_ndfFilter_Tech};
techListsArray = new String[][] {
new String[] {NFCPlugin.class.getName()},
new String[] {TagTechnology.class.getName()},
new String[] {NfcA.class.getName()},
new String[] {NfcB.class.getName()},
new String[] {NfcV.class.getName()},
new String[] {IsoDep.class.getName()},
new String[] {Ndef.class.getName()},
new String[] {NdefFormatable.class.getName()}
};
}
#Override
public void onResume() {
super.onResume();
Log.e(NFCPlugin.class.toString(), "onResume Activity");
mNfcAdapter.enableForegroundDispatch(this, pendingIntent, mIntentFilter, techListsArray);
}
#Override
public void onPause() {
super.onPause();
Log.e(NFCPlugin.class.toString(), "onPause Activity");
mNfcAdapter.disableForegroundDispatch(this);
}
Next is the manifest content.
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.package.product"
xmlns:tools="http://schemas.android.com/tools"
android:installLocation="preferExternal">
<uses-permission android:name="android.permission.NFC" >
</uses-permission>
<uses-feature
android:name="android.hardware.nfc"
android:required="true" >
</uses-feature>
<supports-screens
android:smallScreens="true"
android:normalScreens="true"
android:largeScreens="true"
android:xlargeScreens="true"
android:anyDensity="true"/>
<application
android:theme="#style/UnityThemeSelector"
android:icon="#mipmap/app_icon"
android:label="#string/app_name"
android:networkSecurityConfig="#xml/network_security_config"
>
<activity android:name=".NFCPlugin"
android:launchMode="singleTask"
android:configChanges="keyboard|keyboardHidden|screenLayout|screenSize|orientation"
android:screenOrientation="landscape"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.TAG_DISCOVERED" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.TECH_DISCOVERED"/>
</intent-filter>
<meta-data android:name="android.nfc.action.TECH_DISCOVERED"
android:resource="#xml/tech_list" />
<meta-data android:name="unityplayer.UnityActivity" android:value="true" />
</activity>
</application>
</manifest>
lastly, it is the contents of techList required for TECH_DISCOVERED.
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<tech-list>
<tech>android.nfc.tech.IsoDep</tech>
<tech>android.nfc.tech.NfcA</tech>
<tech>android.nfc.tech.NfcB</tech>
<tech>android.nfc.tech.NfcF</tech>
<tech>android.nfc.tech.NfcV</tech>
<tech>android.nfc.tech.Ndef</tech>
<tech>android.nfc.tech.NdefFormatable</tech>
<tech>android.nfc.tech.MifareClassic</tech>
<tech>android.nfc.tech.MifareUltralight</tech>
</tech-list>
</resources>
My application is a Phone Dialer User-interface. I want to mute the mic. So I need to do something like InCallService.setMuted(true), but I'm having trouble access this android "system" service? The problem is that I cannot access the Android System Servies.
This is the code:
Manifest file:
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name="MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<action android:name="android.intent.action.DIAL"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="tel"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.DIAL"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<activity
android:name=".CallActivity"
android:theme="#style/AppTheme.CallScreen"/>
<service
android:name=".CallService"
android:permission="android.permission.BIND_INCALL_SERVICE">
<meta-data
android:name="android.telecom.IN_CALL_SERVICE_UI"
android:value="true"/>
<intent-filter>
<action android:name="android.telecom.InCallService"/>
</intent-filter>
</service>
</application>
</manifest>
</code>
<code>
class CallActivity : AppCompatActivity() {
companion object {
private const val LOG_TAG = "CallActivity"
}
private var updatesDisposable = Disposables.empty()
private var timerDisposable = Disposables.empty()
private var inCallService: InCallService? = null
/* A VER2
// Variable to hold service class name
val serviceClass = CallService::class.java
// Initialize a new Intent instance
val intentService = Intent(applicationContext, serviceClass)
*/
private val myConnection = object : ServiceConnection {
override fun onServiceConnected(className: ComponentName, service: IBinder) {
val binder = service as CallService.CallBinder
inCallService = binder.getService()
Log.i("SERV_BOUND", "Service in Call Bound!")
}
override fun onServiceDisconnected(name: ComponentName) {
Log.i("SERV_UNBOUND", "Service in Call Unbound!")
}
}
// val inCallService: InCallService = getSystemService(Context.TELECOM_SERVICE)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
setContentView(R.layout.activity_call)
val intent = Intent(this, CallService::class.java)
hideBottomNavigationBar()
buttonHangup.setOnClickListener { CallManager.cancelCall() }
buttonAnswer.setOnClickListener { CallManager.acceptCall() }
buttonMute.setOnClickListener { muteCall() }
}
override fun onResume() {
super.onResume()
updatesDisposable = CallManager.updates()
.doOnEach { Log.i(LOG_TAG, "updated call: $it") }
.doOnError { throwable -> Log.e(LOG_TAG, "Error processing
call", throwable) }
.subscribe { updateView(it) }
}
private fun updateView(gsmCall: GsmCall) {
textStatus.visibility = when (gsmCall.status) {
GsmCall.Status.ACTIVE -> View.GONE
else -> View.VISIBLE
}
textStatus.text = when (gsmCall.status) {
GsmCall.Status.CONNECTING -> "Connecting…"
GsmCall.Status.DIALING -> "Calling…"
GsmCall.Status.RINGING -> "Incoming call"
GsmCall.Status.ACTIVE -> ""
GsmCall.Status.DISCONNECTED -> "Finished call"
GsmCall.Status.UNKNOWN -> ""
}
textDuration.visibility = when (gsmCall.status) {
GsmCall.Status.ACTIVE -> View.VISIBLE
else -> View.GONE
}
buttonHangup.visibility = when (gsmCall.status) {
GsmCall.Status.DISCONNECTED -> View.GONE
else -> View.VISIBLE
}
if (gsmCall.status == GsmCall.Status.DISCONNECTED) {
buttonHangup.postDelayed({ finish() }, 3000)
}
when (gsmCall.status) {
GsmCall.Status.ACTIVE -> startTimer()
GsmCall.Status.DISCONNECTED -> stopTimer()
else -> Unit
}
textDisplayName.text = gsmCall.displayName ?: "Unknown"
buttonAnswer.visibility = when (gsmCall.status) {
GsmCall.Status.RINGING -> View.VISIBLE
else -> View.GONE
}
}
override fun onPause() {
super.onPause()
updatesDisposable.dispose()
}
private fun hideBottomNavigationBar() {
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
}
private fun startTimer() {
timerDisposable = Observable.interval(1, TimeUnit.SECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe { textDuration.text = it.toDurationString() }
}
private fun stopTimer() {
timerDisposable.dispose()
}
private fun Long.toDurationString() = String.format("%02d:%02d:%02d", this /
3600, (this % 3600) / 60, (this % 60))
fun muteCall() {
Log.i(CallManager.LOG_TAG, "muteCall")
inCallService!!.setMuted(!inCallService!!.callAudioState.isMuted)
}
class CallService : InCallService() {
// This is the object that receives interactions from clients. See
// RemoteService for a more complete example.
// private val myBinder = CallBinder()
//
// override fun onBind(intent: Intent): IBinder? {
// return this.mBinder
// }
/**
* Class for clients to access. Because we know this service always
* runs in the same process as its clients, we don't need to deal with
* IPC.
*/
inner class CallBinder : Binder() {
fun getService(): CallService {
return this#CallService
}
}
override fun onCallAdded(call: Call) { //app receives a new incoming call via onCallAdded
super.onCallAdded(call)
Log.i(LOG_TAG, "onCallAdded: $call")
call.registerCallback(callCallback)
val i = Intent(this, CallActivity::class.java)
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(i)
CallManager.updateCall(call)
}
override fun onCallRemoved(call: Call) {
super.onCallRemoved(call)
Log.i(LOG_TAG, "onCallRemoved: $call")
call.unregisterCallback(callCallback)
CallManager.updateCall(null)
}
private val callCallback = object : Call.Callback() {
override fun onStateChanged(call: Call, state: Int) {
Log.i(LOG_TAG, "Call.Callback onStateChanged: $call, state: $state")
CallManager.updateCall(call)
}
}
}
In CallActivity I need to obtain an instance of the "running" InCallService, to mute it. How can I do it?
The error is onBind in line: val binder = service as CallService.CallBinder
Process: com.mbarrben.dialer, PID: 6923
java.lang.ClassCastException: android.telecom.InCallService$InCallServiceBinder cannot be cast to com.delphi.dialer.CallService$CallBinder
at com.delphi.dialer.CallActivity$myConnection$1.onServiceConnected(CallActivity.kt:46)
at android.app.LoadedApk$ServiceDispatcher.doConnected(LoadedApk.java:1730)
at android.app.LoadedApk$ServiceDispatcher$RunConnection.run(LoadedApk.java:1762)
at android.os.Handler.handleCallback(Handler.java:873)
I attach an image with a watch of the objects on the moments of crash.
Class Cast Error Onbind Service 1
I tried to share an image loaded into the imageview from glide following this guide, it gives me a illegal argument exception. I've posted the code and stacktrace below, it
MainAcitvity.xml
public class MainActivity extends AppCompatActivity {
private EditText editText;
private ShareActionProvider myShareActionProvider;
//private Bitmap bitmap;
private Uri uri;
private Intent shareIntent;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final ImageView imageView = (ImageView) findViewById(R.id.imageView);
String hi = "http://37n98a43dqtb4bua9n28nidp.wpengine.netdna-cdn.com/wp-content/uploads/2016/09/MyFriendPikachu.jpg";
Glide
.with(this)
.load(hi)
.listener(new RequestListener<String, GlideDrawable>() {
#Override
public boolean onException(Exception e, String model, Target<GlideDrawable> target, boolean isFirstResource) {
return false;
}
#Override
public boolean onResourceReady(GlideDrawable resource, String model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
prepareShareIntent(((GlideBitmapDrawable) resource).getBitmap());
attachShareIntentAction();
return false;
}
})
.placeholder(R.drawable.ic_action_name)
.error(R.drawable.ic_img_error)
.centerCrop()
.into(imageView);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.img_menu, menu);
MenuItem item = menu.findItem(R.id.action_share);
myShareActionProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(item);
attachShareIntentAction();
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
return super.onOptionsItemSelected(item);
}
public void prepareShareIntent(Bitmap drawableImage) {
Uri bmpUri = getBitmapFromDrawable(drawableImage);
shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_STREAM, bmpUri);
shareIntent.setType("image/*");
}
public void attachShareIntentAction() {
if (myShareActionProvider != null && shareIntent != null)
myShareActionProvider.setShareIntent(shareIntent);
}
public Uri getBitmapFromDrawable(Bitmap bmp) {
Uri bmpUri = null;
try {
//also tried getExternalDir(Environment.DIRECTORY_PICTURES)
File file = new File(Environment.getExternalStorageDirectory(), "images" + System.currentTimeMillis() + ".png");
FileOutputStream out = new FileOutputStream(file);
bmp.compress(Bitmap.CompressFormat.PNG, 90, out);
out.close();
bmpUri = FileProvider.getUriForFile(MainActivity.this, "com.example.imnobody.sampleprojectnetwork.fileprovider", file); // use this version for API >= 24
// **Note:** For API < 24, you may use bmpUri = Uri.fromFile(file);
} catch (IOException e) {
e.printStackTrace();
}
return bmpUri;
}
}
Androidmanifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.imnobody.sampleprojectnetwork">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.example.imnobody.sampleprojectnetwork.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/fileprovider" />
</provider>
</application>
</manifest>
fileprovider.xml
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-file-path
name="images"
path="Pictures" />
</paths>
Stacktrace
java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/sdcard0/images1502651207040.png
at android.support.v4.content.FileProvider$SimplePathStrategy.getUriForFile(FileProvider.java:711)
at android.support.v4.content.FileProvider.getUriForFile(FileProvider.java:400)
at com.example.imnobody.sampleprojectnetwork.MainActivity.getBitmapFromDrawable(MainActivity.java:134)
at com.example.imnobody.sampleprojectnetwork.MainActivity.prepareShareIntent(MainActivity.java:95)
at com.example.imnobody.sampleprojectnetwork.MainActivity$1.onResourceReady(MainActivity.java:59)
at com.example.imnobody.sampleprojectnetwork.MainActivity$1.onResourceReady(MainActivity.java:51)
at com.bumptech.glide.request.GenericRequest.onResourceReady(GenericRequest.java:522)
at com.bumptech.glide.request.GenericRequest.onResourceReady(GenericRequest.java:507)
at com.bumptech.glide.load.engine.EngineJob.handleResultOnMainThread(EngineJob.java:158)
at com.bumptech.glide.load.engine.EngineJob.access$100(EngineJob.java:22)
at com.bumptech.glide.load.engine.EngineJob$MainThreadCallback.handleMessage(EngineJob.java:202)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:5292)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:824)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:640)
at dalvik.system.NativeS
First, in your metadata, change external-file-path to external-files-path, adding the missing s.
Second, /storage/sdcard0/images1502651207040.png will not match that repaired metadata. The metadata is expecting you to be using getExternalFilesDir(), and specifically a Pictures directory under there. Your path does not resemble that.
According to the documentation:
<external-path name="name" path="path" />
Represents files in the root
of the external storage area. The root path of this subdirectory is
the same as the value returned
byEnvironment.getExternalStorageDirectory().
You are calling a reference from your external storage root (/storage/sdcard0) but you need to reference your package root at /storage/sdcard0/Android/data/your.package.name/Pictures instead.