NTP-38623 | Dialer app (#1104)

Co-authored-by: Varnit Goyal <varnit.goyal@navi.com>
Co-authored-by: Ashish Deo <ashish.deo@navi.com>
Co-authored-by: Mantri Ramkishor <mantri.ramkishor@navi.com>
This commit is contained in:
Aman Chaturvedi
2025-02-21 21:55:25 +05:30
committed by GitHub
parent 3eb7dd58f7
commit ab11f55f72
57 changed files with 2062 additions and 164 deletions

View File

@@ -114,7 +114,7 @@ def enableHermes = project.ext.react.get("enableHermes", false);
def VERSION_CODE = 237
def VERSION_NAME = "2.17.4"
def VERSION_NAME = "2.18.0"
android {
namespace "com.avapp"
@@ -223,10 +223,12 @@ dependencies {
implementation 'androidx.core:core-splashscreen:1.0.1'
implementation 'com.navi.android:pulse:1.0.1-cosmos'
implementation 'androidx.work:work-runtime-ktx:2.8.1'
implementation "androidx.room:room-runtime:2.5.0"
annotationProcessor "androidx.room:room-compiler:2.5.0"
//noinspection GradleDynamicVersion
implementation("com.facebook.react:react-android")
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}")
debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
exclude group:'com.squareup.okhttp3', module:'okhttp'

View File

@@ -16,4 +16,5 @@
-keep class com.navi.alfred.db.** { *; }
-keep class com.navi.alfred.AlfredConfig { *; }
-keepattributes SourceFile,LineNumberTable # Keep file names and line numbers.
-keep public class * extends java.lang.Exception # Optional: Keep custom exceptions.
-keep public class * extends java.lang.Exception # Optional: Keep custom exceptions.
-keep class com.avapp.contentProvider.ackNumbers.AckNumberContentProvider { *; }

View File

@@ -1,6 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.avapp">
package="com.avapp"
>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
@@ -39,6 +40,7 @@
<queries>
<package android:name="com.whatsapp" />
<package android:name="com.whatsapp.w4b" />
<package android:name="com.avapp" />
</queries>
<uses-permission android:name="android.permission.USE_EXACT_ALARM"/>
<application
@@ -64,8 +66,21 @@
/>
<service android:name="com.supersami.foregroundservice.ForegroundService" android:foregroundServiceType="location"></service>
<service android:name="com.supersami.foregroundservice.ForegroundServiceTask" android:foregroundServiceType="location"></service>
<provider
<provider
android:name="com.avapp.contentProvider.ackNumbers.AckNumberContentProvider"
android:authorities="com.avapp.ackProvider"
android:exported="true"
android:grantUriPermissions="true"
android:enabled="true">
</provider>
<provider
android:name="com.avapp.contentProvider.primaryNumber.PrimaryNumberContentProvider"
android:authorities="com.avapp.primaryNumberProvider"
android:exported="true"
android:grantUriPermissions="true"
android:enabled="true">
</provider>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
@@ -73,7 +88,9 @@
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</provider>
<activity
android:name=".MainActivity"

View File

@@ -15,6 +15,7 @@ import androidx.annotation.Nullable;
import androidx.core.content.FileProvider;
import com.facebook.react.bridge.ActivityEventListener;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.BaseActivityEventListener;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.Callback;
@@ -22,6 +23,8 @@ import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.uimanager.UIManagerModule;
import com.navi.alfred.AlfredManager;
import com.navi.pulse.PulseManager;
@@ -53,7 +56,7 @@ public class DeviceUtilsModule extends ReactContextBaseJavaModule {
private int WHATSAPP_SHARE_REQUEST_CODE = 12345;
public DeviceUtilsModule(@Nullable ReactApplicationContext reactContext){
public DeviceUtilsModule(@Nullable ReactApplicationContext reactContext) {
super(reactContext);
RNContext = reactContext;
reactContext.addActivityEventListener(mActivityEventListener);
@@ -67,10 +70,10 @@ public class DeviceUtilsModule extends ReactContextBaseJavaModule {
private final ActivityEventListener mActivityEventListener = new BaseActivityEventListener() {
@Override
public void onActivityResult(Activity activity, int i, int i1, @Nullable Intent intent) {
if(i1 != RESULT_CANCELED) {
if (i == WHATSAPP_SHARE_REQUEST_CODE && (imageFile!=null)) {
new File(Uri.fromFile(imageFile).getPath()).delete();
}
if (i1 != RESULT_CANCELED) {
if (i == WHATSAPP_SHARE_REQUEST_CODE && (imageFile != null)) {
new File(Uri.fromFile(imageFile).getPath()).delete();
}
}
}
@@ -306,7 +309,7 @@ public class DeviceUtilsModule extends ReactContextBaseJavaModule {
}
public Intent getWhatsappShareIntent(String message, String imageUrl, String mimeType, String packageName,
String format, String fileName) {
String format, String fileName) {
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, message);
@@ -332,7 +335,7 @@ public class DeviceUtilsModule extends ReactContextBaseJavaModule {
@ReactMethod
public void sendContentToWhatsapp(String message, String fileUri, String mimeType, String format, String fileName,
Promise promise) {
Promise promise) {
try {
ArrayList<String> appsInstalled = isWhatsAppInstalled();
int numberOfAppsInstalled = appsInstalled.size();
@@ -378,5 +381,60 @@ public class DeviceUtilsModule extends ReactContextBaseJavaModule {
}
}
@ReactMethod
public void isAppInstalled(String packageName, Promise promise) {
PackageManager pm = RNContext.getPackageManager();
try {
ApplicationInfo info = pm.getApplicationInfo(packageName, 0);
if (info != null) {
promise.resolve(true); // App is installed
} else {
promise.resolve(false); // App is not installed
}
} catch (PackageManager.NameNotFoundException e) {
promise.resolve(false); // App is not installed
} catch (Exception e) {
promise.reject("ERROR", "An unexpected error occurred: " + e.getMessage());
}
}
@ReactMethod
public void getAppInfo(String packageName, Promise promise) {
PackageManager pm = RNContext.getPackageManager();
try {
// Get PackageInfo for the given package name
PackageInfo packageInfo = pm.getPackageInfo(packageName, 0);
// Create a WritableMap to store the app information
WritableMap map = Arguments.createMap();
map.putInt("version", packageInfo.versionCode); // App version name
map.putString("versionName", packageInfo.versionName); // App version code (deprecated in API 28+ but still useful for older devices)
promise.resolve(map); // Resolve the promise with the app info map
} catch (PackageManager.NameNotFoundException e) {
promise.resolve(false); // App not found, resolve with false
} catch (Exception e) {
promise.reject("ERROR", "An unexpected error occurred: " + e.getMessage()); // Handle unexpected errors
}
}
@ReactMethod
public void getAppInfoFromFilePath(String apkFilePath, Promise promise) {
try {
PackageManager pm = RNContext.getPackageManager();
PackageInfo packageInfo = pm.getPackageArchiveInfo(apkFilePath, 0);
if (packageInfo != null) {
WritableMap map = Arguments.createMap();
map.putString("packageName", packageInfo.packageName);
map.putInt("version", packageInfo.versionCode);
map.putString("versionName", packageInfo.versionName);
promise.resolve(map);
} else {
promise.reject("APK_ERROR", "Failed to read APK file.");
}
} catch (Exception e) {
promise.reject(e);
}
}
}

View File

@@ -11,9 +11,12 @@ import android.app.Application;
import android.content.Context;
import com.avapp.appInstallerModule.ApkInstallerPackage;
import com.avapp.contentProvider.ContentProviderPackage;
import com.avapp.callModule.CallModulePackage;
import com.avapp.deviceDataSync.DeviceDataSyncPackage;
import com.avapp.photoModule.PhotoModulePackage;
import com.avapp.phoneStateBroadcastReceiver.PhoneStateModulePackage;
import com.avapp.sharedPreference.SharedPreferencesPackage;
import com.avapp.utils.FirebaseRemoteConfigHelper;
import com.avapp.wifiDetailsModule.WifiDetailsModulePackage;
import com.avapp.restartApp.RestartPackage;
@@ -69,6 +72,9 @@ public class MainApplication extends Application implements ReactApplication, Ap
packages.add(new WifiDetailsModulePackage());
packages.add(new ApkInstallerPackage());
packages.add(new RestartPackage());
packages.add(new CallModulePackage());
packages.add(new SharedPreferencesPackage());
packages.add(new ContentProviderPackage());
return packages;
}
@@ -280,4 +286,4 @@ public class MainApplication extends Application implements ReactApplication, Ap
public void onActivityDestroyed(@NonNull Activity activity) {
}
}
}

View File

@@ -0,0 +1,226 @@
package com.avapp.callModule;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.util.Log;
import androidx.annotation.NonNull;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Callback;
import android.os.Build;
import androidx.core.content.FileProvider;
import com.avapp.BuildConfig;
import java.io.File;
import java.io.FileInputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import com.facebook.react.bridge.Callback;
import android.content.IntentFilter;
import android.content.BroadcastReceiver;
import android.content.Context;
import com.facebook.react.bridge.Promise;
public class CallModule extends ReactContextBaseJavaModule {
private final ReactApplicationContext reactContext;
private static final String TAG = "CallModule";
private static final String CALLING_APP_PACKAGE = "org.fossify.phone";
private String pendingRecipient;
private String pendingExotelNumber;
private BroadcastReceiver installReceiver;
public CallModule(ReactApplicationContext reactContext) {
super(reactContext);
this.reactContext = reactContext;
registerInstallReceiver();
}
@NonNull
@Override
public String getName() {
return "CallModule";
}
private void registerInstallReceiver() {
IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
filter.addDataScheme("package");
installReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Uri data = intent.getData();
Log.d(TAG, "InstallReceiver: onReceive: " + intent.getAction());
if (data != null) {
String installedPackage = data.getSchemeSpecificPart();
if (CALLING_APP_PACKAGE.equals(installedPackage)) {
Log.d(TAG, "Installed package detected: " + installedPackage);
retryCallAfterInstall();
}
}
}
};
reactContext.registerReceiver(installReceiver, filter);
}
private void retryCallAfterInstall() {
Activity activity = getCurrentActivity();
if (activity != null && pendingRecipient != null) {
makeCall(activity, pendingRecipient, pendingExotelNumber);
} else if (pendingRecipient != null) {
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:" + pendingRecipient));
intent.putExtra("cosmos_call_numb", pendingRecipient);
intent.putExtra("cosmos_call_numb_exotel", pendingExotelNumber);
intent.setPackage(CALLING_APP_PACKAGE);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // REQUIRED!
activity.startActivity(intent);
}
// Reset stored values
pendingRecipient = null;
pendingExotelNumber = null;
}
public static String maskFirstSixDigits(String number) {
if (number == null || number.length() != 10) {
Log.e("AVAPP", "number incorrect");
}
if (number.length() == 10) {
return "******" + number.substring(6);
}else {
return "******" + number.substring(8); // Mask first 6 digits
}}
private boolean isAppInstalled(Activity activity, String packageName) {
try {
activity.getPackageManager().getPackageInfo(packageName, 0);
return true;
} catch (Exception e) {
return false;
}
}
@ReactMethod
public void isDialerAppValidApkFile(String filePath, Promise promise) {
try {
boolean isValid = isValidApkFile(filePath);
promise.resolve(isValid);
} catch (Exception e) {
Log.e("ApkInstaller", "Error validating APK file", e);
promise.reject("APK_VALIDATION_ERROR", e);
}
}
public boolean isValidApkFile(String filePath) {
try {
File apkFile = new File(filePath);
if (!apkFile.exists() || apkFile.length() == 0) {
return false;
}
try (ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(apkFile))) {
ZipEntry zipEntry = zipInputStream.getNextEntry();
return zipEntry != null;
} catch (Exception e) {
Log.e("ApkInstaller", "Invalid APK file", e);
return false;
}
} catch (Exception e) {
Log.e("ApkInstaller", "Error installing APK", e);
return false;
}
}
@ReactMethod
public void launchCallIntent(String recipient, String exotelNumber, String filePath, Callback callback) {
Activity activity = getCurrentActivity();
if (activity == null) {
Log.e(TAG, "Activity is null, cannot make call");
return;
}
// Mask the recipient number for logging
String maskedNumber = maskFirstSixDigits(recipient);
Log.d(TAG, "Attempting to call: " + maskedNumber + " using " + CALLING_APP_PACKAGE);
pendingRecipient = recipient;
pendingExotelNumber = exotelNumber;
if (!isAppInstalled(activity, CALLING_APP_PACKAGE)) {
Log.d(TAG, "App not installed, installing it...");
installApk(filePath, callback);
return;
}
// Make the call if the app is installed
makeCall(activity, recipient, exotelNumber);
}
private void makeCall(Activity activity, String recipient, String exotelNumber) {
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:" + pendingRecipient));
intent.putExtra("cosmos_call_numb", pendingRecipient);
intent.putExtra("cosmos_call_numb_exotel", pendingExotelNumber);
intent.setPackage(CALLING_APP_PACKAGE);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // Ensure the specific app is used
if (intent.resolveActivity(activity.getPackageManager()) != null) {
activity.startActivity(intent);
Log.d(TAG, "Call initiated successfully.");
} else {
Log.e(TAG, "No app found to handle the call.");
}
}
private void promptForInstall(Uri apkUri) {
try {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
reactContext.startActivity(intent);
} catch (Exception e) {
Log.e("ApkInstaller", "promptForInstall: Error installing APK", e);
}
}
public void installApk(String filePath, Callback callback) {
try {
File apkFile = new File(filePath);
if (!isValidApkFile(filePath)) {
callback.invoke("Invalid or corrupted apk file", null);
return;
}
Uri apkUri;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
apkUri = FileProvider.getUriForFile(reactContext, BuildConfig.APPLICATION_ID + ".provider", apkFile);
} else {
apkUri = Uri.fromFile(apkFile);
}
promptForInstall(apkUri);
callback.invoke(null, "Success");
} catch (Exception e) {
Log.e("ApkInstaller", "Error installing APK", e);
callback.invoke(e.toString(), null);
}
}
@Override
public void onCatalystInstanceDestroy() {
if (installReceiver != null) {
reactContext.unregisterReceiver(installReceiver);
installReceiver = null;
}
super.onCatalystInstanceDestroy();
}
}

View File

@@ -0,0 +1,28 @@
package com.avapp.callModule;
import com.facebook.react.uimanager.ViewManager;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class CallModulePackage implements ReactPackage {
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
@Override
public List<NativeModule> createNativeModules(
ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new CallModule(reactContext));
return modules;
}
}

View File

@@ -0,0 +1,172 @@
package com.avapp.contentProvider;
import com.avapp.contentProvider.ackNumbers.AckNumbersEntity;
import com.avapp.contentProvider.primaryNumber.PrimaryNumberEntity;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.facebook.react.bridge.ReadableArray;
public class ContentProviderModule extends ReactContextBaseJavaModule {
private ReactApplicationContext reactApplicationContext;
private CosmosDatabase database;
private final ExecutorService executorService = Executors.newSingleThreadExecutor();
public ContentProviderModule(ReactApplicationContext reactContext) {
super(reactContext);
this.reactApplicationContext = reactContext;
database = CosmosDatabase.getInstance(reactApplicationContext);
}
@Override
public String getName() {
return "ContentProviderModule";
}
@ReactMethod
public void getAckNumbers(Promise promise) {
ContentProviderThreadPool.getExecutorService().execute(() -> {
try {
List<AckNumbersEntity> ackNumbers = database.ackNumberDataDao().getAllData();
WritableArray writableArray = Arguments.createArray();
for (AckNumbersEntity entity : ackNumbers) {
WritableMap map = Arguments.createMap();
map.putString("fromNumber", entity.fromNumber);
map.putString("toNumber", entity.toNumber);
map.putString("created_at", entity.createdAt+"");
map.putInt("id", entity.id);
writableArray.pushMap(map);
}
promise.resolve(writableArray);
} catch (Exception e) {
promise.reject(e);
}
});
}
@ReactMethod
public void deleteAckNumberById(Integer id, Promise promise) {
ContentProviderThreadPool.getExecutorService().execute(() -> {
try {
database.ackNumberDataDao().deleteDataById(id);
promise.resolve(true);
} catch (Exception e) {
promise.reject(e);
}
});
}
@ReactMethod
public void addPhoneNumber(String phoneNumber, Promise promise) {
ContentProviderThreadPool.getExecutorService().execute(()->{
try {
//check if entry already exist
PrimaryNumberEntity primaryNumberExist = database.primaryNumberDataDao().getDataByPhone(phoneNumber);
if(primaryNumberExist !=null) {
promise.resolve(phoneNumber);
return;
}
PrimaryNumberEntity primaryNumber = new PrimaryNumberEntity();
primaryNumber.phoneNumber = phoneNumber;
database.primaryNumberDataDao().insert(primaryNumber);
promise.resolve(phoneNumber);
}
catch (Exception e) {
promise.resolve(e);
}
});
}
@ReactMethod
public void deletePhoneNumber(String phoneNumber, Promise promise) {
ContentProviderThreadPool.getExecutorService().execute(()->{
try {
database.primaryNumberDataDao().deleteDataByPhone(phoneNumber);
promise.resolve(phoneNumber);
}
catch (Exception e) {
promise.reject(e);
}
});
}
@ReactMethod
public void bulkAddPhoneNumbers(ReadableArray phoneNumbers, Promise promise) {
executorService.execute(() -> {
try {
for (int i = 0; i < phoneNumbers.size(); i++) {
String phoneNumber = phoneNumbers.getString(i);
PrimaryNumberEntity primaryNumberExist = database.primaryNumberDataDao().getDataByPhone(phoneNumber);
if(primaryNumberExist !=null) {
promise.resolve(phoneNumber);
return;
}
PrimaryNumberEntity primaryNumber = new PrimaryNumberEntity();
primaryNumber.phoneNumber = phoneNumber;
database.primaryNumberDataDao().insert(primaryNumber);
}
promise.resolve(true);
} catch (Exception e) {
promise.reject(e);
}
});
}
@ReactMethod
public void bulkDeletePhoneNumbers(ReadableArray phoneNumbers, Promise promise) {
executorService.execute(() -> {
try {
for (int i = 0; i < phoneNumbers.size(); i++) {
String phoneNumber = phoneNumbers.getString(i);
database.primaryNumberDataDao().deleteDataByPhone(phoneNumber);
}
promise.resolve(true);
} catch (Exception e) {
promise.reject(e);
}
});
}
@ReactMethod
public void cleanTables(Promise promise) {
ContentProviderThreadPool.getExecutorService().execute(()->{
try {
database.primaryNumberDataDao().deleteAllRows();
database.ackNumberDataDao().deleteAllRows();
promise.resolve("done");
}
catch (Exception e) {
promise.reject(e);
}
});
}
@ReactMethod
public void getTableLength(Promise promise) {
ContentProviderThreadPool.getExecutorService().execute(()->{
try {
promise.resolve(database.primaryNumberDataDao().getTableLength());
}
catch (Exception e) {
promise.reject(e);
}
});
}
}

View File

@@ -0,0 +1,27 @@
package com.avapp.contentProvider;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class ContentProviderPackage implements ReactPackage {
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
@Override
public List<NativeModule> createNativeModules(
ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new ContentProviderModule(reactContext));
return modules;
}
}

View File

@@ -0,0 +1,31 @@
package com.avapp.contentProvider;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ContentProviderThreadPool {
private static final int CORE_POOL_SIZE = 10;
private static final int MAX_POOL_SIZE = 20;
private static final long KEEP_ALIVE_TIME = 30L;
private static final TimeUnit TIME_UNIT = TimeUnit.SECONDS;
private static final BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>();
private static final ExecutorService executorService = new ThreadPoolExecutor(
CORE_POOL_SIZE,
MAX_POOL_SIZE,
KEEP_ALIVE_TIME,
TIME_UNIT,
workQueue
);
public static ExecutorService getExecutorService() {
return executorService;
}
}

View File

@@ -0,0 +1,33 @@
package com.avapp.contentProvider;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import android.content.Context;
import com.avapp.contentProvider.ackNumbers.AckNumberDataDao;
import com.avapp.contentProvider.ackNumbers.AckNumbersEntity;
import com.avapp.contentProvider.primaryNumber.PrimaryNumberDataDao;
import com.avapp.contentProvider.primaryNumber.PrimaryNumberEntity;
@Database(entities = {AckNumbersEntity.class, PrimaryNumberEntity.class}, version = 1)
public abstract class CosmosDatabase extends RoomDatabase {
public abstract AckNumberDataDao ackNumberDataDao();
public abstract PrimaryNumberDataDao primaryNumberDataDao();
private static volatile CosmosDatabase INSTANCE;
public static CosmosDatabase getInstance(Context context) {
if (INSTANCE == null) {
synchronized (CosmosDatabase.class) {
if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
CosmosDatabase.class, "cosmos")
.build();
}
}
}
return INSTANCE;
}
}

View File

@@ -0,0 +1,129 @@
package com.avapp.contentProvider.ackNumbers;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.net.Uri;
import android.os.Looper;
import android.util.Log;
import com.avapp.contentProvider.CosmosDatabase;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class AckNumberContentProvider extends ContentProvider {
private static final int DATA = 100;
private static final int DATA_ID = 101;
private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
sUriMatcher.addURI(AckNumberContractProvider.AUTHORITY, AckNumberContractProvider.PATH_DATA, DATA);
sUriMatcher.addURI(AckNumberContractProvider.AUTHORITY, AckNumberContractProvider.PATH_DATA + "/#", DATA_ID);
}
private CosmosDatabase database;
@Override
public boolean onCreate() {
Log.d("ack number provider", "");
database = CosmosDatabase.getInstance(getContext());
if (database == null) {
Log.e("AckNumber", "Failed to initialise database");
return false; // Return false if the database could not be initialised
}
Log.d("AckNumber", "Content Provider created and database initialised");
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
int match = sUriMatcher.match(uri);
Cursor cursor;
switch (match) {
case DATA:
cursor = database.ackNumberDataDao().getAllDataCursor();
break;
case DATA_ID:
int id = Integer.parseInt(uri.getLastPathSegment());
cursor = database.ackNumberDataDao().getDataByIdCursor(id);
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
Log.d("in insert","");
int match = sUriMatcher.match(uri);
if (match == DATA) {
AckNumbersEntity data = new AckNumbersEntity();
data.fromNumber = values.getAsString("fromPhoneNumber");
data.toNumber = values.getAsString("toPhoneNumber");
data.createdAt= System.currentTimeMillis();
long id = database.ackNumberDataDao().insert(data);
if (id == -1) {
throw new IllegalArgumentException("Failed to insert row into " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return Uri.withAppendedPath(uri, String.valueOf(id));
} else {
throw new IllegalArgumentException("Insertion not supported for " + uri);
}
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int match = sUriMatcher.match(uri);
if (match == DATA_ID) {
int id = Integer.parseInt(uri.getLastPathSegment());
AckNumbersEntity data = database.ackNumberDataDao().getDataById(id);
if (data != null) {
return database.ackNumberDataDao().delete(data);
}
}
throw new IllegalArgumentException("Deletion not supported for " + uri);
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
int match = sUriMatcher.match(uri);
if (match == DATA_ID) {
int id = Integer.parseInt(uri.getLastPathSegment());
AckNumbersEntity data = database.ackNumberDataDao().getDataById(id);
if (data != null) {
data.toNumber = values.getAsString("toPhoneNumber");
data.fromNumber = values.getAsString("fromPhoneNumber");
data.createdAt= System.currentTimeMillis();
return database.ackNumberDataDao().update(data);
}
}
throw new IllegalArgumentException("Update not supported for " + uri);
}
@Override
public String getType(Uri uri) {
int match = sUriMatcher.match(uri);
switch (match) {
case DATA:
return "vnd.android.cursor.dir/" + AckNumberContractProvider.AUTHORITY + "." + AckNumberContractProvider.PATH_DATA;
case DATA_ID:
return "vnd.android.cursor.item/" + AckNumberContractProvider.AUTHORITY + "." + AckNumberContractProvider.PATH_DATA;
default:
throw new IllegalStateException("Unknown URI: " + uri);
}
}
}

View File

@@ -0,0 +1,21 @@
package com.avapp.contentProvider.ackNumbers;
import android.net.Uri;
import android.provider.BaseColumns;
public final class AckNumberContractProvider {
//com.avapp.ack.provider
public static final String AUTHORITY = "com.avapp.ackProvider";
public static final Uri BASE_CONTENT_URI = Uri.parse("content://" + AUTHORITY);
public static final String PATH_DATA = "data";
public static final class DataEntry implements BaseColumns {
public static final Uri CONTENT_URI = Uri.withAppendedPath(BASE_CONTENT_URI, PATH_DATA);
public static final String TABLE_NAME = "cosmos";
public static final String COLUMN_NAME_FROM_NUMBER = "fromNumber";
public static final String COLUMN_NAME_TO_NUMBER = "toNumber";
}
}

View File

@@ -0,0 +1,52 @@
package com.avapp.contentProvider.ackNumbers;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Update;
import androidx.room.Delete;
import android.database.Cursor;
import com.avapp.contentProvider.primaryNumber.PrimaryNumberEntity;
import java.util.List;
@Dao
public interface AckNumberDataDao {
@Insert
long insert(AckNumbersEntity data);
@Query("SELECT * FROM ack")
List<AckNumbersEntity> getAllData();
@Query("SELECT * FROM ack WHERE id = :id")
AckNumbersEntity getDataById(int id);
@Query("DELETE FROM ack WHERE id = :id")
int deleteDataById(int id);
@Query("SELECT * FROM ack WHERE fromNumber= :fromPhone")
AckNumbersEntity getDataByFromPhone(String fromPhone);
@Query("SELECT * FROM ack WHERE toNumber= :toPhone")
AckNumbersEntity getDataByToPhone(String toPhone);
@Query("DELETE FROM ack")
void deleteAllRows();
@Update
int update(AckNumbersEntity data);
@Delete
int delete(AckNumbersEntity data);
// Custom methods to return Cursor for Content Provider
@Query("SELECT * FROM ack")
Cursor getAllDataCursor();
@Query("SELECT * FROM ack WHERE id = :id")
Cursor getDataByIdCursor(int id);
}

View File

@@ -0,0 +1,19 @@
package com.avapp.contentProvider.ackNumbers;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
@Entity(tableName = "ack")
public class AckNumbersEntity {
@PrimaryKey(autoGenerate = true)
public int id;
@ColumnInfo(name = "created_at")
public long createdAt;
public String fromNumber;
public String toNumber;
}

View File

@@ -0,0 +1,110 @@
package com.avapp.contentProvider.primaryNumber;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.net.Uri;
import com.avapp.contentProvider.CosmosDatabase;
public class PrimaryNumberContentProvider extends ContentProvider {
private static final int DATA = 100;
private static final int DATA_ID = 101;
private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
sUriMatcher.addURI(PrimaryNumberContractProvider.AUTHORITY, PrimaryNumberContractProvider.PATH_DATA, DATA);
sUriMatcher.addURI(PrimaryNumberContractProvider.AUTHORITY, PrimaryNumberContractProvider.PATH_DATA + "/#", DATA_ID);
}
private CosmosDatabase database;
@Override
public boolean onCreate() {
database = CosmosDatabase.getInstance(getContext());
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
int match = sUriMatcher.match(uri);
Cursor cursor;
switch (match) {
case DATA:
cursor = database.primaryNumberDataDao().getAllDataCursor();
break;
case DATA_ID:
String id = uri.getLastPathSegment();
cursor = database.primaryNumberDataDao().getDataByPhoneCursor(id);
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
int match = sUriMatcher.match(uri);
if (match == DATA) {
PrimaryNumberEntity data = new PrimaryNumberEntity();
data.phoneNumber = values.getAsString("phoneNumber");
long id = database.primaryNumberDataDao().insert(data);
if (id == -1) {
throw new IllegalArgumentException("Failed to insert row into " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return Uri.withAppendedPath(uri, String.valueOf(id));
} else {
throw new IllegalArgumentException("Insertion not supported for " + uri);
}
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int match = sUriMatcher.match(uri);
if (match == DATA_ID) {
int id = Integer.parseInt(uri.getLastPathSegment());
PrimaryNumberEntity data = database.primaryNumberDataDao().getDataById(id);
if (data != null) {
return database.primaryNumberDataDao().delete(data);
}
}
throw new IllegalArgumentException("Deletion not supported for " + uri);
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
int match = sUriMatcher.match(uri);
if (match == DATA_ID) {
int id = Integer.parseInt(uri.getLastPathSegment());
PrimaryNumberEntity data = database.primaryNumberDataDao().getDataById(id);
if (data != null) {
data.phoneNumber = values.getAsString("phoneNumber");
return database.primaryNumberDataDao().update(data);
}
}
throw new IllegalArgumentException("Update not supported for " + uri);
}
@Override
public String getType(Uri uri) {
int match = sUriMatcher.match(uri);
switch (match) {
case DATA:
return "vnd.android.cursor.dir/" + PrimaryNumberContractProvider.AUTHORITY + "." + PrimaryNumberContractProvider.PATH_DATA;
case DATA_ID:
return "vnd.android.cursor.item/" + PrimaryNumberContractProvider.AUTHORITY + "." + PrimaryNumberContractProvider.PATH_DATA;
default:
throw new IllegalStateException("Unknown URI: " + uri);
}
}
}

View File

@@ -0,0 +1,18 @@
package com.avapp.contentProvider.primaryNumber;
import android.net.Uri;
import android.provider.BaseColumns;
public final class PrimaryNumberContractProvider {
public static final String AUTHORITY = "com.avapp.primaryNumberProvider";//"com.cosmos.myapp.provider";
public static final Uri BASE_CONTENT_URI = Uri.parse("content://" + AUTHORITY);
public static final String PATH_DATA = "data";
public static final class DataEntry implements BaseColumns {
public static final Uri CONTENT_URI = Uri.withAppendedPath(BASE_CONTENT_URI, PATH_DATA);
public static final String TABLE_NAME = "cosmos";
public static final String COLUMN_NAME_TITLE = "phone";
}
}

View File

@@ -0,0 +1,51 @@
package com.avapp.contentProvider.primaryNumber;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Update;
import androidx.room.Delete;
import android.database.Cursor;
import java.util.List;
@Dao
public interface PrimaryNumberDataDao {
@Insert
long insert(PrimaryNumberEntity data);
@Query("SELECT * FROM `primary`")
List<PrimaryNumberEntity> getAllData();
@Query("SELECT * FROM `primary` WHERE id = :id")
PrimaryNumberEntity getDataById(int id);
@Query("SELECT * FROM `primary` WHERE phoneNumber = :phoneNumber LIMIT 1")
PrimaryNumberEntity getDataByPhone(String phoneNumber);
@Query("SELECT * FROM `primary` WHERE phoneNumber = :phoneNumber LIMIT 1")
Cursor getDataByPhoneCursor(String phoneNumber);
@Query("DELETE FROM `primary`")
void deleteAllRows();
@Query("SELECT DISTINCT COUNT(*) FROM `primary`")
int getTableLength();
@Update
int update(PrimaryNumberEntity data);
@Delete
int delete(PrimaryNumberEntity data);
@Query("DELETE FROM `primary` WHERE phoneNumber = :phoneNumber")
int deleteDataByPhone(String phoneNumber);
// Custom methods to return Cursor for Content Provider
@Query("SELECT * FROM `primary`")
Cursor getAllDataCursor();
@Query("SELECT * FROM `primary` WHERE id = :id")
Cursor getDataByIdCursor(int id);
}

View File

@@ -0,0 +1,11 @@
package com.avapp.contentProvider.primaryNumber;
import androidx.room.PrimaryKey;
import androidx.room.Entity;
@Entity(tableName = "primary")
public class PrimaryNumberEntity {
@PrimaryKey(autoGenerate = true)
public int id;
public String phoneNumber;
}

View File

@@ -0,0 +1,62 @@
package com.avapp.sharedPreference;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Log;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
public class SharedPreferencesModule extends ReactContextBaseJavaModule {
private static final String PREFS_NAME = "MyPrefs";
private ReactApplicationContext reactApplicationContext;
private SharedPreferences sharedPreferences;
public SharedPreferencesModule(ReactApplicationContext reactContext) {
super(reactContext);
this.reactApplicationContext = reactContext;
sharedPreferences = reactContext.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
}
@Override
public String getName() {
return "SharedPreferencesModule";
}
@ReactMethod
public void saveString(String key, String value) {
try {
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString(key, value);
Log.d("SharedPreferences", "SharedPreferences New Val" + value);
Log.d("SharedPreferences", "SharedPreferences editor" + editor);
editor.apply();
} catch (Exception e) {
Log.d("SharedPreferences", "SharedPreferences Exception" + e);
}
}
@ReactMethod
public void getString(String key, String defaultValue, Promise promise) {
try {
String value = sharedPreferences.getString(key, defaultValue);
promise.resolve(value);
} catch (Exception e) {
promise.reject(e);
}
}
@ReactMethod
public void removeKey(String key, Promise promise) {
try {
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.remove(key);
editor.apply();
promise.resolve(null);
} catch (Exception e) {
promise.reject(e);
}
}
}

View File

@@ -0,0 +1,26 @@
package com.avapp.sharedPreference;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class SharedPreferencesPackage implements ReactPackage {
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
@Override
public List<NativeModule> createNativeModules(
ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new SharedPreferencesModule(reactContext));
return modules;
}
}

View File

@@ -32,6 +32,8 @@ buildscript {
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.2'
classpath 'com.google.firebase:perf-plugin:1.4.2'
}
}

View File

@@ -1,6 +1,6 @@
#Fri Feb 14 13:50:09 IST 2025
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.1-all.zip
networkTimeout=10000
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists