Skip to content

Commit

Permalink
task(SDK-4057) - Convert ManifestValidator to kotlin
Browse files Browse the repository at this point in the history
  • Loading branch information
Anush-Shand committed Nov 12, 2024
1 parent 7a9d8ea commit 65974f5
Showing 1 changed file with 137 additions and 121 deletions.
Original file line number Diff line number Diff line change
@@ -1,161 +1,177 @@
package com.clevertap.android.sdk.validation;

import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.text.TextUtils;
import com.clevertap.android.sdk.ActivityLifecycleCallback;
import com.clevertap.android.sdk.CleverTapAPI;
import com.clevertap.android.sdk.DeviceInfo;
import com.clevertap.android.sdk.InAppNotificationActivity;
import com.clevertap.android.sdk.Logger;
import com.clevertap.android.sdk.ManifestInfo;
import com.clevertap.android.sdk.Utils;
import com.clevertap.android.sdk.inbox.CTInboxActivity;
import com.clevertap.android.sdk.pushnotification.CTNotificationIntentService;
import com.clevertap.android.sdk.pushnotification.CTPushNotificationReceiver;
import com.clevertap.android.sdk.pushnotification.PushConstants.PushType;
import com.clevertap.android.sdk.pushnotification.PushProviders;
import java.util.ArrayList;


public final class ManifestValidator {

private final static String ourApplicationClassName = "com.clevertap.android.sdk.Application";

public static void validate(final Context context, DeviceInfo deviceInfo, PushProviders pushProviders) {
package com.clevertap.android.sdk.validation

import android.content.Context
import android.content.pm.PackageManager
import android.text.TextUtils
import com.clevertap.android.sdk.ActivityLifecycleCallback
import com.clevertap.android.sdk.CleverTapAPI
import com.clevertap.android.sdk.DeviceInfo
import com.clevertap.android.sdk.InAppNotificationActivity
import com.clevertap.android.sdk.Logger
import com.clevertap.android.sdk.ManifestInfo
import com.clevertap.android.sdk.Utils
import com.clevertap.android.sdk.inbox.CTInboxActivity
import com.clevertap.android.sdk.pushnotification.CTNotificationIntentService
import com.clevertap.android.sdk.pushnotification.CTPushNotificationReceiver
import com.clevertap.android.sdk.pushnotification.PushConstants.PushType
import com.clevertap.android.sdk.pushnotification.PushProviders

object ManifestValidator {
private const val ourApplicationClassName = "com.clevertap.android.sdk.Application"

@JvmStatic
fun validate(context: Context, deviceInfo: DeviceInfo, pushProviders: PushProviders) {
if (!Utils.hasPermission(context, "android.permission.INTERNET")) {
Logger.d("Missing Permission: android.permission.INTERNET");
Logger.d("Missing Permission: android.permission.INTERNET")
}
checkSDKVersion(deviceInfo);
validationApplicationLifecyleCallback(context);
checkReceiversServices(context, pushProviders);
if (!TextUtils.isEmpty(ManifestInfo.getInstance(context).getFCMSenderId())){
Logger.i("We have noticed that your app is using a custom FCM Sender ID, this feature will " +
"be DISCONTINUED from the next version of the CleverTap Android SDK. With the next release, " +
"CleverTap Android SDK will only fetch the token using the google-services.json." +
" Please reach out to CleverTap Support for any questions.");
checkSDKVersion(deviceInfo)
validationApplicationLifecycleCallback(context)
checkReceiversServices(context, pushProviders)
if (!TextUtils.isEmpty(ManifestInfo.getInstance(context).fcmSenderId)) {
Logger.i(
"We have noticed that your app is using a custom FCM Sender ID, this feature will " +
"be DISCONTINUED from the next version of the CleverTap Android SDK. With the next release, " +
"CleverTap Android SDK will only fetch the token using the google-services.json." +
" Please reach out to CleverTap Support for any questions."
)
}
}

private static void checkApplicationClass(final Context context) {
String appName = context.getApplicationInfo().className;
private fun checkApplicationClass(context: Context) {
val appName = context.applicationInfo.className
if (appName == null || appName.isEmpty()) {
Logger.i("Unable to determine Application Class");
} else if (appName.equals(ourApplicationClassName)) {
Logger.i("Unable to determine Application Class")
} else if (appName == ourApplicationClassName) {
Logger.i("AndroidManifest.xml uses the CleverTap Application class, " +
"be sure you have properly added the CleverTap Account ID and Token to your AndroidManifest.xml, \n"
+
"or set them programmatically in the onCreate method of your custom application class prior to calling super.onCreate()");
"or set them programmatically in the onCreate method of your custom application class prior to calling super.onCreate()")
} else {
Logger.i("Application Class is " + appName);
Logger.i("Application Class is $appName")
}
}

@SuppressWarnings("ConstantConditions")
private static void checkReceiversServices(final Context context, PushProviders pushProviders) {
validateComponentInManifest(context.getApplicationContext(),
CTPushNotificationReceiver.class.getName(), ComponentType.RECEIVER);
validateComponentInManifest(context.getApplicationContext(),
CTNotificationIntentService.class.getName(), ComponentType.SERVICE);
validateComponentInManifest(context.getApplicationContext(),
InAppNotificationActivity.class.getName(), ComponentType.ACTIVITY);
validateComponentInManifest(context.getApplicationContext(),
CTInboxActivity.class.getName(), ComponentType.ACTIVITY);
validateComponentInManifest(context.getApplicationContext(),
"com.clevertap.android.geofence.CTGeofenceReceiver", ComponentType.RECEIVER);
validateComponentInManifest(context.getApplicationContext(),
"com.clevertap.android.geofence.CTLocationUpdateReceiver", ComponentType.RECEIVER);
validateComponentInManifest(context.getApplicationContext(),
"com.clevertap.android.geofence.CTGeofenceBootReceiver", ComponentType.RECEIVER);

ArrayList<PushType> enabledPushTypes = pushProviders.getAvailablePushTypes();
if (enabledPushTypes == null) {
return;
}

for (PushType pushType : enabledPushTypes) {
private fun checkReceiversServices(context: Context, pushProviders: PushProviders) {
validateComponentInManifest(
context.applicationContext,
CTPushNotificationReceiver::class.java.name, ComponentType.RECEIVER
)
validateComponentInManifest(
context.applicationContext,
CTNotificationIntentService::class.java.name, ComponentType.SERVICE
)
validateComponentInManifest(
context.applicationContext,
InAppNotificationActivity::class.java.name, ComponentType.ACTIVITY
)
validateComponentInManifest(
context.applicationContext,
CTInboxActivity::class.java.name, ComponentType.ACTIVITY
)
validateComponentInManifest(
context.applicationContext,
"com.clevertap.android.geofence.CTGeofenceReceiver", ComponentType.RECEIVER
)
validateComponentInManifest(
context.applicationContext,
"com.clevertap.android.geofence.CTLocationUpdateReceiver", ComponentType.RECEIVER
)
validateComponentInManifest(
context.applicationContext,
"com.clevertap.android.geofence.CTGeofenceBootReceiver", ComponentType.RECEIVER
)
validateComponentInManifest(
context.applicationContext,
"com.clevertap.android.pushtemplates.TimerTemplateService", ComponentType.SERVICE
)

val enabledPushTypes = pushProviders.availablePushTypes

for (pushType in enabledPushTypes) {
if (pushType == PushType.FCM) {
// use class name string directly here to avoid class not found issues on class import
validateComponentInManifest(context.getApplicationContext(),
"com.clevertap.android.sdk.pushnotification.fcm.FcmMessageListenerService", ComponentType.SERVICE);

validateComponentInManifest(
context.applicationContext,
"com.clevertap.android.sdk.pushnotification.fcm.FcmMessageListenerService",
ComponentType.SERVICE
)
} else if (pushType == PushType.HPS) {
// use class name string directly here to avoid class not found issues on class import
validateComponentInManifest(context.getApplicationContext(),
"com.clevertap.android.hms.CTHmsMessageService", ComponentType.SERVICE);
validateComponentInManifest(
context.applicationContext,
"com.clevertap.android.hms.CTHmsMessageService", ComponentType.SERVICE
)
}
}

}

private static void checkSDKVersion(DeviceInfo deviceInfo) {
Logger.i("SDK Version Code is " + deviceInfo.getSdkVersion());
private fun checkSDKVersion(deviceInfo: DeviceInfo) {
Logger.i("SDK Version Code is " + deviceInfo.sdkVersion)
}

public enum ComponentType {
RECEIVER(PackageManager.GET_RECEIVERS),
SERVICE(PackageManager.GET_SERVICES),
ACTIVITY(PackageManager.GET_ACTIVITIES);

final int flag;

ComponentType(int flag) {
this.flag = flag;
}
}

private static void validateComponentInManifest(Context context, String componentClassName, ComponentType componentType) {
private fun validateComponentInManifest(
context: Context,
componentClassName: String,
componentType: ComponentType
) {
if (isComponentPresentInManifest(context, componentClassName, componentType)) {
Logger.i(componentClassName.replaceFirst("com.clevertap.android.sdk.", "") + " is present");
Logger.i(
componentClassName.replaceFirst(
"com.clevertap.android.sdk.",
""
) + " is present"
)
} else {
Logger.i(componentClassName.replaceFirst("com.clevertap.android.sdk.", "") + " not present");
Logger.i(
componentClassName.replaceFirst(
"com.clevertap.android.sdk.",
""
) + " not present"
)
}
}

public static boolean isComponentPresentInManifest(Context context, String componentClassName, ComponentType componentType) {
try {
PackageManager pm = context.getPackageManager();
String packageName = context.getPackageName();

PackageInfo packageInfo = pm.getPackageInfo(packageName, componentType.flag);
if (componentType == ComponentType.SERVICE && packageInfo.services != null) {
for (ServiceInfo serviceInfo : packageInfo.services) {
if (componentClassName.equals(serviceInfo.name)) {
return true;
}
}
} else if (componentType == ComponentType.RECEIVER && packageInfo.receivers != null) {
for (ActivityInfo receiverInfo : packageInfo.receivers) {
if (componentClassName.equals(receiverInfo.name)) {
return true;
}
}
} else if(componentType == ComponentType.ACTIVITY && packageInfo.activities != null) {
for (ActivityInfo activityInfo : packageInfo.activities) {
if (componentClassName.equals(activityInfo.name)) {
return true;
}
}
@JvmStatic
fun isComponentPresentInManifest(
context: Context,
componentClassName: String,
componentType: ComponentType
): Boolean {
val pm = context.packageManager
val packageName = context.packageName

return try {
val packageInfo = pm.getPackageInfo(packageName, componentType.flag)
val components = when (componentType) {
ComponentType.SERVICE -> packageInfo.services
ComponentType.RECEIVER -> packageInfo.receivers
ComponentType.ACTIVITY -> packageInfo.activities
}
} catch (PackageManager.NameNotFoundException e) {
Logger.v("Issue in " + componentType.name().toLowerCase() + ": " + componentClassName + " - " + e);

components?.any { it.name == componentClassName } ?: false
} catch (e: PackageManager.NameNotFoundException) {
Logger.v("Issue in ${componentType.name.lowercase()}: $componentClassName - $e")
false
}
return false;
}

private static void validationApplicationLifecyleCallback(final Context context) {

private fun validationApplicationLifecycleCallback(context: Context) {
// some of the ancillary SDK wrappers have to manage the activity lifecycle manually because they don't have access to the application object or whatever
// for those cases also consider CleverTapAPI.isAppForeground() as a proxy for the SDK being in sync with the activity lifecycle
if (!ActivityLifecycleCallback.registered && !CleverTapAPI.isAppForeground()) {
Logger.i(
"Activity Lifecycle Callback not registered. Either set the android:name in your AndroidManifest.xml application tag to com.clevertap.android.sdk.Application, \n or, "
+
"if you have a custom Application class, call ActivityLifecycleCallback.register(this); before super.onCreate() in your class");
"Activity Lifecycle Callback not registered. Either set the android:name in your AndroidManifest.xml application tag to com.clevertap.android.sdk.Application, \n or, "
+
"if you have a custom Application class, call ActivityLifecycleCallback.register(this); before super.onCreate() in your class")
//Check for Application class only if the application lifecycle seems to be a problem
checkApplicationClass(context);
checkApplicationClass(context)
}
}

enum class ComponentType(val flag: Int) {
RECEIVER(PackageManager.GET_RECEIVERS),
SERVICE(PackageManager.GET_SERVICES),
ACTIVITY(PackageManager.GET_ACTIVITIES)
}
}

0 comments on commit 65974f5

Please sign in to comment.