Commit a6be43fa authored by Wei Han's avatar Wei Han

ios progress

parent 33bf9979
...@@ -103,25 +103,6 @@ export default function App() { ...@@ -103,25 +103,6 @@ export default function App() {
))} ))}
{allGranted && <Text style={styles.granted}>All permissions granted ✅</Text>} {allGranted && <Text style={styles.granted}>All permissions granted ✅</Text>}
<Button title="Check Again" onPress={checkAllPermissions} /> <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> </View>
); );
} }
......
...@@ -25,11 +25,11 @@ Please follow this installation instruction. ...@@ -25,11 +25,11 @@ Please follow this installation instruction.
> :information_source: **Note:** If you hit error permission denied, please execute `chmod +x gradlew` > :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` - Go into iOS directory: `cd ios`
- Install pod: `pod install` - Install pod: `pod install`
- After completion, return to main directory: `cd ../` --> - After completion, return to main directory: `cd ../`
5. Finish installation :clap: 5. Finish installation :clap:
...@@ -41,12 +41,12 @@ Below is all command that use to execute react native project for android and iO ...@@ -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` 1. Run : `npx react-native run-android`
<!-- ## iOS ## iOS
1. go to iOS directory by execute : `cd ios` <br> 1. go to iOS directory by execute : `cd ios` <br>
2. open xCode workspace by execute : `Runner.xcworkspace` <br> 2. open xCode workspace by execute : `Runner.xcworkspace` <br>
3. clean app by go to Product > Clean Build Folder... 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 ## Expected behavior
- When user's phone is called, alert shows up showing the caller number + who the caller is - When user's phone is called, alert shows up showing the caller number + who the caller is
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.READ_CALL_LOG" /> <uses-permission android:name="android.permission.READ_CALL_LOG" />
<uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.ACTION_MANAGE_OVERLAY_PERMISSION" />
<application <application
android:name=".MainApplication" android:name=".MainApplication"
......
...@@ -4,30 +4,21 @@ import android.content.Context ...@@ -4,30 +4,21 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle
import android.provider.Settings import android.provider.Settings
import android.app.role.RoleManager import android.app.role.RoleManager
import androidx.activity.ComponentActivity
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import com.whoscall.databinding.ActivityMainBinding import com.facebook.react.ReactActivity
import android.content.SharedPreferences
import android.util.Log
class MainActivity : ComponentActivity() {
private lateinit var binding: ActivityMainBinding class MainActivity : ReactActivity() {
private val roleLauncher = registerForActivityResult( private val roleLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult() ActivityResultContracts.StartActivityForResult()
) { /* no-op */ } ) { /* no-op */ }
override fun onCreate(savedInstanceState: Bundle?) { override fun getMainComponentName(): String {
super.onCreate(savedInstanceState) // 👇 must match what you registered in index.js
binding = ActivityMainBinding.inflate(layoutInflater) return "whoscall"
setContentView(binding.root)
binding.btnOverlay.setOnClickListener { requestOverlay() }
binding.btnRole.setOnClickListener { requestCallScreeningRoleIfNeeded() }
} }
private fun requestOverlay() { private fun requestOverlay() {
...@@ -50,19 +41,16 @@ class MainActivity : ComponentActivity() { ...@@ -50,19 +41,16 @@ class MainActivity : ComponentActivity() {
} }
private fun simulateVipCall() { private fun simulateVipCall() {
// Example VIP number and name val vipNumber = "+60189884118"
val vipNumber = "+60189884118" val vipName = "Wei Yi"
val vipName = "Wei Yi"
// Save to SharedPreferences val prefs = getSharedPreferences("WhoscallPrefs", MODE_PRIVATE)
val prefs = getSharedPreferences("WhoscallPrefs", MODE_PRIVATE) prefs.edit().putString(vipNumber, vipName).apply()
prefs.edit().putString(vipNumber, vipName).apply()
// Start overlay service directly val overlayIntent = Intent(this, OverlayService::class.java).apply {
val overlayIntent = Intent(this, OverlayService::class.java).apply { putExtra("number", vipNumber)
putExtra("number", vipNumber) putExtra("displayName", vipName)
putExtra("displayName", vipName)
}
startService(overlayIntent)
} }
startService(overlayIntent)
}
} }
...@@ -19,6 +19,7 @@ class MainApplication : Application(), ReactApplication { ...@@ -19,6 +19,7 @@ class MainApplication : Application(), ReactApplication {
// Packages that cannot be autolinked yet can be added manually here, for example: // Packages that cannot be autolinked yet can be added manually here, for example:
// add(MyReactNativePackage()) // add(MyReactNativePackage())
add(StoragePackage()) add(StoragePackage())
} }
override fun getJSMainModuleName(): String = "index" override fun getJSMainModuleName(): String = "index"
......
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
...@@ -25,21 +25,4 @@ class StorageModule(reactContext: ReactApplicationContext) : ...@@ -25,21 +25,4 @@ class StorageModule(reactContext: ReactApplicationContext) :
promise.reject("SAVE_ERROR", e) 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)
}
} }
...@@ -4,3 +4,7 @@ extensions.configure(com.facebook.react.ReactSettingsExtension){ ex -> ex.autoli ...@@ -4,3 +4,7 @@ extensions.configure(com.facebook.react.ReactSettingsExtension){ ex -> ex.autoli
rootProject.name = 'whoscall' rootProject.name = 'whoscall'
include ':app' include ':app'
includeBuild('../node_modules/@react-native/gradle-plugin') 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
...@@ -32,4 +32,10 @@ target 'whoscall' do ...@@ -32,4 +32,10 @@ target 'whoscall' do
# :ccache_enabled => true # :ccache_enabled => true
) )
end 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 end
This diff is collapsed.
This diff is collapsed.
...@@ -31,10 +31,15 @@ ...@@ -31,10 +31,15 @@
<key>NSAllowsLocalNetworking</key> <key>NSAllowsLocalNetworking</key>
<true/> <true/>
</dict> </dict>
<key>NSUserActivityTypes</key>
<array>
<string>INStartAudioCallIntent</string>
<string>INStartVideoCallIntent</string>
</array>
<key>NSLocationWhenInUseUsageDescription</key> <key>NSLocationWhenInUseUsageDescription</key>
<string></string> <string></string>
<key>RCTNewArchEnabled</key> <key>RCTNewArchEnabled</key>
<false/> <true/>
<key>UILaunchStoryboardName</key> <key>UILaunchStoryboardName</key>
<string>LaunchScreen</string> <string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key> <key>UIRequiredDeviceCapabilities</key>
......
...@@ -14,6 +14,14 @@ ...@@ -14,6 +14,14 @@
</dict> </dict>
<dict> <dict>
<key>NSPrivacyAccessedAPIType</key> <key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>35F9.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryUserDefaults</string> <string>NSPrivacyAccessedAPICategoryUserDefaults</string>
<key>NSPrivacyAccessedAPITypeReasons</key> <key>NSPrivacyAccessedAPITypeReasons</key>
<array> <array>
...@@ -22,10 +30,10 @@ ...@@ -22,10 +30,10 @@
</dict> </dict>
<dict> <dict>
<key>NSPrivacyAccessedAPIType</key> <key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategorySystemBootTime</string> <string>NSPrivacyAccessedAPICategoryDiskSpace</string>
<key>NSPrivacyAccessedAPITypeReasons</key> <key>NSPrivacyAccessedAPITypeReasons</key>
<array> <array>
<string>35F9.1</string> <string>85F4.1</string>
</array> </array>
</dict> </dict>
</array> </array>
......
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)")
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.callkit.call-directory</string>
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).CallDirectoryHandler</string>
</dict>
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>CXCallDirectoryExtensionAttributes</key>
<dict>
<key>CXCallDirectoryExtensionIdentifier</key>
<string>com.yourapp.callid</string>
</dict>
</dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.callkit.call-directory</string>
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).CallDirectoryHandler</string>
</dict>
</dict>
</plist>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment