|  |  | @ -16,395 +16,317 @@ | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | package net.micode.notes.tool; |  |  |  | package net.micode.notes.tool; | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | import android.content.Context; |  |  |  | import android.content.ContentProviderOperation; | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | import android.content.ContentProviderResult; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | import android.content.ContentResolver; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | import android.content.ContentUris; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | import android.content.ContentValues; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | import android.content.OperationApplicationException; | 
			
		
	
		
		
			
				
					
					|  |  |  | import android.database.Cursor; |  |  |  | import android.database.Cursor; | 
			
		
	
		
		
			
				
					
					|  |  |  | import android.os.Build; |  |  |  | import android.os.RemoteException; | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | import android.os.Environment; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | import android.text.TextUtils; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | import android.text.format.DateFormat; |  |  |  |  | 
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | import android.util.Log; |  |  |  | import android.util.Log; | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | import net.micode.notes.R; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | import net.micode.notes.data.Notes; |  |  |  | import net.micode.notes.data.Notes; | 
			
		
	
		
		
			
				
					
					|  |  |  | import net.micode.notes.data.Notes.DataColumns; |  |  |  | import net.micode.notes.data.Notes.CallNote; | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | import net.micode.notes.data.Notes.DataConstants; |  |  |  |  | 
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | import net.micode.notes.data.Notes.NoteColumns; |  |  |  | import net.micode.notes.data.Notes.NoteColumns; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | import net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute; | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | import java.io.BufferedWriter; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | import java.io.File; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | import java.io.FileOutputStream; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | import java.io.IOException; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | import java.io.OutputStreamWriter; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | import java.nio.charset.StandardCharsets; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | import java.util.ArrayList; |  |  |  | import java.util.ArrayList; | 
			
		
	
		
		
			
				
					
					|  |  |  | import java.util.List; |  |  |  | import java.util.HashSet; | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | public class BackupUtils { |  |  |  | public class DataUtils { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     private static final String TAG = "BackupUtils"; |  |  |  |     public static final String TAG = "DataUtils"; | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     // Singleton stuff
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     private static BackupUtils sInstance; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     public static synchronized BackupUtils getInstance(Context context) { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         if (sInstance == null) { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             sInstance = new BackupUtils(context); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         } |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         return sInstance; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |  | 
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |      |  |  |  |      | 
			
		
	
		
		
			
				
					
					|  |  |  |     /** |  |  |  |     /** | 
			
		
	
		
		
			
				
					
					|  |  |  |      * Following states are signs to represents backup or restore |  |  |  |      * 批量删除笔记 | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |      * status |  |  |  |  | 
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |      */ |  |  |  |      */ | 
			
		
	
		
		
			
				
					
					|  |  |  |     // Currently, the sdcard is not mounted
 |  |  |  |     public static boolean batchDeleteNotes(ContentResolver resolver, HashSet<Long> ids) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     public static final int STATE_SD_CARD_UNMOUONTED           = 0; |  |  |  |         if (ids == null) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     // The backup file not exist
 |  |  |  |             Log.d(TAG, "the ids is null"); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     public static final int STATE_BACKUP_FILE_NOT_EXIST        = 1; |  |  |  |             return true; | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     // The data is not well formated, may be changed by other programs
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     public static final int STATE_DATA_DESTROIED               = 2; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     // Some run-time exception which causes restore or backup fails
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     public static final int STATE_SYSTEM_ERROR                 = 3; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     // Backup or restore success
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     public static final int STATE_SUCCESS                      = 4; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     // Permission denied
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     public static final int STATE_PERMISSION_DENIED            = 5; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     private TextExport mTextExport; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     private BackupUtils(Context context) { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         mTextExport = new TextExport(context); |  |  |  |  | 
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |         } |  |  |  |         } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |         if (ids.size() == 0) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     private static boolean externalStorageAvailable() { |  |  |  |             Log.d(TAG, "no id is in the hashset"); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()); |  |  |  |             return true; | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |         } |  |  |  |         } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     public int exportToText() { |  |  |  |         ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>(); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         return mTextExport.exportToText(); |  |  |  |         for (long id : ids) { | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             if(id == Notes.ID_ROOT_FOLDER) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 Log.e(TAG, "Don't delete system folder root"); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 continue; | 
			
		
	
		
		
			
				
					
					|  |  |  |             } |  |  |  |             } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |             ContentProviderOperation.Builder builder = ContentProviderOperation | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     public String getExportedTextFileName() { |  |  |  |                     .newDelete(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id)); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         return mTextExport.mFileName; |  |  |  |             operationList.add(builder.build()); | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |         } |  |  |  |         } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |         try { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     public String getExportedTextFileDir() { |  |  |  |             ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         return mTextExport.mFileDirectory; |  |  |  |             if (results == null || results.length == 0 || results[0] == null) { | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 Log.d(TAG, "delete notes failed, ids:" + ids.toString()); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 return false; | 
			
		
	
		
		
			
				
					
					|  |  |  |             } |  |  |  |             } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |             return true; | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     private static class TextExport { |  |  |  |         } catch (RemoteException e) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         private static final String[] NOTE_PROJECTION = { |  |  |  |             Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                 NoteColumns.ID, |  |  |  |         } catch (OperationApplicationException e) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                 NoteColumns.MODIFIED_DATE, |  |  |  |             Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                 NoteColumns.SNIPPET, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 NoteColumns.TYPE, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 NoteColumns.PARENT_ID |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         }; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         private static final int NOTE_COLUMN_ID = 0; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         private static final int NOTE_COLUMN_MODIFIED_DATE = 1; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         private static final int NOTE_COLUMN_SNIPPET = 2; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         private static final int NOTE_COLUMN_TYPE = 3; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         private static final int NOTE_COLUMN_PARENT_ID = 4; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         private static final String[] DATA_PROJECTION = { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 DataColumns.CONTENT, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 DataColumns.MIME_TYPE, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 DataColumns.DATA1, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 DataColumns.DATA2, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 DataColumns.DATA3, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 DataColumns.DATA4, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         }; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         private static final int DATA_COLUMN_CONTENT = 0; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         private static final int DATA_COLUMN_MIME_TYPE = 1; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         private static final int DATA_COLUMN_CALL_DATE = 2; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         private static final int DATA_COLUMN_PHONE_NUMBER = 4; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         private final String [] TEXT_FORMAT; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         private static final int FORMAT_FOLDER_NAME          = 0; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         private static final int FORMAT_NOTE_DATE            = 1; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         private static final int FORMAT_NOTE_CONTENT         = 2; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         private Context mContext; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         private String mFileName; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         private String mFileDirectory; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         private BufferedWriter mWriter; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         private int mNotesCount; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         private int mFoldersCount; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         public TextExport(Context context) { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             TEXT_FORMAT = context.getResources().getStringArray(R.array.format_for_exported_note); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             mContext = context; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             mFileName = ""; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             mFileDirectory = ""; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             mNotesCount = 0; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             mFoldersCount = 0; |  |  |  |  | 
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |         } |  |  |  |         } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |         return false; | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         private String getFormat(int id) { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             return TEXT_FORMAT[id]; |  |  |  |  | 
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     /** |  |  |  |     /** | 
			
		
	
		
		
			
				
					
					|  |  |  |          * Export notes to text file |  |  |  |      * 移动单个笔记到文件夹 | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |      */ |  |  |  |      */ | 
			
		
	
		
		
			
				
					
					|  |  |  |         public int exportToText() { |  |  |  |     public static void moveNoteToFoler(ContentResolver resolver, long id, long srcFolderId, long desFolderId) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             if (!externalStorageAvailable()) { |  |  |  |         ContentValues values = new ContentValues(); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                 Log.d(TAG, "Media was not mounted"); |  |  |  |         values.put(NoteColumns.PARENT_ID, desFolderId); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                 return STATE_SD_CARD_UNMOUONTED; |  |  |  |         values.put(NoteColumns.ORIGIN_PARENT_ID, srcFolderId); | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         values.put(NoteColumns.LOCAL_MODIFIED, 1); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         resolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id), values, null, null); | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |             File file = generateFileMountedOnSDcard(mContext, R.string.file_path, |  |  |  |     /** | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                     R.string.file_name_txt_format); |  |  |  |      * 批量移动笔记到文件夹 | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             if (file == null) { |  |  |  |      */ | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                 Log.e(TAG, "create file to exported failed"); |  |  |  |     public static boolean batchMoveToFolder(ContentResolver resolver, HashSet<Long> ids, | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                 return STATE_SYSTEM_ERROR; |  |  |  |             long folderId) { | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         if (ids == null) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             Log.d(TAG, "the ids is null"); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             return true; | 
			
		
	
		
		
			
				
					
					|  |  |  |         } |  |  |  |         } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |             mFileName = file.getName(); |  |  |  |         ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>(); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             mFileDirectory = mContext.getString(R.string.file_path); |  |  |  |         for (long id : ids) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |              |  |  |  |             ContentProviderOperation.Builder builder = ContentProviderOperation | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             // 使用try-with-resources确保资源释放
 |  |  |  |                     .newUpdate(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id)); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             try (BufferedWriter writer = new BufferedWriter( |  |  |  |             builder.withValue(NoteColumns.PARENT_ID, folderId); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                     new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8))) { |  |  |  |             builder.withValue(NoteColumns.LOCAL_MODIFIED, 1); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                 mWriter = writer; |  |  |  |             operationList.add(builder.build()); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                  |  |  |  |         } | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                 // 导出文件夹和笔记
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 exportFoldersAndNotes(); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 // 写入导出统计信息
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 writeExportSummary(); |  |  |  |  | 
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |                 Log.d(TAG, "Export successful. Exported " + mFoldersCount + " folders and " + mNotesCount + " notes."); |  |  |  |         try { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                 return STATE_SUCCESS; |  |  |  |             ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             } catch (IOException e) { |  |  |  |             if (results == null || results.length == 0 || results[0] == null) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                 Log.e(TAG, "Error during export: " + e.getMessage(), e); |  |  |  |                 Log.d(TAG, "delete notes failed, ids:" + ids.toString()); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                 return STATE_SYSTEM_ERROR; |  |  |  |                 return false; | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |             } |  |  |  |             } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             return true; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         } catch (RemoteException e) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         } catch (OperationApplicationException e) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); | 
			
		
	
		
		
			
				
					
					|  |  |  |         } |  |  |  |         } | 
			
		
	
		
		
			
				
					
					|  |  |  |          |  |  |  |         return false; | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         /** |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |          * 写入导出摘要信息 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |          */ |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         private void writeExportSummary() throws IOException { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             mWriter.write("\n"); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             mWriter.write("==============================\n"); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             mWriter.write(mContext.getString(R.string.export_summary) + "\n"); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             mWriter.write(mContext.getString(R.string.export_date) + ": "  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     + DateFormat.format(mContext.getString(R.string.format_datetime_ymdhm), System.currentTimeMillis()) + "\n"); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             mWriter.write(mContext.getString(R.string.folder_count) + ": " + mFoldersCount + "\n"); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             mWriter.write(mContext.getString(R.string.note_count) + ": " + mNotesCount + "\n"); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             mWriter.write("==============================\n"); |  |  |  |  | 
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     /** |  |  |  |     /** | 
			
		
	
		
		
			
				
					
					|  |  |  |          * 导出所有文件夹和笔记 |  |  |  |      * 获取用户文件夹数量(不包括系统文件夹) | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |      */ |  |  |  |      */ | 
			
		
	
		
		
			
				
					
					|  |  |  |         private void exportFoldersAndNotes() throws IOException { |  |  |  |     public static int getUserFolderCount(ContentResolver resolver) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             // 首先导出文件夹和其中的笔记
 |  |  |  |         Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             exportFolders(); |  |  |  |                 new String[] { "COUNT(*)" }, | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |              |  |  |  |                 NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>?", | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             // 导出根文件夹中的笔记
 |  |  |  |                 new String[] { String.valueOf(Notes.TYPE_FOLDER), String.valueOf(Notes.ID_TRASH_FOLER)}, | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             exportRootNotes(); |  |  |  |                 null); | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         int count = 0; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         if(cursor != null) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             if(cursor.moveToFirst()) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 try { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                     count = cursor.getInt(0); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 } catch (IndexOutOfBoundsException e) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                     Log.e(TAG, "get folder count failed:" + e.toString()); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 } finally { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                     cursor.close(); | 
			
		
	
		
		
			
				
					
					|  |  |  |                 } |  |  |  |                 } | 
			
		
	
		
		
			
				
					
					|  |  |  |          |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         /** |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |          * 导出所有文件夹 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |          */ |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         private void exportFolders() throws IOException { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             // 查询所有文件夹
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             String selection = "(" + NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER + " AND " |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + ") OR " |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     + NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |              |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             try (Cursor folderCursor = mContext.getContentResolver().query( |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     Notes.CONTENT_NOTE_URI, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     NOTE_PROJECTION, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     selection, null, null)) { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 if (folderCursor != null && folderCursor.moveToFirst()) { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     do { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                         mFoldersCount++; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                         exportFolder(folderCursor); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     } while (folderCursor.moveToNext()); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             } |  |  |  |             } | 
			
		
	
		
		
			
				
					
					|  |  |  |         } |  |  |  |         } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         return count; | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     /** |  |  |  |     /** | 
			
		
	
		
		
			
				
					
					|  |  |  |          * 导出单个文件夹及其包含的笔记 |  |  |  |      * 检查笔记是否存在且可见(不在回收站) | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |      */ |  |  |  |      */ | 
			
		
	
		
		
			
				
					
					|  |  |  |         private void exportFolder(Cursor folderCursor) throws IOException { |  |  |  |     public static boolean visibleInNoteDatabase(ContentResolver resolver, long noteId, int type) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             String folderId = folderCursor.getString(NOTE_COLUMN_ID); |  |  |  |         Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             String folderName; |  |  |  |                 null, | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 new String [] {String.valueOf(type)}, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 null); | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |             if(folderCursor.getLong(NOTE_COLUMN_ID) == Notes.ID_CALL_RECORD_FOLDER) { |  |  |  |         boolean exist = false; | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                 folderName = mContext.getString(R.string.call_record_folder_name); |  |  |  |         if (cursor != null) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             } else { |  |  |  |             if (cursor.getCount() > 0) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                 folderName = folderCursor.getString(NOTE_COLUMN_SNIPPET); |  |  |  |                 exist = true; | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |             } |  |  |  |             } | 
			
		
	
		
		
			
				
					
					|  |  |  |              |  |  |  |             cursor.close(); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             if (!TextUtils.isEmpty(folderName)) { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 mWriter.write(String.format(getFormat(FORMAT_FOLDER_NAME), folderName)); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 mWriter.newLine(); |  |  |  |  | 
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |         } |  |  |  |         } | 
			
		
	
		
		
			
				
					
					|  |  |  |              |  |  |  |         return exist; | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             // 导出文件夹中的笔记
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             exportNotesInFolder(folderId); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |              |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             // 添加文件夹分隔线
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             mWriter.write("\n"); |  |  |  |  | 
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     /** |  |  |  |     /** | 
			
		
	
		
		
			
				
					
					|  |  |  |          * 导出指定文件夹中的所有笔记 |  |  |  |      * 检查笔记是否存在于数据库中 | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |      */ |  |  |  |      */ | 
			
		
	
		
		
			
				
					
					|  |  |  |         private void exportNotesInFolder(String folderId) throws IOException { |  |  |  |     public static boolean existInNoteDatabase(ContentResolver resolver, long noteId) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             try (Cursor notesCursor = mContext.getContentResolver().query(Notes.CONTENT_NOTE_URI, |  |  |  |         Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                     NOTE_PROJECTION, NoteColumns.PARENT_ID + "=?", new String[] { folderId }, null)) { |  |  |  |                 null, null, null, null); | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |                 if (notesCursor != null && notesCursor.moveToFirst()) { |  |  |  |         boolean exist = false; | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                     do { |  |  |  |         if (cursor != null) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                         mNotesCount++; |  |  |  |             if (cursor.getCount() > 0) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                         exportNote(notesCursor); |  |  |  |                 exist = true; | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                     } while (notesCursor.moveToNext()); |  |  |  |  | 
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |             } |  |  |  |             } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             cursor.close(); | 
			
		
	
		
		
			
				
					
					|  |  |  |         } |  |  |  |         } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         return exist; | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     /** |  |  |  |     /** | 
			
		
	
		
		
			
				
					
					|  |  |  |          * 导出根文件夹中的笔记 |  |  |  |      * 检查数据是否存在于数据库中 | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |      */ |  |  |  |      */ | 
			
		
	
		
		
			
				
					
					|  |  |  |         private void exportRootNotes() throws IOException { |  |  |  |     public static boolean existInDataDatabase(ContentResolver resolver, long dataId) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             try (Cursor noteCursor = mContext.getContentResolver().query( |  |  |  |         Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                     Notes.CONTENT_NOTE_URI, |  |  |  |                 null, null, null, null); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                     NOTE_PROJECTION, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     NoteColumns.TYPE + "=" + Notes.TYPE_NOTE + " AND " + NoteColumns.PARENT_ID + "=0",  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     null, null)) { |  |  |  |  | 
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |                 if (noteCursor != null && noteCursor.moveToFirst()) { |  |  |  |         boolean exist = false; | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                     do { |  |  |  |         if (cursor != null) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                         mNotesCount++; |  |  |  |             if (cursor.getCount() > 0) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                         exportNote(noteCursor); |  |  |  |                 exist = true; | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                     } while (noteCursor.moveToNext()); |  |  |  |  | 
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |             } |  |  |  |             } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             cursor.close(); | 
			
		
	
		
		
			
				
					
					|  |  |  |         } |  |  |  |         } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         return exist; | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     /** |  |  |  |     /** | 
			
		
	
		
		
			
				
					
					|  |  |  |          * 导出单个笔记 |  |  |  |      * 检查可见文件夹名称是否已存在 | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |      */ |  |  |  |      */ | 
			
		
	
		
		
			
				
					
					|  |  |  |         private void exportNote(Cursor noteCursor) throws IOException { |  |  |  |     public static boolean checkVisibleFolderName(ContentResolver resolver, String name) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             // 打印笔记的最后修改日期
 |  |  |  |         Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, null, | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             mWriter.write(String.format(getFormat(FORMAT_NOTE_DATE),  |  |  |  |                 NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER + | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                     DateFormat.format(mContext.getString(R.string.format_datetime_mdhm), |  |  |  |                 " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                             noteCursor.getLong(NOTE_COLUMN_MODIFIED_DATE)))); |  |  |  |                 " AND " + NoteColumns.SNIPPET + "=?", | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             mWriter.newLine(); |  |  |  |                 new String[] { name }, null); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |              |  |  |  |         boolean exist = false; | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             // 查询并导出笔记的数据
 |  |  |  |         if(cursor != null) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             String noteId = noteCursor.getString(NOTE_COLUMN_ID); |  |  |  |             if(cursor.getCount() > 0) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             exportNoteData(noteId); |  |  |  |                 exist = true; | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |              |  |  |  |             } | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             // 添加笔记分隔线
 |  |  |  |             cursor.close(); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             mWriter.write("\n"); |  |  |  |         } | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         return exist; | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     /** |  |  |  |     /** | 
			
		
	
		
		
			
				
					
					|  |  |  |          * 导出笔记的所有数据 |  |  |  |      * 获取与文件夹关联的小部件信息 | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |      */ |  |  |  |      */ | 
			
		
	
		
		
			
				
					
					|  |  |  |         private void exportNoteData(String noteId) throws IOException { |  |  |  |     public static HashSet<AppWidgetAttribute> getFolderNoteWidget(ContentResolver resolver, long folderId) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             try (Cursor dataCursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, |  |  |  |         Cursor c = resolver.query(Notes.CONTENT_NOTE_URI, | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                     DATA_PROJECTION, DataColumns.NOTE_ID + "=?", new String[] { noteId }, null)) { |  |  |  |                 new String[] { NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE }, | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                  |  |  |  |                 NoteColumns.PARENT_ID + "=?", | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                 if (dataCursor != null && dataCursor.moveToFirst()) { |  |  |  |                 new String[] { String.valueOf(folderId) }, | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 null); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         HashSet<AppWidgetAttribute> set = null; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         if (c != null) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             if (c.moveToFirst()) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 set = new HashSet<AppWidgetAttribute>(); | 
			
		
	
		
		
			
				
					
					|  |  |  |                 do { |  |  |  |                 do { | 
			
		
	
		
		
			
				
					
					|  |  |  |                         String mimeType = dataCursor.getString(DATA_COLUMN_MIME_TYPE); |  |  |  |                     try { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                         if (DataConstants.CALL_NOTE.equals(mimeType)) { |  |  |  |                         AppWidgetAttribute widget = new AppWidgetAttribute(); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                             exportCallNote(dataCursor); |  |  |  |                         widget.widgetId = c.getInt(0); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                         } else if (DataConstants.NOTE.equals(mimeType)) { |  |  |  |                         widget.widgetType = c.getInt(1); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                             exportTextNote(dataCursor); |  |  |  |                         set.add(widget); | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                     } catch (IndexOutOfBoundsException e) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                         Log.e(TAG, e.toString()); | 
			
		
	
		
		
			
				
					
					|  |  |  |                     } |  |  |  |                     } | 
			
		
	
		
		
			
				
					
					|  |  |  |                     } while (dataCursor.moveToNext()); |  |  |  |                 } while (c.moveToNext()); | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |             } |  |  |  |             } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             c.close(); | 
			
		
	
		
		
			
				
					
					|  |  |  |         } |  |  |  |         } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         return set; | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     /** |  |  |  |     /** | 
			
		
	
		
		
			
				
					
					|  |  |  |          * 导出通话记录笔记 |  |  |  |      * 根据笔记ID获取通话号码 | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |      */ |  |  |  |      */ | 
			
		
	
		
		
			
				
					
					|  |  |  |         private void exportCallNote(Cursor dataCursor) throws IOException { |  |  |  |     public static String getCallNumberByNoteId(ContentResolver resolver, long noteId) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             // 打印电话号码
 |  |  |  |         Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI, | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             String phoneNumber = dataCursor.getString(DATA_COLUMN_PHONE_NUMBER); |  |  |  |                 new String [] { CallNote.PHONE_NUMBER }, | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             long callDate = dataCursor.getLong(DATA_COLUMN_CALL_DATE); |  |  |  |                 CallNote.NOTE_ID + "=? AND " + CallNote.MIME_TYPE + "=?", | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             String location = dataCursor.getString(DATA_COLUMN_CONTENT); |  |  |  |                 new String [] { String.valueOf(noteId), CallNote.CONTENT_ITEM_TYPE }, | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |                 null); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             if (!TextUtils.isEmpty(phoneNumber)) { |  |  |  | 
 | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                 mWriter.write(String.format(getFormat(FORMAT_NOTE_CONTENT), phoneNumber)); |  |  |  |         if (cursor != null && cursor.moveToFirst()) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                 mWriter.newLine(); |  |  |  |             try { | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 return cursor.getString(0); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             } catch (IndexOutOfBoundsException e) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 Log.e(TAG, "Get call number fails " + e.toString()); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             } finally { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 cursor.close(); | 
			
		
	
		
		
			
				
					
					|  |  |  |             } |  |  |  |             } | 
			
		
	
		
		
			
				
					
					|  |  |  |              |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             // 打印通话日期
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             mWriter.write(String.format(getFormat(FORMAT_NOTE_CONTENT),  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     DateFormat.format(mContext.getString(R.string.format_datetime_mdhm), callDate))); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             mWriter.newLine(); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |              |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             // 打印通话附件位置
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             if (!TextUtils.isEmpty(location)) { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 mWriter.write(String.format(getFormat(FORMAT_NOTE_CONTENT), location)); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 mWriter.newLine(); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         } |  |  |  |         } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         return ""; | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     /** |  |  |  |     /** | 
			
		
	
		
		
			
				
					
					|  |  |  |          * 导出文本笔记 |  |  |  |      * 根据电话号码和通话日期获取笔记ID | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |      */ |  |  |  |      */ | 
			
		
	
		
		
			
				
					
					|  |  |  |         private void exportTextNote(Cursor dataCursor) throws IOException { |  |  |  |     public static long getNoteIdByPhoneNumberAndCallDate(ContentResolver resolver, String phoneNumber, long callDate) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             String content = dataCursor.getString(DATA_COLUMN_CONTENT); |  |  |  |         Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI, | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             if (!TextUtils.isEmpty(content)) { |  |  |  |                 new String [] { CallNote.NOTE_ID }, | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                 mWriter.write(String.format(getFormat(FORMAT_NOTE_CONTENT), content)); |  |  |  |                 CallNote.CALL_DATE + "=? AND " + CallNote.MIME_TYPE + "=? AND PHONE_NUMBERS_EQUAL(" | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                 mWriter.newLine(); |  |  |  |                 + CallNote.PHONE_NUMBER + ",?)", | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 new String [] { String.valueOf(callDate), CallNote.CONTENT_ITEM_TYPE, phoneNumber }, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 null); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         if (cursor != null) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             if (cursor.moveToFirst()) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 try { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                     return cursor.getLong(0); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 } catch (IndexOutOfBoundsException e) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                     Log.e(TAG, "Get call note id fails " + e.toString()); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 } | 
			
		
	
		
		
			
				
					
					|  |  |  |             } |  |  |  |             } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             cursor.close(); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         return 0; | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     /** |  |  |  |     /** | 
			
		
	
		
		
			
				
					
					|  |  |  |          * Generate the text file to store imported data |  |  |  |      * 根据笔记ID获取摘要 | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |      */ |  |  |  |      */ | 
			
		
	
		
		
			
				
					
					|  |  |  |         private static File generateFileMountedOnSDcard(Context context, int filePathResId, int fileNameFormatResId) { |  |  |  |     public static String getSnippetById(ContentResolver resolver, long noteId) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             StringBuilder sb = new StringBuilder(); |  |  |  |         Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 new String [] { NoteColumns.SNIPPET }, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 NoteColumns.ID + "=?", | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 new String [] { String.valueOf(noteId)}, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 null); | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |             // 适配Android 10+的存储访问
 |  |  |  |         if (cursor != null) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { |  |  |  |             String snippet = ""; | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                 sb.append(context.getExternalFilesDir(null)); |  |  |  |             if (cursor.moveToFirst()) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             } else { |  |  |  |                 snippet = cursor.getString(0); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                 sb.append(Environment.getExternalStorageDirectory()); |  |  |  |  | 
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |             } |  |  |  |             } | 
			
		
	
		
		
			
				
					
					|  |  |  |              |  |  |  |             cursor.close(); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             sb.append(context.getString(filePathResId)); |  |  |  |             return snippet; | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             File filedir = new File(sb.toString()); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |              |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             // 确保目录存在
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             if (!filedir.exists() && !filedir.mkdirs()) { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 Log.e(TAG, "Failed to create directory: " + filedir.getAbsolutePath()); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 return null; |  |  |  |  | 
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |         } |  |  |  |         } | 
			
		
	
		
		
			
				
					
					|  |  |  |              |  |  |  |         throw new IllegalArgumentException("Note is not found with id: " + noteId); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             // 构建文件名
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             sb.append(context.getString( |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     fileNameFormatResId, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     DateFormat.format(context.getString(R.string.format_date_ymd), |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                             System.currentTimeMillis()))); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             File file = new File(sb.toString()); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             try { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 if (!file.exists() && !file.createNewFile()) { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     Log.e(TAG, "Failed to create file: " + file.getAbsolutePath()); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     return null; |  |  |  |  | 
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  |                 return file; |  |  |  | 
 | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             } catch (SecurityException | IOException e) { |  |  |  |     /** | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                 Log.e(TAG, "Error creating file: " + e.getMessage(), e); |  |  |  |      * 格式化笔记摘要 | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                 return null; |  |  |  |      */ | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     public static String getFormattedSnippet(String snippet) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         if (snippet != null) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             snippet = snippet.trim(); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             int index = snippet.indexOf('\n'); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             if (index != -1) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 snippet = snippet.substring(0, index); | 
			
		
	
		
		
			
				
					
					|  |  |  |             } |  |  |  |             } | 
			
		
	
		
		
			
				
					
					|  |  |  |         } |  |  |  |         } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         return snippet; | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
	
		
		
			
				
					|  |  | 
 |