├── settings.gradle
├── app
├── src
│ └── main
│ │ ├── res
│ │ ├── drawable
│ │ │ ├── icon.png
│ │ │ ├── icon_256x256.png
│ │ │ ├── icon_512x512.png
│ │ │ └── black_white_gradient.xml
│ │ ├── xml
│ │ │ └── provider_paths.xml
│ │ ├── values
│ │ │ ├── styles.xml
│ │ │ └── strings.xml
│ │ ├── menu
│ │ │ └── app_menu.xml
│ │ ├── color
│ │ │ └── archives_list_item.xml
│ │ └── layout
│ │ │ ├── fatal_message.xml
│ │ │ ├── progress_view.xml
│ │ │ ├── archives_list_item.xml
│ │ │ ├── result.xml
│ │ │ ├── archives.xml
│ │ │ └── main.xml
│ │ ├── assets
│ │ ├── index.head.snippet.html
│ │ ├── EULA
│ │ ├── acb_style.css
│ │ ├── index.body.html
│ │ ├── index.html
│ │ └── acb_script.js
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ └── net
│ │ └── ugorji
│ │ └── android
│ │ └── conversationbackup
│ │ ├── BaseCBActivity.java
│ │ ├── ArchivesActivity.java
│ │ ├── HomeActivity.java
│ │ ├── Helper.java
│ │ └── ProcessingService.java
└── build.gradle
├── README.md
└── PRIVACY.md
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ugorji/android-conversation-backup/HEAD/app/src/main/res/drawable/icon.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon_256x256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ugorji/android-conversation-backup/HEAD/app/src/main/res/drawable/icon_256x256.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon_512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ugorji/android-conversation-backup/HEAD/app/src/main/res/drawable/icon_512x512.png
--------------------------------------------------------------------------------
/app/src/main/res/xml/provider_paths.xml:
--------------------------------------------------------------------------------
1 |
2 |
33 | Thank you for using Android Conversation Backup. If you like it, 34 | please let us know at 35 | Android Market / Google Play (click User Reviews). 36 | More information is available on 37 | our web site. 38 | 39 | -- Ugorji 40 |41 | 42 | -------------------------------------------------------------------------------- /app/src/main/res/layout/result.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 |
49 | Thank you for using Android Conversation Backup. If you like it, 50 | please let us know at 51 | Android Market / Google Play (click User Reviews). 52 | More information is available on 53 | our web site. 54 | 55 | -- Ugorji 56 |57 | 58 | 59 | -------------------------------------------------------------------------------- /app/src/main/res/layout/archives.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 |
| Color Key | |
|---|---|
| Call Type | Message |
| N/A | SMS and From Other Person |
| Run Parameters | |
|---|---|
| " + key + " | " + s(val) + " |
| ID | Name | Number | Date | Duration | " + 41 | "Type |
|---|---|---|---|---|---|
| " + value.id + 49 | " | " + s(value.name) + 50 | " | " + s(value.number) + 51 | " | " + value.datetime + 52 | " | " + value.duration + 53 | " | " + value.type + 54 | " |
| ID | Name | Number | Date | " + 67 | "Subject | Text | Entries | Type |
|---|
52 | * - when u click: process() 53 | * - savePrefs (so buttons can be loaded back) 54 | * - set Permissions struct to a blank "unset" one. 55 | * - check permissions to write to external media. 56 | * - if granted and u can make dir, call process2() 57 | * - process2() 58 | * - check for permissions, and decide what to do 59 | * - each permission check should update a field in HomeActivity: 60 | * - not set (check for permissions) 61 | * - set (permission defined) 62 | * - consequently, use a Boolean object. 63 | * - each time we check an 'unset' permission, we re-call process again. 64 | * - once all perms set, then we continue to finish process 65 | * - create bundle and contain the permissions accepted: sms, mms, calls, delete bools set (1=true) 66 | * - update the preferences based on that??? 67 | * - 68 | * - if none of the required backup perms are defined, 69 | * - showDialog and return. 70 | * - else 71 | * - call processing service intent, with a Bundle set 72 | * 73 | *- ProcessingService 74 | * - look at bundle to determine what to do (not preferences) 75 | * - recreate Permissions 76 | * 77 | * After each call to check for permissions, call process2() again. 78 | * 79 | * onRequestPermissionsResult will always now just call: process2() 80 | * - same requestCode: PROCESSING 81 | * - set field in Permissions struct 82 | * - call process2() 83 | * 84 | * Delete after backup: 85 | * - delete call records 86 | * - cannot delete SMS messages 87 | * 88 | *89 | */ 90 | public class HomeActivity extends BaseCBActivity { 91 | 92 | private static final int PERMISSION_PROCESSING = 301; 93 | 94 | private static class Perms { 95 | Boolean sms; 96 | // Boolean mms; 97 | Boolean calls; 98 | Boolean contacts; 99 | } 100 | 101 | // private String progressMessage = ""; 102 | private static final String TAG = HomeActivity.class.getSimpleName(); 103 | 104 | private Helper.MyDialogFrag dfEula = new Helper.MyDialogFrag(); 105 | private Helper.MyDialogFrag dfProcess = new Helper.MyDialogFrag(); 106 | private Helper.MyDialogFrag dfProgress = new Helper.MyDialogFrag(); 107 | 108 | private EditText specNumBackupEditView; 109 | 110 | private BroadcastReceiver progressReceiver; 111 | 112 | private Perms rperms; 113 | private Perms wperms; 114 | 115 | @Override 116 | protected void afterFinish() { 117 | NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); 118 | nm.cancel(Helper.PROCESSING_NOTIFICATION_ID); 119 | // Intent exi = new Intent(ResultActivity.this, HomeActivity.class); 120 | // exi.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); 121 | // exi.putExtra(Helper.EXIT_ACTION, true); 122 | // startActivity(exi); 123 | } 124 | 125 | @Override 126 | protected void handleIntent(Intent intent) { 127 | // add support for app linking - so google search drives traffic here 128 | String appLinkAction = intent.getAction(); 129 | Uri appLinkData = intent.getData(); 130 | if (Intent.ACTION_VIEW.equals(appLinkAction) && appLinkData != null){ 131 | // String recipeId = appLinkData.getLastPathSegment(); 132 | // Uri appData = Uri.parse("content://com.recipe_app/recipe/").buildUpon().appendPath(recipeId).build(); 133 | // showRecipe(appData); 134 | } 135 | 136 | } 137 | 138 | @Override 139 | protected void onCreateBaseCallback() { 140 | dfEula.tag = "eula"; 141 | dfEula.titleId = R.string.eula_title; 142 | dfEula.finishId = R.string.eula_refuse; 143 | dfEula.actionMsgId = R.string.eula_accept; 144 | try { 145 | dfEula.msg = Helper.read(new InputStreamReader(getAssets().open(Helper.ASSET_EULA)), true); 146 | } catch (Exception e) { 147 | Log.e(TAG, "Error loading eula", e); 148 | throw new RuntimeException(e); 149 | } 150 | dfEula.setMyAction((dialog1, id1) -> { 151 | SharedPreferences sharedPreferences = Helper.getPreferences(HomeActivity.this); 152 | sharedPreferences.edit().putString(Helper.PREFERENCE_EULA_ACCEPTED, BuildConfig.VERSION_NAME).commit(); 153 | }); 154 | 155 | dfProcess.tag = "processing"; 156 | dfProcess.msgId = R.string.start_processing_prompt_message; 157 | dfProcess.cancelId = R.string.prompt_no; 158 | dfProcess.actionMsgId = R.string.prompt_yes; 159 | dfProcess.setMyAction((dialog1, id1) -> HomeActivity.this.process()); 160 | 161 | // dfProgress.msgId = R.string.progress_message_default; 162 | dfProgress.tag = "progress"; 163 | dfProgress.dismissId = R.string.prompt_ok; 164 | dfProgress.msgView = LayoutInflater.from(this).inflate(R.layout.progress_view, null); 165 | 166 | progressReceiver = 167 | new BroadcastReceiver() { 168 | @Override 169 | public void onReceive(Context context, Intent intent) { 170 | String mAction = intent.getAction(); 171 | if (mAction != null && mAction.equals(Helper.UPDATE_PROGRESS_ACTION)) { 172 | updateProgress(intent); 173 | } 174 | } 175 | }; 176 | 177 | if (Helper.SAFETY_DEV_MODE) { 178 | Helper.debugAllContentResolvers(this); 179 | Helper.getPreferences(this) 180 | .edit() 181 | .putString(Helper.PREFERENCE_EULA_ACCEPTED, BuildConfig.VERSION_NAME) 182 | .commit(); 183 | } 184 | 185 | SharedPreferences sharedPreferences = Helper.getPreferences(this); 186 | if (!sharedPreferences 187 | .getString(Helper.PREFERENCE_EULA_ACCEPTED, "-") 188 | .equals(BuildConfig.VERSION_NAME)) { 189 | showDialog(dfEula); 190 | } 191 | setContentView(R.layout.main); 192 | 193 | specNumBackupEditView = (EditText) findViewById(R.id.specific_numbers_to_backup_edit); 194 | exitAppButton = (Button) findViewById(R.id.exit_app); 195 | Button processButton = (Button) findViewById(R.id.confirm); 196 | processButton.setOnClickListener(view -> showDialog(dfProcess)); 197 | 198 | final Button selectContactButton = (Button) findViewById(R.id.select_contact); 199 | selectContactButton.setOnClickListener(view -> { 200 | Intent intent = new Intent(Intent.ACTION_PICK, Contacts.CONTENT_URI); 201 | startActivityForResult(intent, Helper.SELECT_CONTACT_REQUEST); 202 | }); 203 | 204 | resetState(sharedPreferences, R.id.backup_all_numbers, "backup_all_numbers", false); 205 | resetState(sharedPreferences, R.id.backup_messages, "backup_messages", true); 206 | resetState(sharedPreferences, R.id.backup_mms_attachments, "backup_mms_attachments", true); 207 | resetState(sharedPreferences, R.id.backup_call_records, "backup_call_records", true); 208 | resetState(sharedPreferences, R.id.delete_after_backup, "delete_after_backup", false); 209 | resetState(sharedPreferences, R.id.share_archive, "share_archive", true); 210 | resetState(sharedPreferences, R.id.random_question, "random_question", true); 211 | resetState(sharedPreferences, R.id.specific_numbers_to_backup_edit, "specific_numbers_to_backup_edit", false); 212 | 213 | final CheckBox backupAllCB = (CheckBox) findViewById(R.id.backup_all_numbers); 214 | final EditText specNumBackupEditView2 = specNumBackupEditView; 215 | specNumBackupEditView.setEnabled(!backupAllCB.isChecked()); 216 | selectContactButton.setEnabled(!backupAllCB.isChecked()); 217 | backupAllCB.setOnClickListener(view -> { 218 | specNumBackupEditView2.setEnabled(!backupAllCB.isChecked()); 219 | selectContactButton.setEnabled(!backupAllCB.isChecked()); 220 | }); 221 | 222 | registerReceiver(progressReceiver, Helper.PROGRESS_INTENT_FILTER); 223 | } 224 | 225 | // public void onSaveInstanceState(Bundle savedInstanceState) { 226 | // saveState(savedInstanceState, R.id.backup_all_numbers, "backup_all_numbers"); 227 | // super.onSaveInstanceState(savedInstanceState); 228 | // } 229 | 230 | private Dialog permsOkDialog(String message) { 231 | AlertDialog.Builder builder = 232 | new AlertDialog.Builder(this) 233 | .setTitle(R.string.perms_title) 234 | .setMessage(message) 235 | .setCancelable(false) 236 | .setNeutralButton(getString(R.string.prompt_yes), Helper.DismissDialogOnClick); 237 | Dialog dialog = builder.create(); 238 | return dialog; 239 | } 240 | 241 | @Override 242 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 243 | // Log.d(TAG, "onActivityResult: requestCode: " + requestCode + ", resultCode: " + resultCode); 244 | if (requestCode == Helper.SEND_ARCHIVE_REQUEST) { 245 | String longMsg = 246 | (resultCode == RESULT_OK 247 | ? getString(R.string.archive_shared_success) 248 | : getString(R.string.archive_shared_fail)); 249 | // For some reason, when sending using email (gmail), we always got RESULT_CANCELLED 250 | // even when the email was successfully sent. 251 | // see https://stackoverflow.com/questions/17102578/detect-email-sent-or-not-in-onactivity-result 252 | // So don't be specific 253 | longMsg = getString(R.string.archive_shared); 254 | updateProgress(longMsg, null, 100, false); 255 | } else if (requestCode == Helper.SELECT_CONTACT_REQUEST) { 256 | if (resultCode == RESULT_OK) addSelectedContactNumbers(data); 257 | } 258 | } 259 | 260 | @Override 261 | public void onRequestPermissionsResult( 262 | int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { 263 | boolean status = true; 264 | String perm = null; 265 | for(int i = 0; i < permissions.length; i++) { 266 | perm = permissions[i]; 267 | if (grantResults[i] != PackageManager.PERMISSION_GRANTED) { 268 | status = false; 269 | } 270 | } 271 | switch (requestCode) { 272 | case PERMISSION_PROCESSING: 273 | switch(perm) { 274 | case Manifest.permission.READ_SMS: 275 | rperms.sms = status; 276 | // rperms.mms = status; 277 | break; 278 | case Manifest.permission.READ_CALL_LOG: 279 | rperms.calls = status; 280 | break; 281 | case Manifest.permission.READ_PHONE_STATE: 282 | break; 283 | case Manifest.permission.READ_CONTACTS: 284 | rperms.contacts = status; 285 | break; 286 | // case Manifest.permission.WRITE_SMS: 287 | // wperms.sms = status; 288 | // break; 289 | case Manifest.permission.WRITE_CONTACTS: 290 | wperms.contacts = status; 291 | break; 292 | case Manifest.permission.WRITE_CALL_LOG: 293 | wperms.calls = status; 294 | break; 295 | } 296 | process3(); 297 | break; 298 | } 299 | } 300 | 301 | private void addSelectedContactNumbers(Intent data) { 302 | Log.d(TAG, "addSelectedContactNumbers"); 303 | StringBuilder sb = new StringBuilder(); 304 | Set