Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend user session #2876

Draft
wants to merge 5 commits into
base: commcare_2.54
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import android.widget.TextView;
import android.widget.Toast;

import org.commcare.CommCareApplication;
import org.commcare.activities.components.FormEntryConstants;
import org.commcare.activities.components.FormLayoutHelpers;
import org.commcare.activities.components.FormNavigationController;
Expand Down Expand Up @@ -246,6 +247,10 @@ private void showView(QuestionsView next, AnimationType from, boolean animateLas

setupGroupLabel();
checkForOrientationRequirements();

if (questionsView.hasNonRecoverableWidgets()) {
CommCareApplication.instance().getSession().extendUserSessionIfNeeded();
}
}
Comment on lines +251 to 254
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would think a better place to do this would be when user take an action to start the unrecoverable action. For eg. for the audio recording widget we should only extend the session when user initiates the recording and do nothing if user decides to skip the recording widget present in the form. Also if user decies to attach a recording from the FileSystem (instead of live recording through CC) , the session should not be extended.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shubham1g5 I have raised a new PR with a new approach to this. On that approach, the extension happens during the widget changed event.


private void checkForOrientationRequirements() {
Expand Down
91 changes: 55 additions & 36 deletions app/src/org/commcare/services/CommCareSessionService.java
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ public class CommCareSessionService extends Service {
*/
public static final ReentrantLock sessionAliveLock = new ReentrantLock();

/**
* 2h time in Milliseconds to extend the session if needed
*/
private static final long SESSION_EXTENSION_TIME = 2 * 60 * 60 * 1000;

private Timer maintenanceTimer;
private CipherPool pool;

Expand Down Expand Up @@ -202,44 +207,9 @@ public IBinder onBind(Intent intent) {
*/
@SuppressLint("UnspecifiedImmutableFlag")
public void showLoggedInNotification(@Nullable User user) {
//We always want this click to simply bring the live stack back to the top
Intent callable = new Intent(this, DispatchActivity.class);
callable.setAction("android.intent.action.MAIN");
callable.addCategory("android.intent.category.LAUNCHER");

// The PendingIntent to launch our activity if the user selects this notification
PendingIntent contentIntent;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
contentIntent = PendingIntent.getActivity(this, 0, callable, PendingIntent.FLAG_IMMUTABLE);
else
contentIntent = PendingIntent.getActivity(this, 0, callable, 0);

String notificationText;
if (AppUtils.getInstalledAppRecords().size() > 1) {
try {
notificationText = Localization.get("notification.logged.in",
new String[]{Localization.get("app.display.name")});
} catch (NoLocalizedTextException e) {
notificationText = getString(NOTIFICATION);
}
} else {
notificationText = getString(NOTIFICATION);
}

// Set the icon, scrolling text and timestamp
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, CommCareNoficationManager.NOTIFICATION_CHANNEL_ERRORS_ID)
.setContentTitle(notificationText)
.setSmallIcon(R.drawable.notification)
.setContentIntent(contentIntent);

if (user != null) {
String contentText = "Session Expires: " + DateFormat.format("MMM dd h:mmaa", sessionExpireDate);
notificationBuilder.setContentText(contentText);
}

// Send the notification. This will cause error messages if CommCare doesn't have
// permission to post notifications
this.startForeground(NOTIFICATION, notificationBuilder.build());
this.startForeground(NOTIFICATION, createSessionNotification());
}

/**
Expand Down Expand Up @@ -709,4 +679,53 @@ public void hideInAppUpdate() {
public boolean shouldShowInAppUpdate() {
return this.showInAppUpdate;
}

public void extendUserSessionIfNeeded(){
long currentTime = new Date().getTime();

if (sessionExpireDate.getTime() < currentTime + SESSION_EXTENSION_TIME) {
sessionExpireDate.setTime(sessionExpireDate.getTime() + SESSION_EXTENSION_TIME);
sessionLength += SESSION_EXTENSION_TIME;

mNM.notify(NOTIFICATION, createSessionNotification());
}
}

private Notification createSessionNotification(){
//We always want this click to simply bring the live stack back to the top
Intent callable = new Intent(this, DispatchActivity.class);
callable.setAction("android.intent.action.MAIN");
callable.addCategory("android.intent.category.LAUNCHER");

// The PendingIntent to launch our activity if the user selects this notification
PendingIntent contentIntent;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
contentIntent = PendingIntent.getActivity(this, 0, callable, PendingIntent.FLAG_IMMUTABLE);
else
contentIntent = PendingIntent.getActivity(this, 0, callable, 0);

String notificationText;
if (AppUtils.getInstalledAppRecords().size() > 1) {
try {
notificationText = Localization.get("notification.logged.in",
new String[]{Localization.get("app.display.name")});
} catch (NoLocalizedTextException e) {
notificationText = getString(NOTIFICATION);
}
} else {
notificationText = getString(NOTIFICATION);
}

// Set the icon, scrolling text and timestamp
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, CommCareNoficationManager.NOTIFICATION_CHANNEL_ERRORS_ID)
.setContentTitle(notificationText)
.setSmallIcon(R.drawable.notification)
.setContentIntent(contentIntent);

if (user != null) {
String contentText = "Session Expires: " + DateFormat.format("MMM dd h:mmaa", sessionExpireDate);
notificationBuilder.setContentText(contentText);
}
return notificationBuilder.build();
}
}
7 changes: 7 additions & 0 deletions app/src/org/commcare/views/QuestionsView.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ public class QuestionsView extends ScrollView
*/
private static final boolean SEPERATORS_ENABLED = false;

private boolean containsNonRecoverableWidgets = false;

public QuestionsView(Context context, BlockingActionsManager blockingActionsManager) {
super(context);
mQuestionFontsize = FormEntryPreferences.getQuestionFontSize();
Expand Down Expand Up @@ -137,6 +139,7 @@ public QuestionsView(Context context, FormEntryPrompt[] questionPrompts,
mView.addView(qw, mLayout);

qw.setChangedListeners(this, blockingActionsManager);
containsNonRecoverableWidgets = containsNonRecoverableWidgets || qw.isNonRecoverable();
}

markLastStringWidget();
Expand Down Expand Up @@ -496,4 +499,8 @@ public CompoundIntentList getAggregateIntentCallout() {
}
return compoundedCallout;
}

public boolean hasNonRecoverableWidgets(){
return containsNonRecoverableWidgets;
}
}
3 changes: 1 addition & 2 deletions app/src/org/commcare/views/widgets/MediaWidget.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public abstract class MediaWidget extends QuestionWidget {

public MediaWidget(Context context, FormEntryPrompt prompt,
PendingCalloutInterface pendingCalloutInterface) {
super(context, prompt);
super(context, prompt, true);

this.pendingCalloutInterface = pendingCalloutInterface;

Expand All @@ -80,7 +80,6 @@ public MediaWidget(Context context, FormEntryPrompt prompt,
setupLayout();
}


@Override
protected void onVisibilityChanged(@NonNull View changedView, int visibility) {
super.onVisibilityChanged(changedView, visibility);
Expand Down
16 changes: 14 additions & 2 deletions app/src/org/commcare/views/widgets/QuestionWidget.java
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ public abstract class QuestionWidget extends LinearLayout implements QuestionExt
private ShrinkingTextView mHintText;
private View warningView;

// This signals that the widget can't be recovered after a session expiration
protected boolean mNonRecoverable;

//Whether this question widget needs to request focus on
//its next draw, due to a new element having been added (which couldn't have
//requested focus yet due to having not been layed out)
Expand All @@ -103,13 +106,18 @@ public abstract class QuestionWidget extends LinearLayout implements QuestionExt
private LinearLayout compactLayout;

public QuestionWidget(Context context, FormEntryPrompt p) {
this(context, p, false);
this(context, p, false, false);
}

public QuestionWidget(Context context, FormEntryPrompt p, boolean nonRecoverable) {
this(context, p, false, nonRecoverable);
}

public QuestionWidget(Context context, FormEntryPrompt p, boolean inCompactGroup) {
public QuestionWidget(Context context, FormEntryPrompt p, boolean inCompactGroup, boolean nonRecoverable) {
super(context);
mPrompt = p;
mCompact = inCompactGroup;
mNonRecoverable = nonRecoverable;

//this is pretty sketch but is the only way to make the required background to work trivially for now
this.setClipToPadding(false);
Expand All @@ -135,6 +143,10 @@ public QuestionWidget(Context context, FormEntryPrompt p, boolean inCompactGroup
}
}

public boolean isNonRecoverable(){
return mNonRecoverable;
}

protected void acceptFocus() {
}

Expand Down