Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
W
whoscall
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Wei Han
whoscall
Commits
a6be43fa
Commit
a6be43fa
authored
Sep 03, 2025
by
Wei Han
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
ios progress
parent
33bf9979
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
133 additions
and
60 deletions
+133
-60
App.tsx
App.tsx
+0
-19
README.md
README.md
+4
-4
AndroidManifest.xml
android/app/src/main/AndroidManifest.xml
+1
-0
MainActivity.kt
android/app/src/main/java/com/whoscall/MainActivity.kt
+5
-17
MainApplication.kt
android/app/src/main/java/com/whoscall/MainApplication.kt
+1
-0
OverlayPermissionChecker.kt
...pp/src/main/java/com/whoscall/OverlayPermissionChecker.kt
+19
-0
StorageModule.kt
android/app/src/main/java/com/whoscall/StorageModule.kt
+0
-17
settings.gradle
android/settings.gradle
+5
-0
Podfile
ios/Podfile
+6
-0
Podfile.lock
ios/Podfile.lock
+0
-0
project.pbxproj
ios/whoscall.xcodeproj/project.pbxproj
+0
-0
Info.plist
ios/whoscall/Info.plist
+6
-1
PrivacyInfo.xcprivacy
ios/whoscall/PrivacyInfo.xcprivacy
+10
-2
CallDirectoryHandler.swift
ios/whoscallDirectoryExtension/CallDirectoryHandler.swift
+48
-0
Info.plist
ios/whoscallDirectoryExtension/Info.plist
+28
-0
No files found.
App.tsx
View file @
a6be43fa
...
...
@@ -103,25 +103,6 @@ export default function App() {
))
}
{
allGranted
&&
<
Text
style=
{
styles
.
granted
}
>
All permissions granted ✅
</
Text
>
}
<
Button
title=
"Check Again"
onPress=
{
checkAllPermissions
}
/>
<
Button
title=
"Simulate VIP Call"
onPress=
{
async
()
=>
{
const
number
=
'+60123456789'
;
const
name
=
'VIP User'
;
// Save VIP info in AsyncStorage + native storage
await
saveVIPNumber
(
name
,
number
);
// Call native module to start OverlayService (simulate incoming call)
if
(
NativeModules
.
VIPStorage
?.
simulateVipCall
)
{
NativeModules
.
VIPStorage
.
simulateVipCall
(
number
,
name
);
}
else
{
console
.
warn
(
'simulateVipCall not implemented in NativeModules.'
);
}
Alert
.
alert
(
'Simulated VIP call'
,
`${name} (${number})`
);
}
}
/>
</
View
>
);
}
...
...
README.md
View file @
a6be43fa
...
...
@@ -25,11 +25,11 @@ Please follow this installation instruction.
> :information_source: **Note:** If you hit error permission denied, please execute `chmod +x gradlew`
<!--
4. Pod install for iOS :
4.
Pod install for iOS :
-
Go into iOS directory:
`cd ios`
-
Install pod:
`pod install`
-
After completion, return to main directory:
`cd ../`
-->
-
After completion, return to main directory:
`cd ../`
5.
Finish installation :clap:
...
...
@@ -41,12 +41,12 @@ Below is all command that use to execute react native project for android and iO
1.
Run :
`npx react-native run-android`
<!--
## iOS
## iOS
1.
go to iOS directory by execute :
`cd ios`
<br>
2.
open xCode workspace by execute :
`Runner.xcworkspace`
<br>
3.
clean app by go to Product > Clean Build Folder...
4.
run app by go to Product > Build
-->
4.
run app by go to Product > Build
## Expected behavior
-
When user's phone is called, alert shows up showing the caller number + who the caller is
...
...
android/app/src/main/AndroidManifest.xml
View file @
a6be43fa
...
...
@@ -7,6 +7,7 @@
<uses-permission
android:name=
"android.permission.FOREGROUND_SERVICE"
/>
<uses-permission
android:name=
"android.permission.READ_CALL_LOG"
/>
<uses-permission
android:name=
"android.permission.READ_CONTACTS"
/>
<uses-permission
android:name=
"android.permission.ACTION_MANAGE_OVERLAY_PERMISSION"
/>
<application
android:name=
".MainApplication"
...
...
android/app/src/main/java/com/whoscall/MainActivity.kt
View file @
a6be43fa
...
...
@@ -4,30 +4,21 @@ import android.content.Context
import
android.content.Intent
import
android.net.Uri
import
android.os.Build
import
android.os.Bundle
import
android.provider.Settings
import
android.app.role.RoleManager
import
androidx.activity.ComponentActivity
import
androidx.activity.result.contract.ActivityResultContracts
import
com.whoscall.databinding.ActivityMainBinding
import
android.content.SharedPreferences
import
android.util.Log
import
com.facebook.react.ReactActivity
class
MainActivity
:
ComponentActivity
()
{
private
lateinit
var
binding
:
ActivityMainBinding
class
MainActivity
:
ReactActivity
()
{
private
val
roleLauncher
=
registerForActivityResult
(
ActivityResultContracts
.
StartActivityForResult
()
)
{
/* no-op */
}
override
fun
onCreate
(
savedInstanceState
:
Bundle
?)
{
super
.
onCreate
(
savedInstanceState
)
binding
=
ActivityMainBinding
.
inflate
(
layoutInflater
)
setContentView
(
binding
.
root
)
binding
.
btnOverlay
.
setOnClickListener
{
requestOverlay
()
}
binding
.
btnRole
.
setOnClickListener
{
requestCallScreeningRoleIfNeeded
()
}
override
fun
getMainComponentName
():
String
{
// 👇 must match what you registered in index.js
return
"whoscall"
}
private
fun
requestOverlay
()
{
...
...
@@ -50,15 +41,12 @@ class MainActivity : ComponentActivity() {
}
private
fun
simulateVipCall
()
{
// Example VIP number and name
val
vipNumber
=
"+60189884118"
val
vipName
=
"Wei Yi"
// Save to SharedPreferences
val
prefs
=
getSharedPreferences
(
"WhoscallPrefs"
,
MODE_PRIVATE
)
prefs
.
edit
().
putString
(
vipNumber
,
vipName
).
apply
()
// Start overlay service directly
val
overlayIntent
=
Intent
(
this
,
OverlayService
::
class
.
java
).
apply
{
putExtra
(
"number"
,
vipNumber
)
putExtra
(
"displayName"
,
vipName
)
...
...
android/app/src/main/java/com/whoscall/MainApplication.kt
View file @
a6be43fa
...
...
@@ -19,6 +19,7 @@ class MainApplication : Application(), ReactApplication {
// Packages that cannot be autolinked yet can be added manually here, for example:
// add(MyReactNativePackage())
add
(
StoragePackage
())
}
override
fun
getJSMainModuleName
():
String
=
"index"
...
...
android/app/src/main/java/com/whoscall/OverlayPermissionChecker.kt
0 → 100644
View file @
a6be43fa
package
com.whoscall
import
android.content.Context
import
android.os.Build
import
android.provider.Settings
object
OverlayPermissionChecker
{
/**
* Returns true if the app has permission to draw over other apps
*/
fun
hasOverlayPermission
(
context
:
Context
):
Boolean
{
return
if
(
Build
.
VERSION
.
SDK_INT
>=
Build
.
VERSION_CODES
.
M
)
{
Settings
.
canDrawOverlays
(
context
)
}
else
{
true
//Automaticallt allowed on older android versions
}
}
}
\ No newline at end of file
android/app/src/main/java/com/whoscall/StorageModule.kt
View file @
a6be43fa
...
...
@@ -25,21 +25,4 @@ class StorageModule(reactContext: ReactApplicationContext) :
promise
.
reject
(
"SAVE_ERROR"
,
e
)
}
}
@ReactMethod
fun
simulateVipCall
(
number
:
String
,
name
:
String
)
{
Log
.
d
(
"VIPStorage"
,
"Simulating VIP call: $name ($number)"
)
val
context
=
reactApplicationContext
// Save VIP first
prefs
.
edit
().
putString
(
number
,
name
).
apply
()
// Start OverlayService
val
intent
=
Intent
(
context
,
OverlayService
::
class
.
java
).
apply
{
putExtra
(
"number"
,
number
)
putExtra
(
"displayName"
,
name
)
addFlags
(
Intent
.
FLAG_ACTIVITY_NEW_TASK
)
}
context
.
startService
(
intent
)
}
}
android/settings.gradle
View file @
a6be43fa
...
...
@@ -4,3 +4,7 @@ extensions.configure(com.facebook.react.ReactSettingsExtension){ ex -> ex.autoli
rootProject
.
name
=
'whoscall'
include
':app'
includeBuild
(
'../node_modules/@react-native/gradle-plugin'
)
include
':rn-android-overlay-permission'
project
(
':rn-android-overlay-permission'
).
projectDir
=
new
File
(
rootProject
.
projectDir
,
'../node_modules/rn-android-overlay-permission/android'
)
\ No newline at end of file
ios/Podfile
View file @
a6be43fa
...
...
@@ -32,4 +32,10 @@ target 'whoscall' do
# :ccache_enabled => true
)
end
permissions_path
=
'../node_modules/react-native-permissions/ios'
pod
'Permission-Contacts'
,
:path
=>
"
#{
permissions_path
}
/Contacts.podspec"
pod
'Permission-Phone'
,
:path
=>
"
#{
permissions_path
}
/Phone.podspec"
pod
'Permission-Notifications'
,
:path
=>
"
#{
permissions_path
}
/Notifications.podspec"
end
ios/Podfile.lock
View file @
a6be43fa
This diff is collapsed.
Click to expand it.
ios/whoscall.xcodeproj/project.pbxproj
View file @
a6be43fa
This diff is collapsed.
Click to expand it.
ios/whoscall/Info.plist
View file @
a6be43fa
...
...
@@ -31,10 +31,15 @@
<
k
e
y
>
NSAllowsLocalNetworking
<
/k
e
y
>
<
tru
e
/
>
<
/
d
i
c
t
>
<
k
e
y
>
NSUserActivityTypes
<
/k
e
y
>
<
a
rr
a
y
>
<
string
>
INStartAudioCallIntent
<
/string
>
<
string
>
INStartVideoCallIntent
<
/string
>
<
/
a
rr
a
y
>
<
k
e
y
>
NSLocationWhenInUseUsageDescription
<
/k
e
y
>
<
string
><
/string
>
<
k
e
y
>
RCTNewArchEnabled
<
/k
e
y
>
<
fa
ls
e
/
>
<
tru
e
/
>
<
k
e
y
>
UILaunchStoryboardName
<
/k
e
y
>
<
string
>
LaunchScreen
<
/string
>
<
k
e
y
>
UIRequiredDeviceCapabilities
<
/k
e
y
>
...
...
ios/whoscall/PrivacyInfo.xcprivacy
View file @
a6be43fa
...
...
@@ -14,6 +14,14 @@
</dict>
<dict>
<key>
NSPrivacyAccessedAPIType
</key>
<string>
NSPrivacyAccessedAPICategorySystemBootTime
</string>
<key>
NSPrivacyAccessedAPITypeReasons
</key>
<array>
<string>
35F9.1
</string>
</array>
</dict>
<dict>
<key>
NSPrivacyAccessedAPIType
</key>
<string>
NSPrivacyAccessedAPICategoryUserDefaults
</string>
<key>
NSPrivacyAccessedAPITypeReasons
</key>
<array>
...
...
@@ -22,10 +30,10 @@
</dict>
<dict>
<key>
NSPrivacyAccessedAPIType
</key>
<string>
NSPrivacyAccessedAPICategory
SystemBootTim
e
</string>
<string>
NSPrivacyAccessedAPICategory
DiskSpac
e
</string>
<key>
NSPrivacyAccessedAPITypeReasons
</key>
<array>
<string>
35F9
.1
</string>
<string>
85F4
.1
</string>
</array>
</dict>
</array>
...
...
ios/whoscallDirectoryExtension/CallDirectoryHandler.swift
0 → 100644
View file @
a6be43fa
import
Foundation
import
CallKit
class
CallDirectoryHandler
:
CXCallDirectoryProvider
{
override
func
beginRequest
(
with
context
:
CXCallDirectoryExtensionContext
)
{
context
.
delegate
=
self
// Add numbers to identify (VIPs, spam, etc.)
addIdentificationNumbers
(
to
:
context
)
// Add numbers to block (if you want)
// addBlockingNumbers(to: context)
context
.
completeRequest
()
}
private
func
addIdentificationNumbers
(
to
context
:
CXCallDirectoryExtensionContext
)
{
// Example: add VIP numbers
// Numbers must be E.164 format (+60 for Malaysia)
let
vipNumbers
:
[(
number
:
Int64
,
label
:
String
)]
=
[
(
60189884118
,
"VIP: Wei Yi"
),
(
60123456789
,
"Spam Caller"
),
]
for
entry
in
vipNumbers
{
context
.
addIdentificationEntry
(
withNextSequentialPhoneNumber
:
entry
.
number
,
label
:
entry
.
label
)
}
}
private
func
addBlockingNumbers
(
to
context
:
CXCallDirectoryExtensionContext
)
{
// Example: block a number
let
blockedNumbers
:
[
Int64
]
=
[
60199998888
]
for
number
in
blockedNumbers
{
context
.
addBlockingEntry
(
withNextSequentialPhoneNumber
:
number
)
}
}
}
// Optional: log errors
extension
CallDirectoryHandler
:
CXCallDirectoryExtensionContextDelegate
{
func
requestFailed
(
for
extensionContext
:
CXCallDirectoryExtensionContext
,
withError
error
:
Error
)
{
NSLog
(
"Call Directory extension error:
\(
error
.
localizedDescription
)
"
)
}
}
ios/whoscallDirectoryExtension/Info.plist
0 → 100644
View file @
a6be43fa
<
?xml
v
e
rsion="
1
.
0
"
e
n
c
o
d
ing="UT
F
-
8
"?
>
<
!
D
O
C
TYP
E
plist
PU
B
LI
C
"-//
A
ppl
e
//
D
T
D
PLIST
1
.
0
//
E
N"
"http://www.
a
ppl
e
.
c
om/
D
T
D
s/Prop
e
rtyList-
1
.
0
.
d
t
d
"
>
<
plist
v
e
rsion="
1
.
0
"
>
<
d
i
c
t
>
<
k
e
y
>
NSExtension
<
/k
e
y
>
<
d
i
c
t
>
<
k
e
y
>
NSExtensionPointIdentifier
<
/k
e
y
>
<
string
>
com.apple.callkit.call-directory
<
/string
>
<
k
e
y
>
NSExtensionPrincipalClass
<
/k
e
y
>
<
string
>
$
(
PRODUCT_MODULE_NAME
)
.CallDirectoryHandler
<
/string
>
<
/
d
i
c
t
>
<
k
e
y
>
NSExtension
<
/k
e
y
>
<
d
i
c
t
>
<
k
e
y
>
NSExtensionAttributes
<
/k
e
y
>
<
d
i
c
t
>
<
k
e
y
>
CXCallDirectoryExtensionAttributes
<
/k
e
y
>
<
d
i
c
t
>
<
k
e
y
>
CXCallDirectoryExtensionIdentifier
<
/k
e
y
>
<
string
>
com.yourapp.callid
<
/string
>
<
/
d
i
c
t
>
<
/
d
i
c
t
>
<
k
e
y
>
NSExtensionPointIdentifier
<
/k
e
y
>
<
string
>
com.apple.callkit.call-directory
<
/string
>
<
k
e
y
>
NSExtensionPrincipalClass
<
/k
e
y
>
<
string
>
$
(
PRODUCT_MODULE_NAME
)
.CallDirectoryHandler
<
/string
>
<
/
d
i
c
t
>
<
/
d
i
c
t
>
<
/plist
>
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment