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

Show notifications for replied messages and handle oreo quick reply actions #175

Merged
merged 30 commits into from
Apr 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
1d0f83f
handle oreo quick reply actions
spuday90 Mar 7, 2021
22ebc53
bugfix avoid duplicate reply if notification is just updated with our…
spuday90 Mar 8, 2021
f636a62
add internal logging to debug reply failing on some devices
spuday90 Mar 8, 2021
a9a7203
refactor remove unnecessary remoteinput key check, as remoteInput is …
spuday90 Mar 8, 2021
3b1a322
read/write logs from app directory which does not require any storage…
spuday90 Mar 10, 2021
fffac0f
add share logs option in settings
spuday90 Mar 10, 2021
510dfa6
add share logs via email option, add content provider in manifest to …
spuday90 Mar 10, 2021
2f122c5
add notification dismiss on auto reply switch
spuday90 Mar 16, 2021
d300de0
dismiss notification if based on user pref
spuday90 Mar 16, 2021
35481e4
Merge branch 'main' into issue/134/autoreply-not-working-onfew-devices
adeekshith Mar 16, 2021
26c6665
resolve merge conflict of duplicate resources
spuday90 Mar 24, 2021
53562d8
Revert "dismiss notification if based on user pref"
spuday90 Mar 24, 2021
a93cac9
Revert "add notification dismiss on auto reply switch"
spuday90 Mar 24, 2021
28b9703
add switch for show notifications in settings
spuday90 Mar 24, 2021
3d62825
show notification for every replied messages one group per each messa…
spuday90 Mar 31, 2021
d612d5e
show notification if preference is enabled
spuday90 Mar 31, 2021
ed692ae
Merge branch 'main' of https://github.com/adeekshith/watomatic into i…
spuday90 Apr 5, 2021
5ab4a21
refactor remove logging code
spuday90 Apr 5, 2021
4c037b3
change notification priority to default
spuday90 Apr 6, 2021
e26b538
dismiss notification after reply
spuday90 Apr 6, 2021
500311f
bugfix donot reply for auto-replied message, this prevents 2 replies …
spuday90 Apr 8, 2021
d910733
launch corresponding app when notification is clicked
spuday90 Apr 9, 2021
54269dc
bugfix flag should reset to false
spuday90 Apr 9, 2021
4f5f0fc
remove onNotificationRemoved callback
spuday90 Apr 9, 2021
000f1a2
add logic to reset summary shown flag
spuday90 Apr 9, 2021
122e750
add logic to reset notification summary shown flag
spuday90 Apr 9, 2021
30ec2fd
Merge branch 'issue/134/autoreply-not-working-onfew-devices' of https…
spuday90 Apr 9, 2021
b9c0feb
append beta tag for show notification settings title for devices less…
spuday90 Apr 10, 2021
19732ef
Mark debug logs strings as non translatable
adeekshith Apr 13, 2021
4fb19aa
Change settings key from pref_show_notification to pref_show_notifica…
adeekshith Apr 13, 2021
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
8 changes: 8 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.parishod.watomatic">


<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
Expand Down Expand Up @@ -38,6 +39,12 @@
>
</activity>

<activity
android:name="com.parishod.watomatic.activity.notification.NotificationIntentActivity"
android:launchMode="singleTop"
>
</activity>

<service
android:name="com.parishod.watomatic.NotificationService"
android:label="@string/service_name"
Expand All @@ -46,6 +53,7 @@
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>

</application>

</manifest>
49 changes: 47 additions & 2 deletions app/src/main/java/com/parishod/watomatic/NotificationService.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import com.parishod.watomatic.model.logs.MessageLog;
import com.parishod.watomatic.model.logs.MessageLogsDB;
import com.parishod.watomatic.model.preferences.PreferencesManager;
import com.parishod.watomatic.model.utils.NotificationHelper;

import java.util.ArrayList;
import java.util.List;
Expand Down Expand Up @@ -58,7 +59,12 @@ public int onStartCommand(Intent intent, int flags, int startId) {
}

private void sendReply(StatusBarNotification sbn) {
NotificationWear notificationWear = extractWearNotification(sbn);
NotificationWear notificationWear;
if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O){
notificationWear = extractQuickReplyNotification(sbn);
}else{
notificationWear = extractWearNotification(sbn);
}
// Possibly transient or non-user notification from WhatsApp like
// "Checking for new messages" or "WhatsApp web is Active"
if (notificationWear.getRemoteInputs().isEmpty()) { return;}
Expand All @@ -84,12 +90,46 @@ private void sendReply(StatusBarNotification sbn) {
if (notificationWear.getPendingIntent() != null) {
logReply(sbn);
notificationWear.getPendingIntent().send(this, 0, localIntent);
if(PreferencesManager.getPreferencesInstance(this).isShowNotificationEnabled()) {
NotificationHelper.getInstance(getApplicationContext()).sendNotification(sbn.getNotification().extras.getString("android.title"), sbn.getNotification().extras.getString("android.text"), sbn.getPackageName());
}
cancelNotification(sbn.getKey());
}
} catch (PendingIntent.CanceledException e) {
Log.e(TAG, "replyToLastNotification error: " + e.getLocalizedMessage());
}
}

private static NotificationWear extractQuickReplyNotification(StatusBarNotification sbn) {
Notification notification = sbn.getNotification();
List<NotificationCompat.Action> actions = new ArrayList<>();
for(int i = 0; i < NotificationCompat.getActionCount(notification); i++) {
NotificationCompat.Action action = NotificationCompat.getAction(notification, i);
actions.add(action);
}
List<RemoteInput> remoteInputs = new ArrayList<>();
PendingIntent pendingIntent = null;
for(NotificationCompat.Action act : actions) {
if(act != null && act.getRemoteInputs() != null) {
for(int x = 0; x < act.getRemoteInputs().length; x++) {
RemoteInput remoteInput = act.getRemoteInputs()[x];
remoteInputs.add(remoteInput);
pendingIntent = act.actionIntent;
}
}
}

return new NotificationWear(
sbn.getPackageName(),
pendingIntent,
remoteInputs,
null,
sbn.getNotification().extras,
sbn.getTag(),
UUID.randomUUID().toString()
);
}

//unused for now
private void getDetailsOfNotification(RemoteInput remoteInput) {
//Some more details of RemoteInput... no idea what for but maybe it will be useful at some point
Expand Down Expand Up @@ -144,9 +184,14 @@ private boolean isSupportedPackage(StatusBarNotification sbn) {
}

private boolean canSendReplyNow(StatusBarNotification sbn){
String title = getTitle(sbn);
String selfDisplayName = sbn.getNotification().extras.getString("android.selfDisplayName");
if(title != null && selfDisplayName != null && title.equalsIgnoreCase(selfDisplayName)){ //to protect double reply in case where if notification is not dismissed and existing notification is updated with our reply
return false;
}
messageLogsDB = MessageLogsDB.getInstance(getApplicationContext());
long timeDelay = PreferencesManager.getPreferencesInstance(this).getAutoReplyDelay();
return (System.currentTimeMillis() - messageLogsDB.logsDao().getLastReplyTimeStamp(getTitle(sbn), sbn.getPackageName()) >= max(timeDelay, DELAY_BETWEEN_REPLY_IN_MILLISEC));
return (System.currentTimeMillis() - messageLogsDB.logsDao().getLastReplyTimeStamp(title, sbn.getPackageName()) >= max(timeDelay, DELAY_BETWEEN_REPLY_IN_MILLISEC));
}

private void logReply(StatusBarNotification sbn){
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import android.os.Bundle;


import com.parishod.watomatic.R;
import com.parishod.watomatic.activity.BaseActivity;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.parishod.watomatic.activity.notification;

import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;

import com.parishod.watomatic.R;
import com.parishod.watomatic.activity.BaseActivity;
import com.parishod.watomatic.model.utils.NotificationHelper;

public class NotificationIntentActivity extends BaseActivity {

private static final String TAG = NotificationIntentActivity.class.getSimpleName();

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.notification_intent_activity); //dummy layout

if (savedInstanceState == null) {
Bundle extras = getIntent().getExtras();
if (extras != null && extras.getString("package") != null)
{
String packageName = extras.getString("package");
NotificationHelper.getInstance(getApplicationContext()).markNotificationDismissed(packageName);
launchApp(packageName);
}
}
}

private void launchApp(String packageName){
Intent intent;
PackageManager pm = getPackageManager();

intent = pm.getLaunchIntentForPackage(packageName);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);

startActivity(intent);

finish();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@
import androidx.preference.ListPreference;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.SwitchPreference;

import com.parishod.watomatic.R;
import com.parishod.watomatic.activity.main.MainActivity;
import com.parishod.watomatic.model.preferences.PreferencesManager;

public class SettingsFragment extends PreferenceFragmentCompat {
private ListPreference languagePref;
private SwitchPreference showNotificationPref;

@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
Expand All @@ -31,6 +33,11 @@ public boolean onPreferenceChange(Preference preference, Object newValue) {
return true;
}
});

showNotificationPref = findPreference(getString(R.string.pref_show_notification_replied_msg));
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M) {
showNotificationPref.setTitle(getString(R.string.show_notification_label) + "(Beta)");
}
}

private void restartApp() {
Expand Down
64 changes: 64 additions & 0 deletions app/src/main/java/com/parishod/watomatic/model/logs/AppLogs.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package com.parishod.watomatic.model.logs;

import android.content.Context;

import com.parishod.watomatic.R;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;

public class AppLogs {
private static AppLogs _instance;
private Context thisContext;
private String logsFileName;
public AppLogs(Context context){
thisContext = context;
logsFileName = thisContext.getString(R.string.app_logs_file_name);
}

public static AppLogs getInstance(Context context){
if(_instance == null){
_instance = new AppLogs(context);
}
return _instance;
}

public void writeToSDFile(String msg){
FileOutputStream fos = null;
try {
fos = thisContext.openFileOutput(logsFileName, Context.MODE_APPEND);
fos.write(msg.getBytes());
} catch (IOException e) {
e.printStackTrace();
}finally {
if (fos!= null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

public void readFile(){
try {
FileInputStream in = thisContext.openFileInput(logsFileName);
InputStreamReader inputStreamReader = new InputStreamReader(in);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
StringBuilder sb = new StringBuilder();
String line;
while ((line = bufferedReader.readLine()) != null) {
sb.append(line);
}
inputStreamReader.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public class PreferencesManager {
private final String KEY_AUTO_REPLY_THROTTLE_TIME_MS = "pref_auto_reply_throttle_time_ms";
private final String KEY_SELECTED_APPS_ARR = "pref_selected_apps_arr";
private final String KEY_IS_APPEND_WATOMATIC_ATTRIBUTION = "pref_is_append_watomatic_attribution";
private String KEY_IS_SHOW_NOTIFICATIONS_ENABLED;
private String KEY_SELECTED_APP_LANGUAGE;
private static PreferencesManager _instance;
private SharedPreferences _sharedPrefs;
Expand All @@ -49,8 +50,9 @@ public static PreferencesManager getPreferencesInstance(Context context){
* or app upgrade, etc.
*/
private void init () {
// This key is used at more than one places so use key from string resource
// Use key from string resource
KEY_SELECTED_APP_LANGUAGE = thisAppContext.getString(R.string.key_pref_app_language);
KEY_IS_SHOW_NOTIFICATIONS_ENABLED = thisAppContext.getString(R.string.pref_show_notification_replied_msg);

// For new installs, enable all the supported apps
if (Constants.BETA_FACEBOOK_SUPPORT_ENABLED) {
Expand Down Expand Up @@ -186,4 +188,8 @@ public Locale getSelectedLocale () {
? new Locale(languageSplit[0], languageSplit[1])
: new Locale(languageSplit[0]);
}

public boolean isShowNotificationEnabled(){
return _sharedPrefs.getBoolean(KEY_IS_SHOW_NOTIFICATIONS_ENABLED,false);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ object Constants {
const val PERMISSION_DIALOG_DENIED_MSG = "permission_dialog_denied_msg"
const val PERMISSION_DIALOG_DENIED = "permission_dialog_denied"
const val LOGS_DB_NAME = "logs_messages_db"
const val NOTIFICATION_CHANNEL_ID = "watomatic"
const val NOTIFICATION_CHANNEL_NAME = "watomatic_channel"

// Beta feature flags
const val BETA_FACEBOOK_SUPPORT_ENABLED = true
Expand Down
Loading