Skip to content
This repository has been archived by the owner on May 6, 2024. It is now read-only.

Commit

Permalink
refactor: Resolve build-time warnings after Android 13 migration (#1803)
Browse files Browse the repository at this point in the history
- Upgrade libraries to the latest stable versions.
- Address Serializable and Parcelable warnings.
- Update permissions listeners as per SDK.
- Refine onActivityResult handling.
- Handle onBackPressed for UX.
- Update README with comprehensive docs.

Fixes: LEARNER-9413
  • Loading branch information
HamzaIsrar12 committed Jul 29, 2023
1 parent b1e9084 commit e5c717f
Show file tree
Hide file tree
Showing 35 changed files with 623 additions and 626 deletions.
10 changes: 5 additions & 5 deletions OpenEdXMobile/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -119,14 +119,14 @@ dependencies {
// NOTE: Always update the version of google-services and firebase libraries using the ones
// provided in this link: https://developers.google.com/android/guides/releases
implementation "com.google.android.gms:play-services-plus:17.0.0"
implementation "com.google.android.gms:play-services-analytics:18.0.2"
implementation "com.google.android.gms:play-services-auth:20.5.0"
implementation "com.google.android.gms:play-services-analytics:18.0.3"
implementation "com.google.android.gms:play-services-auth:20.6.0"
// Google Firebase
implementation "com.google.firebase:firebase-core:21.1.1"
implementation "com.google.firebase:firebase-messaging:23.1.2"

// Add the dependency for the Performance Monitoring library
implementation "com.google.firebase:firebase-perf:20.3.2"
implementation "com.google.firebase:firebase-perf:20.3.3"
// Firebase remote config
implementation "com.google.firebase:firebase-config:21.4.0"

Expand Down Expand Up @@ -163,7 +163,7 @@ dependencies {
implementation "androidx.multidex:multidex:2.0.1"
implementation "androidx.constraintlayout:constraintlayout:2.1.4"
implementation "de.hdodenhof:circleimageview:2.0.0"
implementation "com.github.bumptech.glide:glide:4.11.0"
implementation "com.github.bumptech.glide:glide:4.14.2"
implementation "com.github.bumptech.glide:okhttp3-integration:4.11.0"

// Segment Library
Expand Down Expand Up @@ -239,7 +239,7 @@ dependencies {
androidTestImplementation "junit:junit:4.13.2"
androidTestImplementation "androidx.test.ext:junit:1.1.5"
androidTestImplementation "androidx.test:rules:1.5.0"
androidTestImplementation "org.hamcrest:hamcrest-library:1.3"
androidTestImplementation "org.hamcrest:hamcrest-library:2.2"
androidTestImplementation "androidx.test.espresso:espresso-core:3.5.1"
androidTestImplementation "com.linkedin.dexmaker:dexmaker-mockito:2.12.1"
androidTestImplementation("org.mockito:mockito-core:5.3.1") {
Expand Down
95 changes: 22 additions & 73 deletions OpenEdXMobile/src/main/java/org/edx/mobile/base/BaseFragment.java
Original file line number Diff line number Diff line change
@@ -1,31 +1,37 @@
package org.edx.mobile.base;

import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Build;
import android.os.Bundle;

import androidx.annotation.NonNull;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.fragment.app.Fragment;

import com.google.android.material.snackbar.Snackbar;

import org.edx.mobile.R;
import org.edx.mobile.http.notifications.SnackbarErrorNotification;
import org.edx.mobile.util.PermissionsUtil;

public class BaseFragment extends Fragment {
public interface PermissionListener {
void onPermissionGranted(String[] permissions, int requestCode);

void onPermissionDenied(String[] permissions, int requestCode);
}

private boolean isFirstVisit = true;
protected PermissionListener permissionListener;

protected ActivityResultLauncher<String> requestPermissionLauncher = registerForActivityResult(

Check warning on line 21 in OpenEdXMobile/src/main/java/org/edx/mobile/base/BaseFragment.java

View check run for this annotation

Codecov / codecov/patch

OpenEdXMobile/src/main/java/org/edx/mobile/base/BaseFragment.java#L21

Added line #L21 was not covered by tests
new ActivityResultContracts.RequestPermission(), isGranted -> {
if (!isGranted) {
showPermissionDeniedMessage();

Check warning on line 24 in OpenEdXMobile/src/main/java/org/edx/mobile/base/BaseFragment.java

View check run for this annotation

Codecov / codecov/patch

OpenEdXMobile/src/main/java/org/edx/mobile/base/BaseFragment.java#L24

Added line #L24 was not covered by tests
}
});

Check warning on line 26 in OpenEdXMobile/src/main/java/org/edx/mobile/base/BaseFragment.java

View check run for this annotation

Codecov / codecov/patch

OpenEdXMobile/src/main/java/org/edx/mobile/base/BaseFragment.java#L26

Added line #L26 was not covered by tests

protected void showPermissionDeniedMessage() {
Snackbar.make(
requireActivity().findViewById(android.R.id.content),
getResources().getString(R.string.permission_not_granted),

Check warning on line 31 in OpenEdXMobile/src/main/java/org/edx/mobile/base/BaseFragment.java

View check run for this annotation

Codecov / codecov/patch

OpenEdXMobile/src/main/java/org/edx/mobile/base/BaseFragment.java#L29-L31

Added lines #L29 - L31 were not covered by tests
Snackbar.LENGTH_LONG
).show();
}

Check warning on line 34 in OpenEdXMobile/src/main/java/org/edx/mobile/base/BaseFragment.java

View check run for this annotation

Codecov / codecov/patch

OpenEdXMobile/src/main/java/org/edx/mobile/base/BaseFragment.java#L33-L34

Added lines #L33 - L34 were not covered by tests

@Override
public void onCreate(Bundle savedInstanceState) {
Expand Down Expand Up @@ -81,84 +87,27 @@ public void onResume() {

/**
* Called when a Fragment is re-displayed to the user (the user has navigated back to it).
* Defined to mock the behavior of {@link Activity#onRestart() Activity.onRestart} function.
* Defined to mock the behavior of {@link `Activity#onRestart()` Activity.onRestart} function.
*/
protected void onRevisit() {
}

/**
* Called when a parent activity receives a new intent in its {@link Activity#onNewIntent(Intent)
* Called when a parent activity receives a new intent in its {@link `Activity#onNewIntent(Intent)`
* Activity.onNewIntent} function.
* Defined to mock the behavior of {@link Activity#onNewIntent(Intent) Activity.onNewIntent} function.
* Defined to mock the behavior of {@link `Activity#onNewIntent(Intent)` Activity.onNewIntent} function.
*/
protected void onNewIntent(Intent intent) {
}

/**
* Checks the status of the provided permissions, if a permission has been given, a callback
* function is called otherwise the said permission is requested.
*
* @param permissions The requested permissions.
* @param requestCode The request code passed in {@link #requestPermissions(String[], int)}.
*/
protected void askForPermission(String[] permissions, int requestCode) {
if (getActivity() == null || permissionListener == null) {
return;
}

if (getGrantedPermissionsCount(permissions) == permissions.length ||
(Build.VERSION.SDK_INT >= Build.VERSION_CODES.R &&
requestCode == PermissionsUtil.WRITE_STORAGE_PERMISSION_REQUEST)) {
permissionListener.onPermissionGranted(permissions, requestCode);

} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
requestCode == PermissionsUtil.READ_STORAGE_PERMISSION_REQUEST) {
PermissionsUtil.requestPermissions(requestCode,
new String[]{Manifest.permission.READ_MEDIA_IMAGES}, BaseFragment.this);

} else {
PermissionsUtil.requestPermissions(requestCode, permissions, BaseFragment.this);
}
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
if (grantResults.length > 0 && getGrantedPermissionsCount(permissions) == permissions.length) {
if (permissionListener != null) {
permissionListener.onPermissionGranted(permissions, requestCode);
}
} else {
// android.R.id.content gives you the root element of a view, without having to know its actual name/type/ID
// Ref: https://stackoverflow.com/questions/47666685/java-lang-illegalargumentexception-no-suitable-parent-found-from-the-given-view
Snackbar.make(getActivity().findViewById(android.R.id.content), getResources().getString(R.string.permission_not_granted), Snackbar.LENGTH_LONG).show();
if (permissionListener != null) {
permissionListener.onPermissionDenied(permissions, requestCode);
}
}

super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}

public int getGrantedPermissionsCount(String[] permissions) {
int grantedPermissionsCount = 0;
for (String permission : permissions) {
if (PermissionsUtil.checkPermissions(permission, requireActivity())) {
grantedPermissionsCount++;
}
}

return grantedPermissionsCount;
}

public void showCalendarRemovedSnackbar() {
SnackbarErrorNotification snackbarErrorNotification = new SnackbarErrorNotification(getView());
SnackbarErrorNotification snackbarErrorNotification = new SnackbarErrorNotification(requireView());

Check warning on line 104 in OpenEdXMobile/src/main/java/org/edx/mobile/base/BaseFragment.java

View check run for this annotation

Codecov / codecov/patch

OpenEdXMobile/src/main/java/org/edx/mobile/base/BaseFragment.java#L104

Added line #L104 was not covered by tests
snackbarErrorNotification.showError(R.string.message_after_course_calendar_removed,
0, R.string.label_close, SnackbarErrorNotification.COURSE_DATE_MESSAGE_DURATION, v -> snackbarErrorNotification.hideError());
}

public void showCalendarUpdatedSnackbar() {
SnackbarErrorNotification snackbarErrorNotification = new SnackbarErrorNotification(getView());
SnackbarErrorNotification snackbarErrorNotification = new SnackbarErrorNotification(requireView());

Check warning on line 110 in OpenEdXMobile/src/main/java/org/edx/mobile/base/BaseFragment.java

View check run for this annotation

Codecov / codecov/patch

OpenEdXMobile/src/main/java/org/edx/mobile/base/BaseFragment.java#L110

Added line #L110 was not covered by tests
snackbarErrorNotification.showError(R.string.message_after_course_calendar_updated,
0, R.string.label_close, SnackbarErrorNotification.COURSE_DATE_MESSAGE_DURATION, v -> snackbarErrorNotification.hideError());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
import android.view.ViewGroup;
import android.webkit.URLUtil;

import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

Expand All @@ -26,7 +28,6 @@
public class WebViewCourseInfoFragment extends BaseWebViewFragment
implements WebViewStatusListener {

private static final int LOG_IN_REQUEST_CODE = 42;
private static final String INSTANCE_COURSE_ID = "enrollCourseId";
private static final String INSTANCE_EMAIL_OPT_IN = "enrollEmailOptIn";

Expand All @@ -37,6 +38,13 @@ public class WebViewCourseInfoFragment extends BaseWebViewFragment

private FragmentWebviewBinding binding;

private final ActivityResultLauncher<Intent> loginRequestLauncher = registerForActivityResult(

Check warning on line 41 in OpenEdXMobile/src/main/java/org/edx/mobile/base/WebViewCourseInfoFragment.java

View check run for this annotation

Codecov / codecov/patch

OpenEdXMobile/src/main/java/org/edx/mobile/base/WebViewCourseInfoFragment.java#L41

Added line #L41 was not covered by tests
new ActivityResultContracts.StartActivityForResult(), result -> {
if (result.getResultCode() == Activity.RESULT_OK) {
defaultActionListener.onClickEnroll(lastClickEnrollCourseId, lastClickEnrollEmailOptIn);

Check warning on line 44 in OpenEdXMobile/src/main/java/org/edx/mobile/base/WebViewCourseInfoFragment.java

View check run for this annotation

Codecov / codecov/patch

OpenEdXMobile/src/main/java/org/edx/mobile/base/WebViewCourseInfoFragment.java#L44

Added line #L44 was not covered by tests
}
});

Check warning on line 46 in OpenEdXMobile/src/main/java/org/edx/mobile/base/WebViewCourseInfoFragment.java

View check run for this annotation

Codecov / codecov/patch

OpenEdXMobile/src/main/java/org/edx/mobile/base/WebViewCourseInfoFragment.java#L46

Added line #L46 was not covered by tests

@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
Expand All @@ -56,14 +64,14 @@ public void onViewCreated(View view, Bundle savedInstanceState) {
}

@Override
public void onSaveInstanceState(Bundle outState) {
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(INSTANCE_COURSE_ID, lastClickEnrollCourseId);
outState.putBoolean(INSTANCE_EMAIL_OPT_IN, lastClickEnrollEmailOptIn);
}

public void setWebViewActionListener() {
defaultActionListener = new DefaultActionListener(getActivity(), progressWheel,
defaultActionListener = new DefaultActionListener(requireActivity(), progressWheel,

Check warning on line 74 in OpenEdXMobile/src/main/java/org/edx/mobile/base/WebViewCourseInfoFragment.java

View check run for this annotation

Codecov / codecov/patch

OpenEdXMobile/src/main/java/org/edx/mobile/base/WebViewCourseInfoFragment.java#L74

Added line #L74 was not covered by tests
new DefaultActionListener.EnrollCallback() {
@Override
public void onResponse(@NonNull EnrolledCoursesResponse course) {
Expand All @@ -78,7 +86,7 @@ public void onFailure(@NonNull Throwable error) {
public void onUserNotLoggedIn(@NonNull String courseId, boolean emailOptIn) {
lastClickEnrollCourseId = courseId;
lastClickEnrollEmailOptIn = emailOptIn;
startActivityForResult(environment.getRouter().getRegisterIntent(), LOG_IN_REQUEST_CODE);
loginRequestLauncher.launch(environment.getRouter().getRegisterIntent());

Check warning on line 89 in OpenEdXMobile/src/main/java/org/edx/mobile/base/WebViewCourseInfoFragment.java

View check run for this annotation

Codecov / codecov/patch

OpenEdXMobile/src/main/java/org/edx/mobile/base/WebViewCourseInfoFragment.java#L89

Added line #L89 was not covered by tests
}
});
client.setActionListener(defaultActionListener);
Expand All @@ -89,16 +97,8 @@ public FullScreenErrorNotification initFullScreenErrorNotification() {
return new FullScreenErrorNotification(binding.webview);
}

@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == LOG_IN_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
defaultActionListener.onClickEnroll(lastClickEnrollCourseId, lastClickEnrollEmailOptIn);
}
}

/**
* Loads the given URL into {@link #webView}.
* Loads the given URL into [webview].
*
* @param url The URL to load.
*/
Expand All @@ -114,7 +114,7 @@ protected void loadUrl(@NonNull String url) {
* By default, all links will not be treated as external.
* Depends on host, as long as the links have same host, they are treated as non-external links.
*
* @return
* @return True to treat every link as an external link
*/
protected boolean isAllLinksExternal() {
return true;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package org.edx.mobile.event

import android.net.Uri
import android.content.Intent

data class FileSelectionEvent(val files: Array<Uri>?) : BaseEvent()
class FileSelectionEvent(val intent: Intent) : BaseEvent()

Check warning on line 5 in OpenEdXMobile/src/main/java/org/edx/mobile/event/FileSelectionEvent.kt

View check run for this annotation

Codecov / codecov/patch

OpenEdXMobile/src/main/java/org/edx/mobile/event/FileSelectionEvent.kt#L5

Added line #L5 was not covered by tests
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package org.edx.mobile.event

import android.net.Uri

class FileShareEvent(val files: Array<Uri>?) : BaseEvent()

Check warning on line 5 in OpenEdXMobile/src/main/java/org/edx/mobile/event/FileShareEvent.kt

View check run for this annotation

Codecov / codecov/patch

OpenEdXMobile/src/main/java/org/edx/mobile/event/FileShareEvent.kt#L5

Added line #L5 was not covered by tests
38 changes: 38 additions & 0 deletions OpenEdXMobile/src/main/java/org/edx/mobile/extenstion/BundleExt.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package org.edx.mobile.extenstion

import android.os.Build
import android.os.Bundle
import android.os.Parcelable
import java.io.Serializable

inline fun <reified T : Serializable> Bundle.serializable(key: String): T? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
getSerializable(key, T::class.java)
} else {
@Suppress("DEPRECATION")
getSerializable(key) as? T
}
}

@Throws(IllegalStateException::class)
inline fun <reified T : Serializable> Bundle?.serializableOrThrow(key: String): T {
return this?.serializable(key) ?: run {
throw IllegalStateException("No arguments available")
}
}

inline fun <reified T : Parcelable> Bundle.parcelable(key: String): T? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
getParcelable(key, T::class.java)
} else {
@Suppress("DEPRECATION")
getParcelable(key) as? T
}
}

@Throws(IllegalStateException::class)
inline fun <reified T : Parcelable> Bundle?.parcelableOrThrow(key: String): T {
return this?.parcelable(key) ?: run {
throw IllegalStateException("No arguments available")
}
}
14 changes: 14 additions & 0 deletions OpenEdXMobile/src/main/java/org/edx/mobile/extenstion/IntentExt.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.edx.mobile.extenstion

import android.content.Intent
import android.os.Build
import java.io.Serializable

inline fun <reified T : Serializable> Intent.serializable(key: String): T? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
getSerializableExtra(key, T::class.java)
} else {
@Suppress("DEPRECATION")
getSerializableExtra(key) as? T
}
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
package org.edx.mobile.extenstion

import android.text.Html
import android.text.SpannableString
import android.text.Spanned
import android.text.method.LinkMovementMethod
import android.text.style.URLSpan
import android.text.util.Linkify
import android.widget.TextView
import androidx.core.text.HtmlCompat

fun TextView.renderHtml(body: String) {
parseHtml(body)?.let { spannedHtml ->
val urlSpans = spannedHtml.getSpans(
0, spannedHtml.length,
URLSpan::class.java
)
this.autoLinkMask = Linkify.ALL
this.autoLinkMask = Linkify.EMAIL_ADDRESSES or Linkify.PHONE_NUMBERS or Linkify.WEB_URLS

Check warning on line 17 in OpenEdXMobile/src/main/java/org/edx/mobile/extenstion/TextViewExt.kt

View check run for this annotation

Codecov / codecov/patch

OpenEdXMobile/src/main/java/org/edx/mobile/extenstion/TextViewExt.kt#L17

Added line #L17 was not covered by tests
this.movementMethod = LinkMovementMethod.getInstance()
this.text = spannedHtml
val viewText = this.text as SpannableString
Expand All @@ -30,10 +30,10 @@ fun TextView.renderHtml(body: String) {
private fun parseHtml(html: String): Spanned? {
// If the HTML contains a paragraph at the end, there will be blank lines following the text
// Therefore, we need to trim the resulting CharSequence to remove those extra lines
return trim(Html.fromHtml(html)) as Spanned?
return trim(HtmlCompat.fromHtml(html, HtmlCompat.FROM_HTML_MODE_COMPACT)) as Spanned?

Check warning on line 33 in OpenEdXMobile/src/main/java/org/edx/mobile/extenstion/TextViewExt.kt

View check run for this annotation

Codecov / codecov/patch

OpenEdXMobile/src/main/java/org/edx/mobile/extenstion/TextViewExt.kt#L33

Added line #L33 was not covered by tests
}

private fun trim(s: CharSequence): CharSequence? {
private fun trim(s: CharSequence): CharSequence {
var start = 0
var end = s.length
while (start < end && Character.isWhitespace(s[start])) {
Expand Down
Loading

0 comments on commit e5c717f

Please sign in to comment.