diff --git a/src/Notes/.gradle/8.0/executionHistory/executionHistory.bin b/src/Notes/.gradle/8.0/executionHistory/executionHistory.bin new file mode 100644 index 0000000..bb0f83e Binary files /dev/null and b/src/Notes/.gradle/8.0/executionHistory/executionHistory.bin differ diff --git a/src/Notes/.gradle/8.0/executionHistory/executionHistory.lock b/src/Notes/.gradle/8.0/executionHistory/executionHistory.lock new file mode 100644 index 0000000..a8a4684 Binary files /dev/null and b/src/Notes/.gradle/8.0/executionHistory/executionHistory.lock differ diff --git a/src/Notes/.gradle/8.0/fileHashes/fileHashes.bin b/src/Notes/.gradle/8.0/fileHashes/fileHashes.bin new file mode 100644 index 0000000..9f47349 Binary files /dev/null and b/src/Notes/.gradle/8.0/fileHashes/fileHashes.bin differ diff --git a/src/Notes/.gradle/8.0/fileHashes/fileHashes.lock b/src/Notes/.gradle/8.0/fileHashes/fileHashes.lock new file mode 100644 index 0000000..2a4f76c Binary files /dev/null and b/src/Notes/.gradle/8.0/fileHashes/fileHashes.lock differ diff --git a/src/Notes/.gradle/8.0/fileHashes/resourceHashesCache.bin b/src/Notes/.gradle/8.0/fileHashes/resourceHashesCache.bin new file mode 100644 index 0000000..285bc0c Binary files /dev/null and b/src/Notes/.gradle/8.0/fileHashes/resourceHashesCache.bin differ diff --git a/src/Notes/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/src/Notes/.gradle/buildOutputCleanup/buildOutputCleanup.lock new file mode 100644 index 0000000..da7f435 Binary files /dev/null and b/src/Notes/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ diff --git a/src/Notes/.gradle/buildOutputCleanup/outputFiles.bin b/src/Notes/.gradle/buildOutputCleanup/outputFiles.bin new file mode 100644 index 0000000..45267e7 Binary files /dev/null and b/src/Notes/.gradle/buildOutputCleanup/outputFiles.bin differ diff --git a/src/Notes/.gradle/file-system.probe b/src/Notes/.gradle/file-system.probe new file mode 100644 index 0000000..b6d9802 Binary files /dev/null and b/src/Notes/.gradle/file-system.probe differ diff --git a/src/Notes/app/build/intermediates/apk/debug/app-debug.apk b/src/Notes/app/build/intermediates/apk/debug/app-debug.apk new file mode 100644 index 0000000..82435ad Binary files /dev/null and b/src/Notes/app/build/intermediates/apk/debug/app-debug.apk differ diff --git a/src/Notes/app/build/intermediates/apk_ide_redirect_file/debug/redirect.txt b/src/Notes/app/build/intermediates/apk_ide_redirect_file/debug/redirect.txt new file mode 100644 index 0000000..46dacc6 --- /dev/null +++ b/src/Notes/app/build/intermediates/apk_ide_redirect_file/debug/redirect.txt @@ -0,0 +1,2 @@ +#- File Locator - +listingFile=../../apk/debug/output-metadata.json diff --git a/src/Notes/app/build/intermediates/desugar_graph/debug/out/currentProject/dirs_bucket_0/graph.bin b/src/Notes/app/build/intermediates/desugar_graph/debug/out/currentProject/dirs_bucket_0/graph.bin new file mode 100644 index 0000000..8737910 Binary files /dev/null and b/src/Notes/app/build/intermediates/desugar_graph/debug/out/currentProject/dirs_bucket_0/graph.bin differ diff --git a/src/Notes/app/build/intermediates/desugar_graph/debug/out/currentProject/dirs_bucket_3/graph.bin b/src/Notes/app/build/intermediates/desugar_graph/debug/out/currentProject/dirs_bucket_3/graph.bin new file mode 100644 index 0000000..8fb5bea Binary files /dev/null and b/src/Notes/app/build/intermediates/desugar_graph/debug/out/currentProject/dirs_bucket_3/graph.bin differ diff --git a/src/Notes/app/build/intermediates/desugar_graph/debug/out/currentProject/dirs_bucket_5/graph.bin b/src/Notes/app/build/intermediates/desugar_graph/debug/out/currentProject/dirs_bucket_5/graph.bin new file mode 100644 index 0000000..a94134c Binary files /dev/null and b/src/Notes/app/build/intermediates/desugar_graph/debug/out/currentProject/dirs_bucket_5/graph.bin differ diff --git a/src/Notes/app/build/intermediates/dex/debug/mergeDexDebug/classes.dex b/src/Notes/app/build/intermediates/dex/debug/mergeDexDebug/classes.dex new file mode 100644 index 0000000..1fe2149 Binary files /dev/null and b/src/Notes/app/build/intermediates/dex/debug/mergeDexDebug/classes.dex differ diff --git a/src/Notes/app/build/intermediates/incremental/debug/mergeDebugResources/compile-file-map.properties b/src/Notes/app/build/intermediates/incremental/debug/mergeDebugResources/compile-file-map.properties new file mode 100644 index 0000000..2215ade --- /dev/null +++ b/src/Notes/app/build/intermediates/incremental/debug/mergeDebugResources/compile-file-map.properties @@ -0,0 +1,96 @@ +#Fri Dec 22 19:43:31 CST 2023 +net.micode.notes.app-main-6\:/color/primary_text_dark.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\color_primary_text_dark.xml.flat +net.micode.notes.app-main-6\:/color/secondary_text_dark.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\color_secondary_text_dark.xml.flat +net.micode.notes.app-main-6\:/drawable-hdpi/bg_btn_set_color.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_bg_btn_set_color.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/bg_color_btn_mask.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_bg_color_btn_mask.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/call_record.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_call_record.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/clock.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_clock.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/delete.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_delete.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/dropdown_icon.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_dropdown_icon.9.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/edit_blue.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_edit_blue.9.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/edit_green.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_edit_green.9.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/edit_red.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_edit_red.9.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/edit_title_blue.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_edit_title_blue.9.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/edit_title_green.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_edit_title_green.9.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/edit_title_red.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_edit_title_red.9.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/edit_title_white.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_edit_title_white.9.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/edit_title_yellow.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_edit_title_yellow.9.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/edit_white.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_edit_white.9.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/edit_yellow.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_edit_yellow.9.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/font_large.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_font_large.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/font_normal.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_font_normal.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/font_size_selector_bg.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_font_size_selector_bg.9.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/font_small.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_font_small.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/font_super.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_font_super.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/icon_app.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_icon_app.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/list_background.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_list_background.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/list_blue_down.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_list_blue_down.9.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/list_blue_middle.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_list_blue_middle.9.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/list_blue_single.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_list_blue_single.9.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/list_blue_up.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_list_blue_up.9.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/list_folder.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_list_folder.9.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/list_footer_bg.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_list_footer_bg.9.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/list_green_down.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_list_green_down.9.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/list_green_middle.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_list_green_middle.9.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/list_green_single.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_list_green_single.9.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/list_green_up.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_list_green_up.9.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/list_red_down.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_list_red_down.9.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/list_red_middle.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_list_red_middle.9.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/list_red_single.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_list_red_single.9.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/list_red_up.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_list_red_up.9.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/list_white_down.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_list_white_down.9.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/list_white_middle.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_list_white_middle.9.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/list_white_single.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_list_white_single.9.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/list_white_up.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_list_white_up.9.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/list_yellow_down.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_list_yellow_down.9.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/list_yellow_middle.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_list_yellow_middle.9.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/list_yellow_single.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_list_yellow_single.9.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/list_yellow_up.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_list_yellow_up.9.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/menu_delete.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_menu_delete.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/menu_move.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_menu_move.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/new_note_normal.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_new_note_normal.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/new_note_pressed.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_new_note_pressed.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/note_edit_color_selector_panel.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_note_edit_color_selector_panel.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/notification.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_notification.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/search_result.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_search_result.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/selected.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_selected.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/title_alert.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_title_alert.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/title_bar_bg.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_title_bar_bg.9.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/widget_2x_blue.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_widget_2x_blue.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/widget_2x_green.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_widget_2x_green.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/widget_2x_red.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_widget_2x_red.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/widget_2x_white.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_widget_2x_white.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/widget_2x_yellow.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_widget_2x_yellow.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/widget_4x_blue.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_widget_4x_blue.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/widget_4x_green.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_widget_4x_green.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/widget_4x_red.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_widget_4x_red.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/widget_4x_white.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_widget_4x_white.png.flat +net.micode.notes.app-main-6\:/drawable-hdpi/widget_4x_yellow.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable-hdpi_widget_4x_yellow.png.flat +net.micode.notes.app-main-6\:/drawable/new_note.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\drawable_new_note.xml.flat +net.micode.notes.app-main-6\:/layout/account_dialog_title.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\layout_account_dialog_title.xml.flat +net.micode.notes.app-main-6\:/layout/add_account_text.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\layout_add_account_text.xml.flat +net.micode.notes.app-main-6\:/layout/datetime_picker.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\layout_datetime_picker.xml.flat +net.micode.notes.app-main-6\:/layout/dialog_edit_text.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\layout_dialog_edit_text.xml.flat +net.micode.notes.app-main-6\:/layout/folder_list_item.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\layout_folder_list_item.xml.flat +net.micode.notes.app-main-6\:/layout/note_edit.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\layout_note_edit.xml.flat +net.micode.notes.app-main-6\:/layout/note_edit_list_item.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\layout_note_edit_list_item.xml.flat +net.micode.notes.app-main-6\:/layout/note_item.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\layout_note_item.xml.flat +net.micode.notes.app-main-6\:/layout/note_list.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\layout_note_list.xml.flat +net.micode.notes.app-main-6\:/layout/note_list_dropdown_menu.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\layout_note_list_dropdown_menu.xml.flat +net.micode.notes.app-main-6\:/layout/note_list_footer.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\layout_note_list_footer.xml.flat +net.micode.notes.app-main-6\:/layout/settings_header.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\layout_settings_header.xml.flat +net.micode.notes.app-main-6\:/layout/widget_2x.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\layout_widget_2x.xml.flat +net.micode.notes.app-main-6\:/layout/widget_4x.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\layout_widget_4x.xml.flat +net.micode.notes.app-main-6\:/menu/call_note_edit.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\menu_call_note_edit.xml.flat +net.micode.notes.app-main-6\:/menu/call_record_folder.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\menu_call_record_folder.xml.flat +net.micode.notes.app-main-6\:/menu/note_edit.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\menu_note_edit.xml.flat +net.micode.notes.app-main-6\:/menu/note_list.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\menu_note_list.xml.flat +net.micode.notes.app-main-6\:/menu/note_list_dropdown.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\menu_note_list_dropdown.xml.flat +net.micode.notes.app-main-6\:/menu/note_list_options.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\menu_note_list_options.xml.flat +net.micode.notes.app-main-6\:/menu/sub_folder.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\menu_sub_folder.xml.flat +net.micode.notes.app-main-6\:/raw-zh-rCN/introduction=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\raw-zh-rCN_introduction.flat +net.micode.notes.app-main-6\:/raw/introduction=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\raw_introduction.flat +net.micode.notes.app-main-6\:/xml/preferences.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\xml_preferences.xml.flat +net.micode.notes.app-main-6\:/xml/searchable.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\xml_searchable.xml.flat +net.micode.notes.app-main-6\:/xml/widget_2x_info.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\xml_widget_2x_info.xml.flat +net.micode.notes.app-main-6\:/xml/widget_4x_info.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\merged_res\\debug\\xml_widget_4x_info.xml.flat diff --git a/src/Notes/app/build/intermediates/incremental/debug/mergeDebugResources/merger.xml b/src/Notes/app/build/intermediates/incremental/debug/mergeDebugResources/merger.xml new file mode 100644 index 0000000..5a13304 --- /dev/null +++ b/src/Notes/app/build/intermediates/incremental/debug/mergeDebugResources/merger.xml @@ -0,0 +1,56 @@ + + + -%s + --%s + --%s + --%s + + Messaging + Email + #335b5b5b33sp26sp20sp17sp14spNotesNotes 2x2Notes 4x4No associated note found, click to create associated note.Privacy mode,can not see note content...Add noteDelete reminder successfullySet reminderExpiredyyyyMMddMMMd kk:mmGot itTake a lookCallSend emailBrowse webOpen map/MIUI/notes/notes_%s.txt(%d)New FolderExport textSyncCancel syncingSettingsSearchDeleteMove to folder%d selectedNothing selected, the operation is invalidSelect allDeselect allFont sizeSmallMediumLargeSuperEnter check listLeave check listView folderDelete folderChange folder nameThe folder %1$s exist, please renameShareSend to homeRemind meDelete reminderSelect folderParent folderNote added to homeConfirm to delete folder and its notes?Delete selected notesConfirm to delete the selected %d notes?Confirm to delete this note?Have moved selected %1$d notes to %2$s folderSD card busy, not available nowExport failed, please check SD cardThe note is not existSorry, can not set clock on empty noteSorry, can not send and empty note to homeExport successfulExport failExport text file (%1$s) to SD (%2$s) directorySyncing notes...Sync is successfulSync is failedSync is canceledSync is successful with account %1$sSync failed, please check network and account settingsSync failed, internal error occursSync is canceledLogging into %1$s...Getting remote note list...Synchronize local notes with Google Task...SettingsSync accountSync notes with google taskLast sync time %1$syyyy-MM-dd hh:mm:ssAdd accountChange sync accountRemove sync accountCancelSync immediatelyCancel syncingCurrent account %1$sAll sync related information will be deleted, which may result in duplicated items sometimeSync notesPlease select a google account. Local notes will be synced with google task.Cannot change the account because sync is in progress%1$s has been set as the sync accountNew note background color randomDeleteCall notesInput nameSearching NotesSearch notesText in your notesNotessetcancel + %1$s result for \"%2$s\" + + %1$s results for \"%2$s\" + + 短信 + 邮件 + 便签便签2x2便签4x4没有关联内容,点击新建便签。访客模式下,便签内容不可见...新建便签成功删除提醒创建提醒已过期yyyyMMddMM月dd日 kk:mm知道了查看呼叫电话发送邮件浏览网页打开地图新建文件夹导出文本同步取消同步设置搜索删除移动到文件夹选中了 %d 项没有选中项,操作无效全选取消全选文字大小正常超大进入清单模式退出清单模式查看文件夹刪除文件夹修改文件夹名称文件夹 %1$s 已存在,请重新命名分享发送到桌面提醒我删除提醒选择文件夹上一级文件夹已添加到桌面删除确认要删除所选的 %d 条便签吗?确认要删除该条便签吗?确认删除文件夹及所包含的便签吗?已将所选 %1$d 条便签移到 %2$s 文件夹SD卡被占用,不能操作导出文本时发生错误,请检查SD卡要查看的便签不存在不能为空便签设置闹钟提醒不能将空便签发送到桌面导出成功导出失败已将文本文件(%1$s)输出至SD卡(%2$s)目录同步便签...同步成功同步失败同步已取消与%1$s同步成功同步失败,请检查网络和帐号设置同步失败,发生内部错误同步已取消登录%1$s...正在获取服务器便签列表...正在同步本地便签...设置同步账号与google task同步便签记录上次同步于 %1$s添加账号更换账号删除账号取消立即同步取消同步当前帐号 %1$s如更换同步帐号,过去的帐号同步信息将被清空,再次切换的同时可能会造成数据重复同步便签请选择google帐号,便签将与该帐号的google task内容同步。正在同步中,不能修改同步帐号同步帐号已设置为%1$s新建便签背景颜色随机删除通话便签请输入名称正在搜索便签搜索便签便签中的文字便签设置取消 + %1$s 条符合“%2$s”的搜索结果 + + 短信 + 郵件 + 便簽便簽2x2便簽4x4沒有關聯內容,點擊新建便簽。訪客模式下,便籤內容不可見...新建便簽成功刪除提醒創建提醒已過期yyyyMMddMM月dd日 kk:mm知道了查看呼叫電話發送郵件浏覽網頁打開地圖已將所選 %1$d 便籤移到 %2$s 文件夾新建文件夾導出文本同步取消同步設置搜尋刪除移動到文件夾選中了 %d 項沒有選中項,操作無效全選取消全選文字大小正常超大進入清單模式退出清單模式查看文件夾刪除文件夾修改文件夾名稱文件夾 %1$s 已存在,請重新命名分享發送到桌面提醒我刪除提醒選擇文件夾上一級文件夾已添加到桌面刪除确认要刪除所選的 %d 條便籤嗎?确认要删除該條便籤嗎?確認刪除檔夾及所包含的便簽嗎?SD卡被佔用,不能操作導出TXT時發生錯誤,請檢查SD卡要查看的便籤不存在不能爲空便籤設置鬧鐘提醒不能將空便籤發送到桌面導出成功導出失敗已將文本文件(%1$s)導出至SD(%2$s)目錄同步便簽...同步成功同步失敗同步已取消與%1$s同步成功同步失敗,請檢查網絡和帳號設置同步失敗,發生內部錯誤同步已取消登陸%1$s...正在獲取服務器便籤列表...正在同步本地便籤...設置同步賬號与google task同步便簽記錄上次同步于 %1$s添加賬號更換賬號刪除賬號取消立即同步取消同步當前帳號 %1$s如更換同步帳號,過去的帳號同步信息將被清空,再次切換的同時可能會造成數據重復同步便簽請選擇google帳號,便簽將與該帳號的google task內容同步。正在同步中,不能修改同步帳號同步帳號已設置為%1$s新建便籤背景顏色隨機刪除通話便籤請輸入名稱正在搜索便籤搜索便籤便籤中的文字便籤設置取消 + %1$s 條符合”%2$s“的搜尋結果 + \ No newline at end of file diff --git a/src/Notes/app/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties b/src/Notes/app/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties new file mode 100644 index 0000000..edd62ef --- /dev/null +++ b/src/Notes/app/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties @@ -0,0 +1,96 @@ +#Fri Dec 22 19:43:31 CST 2023 +net.micode.notes.app-main-5\:/color/primary_text_dark.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\color\\primary_text_dark.xml +net.micode.notes.app-main-5\:/color/secondary_text_dark.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\color\\secondary_text_dark.xml +net.micode.notes.app-main-5\:/drawable-hdpi/bg_btn_set_color.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\bg_btn_set_color.png +net.micode.notes.app-main-5\:/drawable-hdpi/bg_color_btn_mask.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\bg_color_btn_mask.png +net.micode.notes.app-main-5\:/drawable-hdpi/call_record.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\call_record.png +net.micode.notes.app-main-5\:/drawable-hdpi/clock.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\clock.png +net.micode.notes.app-main-5\:/drawable-hdpi/delete.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\delete.png +net.micode.notes.app-main-5\:/drawable-hdpi/dropdown_icon.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\dropdown_icon.9.png +net.micode.notes.app-main-5\:/drawable-hdpi/edit_blue.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\edit_blue.9.png +net.micode.notes.app-main-5\:/drawable-hdpi/edit_green.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\edit_green.9.png +net.micode.notes.app-main-5\:/drawable-hdpi/edit_red.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\edit_red.9.png +net.micode.notes.app-main-5\:/drawable-hdpi/edit_title_blue.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\edit_title_blue.9.png +net.micode.notes.app-main-5\:/drawable-hdpi/edit_title_green.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\edit_title_green.9.png +net.micode.notes.app-main-5\:/drawable-hdpi/edit_title_red.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\edit_title_red.9.png +net.micode.notes.app-main-5\:/drawable-hdpi/edit_title_white.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\edit_title_white.9.png +net.micode.notes.app-main-5\:/drawable-hdpi/edit_title_yellow.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\edit_title_yellow.9.png +net.micode.notes.app-main-5\:/drawable-hdpi/edit_white.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\edit_white.9.png +net.micode.notes.app-main-5\:/drawable-hdpi/edit_yellow.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\edit_yellow.9.png +net.micode.notes.app-main-5\:/drawable-hdpi/font_large.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\font_large.png +net.micode.notes.app-main-5\:/drawable-hdpi/font_normal.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\font_normal.png +net.micode.notes.app-main-5\:/drawable-hdpi/font_size_selector_bg.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\font_size_selector_bg.9.png +net.micode.notes.app-main-5\:/drawable-hdpi/font_small.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\font_small.png +net.micode.notes.app-main-5\:/drawable-hdpi/font_super.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\font_super.png +net.micode.notes.app-main-5\:/drawable-hdpi/icon_app.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\icon_app.png +net.micode.notes.app-main-5\:/drawable-hdpi/list_background.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\list_background.png +net.micode.notes.app-main-5\:/drawable-hdpi/list_blue_down.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\list_blue_down.9.png +net.micode.notes.app-main-5\:/drawable-hdpi/list_blue_middle.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\list_blue_middle.9.png +net.micode.notes.app-main-5\:/drawable-hdpi/list_blue_single.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\list_blue_single.9.png +net.micode.notes.app-main-5\:/drawable-hdpi/list_blue_up.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\list_blue_up.9.png +net.micode.notes.app-main-5\:/drawable-hdpi/list_folder.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\list_folder.9.png +net.micode.notes.app-main-5\:/drawable-hdpi/list_footer_bg.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\list_footer_bg.9.png +net.micode.notes.app-main-5\:/drawable-hdpi/list_green_down.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\list_green_down.9.png +net.micode.notes.app-main-5\:/drawable-hdpi/list_green_middle.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\list_green_middle.9.png +net.micode.notes.app-main-5\:/drawable-hdpi/list_green_single.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\list_green_single.9.png +net.micode.notes.app-main-5\:/drawable-hdpi/list_green_up.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\list_green_up.9.png +net.micode.notes.app-main-5\:/drawable-hdpi/list_red_down.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\list_red_down.9.png +net.micode.notes.app-main-5\:/drawable-hdpi/list_red_middle.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\list_red_middle.9.png +net.micode.notes.app-main-5\:/drawable-hdpi/list_red_single.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\list_red_single.9.png +net.micode.notes.app-main-5\:/drawable-hdpi/list_red_up.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\list_red_up.9.png +net.micode.notes.app-main-5\:/drawable-hdpi/list_white_down.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\list_white_down.9.png +net.micode.notes.app-main-5\:/drawable-hdpi/list_white_middle.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\list_white_middle.9.png +net.micode.notes.app-main-5\:/drawable-hdpi/list_white_single.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\list_white_single.9.png +net.micode.notes.app-main-5\:/drawable-hdpi/list_white_up.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\list_white_up.9.png +net.micode.notes.app-main-5\:/drawable-hdpi/list_yellow_down.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\list_yellow_down.9.png +net.micode.notes.app-main-5\:/drawable-hdpi/list_yellow_middle.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\list_yellow_middle.9.png +net.micode.notes.app-main-5\:/drawable-hdpi/list_yellow_single.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\list_yellow_single.9.png +net.micode.notes.app-main-5\:/drawable-hdpi/list_yellow_up.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\list_yellow_up.9.png +net.micode.notes.app-main-5\:/drawable-hdpi/menu_delete.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\menu_delete.png +net.micode.notes.app-main-5\:/drawable-hdpi/menu_move.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\menu_move.png +net.micode.notes.app-main-5\:/drawable-hdpi/new_note_normal.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\new_note_normal.png +net.micode.notes.app-main-5\:/drawable-hdpi/new_note_pressed.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\new_note_pressed.png +net.micode.notes.app-main-5\:/drawable-hdpi/note_edit_color_selector_panel.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\note_edit_color_selector_panel.png +net.micode.notes.app-main-5\:/drawable-hdpi/notification.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\notification.png +net.micode.notes.app-main-5\:/drawable-hdpi/search_result.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\search_result.png +net.micode.notes.app-main-5\:/drawable-hdpi/selected.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\selected.png +net.micode.notes.app-main-5\:/drawable-hdpi/title_alert.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\title_alert.png +net.micode.notes.app-main-5\:/drawable-hdpi/title_bar_bg.9.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\title_bar_bg.9.png +net.micode.notes.app-main-5\:/drawable-hdpi/widget_2x_blue.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\widget_2x_blue.png +net.micode.notes.app-main-5\:/drawable-hdpi/widget_2x_green.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\widget_2x_green.png +net.micode.notes.app-main-5\:/drawable-hdpi/widget_2x_red.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\widget_2x_red.png +net.micode.notes.app-main-5\:/drawable-hdpi/widget_2x_white.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\widget_2x_white.png +net.micode.notes.app-main-5\:/drawable-hdpi/widget_2x_yellow.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\widget_2x_yellow.png +net.micode.notes.app-main-5\:/drawable-hdpi/widget_4x_blue.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\widget_4x_blue.png +net.micode.notes.app-main-5\:/drawable-hdpi/widget_4x_green.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\widget_4x_green.png +net.micode.notes.app-main-5\:/drawable-hdpi/widget_4x_red.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\widget_4x_red.png +net.micode.notes.app-main-5\:/drawable-hdpi/widget_4x_white.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\widget_4x_white.png +net.micode.notes.app-main-5\:/drawable-hdpi/widget_4x_yellow.png=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable-hdpi-v4\\widget_4x_yellow.png +net.micode.notes.app-main-5\:/drawable/new_note.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\drawable\\new_note.xml +net.micode.notes.app-main-5\:/layout/account_dialog_title.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\layout\\account_dialog_title.xml +net.micode.notes.app-main-5\:/layout/add_account_text.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\layout\\add_account_text.xml +net.micode.notes.app-main-5\:/layout/datetime_picker.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\layout\\datetime_picker.xml +net.micode.notes.app-main-5\:/layout/dialog_edit_text.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\layout\\dialog_edit_text.xml +net.micode.notes.app-main-5\:/layout/folder_list_item.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\layout\\folder_list_item.xml +net.micode.notes.app-main-5\:/layout/note_edit.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\layout\\note_edit.xml +net.micode.notes.app-main-5\:/layout/note_edit_list_item.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\layout\\note_edit_list_item.xml +net.micode.notes.app-main-5\:/layout/note_item.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\layout\\note_item.xml +net.micode.notes.app-main-5\:/layout/note_list.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\layout\\note_list.xml +net.micode.notes.app-main-5\:/layout/note_list_dropdown_menu.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\layout\\note_list_dropdown_menu.xml +net.micode.notes.app-main-5\:/layout/note_list_footer.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\layout\\note_list_footer.xml +net.micode.notes.app-main-5\:/layout/settings_header.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\layout\\settings_header.xml +net.micode.notes.app-main-5\:/layout/widget_2x.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\layout\\widget_2x.xml +net.micode.notes.app-main-5\:/layout/widget_4x.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\layout\\widget_4x.xml +net.micode.notes.app-main-5\:/menu/call_note_edit.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\menu\\call_note_edit.xml +net.micode.notes.app-main-5\:/menu/call_record_folder.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\menu\\call_record_folder.xml +net.micode.notes.app-main-5\:/menu/note_edit.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\menu\\note_edit.xml +net.micode.notes.app-main-5\:/menu/note_list.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\menu\\note_list.xml +net.micode.notes.app-main-5\:/menu/note_list_dropdown.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\menu\\note_list_dropdown.xml +net.micode.notes.app-main-5\:/menu/note_list_options.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\menu\\note_list_options.xml +net.micode.notes.app-main-5\:/menu/sub_folder.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\menu\\sub_folder.xml +net.micode.notes.app-main-5\:/raw-zh-rCN/introduction=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\raw-zh-rCN\\introduction +net.micode.notes.app-main-5\:/raw/introduction=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\raw\\introduction +net.micode.notes.app-main-5\:/xml/preferences.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\xml\\preferences.xml +net.micode.notes.app-main-5\:/xml/searchable.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\xml\\searchable.xml +net.micode.notes.app-main-5\:/xml/widget_2x_info.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\xml\\widget_2x_info.xml +net.micode.notes.app-main-5\:/xml/widget_4x_info.xml=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\packaged_res\\debug\\xml\\widget_4x_info.xml diff --git a/src/Notes/app/build/intermediates/incremental/debug/packageDebugResources/merged.dir/values/values.xml b/src/Notes/app/build/intermediates/incremental/debug/packageDebugResources/merged.dir/values/values.xml new file mode 100644 index 0000000..0102458 --- /dev/null +++ b/src/Notes/app/build/intermediates/incremental/debug/packageDebugResources/merged.dir/values/values.xml @@ -0,0 +1,169 @@ + + + + -%s + --%s + --%s + --%s + + + Messaging + Email + + #335b5b5b + 26sp + 20sp + 17sp + 14sp + 33sp + + %1$s result for \"%2$s\" + + %1$s results for \"%2$s\" + + Confirm to delete folder and its notes? + Confirm to delete this note? + Confirm to delete the selected %d notes? + Delete selected notes + Notes + Notes 2x2 + Notes 4x4 + Delete + Call notes + cancel + set + Delete reminder successfully + Sorry, can not set clock on empty note + Sorry, can not send and empty note to home + The note is not exist + Export failed, please check SD card + SD card busy, not available now + Sync is canceled + Sync failed, internal error occurs + Sync failed, please check network and account settings + Export fail + notes_%s.txt + /MIUI/notes/ + The folder %1$s exist, please rename + yyyyMMdd + MMMd kk:mm + Export text file (%1$s) to SD (%2$s) directory + (%d) + Have moved selected %1$d notes to %2$s folder + Input name + Note added to home + Remind me + New Folder + Delete + Deselect all + Export text + Change folder name + Delete folder + View folder + Large + Medium + Font size + Small + Super + Enter check list + Move to folder + Parent folder + Leave check list + Delete reminder + Search + Select all + Nothing selected, the operation is invalid + %d selected + Send to home + Settings + Share + Sync + Cancel syncing + Select folder + Expired + Send email + Open map + Call + Browse web + Take a look + Got it + Add note + ... + Sync notes with google task + Sync account + Add account + New note background color random + Cancel syncing + Sync immediately + Current account %1$s + All sync related information will be deleted, which may result in duplicated items sometime + Please select a google account. Local notes will be synced with google task. + Sync notes + Last sync time %1$s + yyyy-MM-dd hh:mm:ss + Cancel + Change sync account + Remove sync account + Settings + Cannot change the account because sync is in progress + %1$s has been set as the sync account + Notes + Search notes + Searching Notes + Text in your notes + Set reminder + Export successful + Sync is successful with account %1$s + Getting remote note list... + Logging into %1$s... + Synchronize local notes with Google Task... + Sync is canceled + Sync is failed + Sync is successful + Syncing notes... + No associated note found, click to create associated note. + Privacy mode,can not see note content + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Notes/app/build/intermediates/incremental/debug/packageDebugResources/merger.xml b/src/Notes/app/build/intermediates/incremental/debug/packageDebugResources/merger.xml new file mode 100644 index 0000000..5a13304 --- /dev/null +++ b/src/Notes/app/build/intermediates/incremental/debug/packageDebugResources/merger.xml @@ -0,0 +1,56 @@ + + + -%s + --%s + --%s + --%s + + Messaging + Email + #335b5b5b33sp26sp20sp17sp14spNotesNotes 2x2Notes 4x4No associated note found, click to create associated note.Privacy mode,can not see note content...Add noteDelete reminder successfullySet reminderExpiredyyyyMMddMMMd kk:mmGot itTake a lookCallSend emailBrowse webOpen map/MIUI/notes/notes_%s.txt(%d)New FolderExport textSyncCancel syncingSettingsSearchDeleteMove to folder%d selectedNothing selected, the operation is invalidSelect allDeselect allFont sizeSmallMediumLargeSuperEnter check listLeave check listView folderDelete folderChange folder nameThe folder %1$s exist, please renameShareSend to homeRemind meDelete reminderSelect folderParent folderNote added to homeConfirm to delete folder and its notes?Delete selected notesConfirm to delete the selected %d notes?Confirm to delete this note?Have moved selected %1$d notes to %2$s folderSD card busy, not available nowExport failed, please check SD cardThe note is not existSorry, can not set clock on empty noteSorry, can not send and empty note to homeExport successfulExport failExport text file (%1$s) to SD (%2$s) directorySyncing notes...Sync is successfulSync is failedSync is canceledSync is successful with account %1$sSync failed, please check network and account settingsSync failed, internal error occursSync is canceledLogging into %1$s...Getting remote note list...Synchronize local notes with Google Task...SettingsSync accountSync notes with google taskLast sync time %1$syyyy-MM-dd hh:mm:ssAdd accountChange sync accountRemove sync accountCancelSync immediatelyCancel syncingCurrent account %1$sAll sync related information will be deleted, which may result in duplicated items sometimeSync notesPlease select a google account. Local notes will be synced with google task.Cannot change the account because sync is in progress%1$s has been set as the sync accountNew note background color randomDeleteCall notesInput nameSearching NotesSearch notesText in your notesNotessetcancel + %1$s result for \"%2$s\" + + %1$s results for \"%2$s\" + + 短信 + 邮件 + 便签便签2x2便签4x4没有关联内容,点击新建便签。访客模式下,便签内容不可见...新建便签成功删除提醒创建提醒已过期yyyyMMddMM月dd日 kk:mm知道了查看呼叫电话发送邮件浏览网页打开地图新建文件夹导出文本同步取消同步设置搜索删除移动到文件夹选中了 %d 项没有选中项,操作无效全选取消全选文字大小正常超大进入清单模式退出清单模式查看文件夹刪除文件夹修改文件夹名称文件夹 %1$s 已存在,请重新命名分享发送到桌面提醒我删除提醒选择文件夹上一级文件夹已添加到桌面删除确认要删除所选的 %d 条便签吗?确认要删除该条便签吗?确认删除文件夹及所包含的便签吗?已将所选 %1$d 条便签移到 %2$s 文件夹SD卡被占用,不能操作导出文本时发生错误,请检查SD卡要查看的便签不存在不能为空便签设置闹钟提醒不能将空便签发送到桌面导出成功导出失败已将文本文件(%1$s)输出至SD卡(%2$s)目录同步便签...同步成功同步失败同步已取消与%1$s同步成功同步失败,请检查网络和帐号设置同步失败,发生内部错误同步已取消登录%1$s...正在获取服务器便签列表...正在同步本地便签...设置同步账号与google task同步便签记录上次同步于 %1$s添加账号更换账号删除账号取消立即同步取消同步当前帐号 %1$s如更换同步帐号,过去的帐号同步信息将被清空,再次切换的同时可能会造成数据重复同步便签请选择google帐号,便签将与该帐号的google task内容同步。正在同步中,不能修改同步帐号同步帐号已设置为%1$s新建便签背景颜色随机删除通话便签请输入名称正在搜索便签搜索便签便签中的文字便签设置取消 + %1$s 条符合“%2$s”的搜索结果 + + 短信 + 郵件 + 便簽便簽2x2便簽4x4沒有關聯內容,點擊新建便簽。訪客模式下,便籤內容不可見...新建便簽成功刪除提醒創建提醒已過期yyyyMMddMM月dd日 kk:mm知道了查看呼叫電話發送郵件浏覽網頁打開地圖已將所選 %1$d 便籤移到 %2$s 文件夾新建文件夾導出文本同步取消同步設置搜尋刪除移動到文件夾選中了 %d 項沒有選中項,操作無效全選取消全選文字大小正常超大進入清單模式退出清單模式查看文件夾刪除文件夾修改文件夾名稱文件夾 %1$s 已存在,請重新命名分享發送到桌面提醒我刪除提醒選擇文件夾上一級文件夾已添加到桌面刪除确认要刪除所選的 %d 條便籤嗎?确认要删除該條便籤嗎?確認刪除檔夾及所包含的便簽嗎?SD卡被佔用,不能操作導出TXT時發生錯誤,請檢查SD卡要查看的便籤不存在不能爲空便籤設置鬧鐘提醒不能將空便籤發送到桌面導出成功導出失敗已將文本文件(%1$s)導出至SD(%2$s)目錄同步便簽...同步成功同步失敗同步已取消與%1$s同步成功同步失敗,請檢查網絡和帳號設置同步失敗,發生內部錯誤同步已取消登陸%1$s...正在獲取服務器便籤列表...正在同步本地便籤...設置同步賬號与google task同步便簽記錄上次同步于 %1$s添加賬號更換賬號刪除賬號取消立即同步取消同步當前帳號 %1$s如更換同步帳號,過去的帳號同步信息將被清空,再次切換的同時可能會造成數據重復同步便簽請選擇google帳號,便簽將與該帳號的google task內容同步。正在同步中,不能修改同步帳號同步帳號已設置為%1$s新建便籤背景顏色隨機刪除通話便籤請輸入名稱正在搜索便籤搜索便籤便籤中的文字便籤設置取消 + %1$s 條符合”%2$s“的搜尋結果 + \ No newline at end of file diff --git a/src/Notes/app/build/intermediates/incremental/packageDebug/tmp/debug/dex-renamer-state.txt b/src/Notes/app/build/intermediates/incremental/packageDebug/tmp/debug/dex-renamer-state.txt new file mode 100644 index 0000000..f56ec38 --- /dev/null +++ b/src/Notes/app/build/intermediates/incremental/packageDebug/tmp/debug/dex-renamer-state.txt @@ -0,0 +1,4 @@ +#Fri Dec 22 19:43:32 CST 2023 +base.0=D\:\\Study\\SE\\MiNotes\\src\\Notes\\app\\build\\intermediates\\dex\\debug\\mergeDexDebug\\classes.dex +path.0=classes.dex +renamed.0=classes.dex diff --git a/src/Notes/app/build/intermediates/incremental/packageDebug/tmp/debug/zip-cache/androidResources b/src/Notes/app/build/intermediates/incremental/packageDebug/tmp/debug/zip-cache/androidResources new file mode 100644 index 0000000..8106485 Binary files /dev/null and b/src/Notes/app/build/intermediates/incremental/packageDebug/tmp/debug/zip-cache/androidResources differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/data/Contact.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/data/Contact.class new file mode 100644 index 0000000..bac2d9e Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/data/Contact.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/data/Notes$CallNote.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/data/Notes$CallNote.class new file mode 100644 index 0000000..3afa321 Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/data/Notes$CallNote.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/data/Notes$DataConstants.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/data/Notes$DataConstants.class new file mode 100644 index 0000000..dc242b5 Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/data/Notes$DataConstants.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/data/Notes$TextNote.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/data/Notes$TextNote.class new file mode 100644 index 0000000..009e6aa Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/data/Notes$TextNote.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/data/Notes.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/data/Notes.class new file mode 100644 index 0000000..65a3efc Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/data/Notes.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/data/NotesProvider.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/data/NotesProvider.class new file mode 100644 index 0000000..f99eef0 Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/data/NotesProvider.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/model/Note$NoteData.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/model/Note$NoteData.class new file mode 100644 index 0000000..f1d00ac Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/model/Note$NoteData.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/model/Note.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/model/Note.class new file mode 100644 index 0000000..fc87525 Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/model/Note.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/model/WorkingNote.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/model/WorkingNote.class new file mode 100644 index 0000000..88b628d Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/model/WorkingNote.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/AlarmAlertActivity.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/AlarmAlertActivity.class new file mode 100644 index 0000000..c1d88a5 Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/AlarmAlertActivity.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/AlarmInitReceiver.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/AlarmInitReceiver.class new file mode 100644 index 0000000..d3e4981 Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/AlarmInitReceiver.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/AlarmReceiver.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/AlarmReceiver.class new file mode 100644 index 0000000..7118088 Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/AlarmReceiver.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/DateTimePicker$1.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/DateTimePicker$1.class new file mode 100644 index 0000000..4e3869d Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/DateTimePicker$1.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/DateTimePicker$2.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/DateTimePicker$2.class new file mode 100644 index 0000000..57c1d4f Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/DateTimePicker$2.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/DateTimePicker$3.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/DateTimePicker$3.class new file mode 100644 index 0000000..8a7f8a0 Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/DateTimePicker$3.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/DateTimePicker$4.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/DateTimePicker$4.class new file mode 100644 index 0000000..7bf0d6b Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/DateTimePicker$4.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/DateTimePicker.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/DateTimePicker.class new file mode 100644 index 0000000..f185cd1 Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/DateTimePicker.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/DateTimePickerDialog$1.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/DateTimePickerDialog$1.class new file mode 100644 index 0000000..277fe40 Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/DateTimePickerDialog$1.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/DateTimePickerDialog.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/DateTimePickerDialog.class new file mode 100644 index 0000000..aaf3a90 Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/DateTimePickerDialog.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/DropdownMenu$1.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/DropdownMenu$1.class new file mode 100644 index 0000000..7c0d343 Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/DropdownMenu$1.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/DropdownMenu.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/DropdownMenu.class new file mode 100644 index 0000000..03c8ea0 Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/DropdownMenu.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/FoldersListAdapter$FolderListItem.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/FoldersListAdapter$FolderListItem.class new file mode 100644 index 0000000..445ccf4 Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/FoldersListAdapter$FolderListItem.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/FoldersListAdapter.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/FoldersListAdapter.class new file mode 100644 index 0000000..3b93e5e Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/FoldersListAdapter.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NoteEditActivity$1.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NoteEditActivity$1.class new file mode 100644 index 0000000..aecdf92 Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NoteEditActivity$1.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NoteEditActivity$2.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NoteEditActivity$2.class new file mode 100644 index 0000000..ea8b2ac Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NoteEditActivity$2.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NoteEditActivity$3.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NoteEditActivity$3.class new file mode 100644 index 0000000..72aee1f Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NoteEditActivity$3.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NoteEditActivity$HeadViewHolder.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NoteEditActivity$HeadViewHolder.class new file mode 100644 index 0000000..770d355 Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NoteEditActivity$HeadViewHolder.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NoteEditActivity.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NoteEditActivity.class new file mode 100644 index 0000000..b73d823 Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NoteEditActivity.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NoteEditText$1.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NoteEditText$1.class new file mode 100644 index 0000000..ec15e9e Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NoteEditText$1.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NoteEditText.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NoteEditText.class new file mode 100644 index 0000000..eb8ca51 Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NoteEditText.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$1.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$1.class new file mode 100644 index 0000000..55719be Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$1.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$2.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$2.class new file mode 100644 index 0000000..7bf36f7 Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$2.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$3.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$3.class new file mode 100644 index 0000000..98a649c Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$3.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$4.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$4.class new file mode 100644 index 0000000..e28e197 Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$4.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$5.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$5.class new file mode 100644 index 0000000..09164c1 Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$5.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$6.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$6.class new file mode 100644 index 0000000..568cece Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$6.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$7.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$7.class new file mode 100644 index 0000000..950a358 Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$7.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$8.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$8.class new file mode 100644 index 0000000..b1ecc29 Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$8.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$9.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$9.class new file mode 100644 index 0000000..649a9d5 Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$9.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$BackgroundQueryHandler.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$BackgroundQueryHandler.class new file mode 100644 index 0000000..bd775e5 Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$BackgroundQueryHandler.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$ListEditState.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$ListEditState.class new file mode 100644 index 0000000..8b436df Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$ListEditState.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$ModeCallback$1.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$ModeCallback$1.class new file mode 100644 index 0000000..5b7c24d Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$ModeCallback$1.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$ModeCallback$2.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$ModeCallback$2.class new file mode 100644 index 0000000..004bb5a Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$ModeCallback$2.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$ModeCallback.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$ModeCallback.class new file mode 100644 index 0000000..8ed8a58 Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$ModeCallback.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$NewNoteOnTouchListener.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$NewNoteOnTouchListener.class new file mode 100644 index 0000000..3afadbf Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$NewNoteOnTouchListener.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$OnListItemClickListener.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$OnListItemClickListener.class new file mode 100644 index 0000000..2699bd3 Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity$OnListItemClickListener.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity.class new file mode 100644 index 0000000..0223718 Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListActivity.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListAdapter$AppWidgetAttribute.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListAdapter$AppWidgetAttribute.class new file mode 100644 index 0000000..5c2072b Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListAdapter$AppWidgetAttribute.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListAdapter.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListAdapter.class new file mode 100644 index 0000000..0dfa320 Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListAdapter.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListItem.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListItem.class new file mode 100644 index 0000000..99150f6 Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesListItem.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesPreferenceActivity$1.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesPreferenceActivity$1.class new file mode 100644 index 0000000..4d5c288 Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesPreferenceActivity$1.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesPreferenceActivity$2.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesPreferenceActivity$2.class new file mode 100644 index 0000000..47c78ef Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesPreferenceActivity$2.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesPreferenceActivity$3.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesPreferenceActivity$3.class new file mode 100644 index 0000000..baa7752 Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesPreferenceActivity$3.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesPreferenceActivity$4.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesPreferenceActivity$4.class new file mode 100644 index 0000000..1cc346a Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesPreferenceActivity$4.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesPreferenceActivity$5.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesPreferenceActivity$5.class new file mode 100644 index 0000000..320d722 Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesPreferenceActivity$5.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesPreferenceActivity$6.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesPreferenceActivity$6.class new file mode 100644 index 0000000..0f88f2b Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesPreferenceActivity$6.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesPreferenceActivity$7.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesPreferenceActivity$7.class new file mode 100644 index 0000000..4691731 Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesPreferenceActivity$7.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesPreferenceActivity$8.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesPreferenceActivity$8.class new file mode 100644 index 0000000..56f7e5c Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesPreferenceActivity$8.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesPreferenceActivity$GTaskReceiver.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesPreferenceActivity$GTaskReceiver.class new file mode 100644 index 0000000..8329599 Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesPreferenceActivity$GTaskReceiver.class differ diff --git a/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesPreferenceActivity.class b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesPreferenceActivity.class new file mode 100644 index 0000000..6c4e83e Binary files /dev/null and b/src/Notes/app/build/intermediates/javac/debug/classes/net/micode/notes/ui/NotesPreferenceActivity.class differ diff --git a/src/Notes/app/build/intermediates/manifest_merge_blame_file/debug/manifest-merger-blame-debug-report.txt b/src/Notes/app/build/intermediates/manifest_merge_blame_file/debug/manifest-merger-blame-debug-report.txt new file mode 100644 index 0000000..2d292cc --- /dev/null +++ b/src/Notes/app/build/intermediates/manifest_merge_blame_file/debug/manifest-merger-blame-debug-report.txt @@ -0,0 +1,287 @@ +1 +2 +17 +21 +22 D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:23:5-44 +23 android:minSdkVersion="14" +23-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:23:15-41 +24 android:targetSdkVersion="29" /> +25 +26 +26-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:25:5-81 +26-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:25:22-78 +27 +27-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:26:5-88 +27-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:26:22-85 +28 +28-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:27:5-67 +28-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:27:22-64 +29 +29-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:28:5-72 +29-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:28:22-69 +30 +30-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:29:5-74 +30-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:29:22-71 +31 +31-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:30:5-80 +31-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:30:22-77 +32 +32-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:31:5-71 +32-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:31:22-68 +33 +33-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:32:5-74 +33-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:32:22-71 +34 +34-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:33:5-81 +34-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:33:22-78 +35 +36 D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:35:5-150:19 +37 android:debuggable="true" +38 android:extractNativeLibs="true" +39 android:icon="@drawable/icon_app" +39-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:36:9-42 +40 android:label="@string/app_name" +40-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:37:9-41 +41 android:testOnly="true" > +42 D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:38:9-51:20 +43 android:name="net.micode.notes.ui.NotesListActivity" +43-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:39:13-49 +44 android:configChanges="keyboardHidden|orientation|screenSize" +44-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:41:13-74 +45 android:exported="true" +45-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:40:13-36 +46 android:label="@string/app_name" +46-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:42:13-45 +47 android:launchMode="singleTop" +47-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:43:13-43 +48 android:theme="@style/NoteTheme" +48-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:44:13-45 +49 android:windowSoftInputMode="adjustPan" > +49-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:45:13-52 +50 +50-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:47:13-50:29 +51 +51-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:48:17-69 +51-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:48:25-66 +52 +53 +53-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:49:17-77 +53-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:49:27-74 +54 +55 +56 D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:53:9-82:20 +57 android:name="net.micode.notes.ui.NoteEditActivity" +57-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:54:13-48 +58 android:configChanges="keyboardHidden|orientation|screenSize" +58-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:55:13-74 +59 android:launchMode="singleTop" +59-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:56:13-43 +60 android:theme="@style/NoteTheme" > +60-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:57:13-45 +61 +61-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:59:13-64:29 +62 +62-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:60:17-69 +62-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:60:25-66 +63 +64 +64-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:61:17-76 +64-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:61:27-73 +65 +66 D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:62:17-167 +67 android:host="com.example.notes.provider" +67-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:62:101-142 +68 android:mimeType="vnd.android.cursor.item/text_note" +68-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:62:23-75 +69 android:path="/notes" +69-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:62:143-164 +70 android:scheme="content" /> +70-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:62:76-100 +71 D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:62:17-167 +72 android:host="com.example.notes.provider" +72-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:62:101-142 +73 android:mimeType="vnd.android.cursor.item/call_note" +73-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:62:23-75 +74 android:path="/notes" +74-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:62:143-164 +75 android:scheme="content" /> +75-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:62:76-100 +76 +77 +77-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:67:13-72:29 +78 +78-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:68:17-79 +78-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:68:25-76 +79 +80 +80-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:61:17-76 +80-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:61:27-73 +81 +82 +82-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:62:17-167 +82-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:62:23-75 +83 +83-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:62:17-167 +83-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:62:23-75 +84 +85 +85-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:74:13-77:29 +86 +86-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:75:17-71 +86-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:75:25-68 +87 +88 +88-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:61:17-76 +88-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:61:27-73 +89 +90 +91 D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:79:13-81:54 +92 android:name="android.app.searchable" +92-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:80:17-54 +93 android:resource="@xml/searchable" /> +93-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:81:17-51 +94 +95 +96 D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:84:9-87:43 +97 android:name="net.micode.notes.data.NotesProvider" +97-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:85:13-63 +98 android:authorities="micode_notes" +98-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:86:13-47 +99 android:multiprocess="true" /> +99-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:87:13-40 +100 +101 D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:89:9-101:20 +102 android:name="net.micode.notes.widget.NoteWidgetProvider_2x" +102-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:90:13-57 +103 android:label="@string/app_widget2x2" > +103-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:91:13-50 +104 +104-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:92:13-96:29 +105 +105-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:93:17-84 +105-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:93:25-81 +106 +106-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:94:17-85 +106-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:94:25-82 +107 +107-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:95:17-85 +107-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:95:25-82 +108 +109 +110 D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:98:13-100:58 +111 android:name="android.appwidget.provider" +111-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:99:17-58 +112 android:resource="@xml/widget_2x_info" /> +112-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:100:17-55 +113 +114 D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:102:9-115:20 +115 android:name="net.micode.notes.widget.NoteWidgetProvider_4x" +115-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:103:13-57 +116 android:label="@string/app_widget4x4" > +116-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:104:13-50 +117 +117-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:92:13-96:29 +118 +118-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:93:17-84 +118-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:93:25-81 +119 +119-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:94:17-85 +119-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:94:25-82 +120 +120-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:95:17-85 +120-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:95:25-82 +121 +122 +123 D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:98:13-100:58 +124 android:name="android.appwidget.provider" +124-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:99:17-58 +125 android:resource="@xml/widget_4x_info" /> +125-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:100:17-55 +126 +127 +127-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:117:9-121:20 +127-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:117:19-55 +128 +128-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:118:13-120:29 +129 +129-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:119:17-79 +129-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:119:25-76 +130 +131 +132 D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:123:9-126:20 +133 android:name="net.micode.notes.ui.AlarmReceiver" +133-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:124:13-61 +134 android:process=":remote" > +134-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:125:13-38 +135 +136 +137 D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:128:9-133:20 +138 android:name="net.micode.notes.ui.AlarmAlertActivity" +138-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:129:13-50 +139 android:label="@string/app_name" +139-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:130:13-45 +140 android:launchMode="singleInstance" +140-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:131:13-48 +141 android:theme="@android:style/Theme.Holo.Wallpaper.NoTitleBar" > +141-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:132:13-75 +142 +143 D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:135:9-140:20 +144 android:name="net.micode.notes.ui.NotesPreferenceActivity" +144-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:136:13-71 +145 android:label="@string/preferences_title" +145-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:137:13-54 +146 android:launchMode="singleTop" +146-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:138:13-43 +147 android:theme="@android:style/Theme.Holo.Light" > +147-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:139:13-60 +148 +149 +150 D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:142:9-145:19 +151 android:name="net.micode.notes.gtask.remote.GTaskSyncService" +151-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:143:13-74 +152 android:exported="false" > +152-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:144:13-37 +153 +154 +155 D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:147:9-149:52 +156 android:name="android.app.default_searchable" +156-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:148:13-58 +157 android:value=".ui.NoteEditActivity" /> +157-->D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:149:13-49 +158 +159 +160 diff --git a/src/Notes/app/build/intermediates/merged_manifest/debug/AndroidManifest.xml b/src/Notes/app/build/intermediates/merged_manifest/debug/AndroidManifest.xml new file mode 100644 index 0000000..10e3b54 --- /dev/null +++ b/src/Notes/app/build/intermediates/merged_manifest/debug/AndroidManifest.xml @@ -0,0 +1,160 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Notes/app/build/intermediates/merged_manifests/debug/AndroidManifest.xml b/src/Notes/app/build/intermediates/merged_manifests/debug/AndroidManifest.xml new file mode 100644 index 0000000..10e3b54 --- /dev/null +++ b/src/Notes/app/build/intermediates/merged_manifests/debug/AndroidManifest.xml @@ -0,0 +1,160 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Notes/app/build/intermediates/merged_res/debug/values_values.arsc.flat b/src/Notes/app/build/intermediates/merged_res/debug/values_values.arsc.flat new file mode 100644 index 0000000..577bcf3 Binary files /dev/null and b/src/Notes/app/build/intermediates/merged_res/debug/values_values.arsc.flat differ diff --git a/src/Notes/app/build/intermediates/merged_res_blame_folder/debug/out/multi-v2/debug.json b/src/Notes/app/build/intermediates/merged_res_blame_folder/debug/out/multi-v2/debug.json new file mode 100644 index 0000000..57244ef --- /dev/null +++ b/src/Notes/app/build/intermediates/merged_res_blame_folder/debug/out/multi-v2/debug.json @@ -0,0 +1,272 @@ +{ + "logs": [ + { + "outputFile": "C:\\Users\\11\\.gradle\\daemon\\8.0\\net.micode.notes.app-merged_res-4:\\values-zh-rTW_values-zh-rTW.arsc.flat", + "map": [ + { + "source": "D:\\Study\\SE\\MiNotes\\src\\Notes\\app\\src\\main\\res\\values-zh-rTW\\strings.xml", + "from": { + "startLines": "122,72,71,70,69,19,20,21,112,113,121,120,26,76,77,75,74,73,89,88,87,79,61,29,30,80,37,114,68,64,39,45,50,40,60,59,58,54,53,51,52,55,56,46,67,57,65,44,49,48,47,63,43,62,41,42,66,28,34,36,33,35,32,31,25,24,96,95,98,110,103,102,104,105,107,106,97,101,99,100,94,108,109,119,117,116,118,27,78,86,91,90,92,85,84,83,82,22,23", + "startColumns": "4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4", + "startOffsets": "6629,3647,3580,3506,3455,846,887,936,6169,6215,6574,6523,1225,3911,3980,3851,3784,3720,4654,4593,4529,4114,3018,1398,1452,4169,1805,6273,3395,3178,1917,2204,2455,1971,2957,2903,2851,2652,2603,2507,2556,2699,2747,2248,3335,2798,3222,2160,2407,2350,2294,3122,2115,3079,2022,2064,3275,1346,1657,1755,1609,1707,1561,1515,1173,1119,5036,4976,5179,6092,5495,5425,5560,5639,5824,5750,5112,5369,5237,5303,4926,5929,6013,6484,6375,6326,6421,1288,4058,4469,4769,4710,4837,4420,4374,4325,4273,985,1052", + "endLines": "124,72,71,70,69,19,20,21,112,113,121,120,26,76,77,75,74,73,89,88,87,79,61,29,30,80,37,114,68,64,39,45,50,40,60,59,58,54,53,51,52,55,56,46,67,57,65,44,49,48,47,63,43,62,41,42,66,28,34,36,33,35,32,31,25,24,96,95,98,110,103,102,104,105,107,106,97,101,99,100,94,108,109,119,117,116,118,27,78,86,91,90,92,85,84,83,82,22,23", + "endColumns": "14,71,65,72,49,39,47,47,44,56,53,49,61,67,76,58,65,62,54,59,62,53,59,52,61,83,79,49,58,42,52,42,50,49,59,52,50,45,47,47,45,46,49,44,58,51,51,42,46,55,54,54,43,41,40,49,58,50,48,48,46,46,46,44,50,52,74,58,56,73,63,68,77,109,103,72,65,54,64,64,48,82,77,37,44,47,61,56,54,58,66,57,61,47,44,47,50,65,65", + "endOffsets": "6803,3714,3641,3574,3500,881,930,979,6209,6267,6623,6568,1282,3974,4052,3905,3845,3778,4704,4648,4587,4163,3073,1446,1509,4248,1880,6318,3449,3216,1965,2242,2501,2016,3012,2951,2897,2693,2646,2550,2597,2741,2792,2288,3389,2845,3269,2198,2449,2401,2344,3172,2154,3116,2058,2109,3329,1392,1701,1799,1651,1749,1603,1555,1219,1167,5106,5030,5231,6161,5554,5489,5633,5744,5923,5818,5173,5419,5297,5363,4970,6007,6086,6517,6415,6369,6478,1340,4108,4523,4831,4763,4894,4463,4414,4368,4319,1046,1113" + }, + "to": { + "startLines": "6,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106", + "startColumns": "4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4", + "startOffsets": "215,384,456,522,595,645,685,733,781,826,883,937,987,1049,1117,1194,1253,1319,1382,1437,1497,1560,1614,1674,1727,1789,1873,1953,2003,2062,2105,2158,2201,2252,2302,2362,2415,2466,2512,2560,2608,2654,2701,2751,2796,2855,2907,2959,3002,3049,3105,3160,3215,3259,3301,3342,3392,3451,3502,3551,3600,3647,3694,3741,3786,3837,3890,3965,4024,4081,4155,4219,4288,4366,4476,4580,4653,4719,4774,4839,4904,4953,5036,5114,5152,5197,5245,5307,5364,5419,5478,5545,5603,5665,5713,5758,5806,5857,5923", + "endLines": "8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106", + "endColumns": "14,71,65,72,49,39,47,47,44,56,53,49,61,67,76,58,65,62,54,59,62,53,59,52,61,83,79,49,58,42,52,42,50,49,59,52,50,45,47,47,45,46,49,44,58,51,51,42,46,55,54,54,43,41,40,49,58,50,48,48,46,46,46,44,50,52,74,58,56,73,63,68,77,109,103,72,65,54,64,64,48,82,77,37,44,47,61,56,54,58,66,57,61,47,44,47,50,65,65", + "endOffsets": "379,451,517,590,640,680,728,776,821,878,932,982,1044,1112,1189,1248,1314,1377,1432,1492,1555,1609,1669,1722,1784,1868,1948,1998,2057,2100,2153,2196,2247,2297,2357,2410,2461,2507,2555,2603,2649,2696,2746,2791,2850,2902,2954,2997,3044,3100,3155,3210,3254,3296,3337,3387,3446,3497,3546,3595,3642,3689,3736,3781,3832,3885,3960,4019,4076,4150,4214,4283,4361,4471,4575,4648,4714,4769,4834,4899,4948,5031,5109,5147,5192,5240,5302,5359,5414,5473,5540,5598,5660,5708,5753,5801,5852,5918,5984" + } + }, + { + "source": "D:\\Study\\SE\\MiNotes\\src\\Notes\\app\\src\\main\\res\\values-zh-rTW\\arrays.xml", + "from": { + "startLines": "18", + "startColumns": "4", + "startOffsets": "730", + "endLines": "21", + "endColumns": "19", + "endOffsets": "838" + }, + "to": { + "startLines": "2", + "startColumns": "4", + "startOffsets": "105", + "endLines": "5", + "endColumns": "19", + "endOffsets": "210" + } + } + ] + }, + { + "outputFile": "net.micode.notes.app-merged_res-4:/values_values.arsc.flat", + "map": [ + { + "source": "D:\\Study\\SE\\MiNotes\\src\\Notes\\app\\src\\main\\res\\values\\dimens.xml", + "from": { + "startLines": "-1,-1,-1,-1,-1", + "startColumns": "-1,-1,-1,-1,-1", + "startOffsets": "-1,-1,-1,-1,-1" + }, + "to": { + "startLines": "13,14,15,16,17", + "startColumns": "4,4,4,4,4", + "startOffsets": "477,529,582,635,687", + "endColumns": "51,52,52,51,51", + "endOffsets": "524,577,630,682,734" + } + }, + { + "source": "D:\\Study\\SE\\MiNotes\\src\\Notes\\app\\src\\main\\res\\values\\arrays.xml", + "from": { + "startLines": "-1,-1", + "startColumns": "-1,-1", + "startOffsets": "-1,-1" + }, + "to": { + "startLines": "2,8", + "startColumns": "4,4", + "startOffsets": "105,300", + "endLines": "7,11", + "endColumns": "19,19", + "endOffsets": "295,415" + } + }, + { + "source": "D:\\Study\\SE\\MiNotes\\src\\Notes\\app\\src\\main\\res\\values\\colors.xml", + "from": { + "startLines": "-1", + "startColumns": "-1", + "startOffsets": "-1" + }, + "to": { + "startLines": "12", + "startColumns": "4", + "startOffsets": "420", + "endColumns": "56", + "endOffsets": "472" + } + }, + { + "source": "D:\\Study\\SE\\MiNotes\\src\\Notes\\app\\src\\main\\res\\values\\strings.xml", + "from": { + "startLines": "-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1", + "startColumns": "-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1", + "startOffsets": "-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1" + }, + "to": { + "startLines": "18,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124", + "startColumns": "4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4", + "startOffsets": "739,1092,1188,1271,1367,1436,1479,1531,1583,1632,1695,1753,1804,1888,1982,2090,2161,2245,2328,2394,2477,2579,2640,2702,2753,2831,2884,2944,3049,3108,3210,3265,3336,3385,3443,3490,3549,3606,3677,3738,3795,3845,3897,3950,4000,4050,4110,4163,4229,4291,4354,4401,4456,4544,4602,4664,4714,4759,4802,4862,4929,4984,5039,5092,5139,5192,5248,5296,5351,5404,5488,5555,5619,5715,5789,5870,5959,6122,6266,6345,6420,6502,6561,6641,6721,6776,6898,7001,7042,7095,7152,7226,7291,7359,7445,7525,7594,7688,7747,7802,7864,7924,8034", + "endLines": "22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124", + "endColumns": "14,95,82,95,68,42,51,51,48,62,57,50,83,93,107,70,83,82,65,82,101,60,61,50,77,52,59,104,58,101,54,70,48,57,46,58,56,70,60,56,49,51,52,49,49,59,52,65,61,62,46,54,87,57,61,49,44,42,59,66,54,54,52,46,52,55,47,54,52,83,66,63,95,73,80,88,162,143,78,74,81,58,79,79,54,121,102,40,52,56,73,64,67,85,79,68,93,58,54,61,59,109,89", + "endOffsets": "1087,1183,1266,1362,1431,1474,1526,1578,1627,1690,1748,1799,1883,1977,2085,2156,2240,2323,2389,2472,2574,2635,2697,2748,2826,2879,2939,3044,3103,3205,3260,3331,3380,3438,3485,3544,3601,3672,3733,3790,3840,3892,3945,3995,4045,4105,4158,4224,4286,4349,4396,4451,4539,4597,4659,4709,4754,4797,4857,4924,4979,5034,5087,5134,5187,5243,5291,5346,5399,5483,5550,5614,5710,5784,5865,5954,6117,6261,6340,6415,6497,6556,6636,6716,6771,6893,6996,7037,7090,7147,7221,7286,7354,7440,7520,7589,7683,7742,7797,7859,7919,8029,8119" + } + }, + { + "source": "D:\\Study\\SE\\MiNotes\\src\\Notes\\app\\src\\main\\res\\values\\styles.xml", + "from": { + "startLines": "-1,-1,64,-1,-1,-1,-1,-1,-1,-1,-1", + "startColumns": "-1,-1,4,-1,-1,-1,-1,-1,-1,-1,-1", + "startOffsets": "-1,-1,2704,-1,-1,-1,-1,-1,-1,-1,-1", + "endLines": "-1,-1,67,-1,-1,-1,-1,-1,-1,-1,-1", + "endColumns": "-1,-1,12,-1,-1,-1,-1,-1,-1,-1,-1", + "endOffsets": "-1,-1,2913,-1,-1,-1,-1,-1,-1,-1,-1" + }, + "to": { + "startLines": "125,129,133,137,140,144,148,152,156,160,164", + "startColumns": "4,4,4,4,4,4,4,4,4,4,4", + "startOffsets": "8124,8328,8539,8749,8909,9092,9277,9462,9665,9871,10054", + "endLines": "128,132,136,139,143,147,151,155,159,163,167", + "endColumns": "12,12,12,12,12,12,12,12,12,12,12", + "endOffsets": "8323,8534,8744,8904,9087,9272,9457,9660,9866,10049,10250" + } + } + ] + }, + { + "outputFile": "C:\\Users\\11\\.gradle\\daemon\\8.0\\net.micode.notes.app-merged_res-4:\\values_values.arsc.flat", + "map": [ + { + "source": "D:\\Study\\SE\\MiNotes\\src\\Notes\\app\\src\\main\\res\\values\\dimens.xml", + "from": { + "startLines": "19,20,21,22,18", + "startColumns": "4,4,4,4,4", + "startOffsets": "783,836,890,944,730", + "endColumns": "51,52,52,51,51", + "endOffsets": "830,884,938,991,777" + }, + "to": { + "startLines": "13,14,15,16,17", + "startColumns": "4,4,4,4,4", + "startOffsets": "477,529,582,635,687", + "endColumns": "51,52,52,51,51", + "endOffsets": "524,577,630,682,734" + } + }, + { + "source": "D:\\Study\\SE\\MiNotes\\src\\Notes\\app\\src\\main\\res\\values\\arrays.xml", + "from": { + "startLines": "19,26", + "startColumns": "4,4", + "startOffsets": "758,1073", + "endLines": "24,29", + "endColumns": "19,19", + "endOffsets": "1065,1191" + }, + "to": { + "startLines": "2,8", + "startColumns": "4,4", + "startOffsets": "105,300", + "endLines": "7,11", + "endColumns": "19,19", + "endOffsets": "295,415" + } + }, + { + "source": "D:\\Study\\SE\\MiNotes\\src\\Notes\\app\\src\\main\\res\\values\\colors.xml", + "from": { + "startLines": "18", + "startColumns": "4", + "startOffsets": "730", + "endColumns": "56", + "endOffsets": "782" + }, + "to": { + "startLines": "12", + "startColumns": "4", + "startOffsets": "420", + "endColumns": "56", + "endOffsets": "472" + } + }, + { + "source": "D:\\Study\\SE\\MiNotes\\src\\Notes\\app\\src\\main\\res\\values\\strings.xml", + "from": { + "startLines": "128,72,75,74,73,19,20,21,118,119,127,126,26,81,82,80,79,78,94,93,92,84,39,38,64,29,30,85,41,76,120,71,67,42,48,53,43,63,62,61,57,56,54,55,58,59,49,70,60,68,47,52,51,50,66,46,65,44,45,69,28,34,36,33,35,32,31,25,24,101,100,104,116,109,108,110,111,113,112,102,103,107,105,106,99,114,115,125,123,122,124,27,83,91,96,95,97,90,89,88,87,22,23", + "startColumns": "4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4", + "startOffsets": "8136,3970,4234,4137,4067,846,890,943,7624,7674,8077,8025,1308,4694,4789,4622,4537,4453,5668,5584,5481,4967,2043,1991,3461,1515,1569,5029,2138,4318,7738,3898,3649,2198,2519,2825,2257,3389,3327,3269,3043,2990,2885,2939,3094,3145,2567,3831,3206,3699,2471,2769,2680,2621,3586,2420,3540,2315,2359,3763,1459,1784,1894,1736,1840,1679,1630,1252,1198,6131,6063,6375,7525,6744,6662,6819,6909,7153,7073,6216,6292,6602,6440,6521,6007,7298,7421,7983,7854,7796,7908,1393,4898,5394,5805,5735,5886,5334,5278,5215,5154,996,1107", + "endLines": "132,72,75,74,73,19,20,21,118,119,127,126,26,81,82,80,79,78,94,93,92,84,39,38,64,29,30,85,41,76,120,71,67,42,48,53,43,63,62,61,57,56,54,55,58,59,49,70,60,68,47,52,51,50,66,46,65,44,45,69,28,34,36,33,35,32,31,25,24,101,100,104,116,109,108,110,111,113,112,102,103,107,105,106,99,114,115,125,123,122,124,27,83,91,96,95,97,90,89,88,87,22,23", + "endColumns": "14,95,82,95,68,42,51,51,48,62,57,50,83,93,107,70,83,82,65,82,101,60,61,50,77,52,59,104,58,101,54,70,48,57,46,58,56,70,60,56,49,51,52,49,49,59,52,65,61,62,46,54,87,57,61,49,44,42,59,66,54,54,52,46,52,55,47,54,52,83,66,63,95,73,80,88,162,143,78,74,81,58,79,79,54,121,102,40,52,56,73,64,67,85,79,68,93,58,54,61,59,109,89", + "endOffsets": "8544,4061,4312,4228,4131,884,937,990,7668,7732,8130,8071,1387,4783,4892,4688,4616,4531,5729,5662,5578,5023,2100,2037,3534,1563,1624,5129,2192,4415,7788,3964,3693,2251,2561,2879,2309,3455,3383,3321,3088,3037,2933,2984,3139,3200,2615,3892,3263,3757,2513,2819,2763,2674,3643,2465,3580,2353,2414,3825,1509,1834,1942,1778,1888,1730,1673,1302,1246,6210,6125,6434,7616,6813,6738,6903,7067,7292,7147,6286,6369,6656,6515,6596,6057,7415,7519,8019,7902,7848,7977,1453,4961,5475,5880,5799,5975,5388,5328,5272,5209,1101,1192" + }, + "to": { + "startLines": "18,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124", + "startColumns": "4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4", + "startOffsets": "739,1092,1188,1271,1367,1436,1479,1531,1583,1632,1695,1753,1804,1888,1982,2090,2161,2245,2328,2394,2477,2579,2640,2702,2753,2831,2884,2944,3049,3108,3210,3265,3336,3385,3443,3490,3549,3606,3677,3738,3795,3845,3897,3950,4000,4050,4110,4163,4229,4291,4354,4401,4456,4544,4602,4664,4714,4759,4802,4862,4929,4984,5039,5092,5139,5192,5248,5296,5351,5404,5488,5555,5619,5715,5789,5870,5959,6122,6266,6345,6420,6502,6561,6641,6721,6776,6898,7001,7042,7095,7152,7226,7291,7359,7445,7525,7594,7688,7747,7802,7864,7924,8034", + "endLines": "22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124", + "endColumns": "14,95,82,95,68,42,51,51,48,62,57,50,83,93,107,70,83,82,65,82,101,60,61,50,77,52,59,104,58,101,54,70,48,57,46,58,56,70,60,56,49,51,52,49,49,59,52,65,61,62,46,54,87,57,61,49,44,42,59,66,54,54,52,46,52,55,47,54,52,83,66,63,95,73,80,88,162,143,78,74,81,58,79,79,54,121,102,40,52,56,73,64,67,85,79,68,93,58,54,61,59,109,89", + "endOffsets": "1087,1183,1266,1362,1431,1474,1526,1578,1627,1690,1748,1799,1883,1977,2085,2156,2240,2323,2389,2472,2574,2635,2697,2748,2826,2879,2939,3044,3103,3205,3260,3331,3380,3438,3485,3544,3601,3672,3733,3790,3840,3892,3945,3995,4045,4105,4158,4224,4286,4349,4396,4451,4539,4597,4659,4709,4754,4797,4857,4924,4979,5034,5087,5134,5187,5243,5291,5346,5399,5483,5550,5614,5710,5784,5865,5954,6117,6261,6340,6415,6497,6556,6636,6716,6771,6893,6996,7037,7090,7147,7221,7286,7354,7440,7520,7589,7683,7742,7797,7859,7919,8029,8119" + } + }, + { + "source": "D:\\Study\\SE\\MiNotes\\src\\Notes\\app\\src\\main\\res\\values\\styles.xml", + "from": { + "startLines": "50,55,64,60,22,26,30,35,40,18,45", + "startColumns": "4,4,4,4,4,4,4,4,4,4,4", + "startOffsets": "2112,2322,2704,2539,917,1104,1293,1484,1693,730,1905", + "endLines": "53,58,67,62,25,29,33,38,43,21,48", + "endColumns": "12,12,12,12,12,12,12,12,12,12,12", + "endOffsets": "2314,2531,2910,2696,1098,1287,1476,1685,1897,911,2104" + }, + "to": { + "startLines": "125,129,133,137,140,144,148,152,156,160,164", + "startColumns": "4,4,4,4,4,4,4,4,4,4,4", + "startOffsets": "8124,8328,8539,8746,8906,9089,9274,9459,9662,9868,10051", + "endLines": "128,132,136,139,143,147,151,155,159,163,167", + "endColumns": "12,12,12,12,12,12,12,12,12,12,12", + "endOffsets": "8323,8534,8741,8901,9084,9269,9454,9657,9863,10046,10247" + } + } + ] + }, + { + "outputFile": "C:\\Users\\11\\.gradle\\daemon\\8.0\\net.micode.notes.app-merged_res-4:\\values-zh-rCN_values-zh-rCN.arsc.flat", + "map": [ + { + "source": "D:\\Study\\SE\\MiNotes\\src\\Notes\\app\\src\\main\\res\\values-zh-rCN\\strings.xml", + "from": { + "startLines": "121,71,70,69,68,19,20,21,112,113,120,119,26,77,78,76,75,74,90,89,88,80,60,29,30,81,72,114,67,63,38,44,49,39,59,58,57,53,52,50,51,54,55,45,66,56,64,43,48,47,46,62,42,61,40,41,65,28,34,36,33,35,32,31,25,24,97,96,99,111,104,103,105,106,108,107,98,102,100,101,95,109,110,118,116,115,117,27,79,87,92,91,93,86,85,84,83,22,23", + "startColumns": "4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4", + "startOffsets": "6653,3566,3499,3425,3374,846,887,936,6195,6241,6598,6547,1225,3938,4007,3878,3812,3748,4682,4621,4557,4141,2937,1398,1452,4196,3640,6299,3314,3097,1836,2123,2374,1890,2876,2822,2770,2571,2522,2426,2475,2618,2666,2167,3254,2717,3141,2079,2326,2269,2213,3041,2034,2998,1941,1983,3194,1346,1657,1755,1609,1707,1561,1515,1173,1119,5064,5004,5207,6120,5523,5453,5588,5667,5852,5778,5140,5397,5265,5331,4954,5957,6041,6508,6399,6350,6445,1288,4085,4497,4797,4738,4865,4448,4402,4353,4301,985,1052", + "endLines": "123,71,70,69,68,19,20,21,112,113,120,119,26,77,78,76,75,74,90,89,88,80,60,29,30,81,72,114,67,63,38,44,49,39,59,58,57,53,52,50,51,54,55,45,66,56,64,43,48,47,46,62,42,61,40,41,65,28,34,36,33,35,32,31,25,24,97,96,99,111,104,103,105,106,108,107,98,102,100,101,95,109,110,118,116,115,117,27,79,87,92,91,93,86,85,84,83,22,23", + "endColumns": "14,72,65,72,49,39,47,47,44,56,53,49,61,67,76,58,64,62,54,59,62,53,59,52,61,84,80,49,58,42,52,42,50,49,59,52,50,45,47,47,45,46,49,44,58,51,51,42,46,55,54,54,43,41,40,49,58,50,48,48,46,46,46,44,50,52,74,58,56,73,63,68,77,109,103,72,65,54,64,64,48,82,77,37,44,47,61,56,54,58,66,57,61,47,44,47,50,65,65", + "endOffsets": "6827,3634,3560,3493,3419,881,930,979,6235,6293,6647,6592,1282,4001,4079,3932,3872,3806,4732,4676,4615,4190,2992,1446,1509,4276,3716,6344,3368,3135,1884,2161,2420,1935,2931,2870,2816,2612,2565,2469,2516,2660,2711,2207,3308,2764,3188,2117,2368,2320,2263,3091,2073,3035,1977,2028,3248,1392,1701,1799,1651,1749,1603,1555,1219,1167,5134,5058,5259,6189,5582,5517,5661,5772,5951,5846,5201,5447,5325,5391,4998,6035,6114,6541,6439,6393,6502,1340,4135,4551,4859,4791,4922,4491,4442,4396,4347,1046,1113" + }, + "to": { + "startLines": "6,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106", + "startColumns": "4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4", + "startOffsets": "215,384,457,523,596,646,686,734,782,827,884,938,988,1050,1118,1195,1254,1319,1382,1437,1497,1560,1614,1674,1727,1789,1874,1955,2005,2064,2107,2160,2203,2254,2304,2364,2417,2468,2514,2562,2610,2656,2703,2753,2798,2857,2909,2961,3004,3051,3107,3162,3217,3261,3303,3344,3394,3453,3504,3553,3602,3649,3696,3743,3788,3839,3892,3967,4026,4083,4157,4221,4290,4368,4478,4582,4655,4721,4776,4841,4906,4955,5038,5116,5154,5199,5247,5309,5366,5421,5480,5547,5605,5667,5715,5760,5808,5859,5925", + "endLines": "8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106", + "endColumns": "14,72,65,72,49,39,47,47,44,56,53,49,61,67,76,58,64,62,54,59,62,53,59,52,61,84,80,49,58,42,52,42,50,49,59,52,50,45,47,47,45,46,49,44,58,51,51,42,46,55,54,54,43,41,40,49,58,50,48,48,46,46,46,44,50,52,74,58,56,73,63,68,77,109,103,72,65,54,64,64,48,82,77,37,44,47,61,56,54,58,66,57,61,47,44,47,50,65,65", + "endOffsets": "379,452,518,591,641,681,729,777,822,879,933,983,1045,1113,1190,1249,1314,1377,1432,1492,1555,1609,1669,1722,1784,1869,1950,2000,2059,2102,2155,2198,2249,2299,2359,2412,2463,2509,2557,2605,2651,2698,2748,2793,2852,2904,2956,2999,3046,3102,3157,3212,3256,3298,3339,3389,3448,3499,3548,3597,3644,3691,3738,3783,3834,3887,3962,4021,4078,4152,4216,4285,4363,4473,4577,4650,4716,4771,4836,4901,4950,5033,5111,5149,5194,5242,5304,5361,5416,5475,5542,5600,5662,5710,5755,5803,5854,5920,5986" + } + }, + { + "source": "D:\\Study\\SE\\MiNotes\\src\\Notes\\app\\src\\main\\res\\values-zh-rCN\\arrays.xml", + "from": { + "startLines": "18", + "startColumns": "4", + "startOffsets": "730", + "endLines": "21", + "endColumns": "19", + "endOffsets": "838" + }, + "to": { + "startLines": "2", + "startColumns": "4", + "startOffsets": "105", + "endLines": "5", + "endColumns": "19", + "endOffsets": "210" + } + } + ] + } + ] +} \ No newline at end of file diff --git a/src/Notes/app/build/intermediates/merged_res_blame_folder/debug/out/multi-v2/values.json b/src/Notes/app/build/intermediates/merged_res_blame_folder/debug/out/multi-v2/values.json new file mode 100644 index 0000000..79e33e9 --- /dev/null +++ b/src/Notes/app/build/intermediates/merged_res_blame_folder/debug/out/multi-v2/values.json @@ -0,0 +1,186 @@ +{ + "logs": [ + { + "outputFile": "C:\\Users\\11\\.gradle\\daemon\\8.0\\net.micode.notes.app-mergeDebugResources-2:\\values\\values.xml", + "map": [ + { + "source": "D:\\Study\\SE\\MiNotes\\src\\Notes\\app\\src\\main\\res\\values\\dimens.xml", + "from": { + "startLines": "19,20,21,22,18", + "startColumns": "4,4,4,4,4", + "startOffsets": "783,836,890,944,730", + "endColumns": "51,52,52,51,51", + "endOffsets": "830,884,938,991,777" + }, + "to": { + "startLines": "13,14,15,16,17", + "startColumns": "4,4,4,4,4", + "startOffsets": "477,529,582,635,687", + "endColumns": "51,52,52,51,51", + "endOffsets": "524,577,630,682,734" + } + }, + { + "source": "D:\\Study\\SE\\MiNotes\\src\\Notes\\app\\src\\main\\res\\values\\arrays.xml", + "from": { + "startLines": "19,26", + "startColumns": "4,4", + "startOffsets": "758,1073", + "endLines": "24,29", + "endColumns": "19,19", + "endOffsets": "1065,1191" + }, + "to": { + "startLines": "2,8", + "startColumns": "4,4", + "startOffsets": "105,300", + "endLines": "7,11", + "endColumns": "19,19", + "endOffsets": "295,415" + } + }, + { + "source": "D:\\Study\\SE\\MiNotes\\src\\Notes\\app\\src\\main\\res\\values\\colors.xml", + "from": { + "startLines": "18", + "startColumns": "4", + "startOffsets": "730", + "endColumns": "56", + "endOffsets": "782" + }, + "to": { + "startLines": "12", + "startColumns": "4", + "startOffsets": "420", + "endColumns": "56", + "endOffsets": "472" + } + }, + { + "source": "D:\\Study\\SE\\MiNotes\\src\\Notes\\app\\src\\main\\res\\values\\strings.xml", + "from": { + "startLines": "128,72,75,74,73,19,20,21,118,119,127,126,26,81,82,80,79,78,94,93,92,84,39,38,64,29,30,85,41,76,120,71,67,42,48,53,43,63,62,61,57,56,54,55,58,59,49,70,60,68,47,52,51,50,66,46,65,44,45,69,28,34,36,33,35,32,31,25,24,101,100,104,116,109,108,110,111,113,112,102,103,107,105,106,99,114,115,125,123,122,124,27,83,91,96,95,97,90,89,88,87,22,23", + "startColumns": "4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4", + "startOffsets": "8136,3970,4234,4137,4067,846,890,943,7624,7674,8077,8025,1308,4694,4789,4622,4537,4453,5668,5584,5481,4967,2043,1991,3461,1515,1569,5029,2138,4318,7738,3898,3649,2198,2519,2825,2257,3389,3327,3269,3043,2990,2885,2939,3094,3145,2567,3831,3206,3699,2471,2769,2680,2621,3586,2420,3540,2315,2359,3763,1459,1784,1894,1736,1840,1679,1630,1252,1198,6131,6063,6375,7525,6744,6662,6819,6909,7153,7073,6216,6292,6602,6440,6521,6007,7298,7421,7983,7854,7796,7908,1393,4898,5394,5805,5735,5886,5334,5278,5215,5154,996,1107", + "endLines": "132,72,75,74,73,19,20,21,118,119,127,126,26,81,82,80,79,78,94,93,92,84,39,38,64,29,30,85,41,76,120,71,67,42,48,53,43,63,62,61,57,56,54,55,58,59,49,70,60,68,47,52,51,50,66,46,65,44,45,69,28,34,36,33,35,32,31,25,24,101,100,104,116,109,108,110,111,113,112,102,103,107,105,106,99,114,115,125,123,122,124,27,83,91,96,95,97,90,89,88,87,22,23", + "endColumns": "14,95,82,95,68,42,51,51,48,62,57,50,83,93,107,70,83,82,65,82,101,60,61,50,77,52,59,104,58,101,54,70,48,57,46,58,56,70,60,56,49,51,52,49,49,59,52,65,61,62,46,54,87,57,61,49,44,42,59,66,54,54,52,46,52,55,47,54,52,83,66,63,95,73,80,88,162,143,78,74,81,58,79,79,54,121,102,40,52,56,73,64,67,85,79,68,93,58,54,61,59,109,89", + "endOffsets": "8544,4061,4312,4228,4131,884,937,990,7668,7732,8130,8071,1387,4783,4892,4688,4616,4531,5729,5662,5578,5023,2100,2037,3534,1563,1624,5129,2192,4415,7788,3964,3693,2251,2561,2879,2309,3455,3383,3321,3088,3037,2933,2984,3139,3200,2615,3892,3263,3757,2513,2819,2763,2674,3643,2465,3580,2353,2414,3825,1509,1834,1942,1778,1888,1730,1673,1302,1246,6210,6125,6434,7616,6813,6738,6903,7067,7292,7147,6286,6369,6656,6515,6596,6057,7415,7519,8019,7902,7848,7977,1453,4961,5475,5880,5799,5975,5388,5328,5272,5209,1101,1192" + }, + "to": { + "startLines": "18,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124", + "startColumns": "4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4", + "startOffsets": "739,1092,1188,1271,1367,1436,1479,1531,1583,1632,1695,1753,1804,1888,1982,2090,2161,2245,2328,2394,2477,2579,2640,2702,2753,2831,2884,2944,3049,3108,3210,3265,3336,3385,3443,3490,3549,3606,3677,3738,3795,3845,3897,3950,4000,4050,4110,4163,4229,4291,4354,4401,4456,4544,4602,4664,4714,4759,4802,4862,4929,4984,5039,5092,5139,5192,5248,5296,5351,5404,5488,5555,5619,5715,5789,5870,5959,6122,6266,6345,6420,6502,6561,6641,6721,6776,6898,7001,7042,7095,7152,7226,7291,7359,7445,7525,7594,7688,7747,7802,7864,7924,8034", + "endLines": "22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124", + "endColumns": "14,95,82,95,68,42,51,51,48,62,57,50,83,93,107,70,83,82,65,82,101,60,61,50,77,52,59,104,58,101,54,70,48,57,46,58,56,70,60,56,49,51,52,49,49,59,52,65,61,62,46,54,87,57,61,49,44,42,59,66,54,54,52,46,52,55,47,54,52,83,66,63,95,73,80,88,162,143,78,74,81,58,79,79,54,121,102,40,52,56,73,64,67,85,79,68,93,58,54,61,59,109,89", + "endOffsets": "1087,1183,1266,1362,1431,1474,1526,1578,1627,1690,1748,1799,1883,1977,2085,2156,2240,2323,2389,2472,2574,2635,2697,2748,2826,2879,2939,3044,3103,3205,3260,3331,3380,3438,3485,3544,3601,3672,3733,3790,3840,3892,3945,3995,4045,4105,4158,4224,4286,4349,4396,4451,4539,4597,4659,4709,4754,4797,4857,4924,4979,5034,5087,5134,5187,5243,5291,5346,5399,5483,5550,5614,5710,5784,5865,5954,6117,6261,6340,6415,6497,6556,6636,6716,6771,6893,6996,7037,7090,7147,7221,7286,7354,7440,7520,7589,7683,7742,7797,7859,7919,8029,8119" + } + }, + { + "source": "D:\\Study\\SE\\MiNotes\\src\\Notes\\app\\src\\main\\res\\values\\styles.xml", + "from": { + "startLines": "50,55,64,60,22,26,30,35,40,18,45", + "startColumns": "4,4,4,4,4,4,4,4,4,4,4", + "startOffsets": "2112,2322,2704,2539,917,1104,1293,1484,1693,730,1905", + "endLines": "53,58,67,62,25,29,33,38,43,21,48", + "endColumns": "12,12,12,12,12,12,12,12,12,12,12", + "endOffsets": "2314,2531,2910,2696,1098,1287,1476,1685,1897,911,2104" + }, + "to": { + "startLines": "125,129,133,137,140,144,148,152,156,160,164", + "startColumns": "4,4,4,4,4,4,4,4,4,4,4", + "startOffsets": "8124,8328,8539,8746,8906,9089,9274,9459,9662,9868,10051", + "endLines": "128,132,136,139,143,147,151,155,159,163,167", + "endColumns": "12,12,12,12,12,12,12,12,12,12,12", + "endOffsets": "8323,8534,8741,8901,9084,9269,9454,9657,9863,10046,10247" + } + } + ] + }, + { + "outputFile": "net.micode.notes.app-mergeDebugResources-2:/values/values.xml", + "map": [ + { + "source": "D:\\Study\\SE\\MiNotes\\src\\Notes\\app\\src\\main\\res\\values\\dimens.xml", + "from": { + "startLines": "-1,-1,-1,-1,-1", + "startColumns": "-1,-1,-1,-1,-1", + "startOffsets": "-1,-1,-1,-1,-1" + }, + "to": { + "startLines": "13,14,15,16,17", + "startColumns": "4,4,4,4,4", + "startOffsets": "477,529,582,635,687", + "endColumns": "51,52,52,51,51", + "endOffsets": "524,577,630,682,734" + } + }, + { + "source": "D:\\Study\\SE\\MiNotes\\src\\Notes\\app\\src\\main\\res\\values\\arrays.xml", + "from": { + "startLines": "-1,-1", + "startColumns": "-1,-1", + "startOffsets": "-1,-1" + }, + "to": { + "startLines": "2,8", + "startColumns": "4,4", + "startOffsets": "105,300", + "endLines": "7,11", + "endColumns": "19,19", + "endOffsets": "295,415" + } + }, + { + "source": "D:\\Study\\SE\\MiNotes\\src\\Notes\\app\\src\\main\\res\\values\\colors.xml", + "from": { + "startLines": "-1", + "startColumns": "-1", + "startOffsets": "-1" + }, + "to": { + "startLines": "12", + "startColumns": "4", + "startOffsets": "420", + "endColumns": "56", + "endOffsets": "472" + } + }, + { + "source": "D:\\Study\\SE\\MiNotes\\src\\Notes\\app\\src\\main\\res\\values\\strings.xml", + "from": { + "startLines": "-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1", + "startColumns": "-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1", + "startOffsets": "-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1" + }, + "to": { + "startLines": "18,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124", + "startColumns": "4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4", + "startOffsets": "739,1092,1188,1271,1367,1436,1479,1531,1583,1632,1695,1753,1804,1888,1982,2090,2161,2245,2328,2394,2477,2579,2640,2702,2753,2831,2884,2944,3049,3108,3210,3265,3336,3385,3443,3490,3549,3606,3677,3738,3795,3845,3897,3950,4000,4050,4110,4163,4229,4291,4354,4401,4456,4544,4602,4664,4714,4759,4802,4862,4929,4984,5039,5092,5139,5192,5248,5296,5351,5404,5488,5555,5619,5715,5789,5870,5959,6122,6266,6345,6420,6502,6561,6641,6721,6776,6898,7001,7042,7095,7152,7226,7291,7359,7445,7525,7594,7688,7747,7802,7864,7924,8034", + "endLines": "22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124", + "endColumns": "14,95,82,95,68,42,51,51,48,62,57,50,83,93,107,70,83,82,65,82,101,60,61,50,77,52,59,104,58,101,54,70,48,57,46,58,56,70,60,56,49,51,52,49,49,59,52,65,61,62,46,54,87,57,61,49,44,42,59,66,54,54,52,46,52,55,47,54,52,83,66,63,95,73,80,88,162,143,78,74,81,58,79,79,54,121,102,40,52,56,73,64,67,85,79,68,93,58,54,61,59,109,89", + "endOffsets": "1087,1183,1266,1362,1431,1474,1526,1578,1627,1690,1748,1799,1883,1977,2085,2156,2240,2323,2389,2472,2574,2635,2697,2748,2826,2879,2939,3044,3103,3205,3260,3331,3380,3438,3485,3544,3601,3672,3733,3790,3840,3892,3945,3995,4045,4105,4158,4224,4286,4349,4396,4451,4539,4597,4659,4709,4754,4797,4857,4924,4979,5034,5087,5134,5187,5243,5291,5346,5399,5483,5550,5614,5710,5784,5865,5954,6117,6261,6340,6415,6497,6556,6636,6716,6771,6893,6996,7037,7090,7147,7221,7286,7354,7440,7520,7589,7683,7742,7797,7859,7919,8029,8119" + } + }, + { + "source": "D:\\Study\\SE\\MiNotes\\src\\Notes\\app\\src\\main\\res\\values\\styles.xml", + "from": { + "startLines": "-1,-1,64,-1,-1,-1,-1,-1,-1,-1,-1", + "startColumns": "-1,-1,4,-1,-1,-1,-1,-1,-1,-1,-1", + "startOffsets": "-1,-1,2704,-1,-1,-1,-1,-1,-1,-1,-1", + "endLines": "-1,-1,67,-1,-1,-1,-1,-1,-1,-1,-1", + "endColumns": "-1,-1,12,-1,-1,-1,-1,-1,-1,-1,-1", + "endOffsets": "-1,-1,2913,-1,-1,-1,-1,-1,-1,-1,-1" + }, + "to": { + "startLines": "125,129,133,137,140,144,148,152,156,160,164", + "startColumns": "4,4,4,4,4,4,4,4,4,4,4", + "startOffsets": "8124,8328,8539,8749,8909,9092,9277,9462,9665,9871,10054", + "endLines": "128,132,136,139,143,147,151,155,159,163,167", + "endColumns": "12,12,12,12,12,12,12,12,12,12,12", + "endOffsets": "8323,8534,8744,8904,9087,9272,9457,9660,9866,10049,10250" + } + } + ] + } + ] +} \ No newline at end of file diff --git a/src/Notes/app/build/intermediates/packaged_res/debug/values/values.xml b/src/Notes/app/build/intermediates/packaged_res/debug/values/values.xml new file mode 100644 index 0000000..0102458 --- /dev/null +++ b/src/Notes/app/build/intermediates/packaged_res/debug/values/values.xml @@ -0,0 +1,169 @@ + + + + -%s + --%s + --%s + --%s + + + Messaging + Email + + #335b5b5b + 26sp + 20sp + 17sp + 14sp + 33sp + + %1$s result for \"%2$s\" + + %1$s results for \"%2$s\" + + Confirm to delete folder and its notes? + Confirm to delete this note? + Confirm to delete the selected %d notes? + Delete selected notes + Notes + Notes 2x2 + Notes 4x4 + Delete + Call notes + cancel + set + Delete reminder successfully + Sorry, can not set clock on empty note + Sorry, can not send and empty note to home + The note is not exist + Export failed, please check SD card + SD card busy, not available now + Sync is canceled + Sync failed, internal error occurs + Sync failed, please check network and account settings + Export fail + notes_%s.txt + /MIUI/notes/ + The folder %1$s exist, please rename + yyyyMMdd + MMMd kk:mm + Export text file (%1$s) to SD (%2$s) directory + (%d) + Have moved selected %1$d notes to %2$s folder + Input name + Note added to home + Remind me + New Folder + Delete + Deselect all + Export text + Change folder name + Delete folder + View folder + Large + Medium + Font size + Small + Super + Enter check list + Move to folder + Parent folder + Leave check list + Delete reminder + Search + Select all + Nothing selected, the operation is invalid + %d selected + Send to home + Settings + Share + Sync + Cancel syncing + Select folder + Expired + Send email + Open map + Call + Browse web + Take a look + Got it + Add note + ... + Sync notes with google task + Sync account + Add account + New note background color random + Cancel syncing + Sync immediately + Current account %1$s + All sync related information will be deleted, which may result in duplicated items sometime + Please select a google account. Local notes will be synced with google task. + Sync notes + Last sync time %1$s + yyyy-MM-dd hh:mm:ss + Cancel + Change sync account + Remove sync account + Settings + Cannot change the account because sync is in progress + %1$s has been set as the sync account + Notes + Search notes + Searching Notes + Text in your notes + Set reminder + Export successful + Sync is successful with account %1$s + Getting remote note list... + Logging into %1$s... + Synchronize local notes with Google Task... + Sync is canceled + Sync is failed + Sync is successful + Syncing notes... + No associated note found, click to create associated note. + Privacy mode,can not see note content + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Notes/app/build/intermediates/processed_res/debug/out/resources-debug.ap_ b/src/Notes/app/build/intermediates/processed_res/debug/out/resources-debug.ap_ new file mode 100644 index 0000000..81398cf Binary files /dev/null and b/src/Notes/app/build/intermediates/processed_res/debug/out/resources-debug.ap_ differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/data/Contact.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/data/Contact.dex new file mode 100644 index 0000000..9cf68af Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/data/Contact.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/data/Notes$CallNote.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/data/Notes$CallNote.dex new file mode 100644 index 0000000..0375c44 Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/data/Notes$CallNote.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/data/Notes$DataConstants.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/data/Notes$DataConstants.dex new file mode 100644 index 0000000..e0e5d49 Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/data/Notes$DataConstants.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/data/Notes$TextNote.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/data/Notes$TextNote.dex new file mode 100644 index 0000000..e6f4ff6 Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/data/Notes$TextNote.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/data/Notes.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/data/Notes.dex new file mode 100644 index 0000000..cc7483c Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/data/Notes.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/data/NotesProvider.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/data/NotesProvider.dex new file mode 100644 index 0000000..03e60e2 Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/data/NotesProvider.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/model/Note$NoteData.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/model/Note$NoteData.dex new file mode 100644 index 0000000..3e11789 Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/model/Note$NoteData.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/model/Note.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/model/Note.dex new file mode 100644 index 0000000..b69c737 Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/model/Note.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/model/WorkingNote.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/model/WorkingNote.dex new file mode 100644 index 0000000..d9dbc59 Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/model/WorkingNote.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/AlarmAlertActivity.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/AlarmAlertActivity.dex new file mode 100644 index 0000000..4f89a20 Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/AlarmAlertActivity.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/AlarmInitReceiver.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/AlarmInitReceiver.dex new file mode 100644 index 0000000..661d592 Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/AlarmInitReceiver.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/AlarmReceiver.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/AlarmReceiver.dex new file mode 100644 index 0000000..f38c6d2 Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/AlarmReceiver.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/DateTimePicker$1.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/DateTimePicker$1.dex new file mode 100644 index 0000000..7ba57c0 Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/DateTimePicker$1.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/DateTimePicker$2.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/DateTimePicker$2.dex new file mode 100644 index 0000000..9dec7a4 Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/DateTimePicker$2.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/DateTimePicker$3.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/DateTimePicker$3.dex new file mode 100644 index 0000000..0287241 Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/DateTimePicker$3.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/DateTimePicker$4.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/DateTimePicker$4.dex new file mode 100644 index 0000000..c98deff Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/DateTimePicker$4.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/DateTimePicker.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/DateTimePicker.dex new file mode 100644 index 0000000..e320810 Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/DateTimePicker.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/DateTimePickerDialog$1.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/DateTimePickerDialog$1.dex new file mode 100644 index 0000000..157cf55 Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/DateTimePickerDialog$1.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/DateTimePickerDialog.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/DateTimePickerDialog.dex new file mode 100644 index 0000000..785388d Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/DateTimePickerDialog.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/DropdownMenu$1.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/DropdownMenu$1.dex new file mode 100644 index 0000000..58dd91f Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/DropdownMenu$1.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/DropdownMenu.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/DropdownMenu.dex new file mode 100644 index 0000000..997771d Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/DropdownMenu.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/FoldersListAdapter$FolderListItem.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/FoldersListAdapter$FolderListItem.dex new file mode 100644 index 0000000..b8c96b2 Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/FoldersListAdapter$FolderListItem.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/FoldersListAdapter.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/FoldersListAdapter.dex new file mode 100644 index 0000000..31bec51 Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/FoldersListAdapter.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NoteEditActivity$1.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NoteEditActivity$1.dex new file mode 100644 index 0000000..a9ab536 Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NoteEditActivity$1.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NoteEditActivity$2.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NoteEditActivity$2.dex new file mode 100644 index 0000000..f812a15 Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NoteEditActivity$2.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NoteEditActivity$3.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NoteEditActivity$3.dex new file mode 100644 index 0000000..6b72c9a Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NoteEditActivity$3.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NoteEditActivity$HeadViewHolder.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NoteEditActivity$HeadViewHolder.dex new file mode 100644 index 0000000..37f4f57 Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NoteEditActivity$HeadViewHolder.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NoteEditActivity.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NoteEditActivity.dex new file mode 100644 index 0000000..bda3c37 Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NoteEditActivity.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NoteEditText$1.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NoteEditText$1.dex new file mode 100644 index 0000000..b7cd7c8 Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NoteEditText$1.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NoteEditText.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NoteEditText.dex new file mode 100644 index 0000000..f6616c3 Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NoteEditText.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$1.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$1.dex new file mode 100644 index 0000000..9530b86 Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$1.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$2.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$2.dex new file mode 100644 index 0000000..46c0582 Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$2.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$3.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$3.dex new file mode 100644 index 0000000..c77c1af Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$3.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$4.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$4.dex new file mode 100644 index 0000000..174a744 Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$4.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$5.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$5.dex new file mode 100644 index 0000000..1300b6a Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$5.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$6.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$6.dex new file mode 100644 index 0000000..435e61b Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$6.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$7.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$7.dex new file mode 100644 index 0000000..2c4a38c Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$7.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$8.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$8.dex new file mode 100644 index 0000000..65ff865 Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$8.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$9.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$9.dex new file mode 100644 index 0000000..ab07440 Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$9.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$BackgroundQueryHandler.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$BackgroundQueryHandler.dex new file mode 100644 index 0000000..76bd4b6 Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$BackgroundQueryHandler.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$ListEditState.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$ListEditState.dex new file mode 100644 index 0000000..3fa4402 Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$ListEditState.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$ModeCallback$1.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$ModeCallback$1.dex new file mode 100644 index 0000000..87a4341 Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$ModeCallback$1.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$ModeCallback$2.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$ModeCallback$2.dex new file mode 100644 index 0000000..6192f15 Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$ModeCallback$2.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$ModeCallback.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$ModeCallback.dex new file mode 100644 index 0000000..7bcf861 Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$ModeCallback.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$NewNoteOnTouchListener.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$NewNoteOnTouchListener.dex new file mode 100644 index 0000000..2cdf2cf Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$NewNoteOnTouchListener.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$OnListItemClickListener.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$OnListItemClickListener.dex new file mode 100644 index 0000000..0939499 Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity$OnListItemClickListener.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity.dex new file mode 100644 index 0000000..2784a4f Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListActivity.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListAdapter$AppWidgetAttribute.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListAdapter$AppWidgetAttribute.dex new file mode 100644 index 0000000..871cef8 Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListAdapter$AppWidgetAttribute.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListAdapter.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListAdapter.dex new file mode 100644 index 0000000..531903c Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListAdapter.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListItem.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListItem.dex new file mode 100644 index 0000000..5b6a996 Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesListItem.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesPreferenceActivity$1.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesPreferenceActivity$1.dex new file mode 100644 index 0000000..5e7e15e Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesPreferenceActivity$1.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesPreferenceActivity$2.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesPreferenceActivity$2.dex new file mode 100644 index 0000000..3e96d87 Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesPreferenceActivity$2.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesPreferenceActivity$3.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesPreferenceActivity$3.dex new file mode 100644 index 0000000..881ce90 Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesPreferenceActivity$3.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesPreferenceActivity$4.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesPreferenceActivity$4.dex new file mode 100644 index 0000000..527a8fb Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesPreferenceActivity$4.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesPreferenceActivity$5.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesPreferenceActivity$5.dex new file mode 100644 index 0000000..8314569 Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesPreferenceActivity$5.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesPreferenceActivity$6.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesPreferenceActivity$6.dex new file mode 100644 index 0000000..508384c Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesPreferenceActivity$6.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesPreferenceActivity$7.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesPreferenceActivity$7.dex new file mode 100644 index 0000000..4d668cf Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesPreferenceActivity$7.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesPreferenceActivity$8.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesPreferenceActivity$8.dex new file mode 100644 index 0000000..3fd85c8 Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesPreferenceActivity$8.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesPreferenceActivity$GTaskReceiver.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesPreferenceActivity$GTaskReceiver.dex new file mode 100644 index 0000000..83d6689 Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesPreferenceActivity$GTaskReceiver.dex differ diff --git a/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesPreferenceActivity.dex b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesPreferenceActivity.dex new file mode 100644 index 0000000..4e192fd Binary files /dev/null and b/src/Notes/app/build/intermediates/project_dex_archive/debug/out/net/micode/notes/ui/NotesPreferenceActivity.dex differ diff --git a/src/Notes/app/build/intermediates/signing_config_versions/debug/signing-config-versions.json b/src/Notes/app/build/intermediates/signing_config_versions/debug/signing-config-versions.json new file mode 100644 index 0000000..1920ca8 --- /dev/null +++ b/src/Notes/app/build/intermediates/signing_config_versions/debug/signing-config-versions.json @@ -0,0 +1 @@ +{"enableV1Signing":true,"enableV2Signing":false,"enableV3Signing":false,"enableV4Signing":false} \ No newline at end of file diff --git a/src/Notes/app/build/intermediates/stable_resource_ids_file/debug/stableIds.txt b/src/Notes/app/build/intermediates/stable_resource_ids_file/debug/stableIds.txt new file mode 100644 index 0000000..6e7247b --- /dev/null +++ b/src/Notes/app/build/intermediates/stable_resource_ids_file/debug/stableIds.txt @@ -0,0 +1,283 @@ +net.micode.notes:xml/preferences = 0x7f0c0000 +net.micode.notes:string/error_sdcard_unmounted = 0x7f0a0010 +net.micode.notes:style/TextAppearanceUnderMenuIcon = 0x7f0b000a +net.micode.notes:string/menu_deselect_all = 0x7f0a0022 +net.micode.notes:string/search = 0x7f0a0056 +net.micode.notes:string/search_setting_description = 0x7f0a0059 +net.micode.notes:style/TextAppearanceSuper = 0x7f0b0009 +net.micode.notes:string/preferences_title = 0x7f0a0053 +net.micode.notes:style/HighlightTextAppearanceSecondary = 0x7f0b0001 +net.micode.notes:string/preferences_last_sync_time = 0x7f0a004e +net.micode.notes:string/preferences_menu_change_account = 0x7f0a0051 +net.micode.notes:xml/widget_4x_info = 0x7f0c0003 +net.micode.notes:string/notealert_enter = 0x7f0a0040 +net.micode.notes:string/set_remind_time_message = 0x7f0a005a +net.micode.notes:string/menu_send_to_desktop = 0x7f0a0035 +net.micode.notes:id/account_dialog_subtitle = 0x7f050000 +net.micode.notes:menu/note_list = 0x7f070003 +net.micode.notes:string/sync_progress_login = 0x7f0a005e +net.micode.notes:string/note_link_other = 0x7f0a003d +net.micode.notes:string/preferences_toast_success_set_accout = 0x7f0a0055 +net.micode.notes:string/search_hint = 0x7f0a0057 +net.micode.notes:string/menu_normal_mode = 0x7f0a002f +net.micode.notes:string/menu_setting = 0x7f0a0036 +net.micode.notes:string/preferences_bg_random_appear_title = 0x7f0a0047 +net.micode.notes:string/preferences_dialog_change_account_title = 0x7f0a004a +net.micode.notes:id/navigation_bar = 0x7f05002f +net.micode.notes:string/note_alert_expired = 0x7f0a003b +net.micode.notes:id/ll_font_super = 0x7f05001f +net.micode.notes:dimen/text_font_size_large = 0x7f030000 +net.micode.notes:string/menu_share = 0x7f0a0037 +net.micode.notes:string/error_sync_network = 0x7f0a0013 +net.micode.notes:id/notes_list = 0x7f050035 +net.micode.notes:string/widget_under_visit_mode = 0x7f0a0065 +net.micode.notes:string/menu_font_small = 0x7f0a002a +net.micode.notes:string/menu_font_normal = 0x7f0a0028 +net.micode.notes:string/note_link_tel = 0x7f0a003e +net.micode.notes:string/menu_select_title = 0x7f0a0034 +net.micode.notes:style/TextAppearanceMedium = 0x7f0b0005 +net.micode.notes:id/menu_send_to_desktop = 0x7f050029 +net.micode.notes:string/preferences_last_sync_time_format = 0x7f0a004f +net.micode.notes:menu/call_note_edit = 0x7f070000 +net.micode.notes:id/preference_sync_button = 0x7f050037 +net.micode.notes:string/menu_search = 0x7f0a0031 +net.micode.notes:string/menu_font_size = 0x7f0a0029 +net.micode.notes:string/info_note_enter_desktop = 0x7f0a001e +net.micode.notes:style/TextAppearanceLarge = 0x7f0b0004 +net.micode.notes:string/menu_select_all = 0x7f0a0032 +net.micode.notes:string/note_link_email = 0x7f0a003c +net.micode.notes:string/preferences_dialog_change_account_warn_msg = 0x7f0a004b +net.micode.notes:xml/searchable = 0x7f0c0001 +net.micode.notes:string/menu_folder_change_name = 0x7f0a0024 +net.micode.notes:string/menu_sync_cancel = 0x7f0a0039 +net.micode.notes:string/menu_export_text = 0x7f0a0023 +net.micode.notes:string/menu_remove_remind = 0x7f0a0030 +net.micode.notes:string/menu_font_large = 0x7f0a0027 +net.micode.notes:menu/sub_folder = 0x7f070006 +net.micode.notes:layout/note_item = 0x7f060007 +net.micode.notes:string/datetime_dialog_cancel = 0x7f0a0009 +net.micode.notes:string/menu_create_folder = 0x7f0a0020 +net.micode.notes:drawable/widget_2x_red = 0x7f04003a +net.micode.notes:drawable/widget_4x_yellow = 0x7f040041 +net.micode.notes:string/format_date_ymd = 0x7f0a0018 +net.micode.notes:string/preferences_account_summary = 0x7f0a0044 +net.micode.notes:string/success_sdcard_export = 0x7f0a005b +net.micode.notes:string/format_move_notes_to_folder = 0x7f0a001c +net.micode.notes:string/delete_remind_time_message = 0x7f0a000b +net.micode.notes:string/file_name_txt_format = 0x7f0a0015 +net.micode.notes:drawable/new_note = 0x7f04002f +net.micode.notes:string/folder_exist = 0x7f0a0017 +net.micode.notes:id/note_edit_list = 0x7f050031 +net.micode.notes:string/failed_sdcard_export = 0x7f0a0014 +net.micode.notes:string/menu_select_none = 0x7f0a0033 +net.micode.notes:layout/widget_4x = 0x7f06000d +net.micode.notes:string/error_sync_cancelled = 0x7f0a0011 +net.micode.notes:layout/note_list_dropdown_menu = 0x7f060009 +net.micode.notes:drawable/list_red_single = 0x7f040023 +net.micode.notes:string/ticker_cancel = 0x7f0a0060 +net.micode.notes:style/TextAppearanceSecondaryItem = 0x7f0b0008 +net.micode.notes:drawable/widget_4x_green = 0x7f04003e +net.micode.notes:string/error_sdcard_export = 0x7f0a000f +net.micode.notes:string/note_link_web = 0x7f0a003f +net.micode.notes:id/menu_sync = 0x7f05002c +net.micode.notes:style/TextAppearancePrimaryItem = 0x7f0b0007 +net.micode.notes:xml/widget_2x_info = 0x7f0c0002 +net.micode.notes:string/error_note_empty_for_clock = 0x7f0a000c +net.micode.notes:id/tv_title = 0x7f05003f +net.micode.notes:string/menu_move_parent_folder = 0x7f0a002e +net.micode.notes:string/notealert_ok = 0x7f0a0041 +net.micode.notes:id/iv_bg_yellow_select = 0x7f050017 +net.micode.notes:style/TextAppearanceNormal = 0x7f0b0006 +net.micode.notes:string/button_delete = 0x7f0a0007 +net.micode.notes:string/preferences_menu_remove_account = 0x7f0a0052 +net.micode.notes:id/menu_list_mode = 0x7f050025 +net.micode.notes:string/app_widget2x2 = 0x7f0a0005 +net.micode.notes:string/ticker_fail = 0x7f0a0061 +net.micode.notes:string/app_name = 0x7f0a0004 +net.micode.notes:string/search_label = 0x7f0a0058 +net.micode.notes:string/preferences_dialog_select_account_tips = 0x7f0a004c +net.micode.notes:string/success_sync_account = 0x7f0a005c +net.micode.notes:drawable/widget_2x_yellow = 0x7f04003c +net.micode.notes:id/amPm = 0x7f050003 +net.micode.notes:string/preferences_account_title = 0x7f0a0045 +net.micode.notes:string/alert_title_delete = 0x7f0a0003 +net.micode.notes:string/widget_havenot_content = 0x7f0a0064 +net.micode.notes:string/hint_foler_name = 0x7f0a001d +net.micode.notes:id/menu_delete = 0x7f050021 +net.micode.notes:drawable/list_red_down = 0x7f040021 +net.micode.notes:string/menu_title_select_folder = 0x7f0a003a +net.micode.notes:string/alert_message_delete_note = 0x7f0a0001 +net.micode.notes:string/format_datetime_mdhm = 0x7f0a0019 +net.micode.notes:string/alert_message_delete_folder = 0x7f0a0000 +net.micode.notes:string/preferences_add_account = 0x7f0a0046 +net.micode.notes:string/menu_sync = 0x7f0a0038 +net.micode.notes:id/move = 0x7f05002e +net.micode.notes:string/datetime_dialog_ok = 0x7f0a000a +net.micode.notes:string/ticker_syncing = 0x7f0a0063 +net.micode.notes:id/menu_search = 0x7f050028 +net.micode.notes:drawable/menu_move = 0x7f04002e +net.micode.notes:raw/introduction = 0x7f090000 +net.micode.notes:menu/call_record_folder = 0x7f070001 +net.micode.notes:drawable/list_red_middle = 0x7f040022 +net.micode.notes:layout/widget_2x = 0x7f06000c +net.micode.notes:drawable/icon_app = 0x7f040015 +net.micode.notes:layout/note_list = 0x7f060008 +net.micode.notes:layout/note_edit = 0x7f060005 +net.micode.notes:drawable/list_background = 0x7f040016 +net.micode.notes:string/alert_message_delete_notes = 0x7f0a0002 +net.micode.notes:drawable/widget_2x_blue = 0x7f040038 +net.micode.notes:layout/folder_list_item = 0x7f060004 +net.micode.notes:string/sync_progress_init_list = 0x7f0a005d +net.micode.notes:layout/add_account_text = 0x7f060001 +net.micode.notes:id/btn_new_note = 0x7f050004 +net.micode.notes:id/widget_text = 0x7f050042 +net.micode.notes:id/widget_bg_image = 0x7f050041 +net.micode.notes:string/menu_folder_delete = 0x7f0a0025 +net.micode.notes:id/tv_name = 0x7f05003d +net.micode.notes:id/account_dialog_title = 0x7f050001 +net.micode.notes:id/tv_folder_name = 0x7f05003b +net.micode.notes:id/tv_alert_date = 0x7f05003a +net.micode.notes:color/primary_text_dark = 0x7f020000 +net.micode.notes:id/tv_time = 0x7f05003e +net.micode.notes:id/selection_menu = 0x7f050038 +net.micode.notes:id/ll_font_small = 0x7f05001e +net.micode.notes:id/note_title = 0x7f050034 +net.micode.notes:string/format_exported_file_location = 0x7f0a001a +net.micode.notes:id/note_bg_color_selector = 0x7f050030 +net.micode.notes:id/menu_share = 0x7f05002b +net.micode.notes:string/format_folder_files_count = 0x7f0a001b +net.micode.notes:id/menu_setting = 0x7f05002a +net.micode.notes:string/menu_move = 0x7f0a002d +net.micode.notes:id/date = 0x7f050007 +net.micode.notes:id/note_item = 0x7f050033 +net.micode.notes:drawable/font_size_selector_bg = 0x7f040012 +net.micode.notes:id/note_edit_view = 0x7f050032 +net.micode.notes:style/HighlightTextAppearancePrimary = 0x7f0b0000 +net.micode.notes:string/preferences_dialog_select_account_title = 0x7f0a004d +net.micode.notes:id/menu_font_size = 0x7f050024 +net.micode.notes:string/preferences_button_sync_cancel = 0x7f0a0048 +net.micode.notes:id/menu_alert = 0x7f050020 +net.micode.notes:string/error_note_not_exist = 0x7f0a000e +net.micode.notes:string/error_note_empty_for_send_to_desktop = 0x7f0a000d +net.micode.notes:id/minute = 0x7f05002d +net.micode.notes:id/ll_font_normal = 0x7f05001d +net.micode.notes:id/ll_font_large = 0x7f05001c +net.micode.notes:id/iv_super_select = 0x7f05001b +net.micode.notes:string/preferences_button_sync_immediately = 0x7f0a0049 +net.micode.notes:drawable/bg_color_btn_mask = 0x7f040001 +net.micode.notes:drawable/list_yellow_up = 0x7f04002c +net.micode.notes:id/iv_bg_white = 0x7f050014 +net.micode.notes:id/iv_medium_select = 0x7f050019 +net.micode.notes:string/ticker_success = 0x7f0a0062 +net.micode.notes:id/iv_large_select = 0x7f050018 +net.micode.notes:drawable/bg_btn_set_color = 0x7f040000 +net.micode.notes:id/iv_bg_yellow = 0x7f050016 +net.micode.notes:menu/note_edit = 0x7f070002 +net.micode.notes:drawable/list_white_down = 0x7f040025 +net.micode.notes:drawable/list_white_up = 0x7f040028 +net.micode.notes:id/iv_bg_green = 0x7f050010 +net.micode.notes:drawable/delete = 0x7f040004 +net.micode.notes:id/iv_bg_blue = 0x7f05000e +net.micode.notes:drawable/list_green_single = 0x7f04001f +net.micode.notes:id/iv_alert_icon = 0x7f05000d +net.micode.notes:drawable/list_yellow_single = 0x7f04002b +net.micode.notes:id/cb_edit_item = 0x7f050006 +net.micode.notes:id/font_size_selector = 0x7f05000b +net.micode.notes:color/user_query_highlight = 0x7f020002 +net.micode.notes:drawable/font_super = 0x7f040014 +net.micode.notes:drawable/edit_title_blue = 0x7f040009 +net.micode.notes:id/et_edit_text = 0x7f050009 +net.micode.notes:string/preferences_toast_cannot_change_account = 0x7f0a0054 +net.micode.notes:id/iv_bg_green_select = 0x7f050011 +net.micode.notes:id/menu_new_note = 0x7f050027 +net.micode.notes:drawable/clock = 0x7f040003 +net.micode.notes:array/menu_share_ways = 0x7f010001 +net.micode.notes:drawable/font_small = 0x7f040013 +net.micode.notes:id/btn_set_bg_color = 0x7f050005 +net.micode.notes:style/NoteTheme = 0x7f0b0003 +net.micode.notes:drawable/notification = 0x7f040033 +net.micode.notes:id/iv_bg_blue_select = 0x7f05000f +net.micode.notes:drawable/widget_4x_blue = 0x7f04003d +net.micode.notes:drawable/edit_title_white = 0x7f04000c +net.micode.notes:string/menu_delete = 0x7f0a0021 +net.micode.notes:drawable/list_footer_bg = 0x7f04001c +net.micode.notes:drawable/edit_red = 0x7f040008 +net.micode.notes:string/preferences_menu_cancel = 0x7f0a0050 +net.micode.notes:drawable/widget_2x_white = 0x7f04003b +net.micode.notes:drawable/font_large = 0x7f040010 +net.micode.notes:string/file_path = 0x7f0a0016 +net.micode.notes:string/menu_alert = 0x7f0a001f +net.micode.notes:drawable/title_alert = 0x7f040036 +net.micode.notes:drawable/list_green_down = 0x7f04001d +net.micode.notes:string/error_sync_internal = 0x7f0a0012 +net.micode.notes:drawable/search_result = 0x7f040034 +net.micode.notes:string/app_widget4x4 = 0x7f0a0006 +net.micode.notes:drawable/title_bar_bg = 0x7f040037 +net.micode.notes:drawable/selected = 0x7f040035 +net.micode.notes:plurals/search_results_title = 0x7f080000 +net.micode.notes:drawable/edit_title_green = 0x7f04000a +net.micode.notes:drawable/new_note_normal = 0x7f040030 +net.micode.notes:drawable/edit_white = 0x7f04000e +net.micode.notes:drawable/widget_2x_green = 0x7f040039 +net.micode.notes:id/iv_bg_red = 0x7f050012 +net.micode.notes:drawable/list_white_single = 0x7f040027 +net.micode.notes:id/delete = 0x7f050008 +net.micode.notes:layout/note_edit_list_item = 0x7f060006 +net.micode.notes:string/call_record_folder_name = 0x7f0a0008 +net.micode.notes:drawable/list_green_up = 0x7f040020 +net.micode.notes:id/prefenerece_sync_status_textview = 0x7f050036 +net.micode.notes:id/et_foler_name = 0x7f05000a +net.micode.notes:id/menu_delete_remind = 0x7f050022 +net.micode.notes:drawable/note_edit_color_selector_panel = 0x7f040032 +net.micode.notes:id/menu_export_text = 0x7f050023 +net.micode.notes:id/hour = 0x7f05000c +net.micode.notes:string/menu_folder_view = 0x7f0a0026 +net.micode.notes:drawable/dropdown_icon = 0x7f040005 +net.micode.notes:string/notelist_string_info = 0x7f0a0043 +net.micode.notes:drawable/list_blue_single = 0x7f040019 +net.micode.notes:drawable/list_white_middle = 0x7f040026 +net.micode.notes:id/tv_title_bar = 0x7f050040 +net.micode.notes:drawable/edit_title_red = 0x7f04000b +net.micode.notes:dimen/text_font_size_normal = 0x7f030002 +net.micode.notes:layout/dialog_edit_text = 0x7f060003 +net.micode.notes:color/secondary_text_dark = 0x7f020001 +net.micode.notes:drawable/list_folder = 0x7f04001b +net.micode.notes:drawable/list_green_middle = 0x7f04001e +net.micode.notes:string/sync_progress_syncing = 0x7f0a005f +net.micode.notes:drawable/font_normal = 0x7f040011 +net.micode.notes:drawable/list_blue_up = 0x7f04001a +net.micode.notes:id/iv_small_select = 0x7f05001a +net.micode.notes:drawable/list_blue_down = 0x7f040017 +net.micode.notes:drawable/edit_yellow = 0x7f04000f +net.micode.notes:drawable/list_yellow_down = 0x7f040029 +net.micode.notes:dimen/text_font_size_super = 0x7f030004 +net.micode.notes:drawable/edit_blue = 0x7f040006 +net.micode.notes:array/format_for_exported_note = 0x7f010000 +net.micode.notes:string/menu_list_mode = 0x7f0a002c +net.micode.notes:drawable/list_yellow_middle = 0x7f04002a +net.micode.notes:drawable/list_blue_middle = 0x7f040018 +net.micode.notes:dimen/text_font_size_medium = 0x7f030001 +net.micode.notes:drawable/new_note_pressed = 0x7f040031 +net.micode.notes:id/menu_new_folder = 0x7f050026 +net.micode.notes:id/sv_note_edit = 0x7f050039 +net.micode.notes:drawable/call_record = 0x7f040002 +net.micode.notes:drawable/edit_green = 0x7f040007 +net.micode.notes:id/action_select_all = 0x7f050002 +net.micode.notes:id/tv_modified_date = 0x7f05003c +net.micode.notes:id/iv_bg_white_select = 0x7f050015 +net.micode.notes:drawable/edit_title_yellow = 0x7f04000d +net.micode.notes:layout/note_list_footer = 0x7f06000a +net.micode.notes:string/menu_font_super = 0x7f0a002b +net.micode.notes:drawable/widget_4x_red = 0x7f04003f +net.micode.notes:layout/account_dialog_title = 0x7f060000 +net.micode.notes:string/notelist_menu_new = 0x7f0a0042 +net.micode.notes:drawable/menu_delete = 0x7f04002d +net.micode.notes:id/iv_bg_red_select = 0x7f050013 +net.micode.notes:menu/note_list_dropdown = 0x7f070004 +net.micode.notes:drawable/list_red_up = 0x7f040024 +net.micode.notes:layout/settings_header = 0x7f06000b +net.micode.notes:style/NoteActionBarStyle = 0x7f0b0002 +net.micode.notes:dimen/text_font_size_small = 0x7f030003 +net.micode.notes:layout/datetime_picker = 0x7f060002 +net.micode.notes:menu/note_list_options = 0x7f070005 +net.micode.notes:drawable/widget_4x_white = 0x7f040040 diff --git a/src/Notes/app/build/outputs/logs/manifest-merger-debug-report.txt b/src/Notes/app/build/outputs/logs/manifest-merger-debug-report.txt new file mode 100644 index 0000000..0439431 --- /dev/null +++ b/src/Notes/app/build/outputs/logs/manifest-merger-debug-report.txt @@ -0,0 +1,233 @@ +-- Merging decision tree log --- +manifest +ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:18:1-151:12 +INJECTED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:18:1-151:12 +INJECTED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:18:1-151:12 +INJECTED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:18:1-151:12 + package + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:19:5-31 + INJECTED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml + android:versionName + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:21:5-30 + INJECTED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml + xmlns:android + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:18:11-69 + android:versionCode + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:20:5-28 + INJECTED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml +uses-sdk +ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:23:5-44 +INJECTED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:23:5-44 +INJECTED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:23:5-44 + android:targetSdkVersion + INJECTED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml + android:minSdkVersion + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:23:15-41 + INJECTED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml +uses-permission#android.permission.WRITE_EXTERNAL_STORAGE +ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:25:5-81 + android:name + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:25:22-78 +uses-permission#com.android.launcher.permission.INSTALL_SHORTCUT +ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:26:5-88 + android:name + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:26:22-85 +uses-permission#android.permission.INTERNET +ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:27:5-67 + android:name + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:27:22-64 +uses-permission#android.permission.READ_CONTACTS +ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:28:5-72 + android:name + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:28:22-69 +uses-permission#android.permission.MANAGE_ACCOUNTS +ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:29:5-74 + android:name + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:29:22-71 +uses-permission#android.permission.AUTHENTICATE_ACCOUNTS +ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:30:5-80 + android:name + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:30:22-77 +uses-permission#android.permission.GET_ACCOUNTS +ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:31:5-71 + android:name + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:31:22-68 +uses-permission#android.permission.USE_CREDENTIALS +ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:32:5-74 + android:name + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:32:22-71 +uses-permission#android.permission.RECEIVE_BOOT_COMPLETED +ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:33:5-81 + android:name + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:33:22-78 +application +ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:35:5-150:19 +INJECTED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:35:5-150:19 + android:extractNativeLibs + INJECTED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml + android:label + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:37:9-41 + android:icon + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:36:9-42 +activity#net.micode.notes.ui.NotesListActivity +ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:38:9-51:20 + android:label + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:42:13-45 + android:launchMode + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:43:13-43 + android:windowSoftInputMode + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:45:13-52 + android:exported + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:40:13-36 + android:configChanges + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:41:13-74 + android:theme + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:44:13-45 + android:name + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:39:13-49 +intent-filter#action:name:android.intent.action.MAIN+category:name:android.intent.category.LAUNCHER +ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:47:13-50:29 +action#android.intent.action.MAIN +ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:48:17-69 + android:name + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:48:25-66 +category#android.intent.category.LAUNCHER +ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:49:17-77 + android:name + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:49:27-74 +activity#net.micode.notes.ui.NoteEditActivity +ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:53:9-82:20 + android:launchMode + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:56:13-43 + android:configChanges + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:55:13-74 + android:theme + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:57:13-45 + android:name + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:54:13-48 +intent-filter#action:name:android.intent.action.VIEW+category:name:android.intent.category.DEFAULT+data:host:com.example.notes.provider+data:host:com.example.notes.provider+data:mimeType:vnd.android.cursor.item/call_note+data:mimeType:vnd.android.cursor.item/text_note+data:path:/notes+data:path:/notes+data:scheme:content+data:scheme:content +ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:59:13-64:29 +action#android.intent.action.VIEW +ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:60:17-69 + android:name + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:60:25-66 +category#android.intent.category.DEFAULT +ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:61:17-76 + android:name + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:61:27-73 +data +ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:62:17-167 + android:path + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:62:143-164 + android:host + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:62:101-142 + android:scheme + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:62:76-100 + android:mimeType + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:62:23-75 +intent-filter#action:name:android.intent.action.INSERT_OR_EDIT+category:name:android.intent.category.DEFAULT+data:mimeType:vnd.android.cursor.item/call_note+data:mimeType:vnd.android.cursor.item/text_note +ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:67:13-72:29 +action#android.intent.action.INSERT_OR_EDIT +ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:68:17-79 + android:name + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:68:25-76 +intent-filter#action:name:android.intent.action.SEARCH+category:name:android.intent.category.DEFAULT +ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:74:13-77:29 +action#android.intent.action.SEARCH +ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:75:17-71 + android:name + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:75:25-68 +meta-data#android.app.searchable +ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:79:13-81:54 + android:resource + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:81:17-51 + android:name + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:80:17-54 +provider#net.micode.notes.data.NotesProvider +ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:84:9-87:43 + android:authorities + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:86:13-47 + android:multiprocess + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:87:13-40 + android:name + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:85:13-63 +receiver#net.micode.notes.widget.NoteWidgetProvider_2x +ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:89:9-101:20 + android:label + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:91:13-50 + android:name + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:90:13-57 +intent-filter#action:name:android.appwidget.action.APPWIDGET_DELETED+action:name:android.appwidget.action.APPWIDGET_UPDATE+action:name:android.intent.action.PRIVACY_MODE_CHANGED +ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:92:13-96:29 +action#android.appwidget.action.APPWIDGET_UPDATE +ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:93:17-84 + android:name + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:93:25-81 +action#android.appwidget.action.APPWIDGET_DELETED +ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:94:17-85 + android:name + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:94:25-82 +action#android.intent.action.PRIVACY_MODE_CHANGED +ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:95:17-85 + android:name + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:95:25-82 +meta-data#android.appwidget.provider +ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:98:13-100:58 + android:resource + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:100:17-55 + android:name + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:99:17-58 +receiver#net.micode.notes.widget.NoteWidgetProvider_4x +ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:102:9-115:20 + android:label + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:104:13-50 + android:name + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:103:13-57 +receiver#net.micode.notes.ui.AlarmInitReceiver +ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:117:9-121:20 + android:name + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:117:19-55 +intent-filter#action:name:android.intent.action.BOOT_COMPLETED +ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:118:13-120:29 +action#android.intent.action.BOOT_COMPLETED +ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:119:17-79 + android:name + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:119:25-76 +receiver#net.micode.notes.ui.AlarmReceiver +ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:123:9-126:20 + android:process + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:125:13-38 + android:name + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:124:13-61 +activity#net.micode.notes.ui.AlarmAlertActivity +ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:128:9-133:20 + android:label + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:130:13-45 + android:launchMode + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:131:13-48 + android:theme + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:132:13-75 + android:name + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:129:13-50 +activity#net.micode.notes.ui.NotesPreferenceActivity +ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:135:9-140:20 + android:label + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:137:13-54 + android:launchMode + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:138:13-43 + android:theme + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:139:13-60 + android:name + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:136:13-71 +service#net.micode.notes.gtask.remote.GTaskSyncService +ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:142:9-145:19 + android:exported + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:144:13-37 + android:name + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:143:13-74 +meta-data#android.app.default_searchable +ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:147:9-149:52 + android:value + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:149:13-49 + android:name + ADDED from D:\Study\SE\MiNotes\src\Notes\app\src\main\AndroidManifest.xml:148:13-58 diff --git a/src/Notes/app/build/tmp/compileDebugJavaWithJavac/previous-compilation-data.bin b/src/Notes/app/build/tmp/compileDebugJavaWithJavac/previous-compilation-data.bin new file mode 100644 index 0000000..8bdaa80 Binary files /dev/null and b/src/Notes/app/build/tmp/compileDebugJavaWithJavac/previous-compilation-data.bin differ diff --git a/src/Notes/app/src/main/AndroidManifest.xml b/src/Notes/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..c71be5b --- /dev/null +++ b/src/Notes/app/src/main/AndroidManifest.xml @@ -0,0 +1,185 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Notes/app/src/main/java/net/micode/notes/data/Contact.java b/src/Notes/app/src/main/java/net/micode/notes/data/Contact.java new file mode 100644 index 0000000..d65c239 --- /dev/null +++ b/src/Notes/app/src/main/java/net/micode/notes/data/Contact.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.data;//属于data这个包 + +import android.content.Context; +import android.database.Cursor; +import android.provider.ContactsContract.CommonDataKinds.Phone; +import android.provider.ContactsContract.Data; +import android.telephony.PhoneNumberUtils; +import android.util.Log; + +import java.util.HashMap; +/** + * @Package: net.micode.notes.data + * @ClassName: Contact + * @Description: + * Contact类用于查询联系人信息并进行缓存。 + * 该类包含一个静态的HashMap作为缓存,存储电话号码和对应的联系人名字。 + * 通过调用getContact方法可以根据给定的电话号码查询联系人名字。 + * 如果缓存中已经存在该电话号码对应的联系人名字,则直接返回缓存中的结果,否则通过查询数据库获取联系人名字并更新缓存。 + * 该类还定义了一个私有的SQL筛选语句用于查询联系人信息。 + * 注意:该类是线程不安全的,如果需要在多线程环境下使用,请做好同步控制 + * @Author: YangYizhe + * @CreateDate: 12/17/2023 10:10 AM + * @Version: 1.0 + */ +public class Contact { + /** + * 作为缓存,存储电话号码和对应的联系人名字 + */ + private static HashMap sContactCache; + private static final String TAG = "Contact";//设置日志TAG标签 + + //查询联系人的SQL筛选语句 + private static final String CALLER_ID_SELECTION = "PHONE_NUMBERS_EQUAL(" + Phone.NUMBER + + ",?) AND " + Data.MIMETYPE + "='" + Phone.CONTENT_ITEM_TYPE + "'" + + " AND " + Data.RAW_CONTACT_ID + " IN " + + "(SELECT raw_contact_id " + + " FROM phone_lookup" + + " WHERE min_match = '+')"; + //获取联系人 + public static String getContact(Context context, String phoneNumber) { + if(sContactCache == null) {/*如果缓存为空,就新建一个*/ + sContactCache = new HashMap(); + } + + if(sContactCache.containsKey(phoneNumber)) {/*如果缓存中已经有该电话号码对应的联系人名字,就直接返回*/ + return sContactCache.get(phoneNumber); + } + + String selection = CALLER_ID_SELECTION.replace("+", + PhoneNumberUtils.toCallerIDMinMatch(phoneNumber));//将电话号码转换为最小匹配模式,用于筛选 + Cursor cursor = context.getContentResolver().query( + Data.CONTENT_URI, + new String [] { Phone.DISPLAY_NAME }, + selection,//使用筛选条件 + new String[] { phoneNumber }, + null); + + if (cursor != null && cursor.moveToFirst()) {//如果找到了符合条件的联系人 + try { + String name = cursor.getString(0);//获取联系人名字 + sContactCache.put(phoneNumber, name);//将电话号码和联系人名字添加到缓存中 + return name;//返回联系人名字 + } catch (IndexOutOfBoundsException e) { + Log.e(TAG, " Cursor get string error " + e.toString()); + return null; + } finally { + cursor.close();//关闭游标 + } + } else {/*没找到*/ + Log.d(TAG, "No contact matched with number:" + phoneNumber); + return null; + } + } +} diff --git a/src/Notes/app/src/main/java/net/micode/notes/model/Note.java b/src/Notes/app/src/main/java/net/micode/notes/model/Note.java new file mode 100644 index 0000000..864da2c --- /dev/null +++ b/src/Notes/app/src/main/java/net/micode/notes/model/Note.java @@ -0,0 +1,322 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.model; +import android.content.ContentProviderOperation; +import android.content.ContentProviderResult; +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.Context; +import android.content.OperationApplicationException; +import android.net.Uri; +import android.os.RemoteException; +import android.util.Log; + +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.CallNote; +import net.micode.notes.data.Notes.DataColumns; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.data.Notes.TextNote; + +import java.util.ArrayList; + +/** + * @Package: net.micode.notes.model + * @ClassName: Note + * @Description: 笔记类,用于操作和管理笔记数据 + * @Author: YangYizhe + * @CreateDate: 12/17/2023 10:06 AM + * @UpdateUser: none + * @UpdateDate: 12/17/2023 10:06 AM + * @UpdateRemark: none + * @Version: 1.0 + */ +public class Note { + private ContentValues mNoteDiffValues; + private NoteData mNoteData; + private static final String TAG = "Note"; + /** + * @method getNewNoteId + * @description 获取新的笔记ID,用于向数据库中添加新的笔记 + * @date: 12/20/2023 11:23 PM + * @author: YangYizhe + * @param folderId 文件夹Id + * @return noteId 新的笔记Id + */ + public static synchronized long getNewNoteId(Context context, long folderId) { + // Create a new note in the database + // 在数据库中创建一个新的笔记 + ContentValues values = new ContentValues(); + long createdTime = System.currentTimeMillis(); + values.put(NoteColumns.CREATED_DATE, createdTime); + values.put(NoteColumns.MODIFIED_DATE, createdTime); + values.put(NoteColumns.TYPE, Notes.TYPE_NOTE); + values.put(NoteColumns.LOCAL_MODIFIED, 1); + values.put(NoteColumns.PARENT_ID, folderId); + Uri uri = context.getContentResolver().insert(Notes.CONTENT_NOTE_URI, values); + + long noteId = 0; + try { + noteId = Long.valueOf(uri.getPathSegments().get(1)); + } catch (NumberFormatException e) { + Log.e(TAG, "Get note id error :" + e.toString()); + noteId = 0; + } + if (noteId == -1) { + throw new IllegalStateException("Wrong note id:" + noteId); + } + return noteId; + } + + public Note() { + mNoteDiffValues = new ContentValues(); + mNoteData = new NoteData(); + } + /** + * @method setNoteValue + * @description 设置笔记的值 + * @date: 12/20/2023 11:31 PM + * @author: YangYizhe + * @param key + * @param value + */ + public void setNoteValue(String key, String value) { + mNoteDiffValues.put(key, value); + mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); + mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); + } + /** + * @method setTextData + * @description 设置文本数据 + * @date: 12/20/2023 11:31 PM + * @author: YangYizhe + * @param key + * @param value + */ + public void setTextData(String key, String value) { + mNoteData.setTextData(key, value); + } + /** + * @method setTextDataId + * @description 设置文本数据ID + * @date: 12/20/2023 11:33 PM + * @author: YangYizhe + * @param id + */ + public void setTextDataId(long id) { + mNoteData.setTextDataId(id); + } + /** + * @method getTextDataId + * @description 获取文本数据Id + * @date: 12/20/2023 11:34 PM + * @author: YangYizhe + * @return mTextDataId 文本数据Id + */ + public long getTextDataId() { + return mNoteData.mTextDataId; + } + /** + * @method setCallDataId + * @description 设置通话记录数据ID + * @date: 12/20/2023 11:35 PM + * @author: YangYizhe + * @param id 通话记录数据id + */ + public void setCallDataId(long id) { + mNoteData.setCallDataId(id); + } + /** + * @method setCallData + * @description 设置通话记录数据 + * @date: 12/20/2023 11:36 PM + * @author: YangYizhe + * @param key + * @param value + */ + public void setCallData(String key, String value) { + mNoteData.setCallData(key, value); + } + /** + * @method isLocalModified + * @description 判断笔记是否有本地修改 + * @date: 12/20/2023 11:38 PM + * @author: YangYizhe + * @return bool 是否有本地修改 + */ + public boolean isLocalModified() { + return mNoteDiffValues.size() > 0 || mNoteData.isLocalModified(); + } + /** + * @method syncNote + * @description 同步笔记,将本地修改的数据同步到服务器 + * @date: 12/20/2023 11:42 PM + * @author: YangYizhe + * @param context 上下文对象 + * @param noteId 笔记ID + * @return bool 是否同步成功 + */ + public boolean syncNote(Context context, long noteId) { + if (noteId <= 0) { + throw new IllegalArgumentException("Wrong note id:" + noteId); + } + + if (!isLocalModified()) { + return true; + } + + /** + * In theory, once data changed, the note should be updated on {@link NoteColumns#LOCAL_MODIFIED} and + * {@link NoteColumns#MODIFIED_DATE}. For data safety, though update note fails, we also update the + * note data info + */ + if (context.getContentResolver().update( + ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), mNoteDiffValues, null, + null) == 0) { + Log.e(TAG, "Update note error, should not happen"); + // Do not return, fall through + } + mNoteDiffValues.clear(); + + if (mNoteData.isLocalModified() + && (mNoteData.pushIntoContentResolver(context, noteId) == null)) { + return false; + } + + return true; + } + + private class NoteData { + private long mTextDataId; + + private ContentValues mTextDataValues; + + private long mCallDataId; + + private ContentValues mCallDataValues; + + private static final String TAG = "NoteData"; + + public NoteData() { + mTextDataValues = new ContentValues(); + mCallDataValues = new ContentValues(); + mTextDataId = 0; + mCallDataId = 0; + } + + boolean isLocalModified() { + return mTextDataValues.size() > 0 || mCallDataValues.size() > 0; + } + + void setTextDataId(long id) { + if(id <= 0) { + throw new IllegalArgumentException("Text data id should larger than 0"); + } + mTextDataId = id; + } + + void setCallDataId(long id) { + if (id <= 0) { + throw new IllegalArgumentException("Call data id should larger than 0"); + } + mCallDataId = id; + } + + void setCallData(String key, String value) { + mCallDataValues.put(key, value); + mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); + mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); + } + + void setTextData(String key, String value) { + mTextDataValues.put(key, value); + mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); + mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); + } + + Uri pushIntoContentResolver(Context context, long noteId) { + /** + * Check for safety + */ + if (noteId <= 0) { + throw new IllegalArgumentException("Wrong note id:" + noteId); + } + + ArrayList operationList = new ArrayList(); + ContentProviderOperation.Builder builder = null; + + if(mTextDataValues.size() > 0) { + mTextDataValues.put(DataColumns.NOTE_ID, noteId); + if (mTextDataId == 0) { + mTextDataValues.put(DataColumns.MIME_TYPE, TextNote.CONTENT_ITEM_TYPE); + Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI, + mTextDataValues); + try { + setTextDataId(Long.valueOf(uri.getPathSegments().get(1))); + } catch (NumberFormatException e) { + Log.e(TAG, "Insert new text data fail with noteId" + noteId); + mTextDataValues.clear(); + return null; + } + } else { + builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId( + Notes.CONTENT_DATA_URI, mTextDataId)); + builder.withValues(mTextDataValues); + operationList.add(builder.build()); + } + mTextDataValues.clear(); + } + + if(mCallDataValues.size() > 0) { + mCallDataValues.put(DataColumns.NOTE_ID, noteId); + if (mCallDataId == 0) { + mCallDataValues.put(DataColumns.MIME_TYPE, CallNote.CONTENT_ITEM_TYPE); + Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI, + mCallDataValues); + try { + setCallDataId(Long.valueOf(uri.getPathSegments().get(1))); + } catch (NumberFormatException e) { + Log.e(TAG, "Insert new call data fail with noteId" + noteId); + mCallDataValues.clear(); + return null; + } + } else { + builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId( + Notes.CONTENT_DATA_URI, mCallDataId)); + builder.withValues(mCallDataValues); + operationList.add(builder.build()); + } + mCallDataValues.clear(); + } + + if (operationList.size() > 0) { + try { + ContentProviderResult[] results = context.getContentResolver().applyBatch( + Notes.AUTHORITY, operationList); + return (results == null || results.length == 0 || results[0] == null) ? null + : ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId); + } catch (RemoteException e) { + Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); + return null; + } catch (OperationApplicationException e) { + Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); + return null; + } + } + return null; + } + } +} diff --git a/src/Notes/app/src/main/java/net/micode/notes/model/WorkingNote.java b/src/Notes/app/src/main/java/net/micode/notes/model/WorkingNote.java new file mode 100644 index 0000000..31061f9 --- /dev/null +++ b/src/Notes/app/src/main/java/net/micode/notes/model/WorkingNote.java @@ -0,0 +1,417 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.model; + +import android.appwidget.AppWidgetManager; +import android.content.ContentUris; +import android.content.Context; +import android.database.Cursor; +import android.text.TextUtils; +import android.util.Log; + +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.CallNote; +import net.micode.notes.data.Notes.DataColumns; +import net.micode.notes.data.Notes.DataConstants; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.data.Notes.TextNote; +import net.micode.notes.tool.ResourceParser.NoteBgResources; + +/** + * @Package: net.micode.notes.model + * @ClassName: WorkingNote + * @Description: + * 工作笔记类,用于表示一条工作笔记的信息 + * Note 类是一个基本的笔记类,包含了笔记的基本信息,如标题、内容等 + * WorkingNote 类则是在 Note 类的基础上进行扩展,增加了一些与工作笔记相关的属性和方法,例如提醒日期、背景颜色、小部件等。 + * @Author: YangYizhe + * @CreateDate: 12/20/2023 11:48 PM + * @Version: 1.0 + */ +public class WorkingNote { + // Note for the working note + private Note mNote; + // Note Id + private long mNoteId; + // Note content + private String mContent; + // Note mode + private int mMode; + + private long mAlertDate; + + private long mModifiedDate; + + private int mBgColorId; + + private int mWidgetId; + + private int mWidgetType; + + private long mFolderId; + + private Context mContext; + + private static final String TAG = "WorkingNote"; + + private boolean mIsDeleted; + /** + * 笔记设置状态变化监听器 + */ + private NoteSettingChangedListener mNoteSettingStatusListener; + + public static final String[] DATA_PROJECTION = new String[] { + DataColumns.ID, + DataColumns.CONTENT, + DataColumns.MIME_TYPE, + DataColumns.DATA1, + DataColumns.DATA2, + DataColumns.DATA3, + DataColumns.DATA4, + }; + + public static final String[] NOTE_PROJECTION = new String[] { + NoteColumns.PARENT_ID, + NoteColumns.ALERTED_DATE, + NoteColumns.BG_COLOR_ID, + NoteColumns.WIDGET_ID, + NoteColumns.WIDGET_TYPE, + NoteColumns.MODIFIED_DATE + }; + + private static final int DATA_ID_COLUMN = 0; + + private static final int DATA_CONTENT_COLUMN = 1; + + private static final int DATA_MIME_TYPE_COLUMN = 2; + + private static final int DATA_MODE_COLUMN = 3; + + private static final int NOTE_PARENT_ID_COLUMN = 0; + + private static final int NOTE_ALERTED_DATE_COLUMN = 1; + + private static final int NOTE_BG_COLOR_ID_COLUMN = 2; + + private static final int NOTE_WIDGET_ID_COLUMN = 3; + + private static final int NOTE_WIDGET_TYPE_COLUMN = 4; + + private static final int NOTE_MODIFIED_DATE_COLUMN = 5; + + // New note construct + /** + * @method WorkingNote + * @description 构造方法,创建一个新的工作笔记 + * @date: 12/20/2023 11:54 PM + * @author: YangYizhe + * @param context 上下文环境 + * @param folderId 笔记所属的文件夹ID + */ + private WorkingNote(Context context, long folderId) { + mContext = context; + mAlertDate = 0; + mModifiedDate = System.currentTimeMillis(); + mFolderId = folderId; + mNote = new Note(); + mNoteId = 0; + mIsDeleted = false; + mMode = 0; + mWidgetType = Notes.TYPE_WIDGET_INVALIDE; + } + /** + * @method WorkingNote + * @description 构造方法,加载一个已存在的工作笔记 + * @date: 12/20/2023 11:54 PM + * @author: YangYizhe + * @param context 上下文环境 + * @param noteId 笔记的ID + * @param folderId 笔记所属的文件夹ID + */ + // Existing note construct + private WorkingNote(Context context, long noteId, long folderId) { + mContext = context; + mNoteId = noteId; + mFolderId = folderId; + mIsDeleted = false; + mNote = new Note(); + loadNote(); + } + /** + * @method loadNote + * @description 加载笔记的详细信息 + * @date: 12/20/2023 11:55 PM + * @author: YangYizhe + */ + private void loadNote() { + Cursor cursor = mContext.getContentResolver().query( + ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mNoteId), NOTE_PROJECTION, null, + null, null); + + if (cursor != null) { + if (cursor.moveToFirst()) { + mFolderId = cursor.getLong(NOTE_PARENT_ID_COLUMN); + mBgColorId = cursor.getInt(NOTE_BG_COLOR_ID_COLUMN); + mWidgetId = cursor.getInt(NOTE_WIDGET_ID_COLUMN); + mWidgetType = cursor.getInt(NOTE_WIDGET_TYPE_COLUMN); + mAlertDate = cursor.getLong(NOTE_ALERTED_DATE_COLUMN); + mModifiedDate = cursor.getLong(NOTE_MODIFIED_DATE_COLUMN); + } + cursor.close(); + } else { + Log.e(TAG, "No note with id:" + mNoteId); + throw new IllegalArgumentException("Unable to find note with id " + mNoteId); + } + loadNoteData(); + } + /** + * @method loadNoteData + * @description 加载笔记的Data + * @date: 12/20/2023 11:55 PM + * @author: YangYizhe + */ + private void loadNoteData() { + Cursor cursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, DATA_PROJECTION, + DataColumns.NOTE_ID + "=?", new String[] { + String.valueOf(mNoteId) + }, null); + + if (cursor != null) { + if (cursor.moveToFirst()) { + do { + String type = cursor.getString(DATA_MIME_TYPE_COLUMN); + if (DataConstants.NOTE.equals(type)) { + mContent = cursor.getString(DATA_CONTENT_COLUMN); + mMode = cursor.getInt(DATA_MODE_COLUMN); + mNote.setTextDataId(cursor.getLong(DATA_ID_COLUMN)); + } else if (DataConstants.CALL_NOTE.equals(type)) { + mNote.setCallDataId(cursor.getLong(DATA_ID_COLUMN)); + } else { + Log.d(TAG, "Wrong note type with type:" + type); + } + } while (cursor.moveToNext()); + } + cursor.close(); + } else { + Log.e(TAG, "No data with id:" + mNoteId); + throw new IllegalArgumentException("Unable to find note's data with id " + mNoteId); + } + } + /** + * @method createEmptyNote + * @description 创建一个空的工作笔记 + * @date: 12/20/2023 11:56 PM + * @author: YangYizhe + * @param context 上下文环境 + * @param folderId 笔记所属的文件夹ID + * @param widgetId 笔记的小部件ID + * @param widgetType 笔记的小部件类型 + * @param defaultBgColorId 笔记的默认背景颜色ID + * @return note 创建的空的工作笔记对象 + */ + public static WorkingNote createEmptyNote(Context context, long folderId, int widgetId, + int widgetType, int defaultBgColorId) { + WorkingNote note = new WorkingNote(context, folderId); + note.setBgColorId(defaultBgColorId); + note.setWidgetId(widgetId); + note.setWidgetType(widgetType); + return note; + } + + public static WorkingNote load(Context context, long id) { + return new WorkingNote(context, id, 0); + } + + public synchronized boolean saveNote() { + if (isWorthSaving()) { + if (!existInDatabase()) { + if ((mNoteId = Note.getNewNoteId(mContext, mFolderId)) == 0) { + Log.e(TAG, "Create new note fail with id:" + mNoteId); + return false; + } + } + + mNote.syncNote(mContext, mNoteId); + + /** + * Update widget content if there exist any widget of this note + */ + if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID + && mWidgetType != Notes.TYPE_WIDGET_INVALIDE + && mNoteSettingStatusListener != null) { + mNoteSettingStatusListener.onWidgetChanged(); + } + return true; + } else { + return false; + } + } + + public boolean existInDatabase() { + return mNoteId > 0; + } + + private boolean isWorthSaving() { + if (mIsDeleted || (!existInDatabase() && TextUtils.isEmpty(mContent)) + || (existInDatabase() && !mNote.isLocalModified())) { + return false; + } else { + return true; + } + } + + public void setOnSettingStatusChangedListener(NoteSettingChangedListener l) { + mNoteSettingStatusListener = l; + } + + public void setAlertDate(long date, boolean set) { + if (date != mAlertDate) { + mAlertDate = date; + mNote.setNoteValue(NoteColumns.ALERTED_DATE, String.valueOf(mAlertDate)); + } + if (mNoteSettingStatusListener != null) { + mNoteSettingStatusListener.onClockAlertChanged(date, set); + } + } + + public void markDeleted(boolean mark) { + mIsDeleted = mark; + if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID + && mWidgetType != Notes.TYPE_WIDGET_INVALIDE && mNoteSettingStatusListener != null) { + mNoteSettingStatusListener.onWidgetChanged(); + } + } + + public void setBgColorId(int id) { + if (id != mBgColorId) { + mBgColorId = id; + if (mNoteSettingStatusListener != null) { + mNoteSettingStatusListener.onBackgroundColorChanged(); + } + mNote.setNoteValue(NoteColumns.BG_COLOR_ID, String.valueOf(id)); + } + } + + public void setCheckListMode(int mode) { + if (mMode != mode) { + if (mNoteSettingStatusListener != null) { + mNoteSettingStatusListener.onCheckListModeChanged(mMode, mode); + } + mMode = mode; + mNote.setTextData(TextNote.MODE, String.valueOf(mMode)); + } + } + + public void setWidgetType(int type) { + if (type != mWidgetType) { + mWidgetType = type; + mNote.setNoteValue(NoteColumns.WIDGET_TYPE, String.valueOf(mWidgetType)); + } + } + + public void setWidgetId(int id) { + if (id != mWidgetId) { + mWidgetId = id; + mNote.setNoteValue(NoteColumns.WIDGET_ID, String.valueOf(mWidgetId)); + } + } + + public void setWorkingText(String text) { + if (!TextUtils.equals(mContent, text)) { + mContent = text; + mNote.setTextData(DataColumns.CONTENT, mContent); + } + } + + public void convertToCallNote(String phoneNumber, long callDate) { + mNote.setCallData(CallNote.CALL_DATE, String.valueOf(callDate)); + mNote.setCallData(CallNote.PHONE_NUMBER, phoneNumber); + mNote.setNoteValue(NoteColumns.PARENT_ID, String.valueOf(Notes.ID_CALL_RECORD_FOLDER)); + } + + public boolean hasClockAlert() { + return (mAlertDate > 0 ? true : false); + } + + public String getContent() { + return mContent; + } + + public long getAlertDate() { + return mAlertDate; + } + + public long getModifiedDate() { + return mModifiedDate; + } + + public int getBgColorResId() { + return NoteBgResources.getNoteBgResource(mBgColorId); + } + + public int getBgColorId() { + return mBgColorId; + } + + public int getTitleBgResId() { + return NoteBgResources.getNoteTitleBgResource(mBgColorId); + } + + public int getCheckListMode() { + return mMode; + } + + public long getNoteId() { + return mNoteId; + } + + public long getFolderId() { + return mFolderId; + } + + public int getWidgetId() { + return mWidgetId; + } + + public int getWidgetType() { + return mWidgetType; + } + + public interface NoteSettingChangedListener { + /** + * Called when the background color of current note has just changed + */ + void onBackgroundColorChanged(); + + /** + * Called when user set clock + */ + void onClockAlertChanged(long date, boolean set); + + /** + * Call when user create note from widget + */ + void onWidgetChanged(); + + /** + * Call when switch between check list mode and normal mode + * @param oldMode is previous mode before change + * @param newMode is new mode + */ + void onCheckListModeChanged(int oldMode, int newMode); + } +} diff --git a/src/Notes/app/src/main/java/net/micode/notes/ui/AlarmAlertActivity.java b/src/Notes/app/src/main/java/net/micode/notes/ui/AlarmAlertActivity.java new file mode 100644 index 0000000..96f28bf --- /dev/null +++ b/src/Notes/app/src/main/java/net/micode/notes/ui/AlarmAlertActivity.java @@ -0,0 +1,201 @@ +package net.micode.notes.ui; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; +import android.content.DialogInterface.OnDismissListener; +import android.content.Intent; +import android.media.AudioManager; +import android.media.MediaPlayer; +import android.media.RingtoneManager; +import android.net.Uri; +import android.os.Bundle; +import android.os.PowerManager; +import android.provider.Settings; +import android.view.Window; +import android.view.WindowManager; + +import net.micode.notes.R; +import net.micode.notes.data.Notes; +import net.micode.notes.tool.DataUtils; + +import java.io.IOException; +/** + * @Package: net.micode.notes.ui + * @ClassName: AlarmAlertActivity + * @Description: 闹钟提醒界面 + * @Author: YangYizhe + * @CreateDate: 12/21/2023 12:02 AM + * @Version: 1.0 + */ +public class AlarmAlertActivity extends Activity implements OnClickListener, OnDismissListener { + private long mNoteId; //文本在数据库存储中的ID号 + private String mSnippet; //闹钟提示时出现的文本片段 + private static final int SNIPPET_PREW_MAX_LEN = 60; + MediaPlayer mPlayer; + /** + * @method onCreate + * @description 当 Activity 创建时调用的生命周期方法 + * @date: 12/21/2023 12:05 AM + * @author: YangYizhe + * @param savedInstanceState 保存 Activity 状态的 Bundle 对象 + * @return null + */ + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + requestWindowFeature(Window.FEATURE_NO_TITLE);//设置界面显示——无标题 + + final Window win = getWindow(); + + win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);//设置窗体属性——在锁屏时显示 + + if (!isScreenOn()) { + win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON//保持点亮、点亮屏幕、允许点亮时解锁 + | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON + | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON + | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR); + } + + Intent intent = getIntent();//获取传递的 Intent + + try { + // 从 Intent 中获取数据并处理——获取标签 ID + mNoteId = Long.valueOf(intent.getData().getPathSegments().get(1)); + // 根据 ID 从数据库中获取标签内容 + mSnippet = DataUtils.getSnippetById(this.getContentResolver(), mNoteId); + // 判断标签片段是否达到符合长度 + mSnippet = mSnippet.length() > SNIPPET_PREW_MAX_LEN ? mSnippet.substring(0, + SNIPPET_PREW_MAX_LEN) + getResources().getString(R.string.notelist_string_info) + : mSnippet; + } catch (IllegalArgumentException e) { + e.printStackTrace(); + return; + } + + mPlayer = new MediaPlayer(); + if (DataUtils.visibleInNoteDatabase(getContentResolver(), mNoteId, Notes.TYPE_NOTE)) { + showActionDialog();// 显示对话框 + playAlarmSound();// 播放闹钟提示音 + } else { + finish();// 结束当前 Activity + } + } + /** + * @method isScreenOn + * @description + * 判断屏幕是否锁屏,调用系统函数判断,最后返回值是布尔类型 + * @date: 12/23/2023 11:21 PM + * @author: YangYizhe + * @param + * @return + */ + private boolean isScreenOn() { + PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); + return pm.isScreenOn(); + } + /** + * @method playAlarmSound + * @description + * 播放闹钟提示音 + * @date: 12/23/2023 11:21 PM + * @author: YangYizhe + */ + private void playAlarmSound() { + Uri url = RingtoneManager.getActualDefaultRingtoneUri(this, RingtoneManager.TYPE_ALARM); + //调用系统的铃声管理URI,得到闹钟提示音 + int silentModeStreams = Settings.System.getInt(getContentResolver(), + Settings.System.MODE_RINGER_STREAMS_AFFECTED, 0); + + if ((silentModeStreams & (1 << AudioManager.STREAM_ALARM)) != 0) { + mPlayer.setAudioStreamType(silentModeStreams); + } else { + mPlayer.setAudioStreamType(AudioManager.STREAM_ALARM); + } + try { + mPlayer.setDataSource(this, url); + mPlayer.prepare(); + mPlayer.setLooping(true);//设置是否循环播放 + mPlayer.start();//开始播放 + } catch (IllegalArgumentException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (SecurityException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IllegalStateException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + /** + * @method showActionDialog + * @description + * AlertDialog的构造方法全部是Protected的 + * 所以不能直接通过new一个AlertDialog来创建出一个AlertDialog。 + * 要创建一个AlertDialog,就要用到AlertDialog.Builder中的create()方法 + * 如这里的dialog就是新建了一个AlertDialog + * @date: 12/23/2023 11:28 PM + * @author: YangYizhe + */ + private void showActionDialog() { + AlertDialog.Builder dialog = new AlertDialog.Builder(this); + dialog.setTitle(R.string.app_name);//为对话框设置标题 + dialog.setMessage(mSnippet);//为对话框设置内容 + dialog.setPositiveButton(R.string.notealert_ok, this);//给对话框添加"Yes"按钮 + if (isScreenOn()) { + dialog.setNegativeButton(R.string.notealert_enter, this);//对话框添加"No"按钮 + } + dialog.show().setOnDismissListener(this); + } + /** + * @method onClick + * @description 处理点击事件 + * @date: 12/23/2023 11:27 PM + * @author: YangYizhe + * @param dialog + * @param which + */ + public void onClick(DialogInterface dialog, int which) { + switch (which) {//用which来选择click后下一步的操作 + case DialogInterface.BUTTON_NEGATIVE://这是取消操作 + Intent intent = new Intent(this, NoteEditActivity.class);//实现两个类间的数据传输 + intent.setAction(Intent.ACTION_VIEW);//设置动作属性 + intent.putExtra(Intent.EXTRA_UID, mNoteId);//实现key-value对 EXTRA_UID为key;mNoteId为键 + startActivity(intent);//开始动作 + break; + default: + break; + } + } + /** + * @method onDismiss + * @description 忽略 + * @date: 12/23/2023 11:25 PM + * @author: YangYizhe + * @param dialog + */ + public void onDismiss(DialogInterface dialog) { + stopAlarmSound();//停止闹钟声音 + finish(); + } + /** + * @method stopAlarmSound + * @description 停止闹钟的声音 + * @date: 12/23/2023 11:26 PM + * @author: YangYizhe + */ + private void stopAlarmSound() { + if (mPlayer != null) { + mPlayer.stop();//停止播放 + mPlayer.release();//释放MediaPlayer对象 + mPlayer = null; + } + } +} \ No newline at end of file diff --git a/src/Notes/app/src/main/java/net/micode/notes/ui/AlarmInitReceiver.java b/src/Notes/app/src/main/java/net/micode/notes/ui/AlarmInitReceiver.java new file mode 100644 index 0000000..1982fda --- /dev/null +++ b/src/Notes/app/src/main/java/net/micode/notes/ui/AlarmInitReceiver.java @@ -0,0 +1,57 @@ +package net.micode.notes.ui; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.ContentUris; +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; + +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.NoteColumns; + +/** + * @Package: net.micode.notes.ui + * @ClassName: AlarmInitReceiver + * @Description: + * AlarmInitReceiver 是一个广播接收器(BroadcastReceiver)的类 + * 广播接收器是 Android 中常用的一种组件,用于接收并处理系统或应用发送的广播消息 + * @Author: YangYizhe + * @CreateDate: 12/21/2023 12:09 AM + * @Version: 1.0 + */ +public class AlarmInitReceiver extends BroadcastReceiver { + private static final String [] PROJECTION = new String [] { + NoteColumns.ID, + NoteColumns.ALERTED_DATE + };//对数据库的操作,调用标签ID和闹钟时间 + private static final int COLUMN_ID = 0; + private static final int COLUMN_ALERTED_DATE = 1; + + @Override + public void onReceive(Context context, Intent intent) { + long currentDate = System.currentTimeMillis();//System.currentTimeMillis()产生一个当前的毫秒 + Cursor c = context.getContentResolver().query(Notes.CONTENT_NOTE_URI, + PROJECTION, + NoteColumns.ALERTED_DATE + ">? AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE, + new String[] { String.valueOf(currentDate) }, + null);//将long变量currentDate转化为字符串 + //Cursor在这里的作用是通过查找数据库中的标签内容,找到和当前系统时间相等的标签 + + if (c != null) { + if (c.moveToFirst()) { + do { + long alertDate = c.getLong(COLUMN_ALERTED_DATE); + Intent sender = new Intent(context, AlarmReceiver.class); + sender.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, c.getLong(COLUMN_ID))); + PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, sender, 0); + AlarmManager alermManager = (AlarmManager) context + .getSystemService(Context.ALARM_SERVICE); + alermManager.set(AlarmManager.RTC_WAKEUP, alertDate, pendingIntent); + } while (c.moveToNext()); + } + c.close(); + } + } +} \ No newline at end of file diff --git a/src/Notes/app/src/main/java/net/micode/notes/ui/AlarmReceiver.java b/src/Notes/app/src/main/java/net/micode/notes/ui/AlarmReceiver.java new file mode 100644 index 0000000..646f69f --- /dev/null +++ b/src/Notes/app/src/main/java/net/micode/notes/ui/AlarmReceiver.java @@ -0,0 +1,25 @@ +package net.micode.notes.ui; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +/** + * @Package: net.micode.notes.ui + * @ClassName: AlarmReceiver + * @Description: 闹钟接收器,接收到闹钟触发的广播后启动AlarmAlertActivity进行闹钟提醒 + * @Author: YangYizhe + * @CreateDate: 12/17/2023 10:02 AM + * @UpdateUser: none + * @UpdateDate: 12/17/2023 10:02 AM + * @UpdateRemark: none + * @Version: 1.0 + */ +public class AlarmReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + intent.setClass(context, AlarmAlertActivity.class);//启动AlarmAlertActivity + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent);//启动闹钟提醒界面 + } +} + \ No newline at end of file diff --git a/src/Notes/app/src/main/java/net/micode/notes/ui/DateTimePicker.java b/src/Notes/app/src/main/java/net/micode/notes/ui/DateTimePicker.java new file mode 100644 index 0000000..9cf4ba6 --- /dev/null +++ b/src/Notes/app/src/main/java/net/micode/notes/ui/DateTimePicker.java @@ -0,0 +1,518 @@ +package net.micode.notes.ui; + +import java.text.DateFormatSymbols; +import java.util.Calendar; + +import net.micode.notes.R; + + +import android.content.Context; +import android.text.format.DateFormat; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.NumberPicker; +/** + * @Package: net.micode.notes.ui + * @ClassName: DateTimePicker + * @Description: 继承自FrameLayout,实现了日期和时间的选择功能 + * 提供一个用户界面,让用户可以方便地选择日期和时间,并且能够监听用户对日期和时间的改变 + * 构造方法,包括默认构造方法和带参数的构造方法,用于初始化日期选择器的界面和属性; + * 一系列的回调方法,用于监听日期和时间的改变,包括日期选择、小时选择、分钟选择、上午/下午选择等; + * 一些公开的接口方法,用于设置当前日期、时间,设置24小时模式或12小时模式的切换等; + * 辅助方法,用于更新日期、小时、上午/下午选择器的界面显示和属性; + * 回调接口OnDateTimeChangedListener,用于监听日期和时间的改变事件。 + * @Author: YangYizhe + * @CreateDate: 12/21/2023 12:18 AM + * @Version: 1.0 + */ +public class DateTimePicker extends FrameLayout { + //FrameLayout是布局模板之一 + private static final boolean DEFAULT_ENABLE_STATE = true; + + private static final int HOURS_IN_HALF_DAY = 12; + private static final int HOURS_IN_ALL_DAY = 24; + private static final int DAYS_IN_ALL_WEEK = 7; + private static final int DATE_SPINNER_MIN_VAL = 0; + private static final int DATE_SPINNER_MAX_VAL = DAYS_IN_ALL_WEEK - 1; + private static final int HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW = 0; + private static final int HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW = 23; + private static final int HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW = 1; + private static final int HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW = 12; + private static final int MINUT_SPINNER_MIN_VAL = 0; + private static final int MINUT_SPINNER_MAX_VAL = 59; + private static final int AMPM_SPINNER_MIN_VAL = 0; + private static final int AMPM_SPINNER_MAX_VAL = 1; + /** + * 初始化控件 + * NumberPicker是数字选择器 + * 这里定义的四个变量全部是在设置闹钟时需要选择的变量(如日期、时、分、上午或者下午) + */ + private final NumberPicker mDateSpinner; + private final NumberPicker mHourSpinner; + private final NumberPicker mMinuteSpinner; + private final NumberPicker mAmPmSpinner; + private Calendar mDate;//定义了Calendar类型的变量mDate,用于操作时间 + private String[] mDateDisplayValues = new String[DAYS_IN_ALL_WEEK]; + + private boolean mIsAm; + + private boolean mIs24HourView; + + private boolean mIsEnabled = DEFAULT_ENABLE_STATE; + + private boolean mInitialising; + + private OnDateTimeChangedListener mOnDateTimeChangedListener; + + private NumberPicker.OnValueChangeListener mOnDateChangedListener = new NumberPicker.OnValueChangeListener() { + @Override + public void onValueChange(NumberPicker picker, int oldVal, int newVal) { + mDate.add(Calendar.DAY_OF_YEAR, newVal - oldVal); + updateDateControl(); + onDateTimeChanged(); + } + };//OnValueChangeListener,这是时间改变监听器,这里主要是对日期的监听 + + + private NumberPicker.OnValueChangeListener mOnHourChangedListener = new NumberPicker.OnValueChangeListener() { + //这里是对 小时(Hour) 的监听 + @Override + public void onValueChange(NumberPicker picker, int oldVal, int newVal) { + boolean isDateChanged = false; + Calendar cal = Calendar.getInstance(); + //声明一个Calendar的变量cal,便于后续的操作 + if (!mIs24HourView) { + if (!mIsAm && oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY) { + cal.setTimeInMillis(mDate.getTimeInMillis()); + cal.add(Calendar.DAY_OF_YEAR, 1); + isDateChanged = true; + //这里是对于12小时制时,晚上11点和12点交替时对日期的更改 + } else if (mIsAm && oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) { + cal.setTimeInMillis(mDate.getTimeInMillis()); + cal.add(Calendar.DAY_OF_YEAR, -1); + isDateChanged = true; + } + //这里是对于12小时制时,凌晨11点和12点交替时对日期的更改 + if (oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY || + oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) { + mIsAm = !mIsAm; + updateAmPmControl(); + }//这里是对于12小时制时,中午11点和12点交替时对AM和PM的更改 + } else { + if (oldVal == HOURS_IN_ALL_DAY - 1 && newVal == 0) { + cal.setTimeInMillis(mDate.getTimeInMillis()); + cal.add(Calendar.DAY_OF_YEAR, 1); + isDateChanged = true; + //这里是对于24小时制时,晚上11点和12点交替时对日期的更改 + } else if (oldVal == 0 && newVal == HOURS_IN_ALL_DAY - 1) { + cal.setTimeInMillis(mDate.getTimeInMillis()); + cal.add(Calendar.DAY_OF_YEAR, -1); + isDateChanged = true; + } + } //这里是对于12小时制时,凌晨11点和12点交替时对日期的更改 + int newHour = mHourSpinner.getValue() % HOURS_IN_HALF_DAY + (mIsAm ? 0 : HOURS_IN_HALF_DAY); + //通过数字选择器对newHour的赋值 + mDate.set(Calendar.HOUR_OF_DAY, newHour); + //通过set函数将新的Hour值传给mDate + onDateTimeChanged(); + if (isDateChanged) { + setCurrentYear(cal.get(Calendar.YEAR)); + setCurrentMonth(cal.get(Calendar.MONTH)); + setCurrentDay(cal.get(Calendar.DAY_OF_MONTH)); + } + } + }; + + private NumberPicker.OnValueChangeListener mOnMinuteChangedListener = new NumberPicker.OnValueChangeListener() { + @Override + //这里是对 分钟(Minute)改变的监听 + public void onValueChange(NumberPicker picker, int oldVal, int newVal) { + int minValue = mMinuteSpinner.getMinValue(); + int maxValue = mMinuteSpinner.getMaxValue(); + int offset = 0; + //设置offset,作为小时改变的一个记录数据 + if (oldVal == maxValue && newVal == minValue) { + offset += 1; + } else if (oldVal == minValue && newVal == maxValue) { + offset -= 1; + } + //如果原值为59,新值为0,则offset加1 + //如果原值为0,新值为59,则offset减1 + if (offset != 0) { + mDate.add(Calendar.HOUR_OF_DAY, offset); + mHourSpinner.setValue(getCurrentHour()); + updateDateControl(); + int newHour = getCurrentHourOfDay(); + if (newHour >= HOURS_IN_HALF_DAY) { + mIsAm = false; + updateAmPmControl(); + } else { + mIsAm = true; + updateAmPmControl(); + } + } + mDate.set(Calendar.MINUTE, newVal); + onDateTimeChanged(); + } + }; + + private NumberPicker.OnValueChangeListener mOnAmPmChangedListener = new NumberPicker.OnValueChangeListener() { + //对AM和PM的监听 + @Override + public void onValueChange(NumberPicker picker, int oldVal, int newVal) { + mIsAm = !mIsAm; + if (mIsAm) { + mDate.add(Calendar.HOUR_OF_DAY, -HOURS_IN_HALF_DAY); + } else { + mDate.add(Calendar.HOUR_OF_DAY, HOURS_IN_HALF_DAY); + } + updateAmPmControl(); + onDateTimeChanged(); + } + }; + + public interface OnDateTimeChangedListener { + void onDateTimeChanged(DateTimePicker view, int year, int month, + int dayOfMonth, int hourOfDay, int minute); + } + /** + * 构造方法 + */ + public DateTimePicker(Context context) { + this(context, System.currentTimeMillis());//通过对数据库的访问,获取当前的系统时间 + } + + public DateTimePicker(Context context, long date) { + this(context, date, DateFormat.is24HourFormat(context));//上面函数的得到的是一个天文数字(1970至今的秒数),需要DateFormat将其变得有意义 + } + + public DateTimePicker(Context context, long date, boolean is24HourView) { + super(context); + mDate = Calendar.getInstance();//获取系统时间 + mInitialising = true; + mIsAm = getCurrentHourOfDay() >= HOURS_IN_HALF_DAY; + inflate(context, R.layout.datetime_picker, this); + /* + * 如果当前Activity里用到别的layout,比如对话框layout + * 还要设置这个layout上的其他组件的内容,就必须用inflate()方法先将对话框的layout找出来 + * 然后再用findViewById()找到它上面的其它组件 + */ + mDateSpinner = (NumberPicker) findViewById(R.id.date); + mDateSpinner.setMinValue(DATE_SPINNER_MIN_VAL); + mDateSpinner.setMaxValue(DATE_SPINNER_MAX_VAL); + mDateSpinner.setOnValueChangedListener(mOnDateChangedListener); + + mHourSpinner = (NumberPicker) findViewById(R.id.hour); + mHourSpinner.setOnValueChangedListener(mOnHourChangedListener); + mMinuteSpinner = (NumberPicker) findViewById(R.id.minute); + mMinuteSpinner.setMinValue(MINUT_SPINNER_MIN_VAL); + mMinuteSpinner.setMaxValue(MINUT_SPINNER_MAX_VAL); + mMinuteSpinner.setOnLongPressUpdateInterval(100); + mMinuteSpinner.setOnValueChangedListener(mOnMinuteChangedListener); + + String[] stringsForAmPm = new DateFormatSymbols().getAmPmStrings(); + mAmPmSpinner = (NumberPicker) findViewById(R.id.amPm); + mAmPmSpinner.setMinValue(AMPM_SPINNER_MIN_VAL); + mAmPmSpinner.setMaxValue(AMPM_SPINNER_MAX_VAL); + mAmPmSpinner.setDisplayedValues(stringsForAmPm); + mAmPmSpinner.setOnValueChangedListener(mOnAmPmChangedListener); + + // update controls to initial state + updateDateControl(); + updateHourControl(); + updateAmPmControl(); + + set24HourView(is24HourView); + + // set to current time + setCurrentDate(date); + + setEnabled(isEnabled()); + + // set the content descriptions + mInitialising = false; + } + + /** + * @method setEnabled + * @description + * 用于设置是否启用日期选择器控件的功能 + * 先通过传入的参数enabled判断是否需要改变控件的启用状态。 + * 如果传入的参数和当前的启用状态相同,则直接返回,不进行任何操作 + * 如果传入的参数和当前的启用状态不同,则调用父类的setEnabled方法,来设置整个日期选择器控件的启用状态 + * 分别设置日期选择、分钟选择、小时选择、上午/下午选择这几个子控件的启用状态,即调用对应的setEnabled方法,并将enabled参数传入 + * @date: 12/21/2023 12:27 AM + * @author: YangYizhe + * @param enabled + */ + @Override + public void setEnabled(boolean enabled) { + if (mIsEnabled == enabled) { + return; + } + super.setEnabled(enabled); + mDateSpinner.setEnabled(enabled); + mMinuteSpinner.setEnabled(enabled); + mHourSpinner.setEnabled(enabled); + mAmPmSpinner.setEnabled(enabled); + mIsEnabled = enabled; + } + @Override + public boolean isEnabled() { + return mIsEnabled; + } + + /** + * Get the current date in millis + * + * @return the current date in millis + */ + public long getCurrentDateInTimeMillis() { + return mDate.getTimeInMillis(); + }//实现函数——得到当前的秒数 + + /** + * Set the current date + * + * @param date The current date in millis + */ + public void setCurrentDate(long date) { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(date); + setCurrentDate(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH), + cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE)); + } + + /** + * Set the current date + * + * @param year The current year + * @param month The current month + * @param dayOfMonth The current dayOfMonth + * @param hourOfDay The current hourOfDay + * @param minute The current minute + */ + public void setCurrentDate(int year, int month, + int dayOfMonth, int hourOfDay, int minute) { + setCurrentYear(year); + setCurrentMonth(month); + setCurrentDay(dayOfMonth); + setCurrentHour(hourOfDay); + setCurrentMinute(minute); + } + + /** + * Get current year + * + * @return The current year + */ + public int getCurrentYear() { + return mDate.get(Calendar.YEAR); + } + + /** + * Set current year + * + * @param year The current year + */ + public void setCurrentYear(int year) { + if (!mInitialising && year == getCurrentYear()) { + return; + } + mDate.set(Calendar.YEAR, year); + updateDateControl(); + onDateTimeChanged(); + } + + /** + * Get current month in the year + * + * @return The current month in the year + */ + public int getCurrentMonth() { + return mDate.get(Calendar.MONTH); + } + + /** + * Set current month in the year + * + * @param month The month in the year + */ + public void setCurrentMonth(int month) { + if (!mInitialising && month == getCurrentMonth()) { + return; + } + mDate.set(Calendar.MONTH, month); + updateDateControl(); + onDateTimeChanged(); + } + + /** + * Get current day of the month + * + * @return The day of the month + */ + public int getCurrentDay() { + return mDate.get(Calendar.DAY_OF_MONTH); + } + + /** + * Set current day of the month + * + * @param dayOfMonth The day of the month + */ + public void setCurrentDay(int dayOfMonth) { + if (!mInitialising && dayOfMonth == getCurrentDay()) { + return; + } + mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth); + updateDateControl(); + onDateTimeChanged(); + } + + /** + * Get current hour in 24 hour mode, in the range (0~23) + * @return The current hour in 24 hour mode + */ + public int getCurrentHourOfDay() { + return mDate.get(Calendar.HOUR_OF_DAY); + } + + private int getCurrentHour() { + if (mIs24HourView){ + return getCurrentHourOfDay(); + } else { + int hour = getCurrentHourOfDay(); + if (hour > HOURS_IN_HALF_DAY) { + return hour - HOURS_IN_HALF_DAY; + } else { + return hour == 0 ? HOURS_IN_HALF_DAY : hour; + } + } + } + + /** + * Set current hour in 24 hour mode, in the range (0~23) + * + * @param hourOfDay + */ + public void setCurrentHour(int hourOfDay) { + if (!mInitialising && hourOfDay == getCurrentHourOfDay()) { + return; + } + mDate.set(Calendar.HOUR_OF_DAY, hourOfDay); + if (!mIs24HourView) { + if (hourOfDay >= HOURS_IN_HALF_DAY) { + mIsAm = false; + if (hourOfDay > HOURS_IN_HALF_DAY) { + hourOfDay -= HOURS_IN_HALF_DAY; + } + } else { + mIsAm = true; + if (hourOfDay == 0) { + hourOfDay = HOURS_IN_HALF_DAY; + } + } + updateAmPmControl(); + } + mHourSpinner.setValue(hourOfDay); + onDateTimeChanged(); + } + + /** + * Get currentMinute + * + * @return The Current Minute + */ + public int getCurrentMinute() { + return mDate.get(Calendar.MINUTE); + } + + /** + * Set current minute + */ + public void setCurrentMinute(int minute) { + if (!mInitialising && minute == getCurrentMinute()) { + return; + } + mMinuteSpinner.setValue(minute); + mDate.set(Calendar.MINUTE, minute); + onDateTimeChanged(); + } + + /** + * @return true if this is in 24 hour view else false. + */ + public boolean is24HourView () { + return mIs24HourView; + } + + /** + * Set whether in 24 hour or AM/PM mode. + * + * @param is24HourView True for 24 hour mode. False for AM/PM mode. + */ + public void set24HourView(boolean is24HourView) { + if (mIs24HourView == is24HourView) { + return; + } + mIs24HourView = is24HourView; + mAmPmSpinner.setVisibility(is24HourView ? View.GONE : View.VISIBLE); + int hour = getCurrentHourOfDay(); + updateHourControl(); + setCurrentHour(hour); + updateAmPmControl(); + } + /** + * 几个辅助方法,用于更新日期、上午/下午选择和小时选择控件的显示和属性 + * 这些方法的作用是在选择器控件显示之前或用户改变了日期、时间模式时,更新相应控件的显示和属性,以保持界面的正确性和一致性 + */ + private void updateDateControl() { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(mDate.getTimeInMillis()); + cal.add(Calendar.DAY_OF_YEAR, -DAYS_IN_ALL_WEEK / 2 - 1); + mDateSpinner.setDisplayedValues(null); + for (int i = 0; i < DAYS_IN_ALL_WEEK; ++i) { + cal.add(Calendar.DAY_OF_YEAR, 1); + mDateDisplayValues[i] = (String) DateFormat.format("MM.dd EEEE", cal); + } + mDateSpinner.setDisplayedValues(mDateDisplayValues); + mDateSpinner.setValue(DAYS_IN_ALL_WEEK / 2); + mDateSpinner.invalidate(); + } + + private void updateAmPmControl() { + if (mIs24HourView) { + mAmPmSpinner.setVisibility(View.GONE); + } else { + int index = mIsAm ? Calendar.AM : Calendar.PM; + mAmPmSpinner.setValue(index); + mAmPmSpinner.setVisibility(View.VISIBLE); + } + } + + private void updateHourControl() { + if (mIs24HourView) { + mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW); + mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW); + } else { + mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW); + mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW); + } + } + + /** + * Set the callback that indicates the 'Set' button has been pressed. + * @param callback the callback, if null will do nothing + */ + public void setOnDateTimeChangedListener(OnDateTimeChangedListener callback) { + mOnDateTimeChangedListener = callback; + } + + private void onDateTimeChanged() { + if (mOnDateTimeChangedListener != null) { + mOnDateTimeChangedListener.onDateTimeChanged(this, getCurrentYear(), + getCurrentMonth(), getCurrentDay(), getCurrentHourOfDay(), getCurrentMinute()); + } + } +} \ No newline at end of file diff --git a/src/Notes/app/src/main/java/net/micode/notes/ui/DateTimePickerDialog.java b/src/Notes/app/src/main/java/net/micode/notes/ui/DateTimePickerDialog.java new file mode 100644 index 0000000..52640a8 --- /dev/null +++ b/src/Notes/app/src/main/java/net/micode/notes/ui/DateTimePickerDialog.java @@ -0,0 +1,126 @@ +package net.micode.notes.ui; + +import java.util.Calendar; + +import net.micode.notes.R; +import net.micode.notes.ui.DateTimePicker; +import net.micode.notes.ui.DateTimePicker.OnDateTimeChangedListener; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; +import android.text.format.DateFormat; +import android.text.format.DateUtils; +/** + * @Package: net.micode.notes.ui + * @ClassName: DateTimePickerDialog + * @Description: + * DateTimePickerDialog是一个自定义对话框,允许用户选择日期和时间。 + * 它继承自AlertDialog类,并实现OnClickListener接口。 + * 对话框包含一个DateTimePicker控件,用于选择日期和时间。 + * 通过OnDateTimeSetListener接口将选择的日期和时间传递给监听器。 + * 对话框还提供了设置24小时制和使用选择的日期更新对话框标题的方法。 + * onClick方法处理按钮点击事件,并在日期和时间设置完成时通知监听器。 + * @Author: YangYizhe + * @CreateDate: 12/21/2023 12:32 AM + * @Version: 1.0 + */ +public class DateTimePickerDialog extends AlertDialog implements OnClickListener { + + private Calendar mDate = Calendar.getInstance(); + private boolean mIs24HourView; + private OnDateTimeSetListener mOnDateTimeSetListener; + private DateTimePicker mDateTimePicker; + + /** + * 用于接收用户设置的日期和时间的接口。 + */ + public interface OnDateTimeSetListener { + void OnDateTimeSet(AlertDialog dialog, long date); + } + + /** + * @method DateTimePickerDialog + * @description + * 构造一个新的DateTimePickerDialog + * Remind me功能的对话框 + * @date: 12/21/2023 8:01 AM + * @author: YangYizhe + * @param context 上下文环境。 + * @param date 要在对话框中显示的初始日期。 + */ + public DateTimePickerDialog(Context context, long date) { + super(context); + mDateTimePicker = new DateTimePicker(context); + setView(mDateTimePicker); + + mDateTimePicker.setOnDateTimeChangedListener(new OnDateTimeChangedListener() { + public void onDateTimeChanged(DateTimePicker view, int year, int month, + int dayOfMonth, int hourOfDay, int minute) { + mDate.set(Calendar.YEAR, year); + mDate.set(Calendar.MONTH, month); + mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth); + mDate.set(Calendar.HOUR_OF_DAY, hourOfDay); + mDate.set(Calendar.MINUTE, minute); + updateTitle(mDate.getTimeInMillis()); + } + }); + + mDate.setTimeInMillis(date); + mDate.set(Calendar.SECOND, 0); + mDateTimePicker.setCurrentDate(mDate.getTimeInMillis()); + + setButton(context.getString(R.string.datetime_dialog_ok), this); + setButton2(context.getString(R.string.datetime_dialog_cancel), (OnClickListener) null); + + set24HourView(DateFormat.is24HourFormat(this.getContext())); + updateTitle(mDate.getTimeInMillis()); + } + + /** + * 设置对话框是否为24小时制。 + * + * @param is24HourView 如果对话框为24小时制,则为true;否则为false。 + */ + public void set24HourView(boolean is24HourView) { + mIs24HourView = is24HourView; + } + + /** + * 设置日期和时间设置监听器。 + * + * @param callBack 要通知的监听器。 + */ + public void setOnDateTimeSetListener(OnDateTimeSetListener callBack) { + mOnDateTimeSetListener = callBack; + } + + /** + * 使用指定的日期更新对话框标题。 + * + * @param date 要显示在标题中的日期。 + */ + private void updateTitle(long date) { + int flag = + DateUtils.FORMAT_SHOW_YEAR | + DateUtils.FORMAT_SHOW_DATE | + DateUtils.FORMAT_SHOW_TIME; + flag |= mIs24HourView ? DateUtils.FORMAT_24HOUR : DateUtils.FORMAT_24HOUR; + + setTitle(DateUtils.formatDateTime(this.getContext(), date, flag)); + } + + /** + * 处理按钮点击事件。 + * + * @param arg0 接收到点击事件的对话框。 + * @param arg1 被点击的按钮。 + */ + public void onClick(DialogInterface arg0, int arg1) { + if (mOnDateTimeSetListener != null) { + mOnDateTimeSetListener.OnDateTimeSet(this, mDate.getTimeInMillis()); + } + } + +} \ No newline at end of file diff --git a/src/Notes/app/src/main/java/net/micode/notes/ui/DropdownMenu.java b/src/Notes/app/src/main/java/net/micode/notes/ui/DropdownMenu.java new file mode 100644 index 0000000..b437d41 --- /dev/null +++ b/src/Notes/app/src/main/java/net/micode/notes/ui/DropdownMenu.java @@ -0,0 +1,62 @@ +package net.micode.notes.ui; + +import android.content.Context; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.Button; +import android.widget.PopupMenu; +import android.widget.PopupMenu.OnMenuItemClickListener; + +import net.micode.notes.R; +/** + * @Package: net.micode.notes.ui + * @ClassName: DropdownMenu + * @Description: + * DropdownMenu是一个自定义下拉菜单控件。 + * 它使用Button作为触发器,通过PopupMenu显示菜单选项。 + * 构造函数接收一个上下文环境、一个Button和菜单资源的ID。 + * 通过setOnDropdownMenuItemClickListener方法设置菜单选项的点击监听器。 + * 可以通过findItem方法查找特定的菜单选项。 + * 通过setTitle方法设置下拉菜单的标题 + * @Author: YangYizhe + * @CreateDate: 12/21/2023 12:36 AM + * @Version: 1.0 + */ +public class DropdownMenu { + private Button mButton; + private PopupMenu mPopupMenu; + //声明一个下拉菜单 + private Menu mMenu; + + public DropdownMenu(Context context, Button button, int menuId) { + mButton = button; + mButton.setBackgroundResource(R.drawable.dropdown_icon); + //设置这个view的背景 + mPopupMenu = new PopupMenu(context, mButton); + mMenu = mPopupMenu.getMenu(); + mPopupMenu.getMenuInflater().inflate(menuId, mMenu); + //MenuInflater是用来实例化Menu目录下的Menu布局文件 + //根据ID来确认menu的内容选项 + mButton.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + mPopupMenu.show(); + } + }); + } + + public void setOnDropdownMenuItemClickListener(OnMenuItemClickListener listener) { + if (mPopupMenu != null) { + mPopupMenu.setOnMenuItemClickListener(listener); + } + } + + public MenuItem findItem(int id) { + return mMenu.findItem(id); + }//对于菜单选项的初始化,根据索引搜索菜单需要的选项 + + public void setTitle(CharSequence title) { + mButton.setText(title); + }//布局文件,设置标题 +} \ No newline at end of file diff --git a/src/Notes/app/src/main/java/net/micode/notes/ui/FoldersListAdapter.java b/src/Notes/app/src/main/java/net/micode/notes/ui/FoldersListAdapter.java new file mode 100644 index 0000000..8c3a217 --- /dev/null +++ b/src/Notes/app/src/main/java/net/micode/notes/ui/FoldersListAdapter.java @@ -0,0 +1,83 @@ +package net.micode.notes.ui; + +import android.content.Context; +import android.database.Cursor; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CursorAdapter; +import android.widget.LinearLayout; +import android.widget.TextView; + +import net.micode.notes.R; +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.NoteColumns; + +/** + * @Package: net.micode.notes.ui + * @ClassName: FoldersListAdapter + * @Description: + * FoldersListAdapter是一个用于展示便签文件夹的列表适配器。 + * 它继承了CursorAdapter类,主要负责便签数据库和用户界面的交互。 + * 通过PROJECTION数组定义了需要从数据库中获取的数据列。 + * 它通过newView方法创建文件夹视图,并通过bindView方法将布局文件和数据绑定在一起。 + * getFolderName方法可以根据位置获取对应便签文件夹的名称 + * @Author: YangYizhe + * @CreateDate: 12/21/2023 12:37 AM + * @Version: 1.0 + */ +public class FoldersListAdapter extends CursorAdapter { + //CursorAdapter是Cursor和ListView的接口 + //FoldersListAdapter继承了CursorAdapter的类 + //主要作用是便签数据库和用户的交互 + //这里就是用folder(文件夹)的形式展现给用户 + public static final String [] PROJECTION = { + NoteColumns.ID, + NoteColumns.SNIPPET + };//调用数据库中便签的ID和片段 + + public static final int ID_COLUMN = 0; + public static final int NAME_COLUMN = 1; + + public FoldersListAdapter(Context context, Cursor c) { + super(context, c); + // TODO Auto-generated constructor stub + }//数据库操作 + + @Override + public View newView(Context context, Cursor cursor, ViewGroup parent) { + //ViewGroup是容器 + return new FolderListItem(context); + }//创建一个文件夹,对于各文件夹中子标签的初始化 + + @Override + public void bindView(View view, Context context, Cursor cursor) { + if (view instanceof FolderListItem) { + String folderName = (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context + .getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN); + ((FolderListItem) view).bind(folderName); + } + }//将各个布局文件绑定起来 + + public String getFolderName(Context context, int position) { + Cursor cursor = (Cursor) getItem(position); + return (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context + .getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN); + }//根据数据库中标签的ID得到标签的各项内容 + + private class FolderListItem extends LinearLayout { + private TextView mName; + + public FolderListItem(Context context) { + super(context); + //操作数据库 + inflate(context, R.layout.folder_list_item, this); + //根据布局文件的名字等信息将其找出来 + mName = (TextView) findViewById(R.id.tv_folder_name); + } + + public void bind(String name) { + mName.setText(name); + } + } + +} \ No newline at end of file diff --git a/src/Notes/app/src/main/java/net/micode/notes/ui/NoteEditText.java b/src/Notes/app/src/main/java/net/micode/notes/ui/NoteEditText.java new file mode 100644 index 0000000..c935358 --- /dev/null +++ b/src/Notes/app/src/main/java/net/micode/notes/ui/NoteEditText.java @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.ui; + +import android.content.Context; +import android.graphics.Rect; +import android.text.Layout; +import android.text.Selection; +import android.text.Spanned; +import android.text.TextUtils; +import android.text.style.URLSpan; +import android.util.AttributeSet; +import android.util.Log; +import android.view.ContextMenu; +import android.view.KeyEvent; +import android.view.MenuItem; +import android.view.MenuItem.OnMenuItemClickListener; +import android.view.MotionEvent; +import android.widget.EditText; + +import net.micode.notes.R; + +import java.util.HashMap; +import java.util.Map; + +/** + * @Package: net.micode.notes.ui + * @ClassName: NoteEditText + * @Description: + * @Author: YangYizhe + * @CreateDate: 12/21/2023 12:38 AM + * @Version: 1.0 + */ +public class NoteEditText extends EditText { + //常量标识 + private static final String TAG = "NoteEditText"; + //声明整型变量,文本索引 + private int mIndex; + //声明整型变量 + private int mSelectionStartBeforeDelete; + + //声明字符串常量,标志电话、网址、邮件 + private static final String SCHEME_TEL = "tel:" ; + private static final String SCHEME_HTTP = "http:" ; + private static final String SCHEME_EMAIL = "mailto:" ; + + //设置映射,将文本内容(电话、网址、邮件)做链接处理 + private static final Map sSchemaActionResMap = new HashMap(); + static { + sSchemaActionResMap.put(SCHEME_TEL, R.string.note_link_tel); + sSchemaActionResMap.put(SCHEME_HTTP, R.string.note_link_web); + sSchemaActionResMap.put(SCHEME_EMAIL, R.string.note_link_email); + } + + /** + * Call by the {@link NoteEditActivity} to delete or add edit text + * 该接口用于实现对TextView组件中的文字信息进行修改 + */ + public interface OnTextViewChangeListener { + /** + * Delete current edit text when {@link KeyEvent#KEYCODE_DEL} happens + * and the text is null + * 当delete键按下时删除当前编辑的文字块 + */ + void onEditTextDelete(int index, String text); + + /** + * Add edit text after current edit text when {@link KeyEvent#KEYCODE_ENTER} + * happen + * 当enter键按下时添加一个文字编辑块 + */ + void onEditTextEnter(int index, String text); + + /** + * Hide or show item option when text change + * 当文字发生变化时隐藏或者显示设置 + */ + void onTextChange(int index, boolean hasText); + } + + //声明文本视图变化监听器 + private OnTextViewChangeListener mOnTextViewChangeListener; + + /** + * 构造方法,实例化NoteEditText + */ + public NoteEditText(Context context) { + super(context, null); + mIndex = 0; + } + + //设置索引号 + public void setIndex(int index) { + mIndex = index; + } + + /** + * 设置文本视图变化监听器 + */ + public void setOnTextViewChangeListener(OnTextViewChangeListener listener) { + mOnTextViewChangeListener = listener; + } + + /** + * 构造方法,是由参数集(文本编辑风格)实例化NoteEditText + */ + public NoteEditText(Context context, AttributeSet attrs) { + super(context, attrs, android.R.attr.editTextStyle); + } + /** + * 构造方法,是由参数集(文本编辑风格、定义风格)实例化NoteEditText + */ + public NoteEditText(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + // TODO Auto-generated constructor stub + } + + /** + * @method onTouchEvent + * @description 处理触摸事件,根据触摸点的位置设置光标的位置 + * @date: 12/21/2023 12:41 AM + * @author: YangYizhe + * @param + * @return + */ + @Override + public boolean onTouchEvent(MotionEvent event) { + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + + int x = (int) event.getX(); + int y = (int) event.getY(); + x -= getTotalPaddingLeft(); + y -= getTotalPaddingTop(); + x += getScrollX(); + y += getScrollY(); + Layout layout = getLayout(); + int line = layout.getLineForVertical(y); + int off = layout.getOffsetForHorizontal(line, x); + Selection.setSelection(getText(), off); + break; + } + return super.onTouchEvent(event); + } + /** + * @method onKeyDown + * @description 监听键盘按键按下 + * @date: 12/21/2023 12:40 AM + * @author: YangYizhe + * @param keyCode 键盘按键的编码 + * @param event 按键事件 + * @return boolean + */ + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + switch (keyCode) { + case KeyEvent.KEYCODE_ENTER: + if (mOnTextViewChangeListener != null) { + return false; + } + break; + case KeyEvent.KEYCODE_DEL: + mSelectionStartBeforeDelete = getSelectionStart(); + break; + default: + break; + } + return super.onKeyDown(keyCode, event); + } + + /** + * @method onKeyUp + * @description 监听按键抬起 + * @date: 12/21/2023 12:39 AM + * @author: YangYizhe + * @param keyCode + * @param event + */ + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + switch(keyCode) { + case KeyEvent.KEYCODE_DEL: + if (mOnTextViewChangeListener != null) { + if (0 == mSelectionStartBeforeDelete && mIndex != 0) { + mOnTextViewChangeListener.onEditTextDelete(mIndex, getText().toString()); + return true; + } + } else { + Log.d(TAG, "OnTextViewChangeListener was not seted"); + } + break; + case KeyEvent.KEYCODE_ENTER: + if (mOnTextViewChangeListener != null) { + int selectionStart = getSelectionStart(); + String text = getText().subSequence(selectionStart, length()).toString(); + setText(getText().subSequence(0, selectionStart)); + mOnTextViewChangeListener.onEditTextEnter(mIndex + 1, text); + } else { + Log.d(TAG, "OnTextViewChangeListener was not seted"); + } + break; + default: + break; + } + return super.onKeyUp(keyCode, event); + } + + /** + * @method onFocusChanged + * @description 处理当前视图下的焦点改变事件 + * @date: 12/21/2023 12:39 AM + * @author: YangYizhe + * @param focused 代表获得或失去焦点 + * @param direction + * @param previouslyFocusedRect 上一个访问的焦点区域 + */ + @Override + protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { + if (mOnTextViewChangeListener != null) { + if (!focused && TextUtils.isEmpty(getText())) { + mOnTextViewChangeListener.onTextChange(mIndex, false); + } else { + mOnTextViewChangeListener.onTextChange(mIndex, true); + } + } + super.onFocusChanged(focused, direction, previouslyFocusedRect); + } + + /** + * @method onCreateContextMenu + * @description + * @date: 12/21/2023 12:39 AM + * @author: YangYizhe + * @param + * @return + */ + @Override + protected void onCreateContextMenu(ContextMenu menu) { + if (getText() instanceof Spanned) { + int selStart = getSelectionStart(); + int selEnd = getSelectionEnd(); + + int min = Math.min(selStart, selEnd); + int max = Math.max(selStart, selEnd); + + final URLSpan[] urls = ((Spanned) getText()).getSpans(min, max, URLSpan.class); + if (urls.length == 1) { + int defaultResId = 0; + for(String schema: sSchemaActionResMap.keySet()) { + if(urls[0].getURL().indexOf(schema) >= 0) { + defaultResId = sSchemaActionResMap.get(schema); + break; + } + } + + if (defaultResId == 0) { + defaultResId = R.string.note_link_other; + } + menu.add(0, 0, 0, defaultResId).setOnMenuItemClickListener( + new OnMenuItemClickListener() { + public boolean onMenuItemClick(MenuItem item) { + // goto a new intent + urls[0].onClick(NoteEditText.this); + return true; + } + }); + } + } + super.onCreateContextMenu(menu); + } +} diff --git a/src/Notes/app/src/main/java/net/micode/notes/ui/NotesListActivity.java b/src/Notes/app/src/main/java/net/micode/notes/ui/NotesListActivity.java new file mode 100644 index 0000000..2b0e901 --- /dev/null +++ b/src/Notes/app/src/main/java/net/micode/notes/ui/NotesListActivity.java @@ -0,0 +1,1103 @@ +package net.micode.notes.ui; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.appwidget.AppWidgetManager; +import android.content.AsyncQueryHandler; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.SharedPreferences; +import android.database.Cursor; +import android.os.AsyncTask; +import android.os.Bundle; +import android.preference.PreferenceManager; +import android.text.Editable; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.util.Log; +import android.view.ActionMode; +import android.view.ContextMenu; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.Display; +import android.view.HapticFeedbackConstants; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.MenuItem.OnMenuItemClickListener; +import android.view.MotionEvent; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.View.OnCreateContextMenuListener; +import android.view.View.OnTouchListener; +import android.view.inputmethod.InputMethodManager; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.AdapterView.OnItemLongClickListener; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ListView; +import android.widget.PopupMenu; +import android.widget.TextView; +import android.widget.Toast; + +import net.micode.notes.R; +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.gtask.remote.GTaskSyncService; +import net.micode.notes.model.WorkingNote; +import net.micode.notes.tool.BackupUtils; +import net.micode.notes.tool.DataUtils; +import net.micode.notes.tool.ResourceParser; +import net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute; +import net.micode.notes.widget.NoteWidgetProvider_2x; +import net.micode.notes.widget.NoteWidgetProvider_4x; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.HashSet; +/** + * @Package: net.micode.notes.ui + * @ClassName: NotesListActivity + * @Description: + * 小米便签的主入口,提供了一系列便签操作主要功能 + * @Author: WUSHUXIAN + * @CreateDate: 2023/12/20 23:02 + * @Version: 1.0 + */ +public class NotesListActivity extends Activity implements OnClickListener, OnItemLongClickListener { + + private static final int FOLDER_NOTE_LIST_QUERY_TOKEN = 0; + + private static final int FOLDER_LIST_QUERY_TOKEN = 1; + + private static final int MENU_FOLDER_DELETE = 0; + + private static final int MENU_FOLDER_VIEW = 1; + + private static final int MENU_FOLDER_CHANGE_NAME = 2; + + private static final String PREFERENCE_ADD_INTRODUCTION = "net.micode.notes.introduction"; + + private enum ListEditState { + NOTE_LIST, SUB_FOLDER, CALL_RECORD_FOLDER + }; + + private ListEditState mState; + + private BackgroundQueryHandler mBackgroundQueryHandler; + + private NotesListAdapter mNotesListAdapter; + + private ListView mNotesListView; + + private Button mAddNewNote; + + private boolean mDispatch; + + private int mOriginY; + + private int mDispatchY; + + private TextView mTitleBar; + + private long mCurrentFolderId; + + private ContentResolver mContentResolver; + + private ModeCallback mModeCallBack; + + private static final String TAG = "NotesListActivity"; + + public static final int NOTES_LISTVIEW_SCROLL_RATE = 30; + + private NoteItemData mFocusNoteDataItem; + + private static final String NORMAL_SELECTION = NoteColumns.PARENT_ID + "=?"; + + private static final String ROOT_FOLDER_SELECTION = "(" + NoteColumns.TYPE + "<>" + + Notes.TYPE_SYSTEM + " AND " + NoteColumns.PARENT_ID + "=?)" + " OR (" + + NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER + " AND " + + NoteColumns.NOTES_COUNT + ">0)"; + + private final static int REQUEST_CODE_OPEN_NODE = 102; + private final static int REQUEST_CODE_NEW_NODE = 103; + @Override + /** + * @method onCreate + * @description: + * @date: 2023/12/21 1:52 + * @author: WUSHUXIAN + * @param void + * @return void + */ + protected void onCreate(final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); // 调用父类的onCreate函数 + setContentView(R.layout.note_list); + initResources(); + setAppInfoFromRawRes(); + } + + @Override + /** + * @method onActivityResult + * @description: + * 参数: + * requestCode: + * resultCode: + * data; + * @date: 2023/12/20 23:26 + * @author: WUSHUXIAN + * @param void + * @return void + */ + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (resultCode == RESULT_OK + && (requestCode == REQUEST_CODE_OPEN_NODE || requestCode == REQUEST_CODE_NEW_NODE)) { + mNotesListAdapter.changeCursor(null); + } else { + super.onActivityResult(requestCode, resultCode, data);// 调用 Activity 的onActivityResult() + } + } + /** + * @method setAppInfoFromRawRes + * @description: + * @date: 2023/12/20 23:24 + * @author: WUSHUXIAN + * @param + * @return + */ + private void setAppInfoFromRawRes() { + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);// Android平台给我们提供了一个SharedPreferences类,它是一个轻量级的存储类,特别适合用于保存软件配置参数。 + if (!sp.getBoolean(PREFERENCE_ADD_INTRODUCTION, false)) { + StringBuilder sb = new StringBuilder(); + InputStream in = null; + try { + in = getResources().openRawResource(R.raw.introduction); + if (in != null) { + InputStreamReader isr = new InputStreamReader(in); + BufferedReader br = new BufferedReader(isr); + char [] buf = new char[1024]; + int len = 0; + while ((len = br.read(buf)) > 0) { + sb.append(buf, 0, len); + } + } else { + Log.e(TAG, "Read introduction file error"); + return; + } + } catch (IOException e) { + e.printStackTrace(); + return; + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + + // 创建空的WorkingNote + WorkingNote note = WorkingNote.createEmptyNote(this, Notes.ID_ROOT_FOLDER, + AppWidgetManager.INVALID_APPWIDGET_ID, Notes.TYPE_WIDGET_INVALIDE, + ResourceParser.RED); + note.setWorkingText(sb.toString()); + if (note.saveNote()) { + // 更新保存note的信息 + sp.edit().putBoolean(PREFERENCE_ADD_INTRODUCTION, true).commit(); + } else { + Log.e(TAG, "Save introduction note error"); + return; + } + } + } + + @Override + protected void onStart() { + super.onStart(); + startAsyncNotesListQuery(); + } + /** + * @method initResources + * @description 初始化资源 + * @date: 2023/12/20 23:09 + * @author: WUSHUXIAN + * @param + * @return + */ + private void initResources() { + mContentResolver = this.getContentResolver(); // 获取应用程序的数据,得到类似数据表的东西 + mBackgroundQueryHandler = new BackgroundQueryHandler(this.getContentResolver()); + mCurrentFolderId = Notes.ID_ROOT_FOLDER; + + // findViewById 是安卓编程的定位函数,主要是引用.R文件里的引用名 + mNotesListView = (ListView) findViewById(R.id.notes_list); // 绑定XML中的ListView,作为Item的容器 + mNotesListView.addFooterView(LayoutInflater.from(this).inflate(R.layout.note_list_footer, null), + null, false); + mNotesListView.setOnItemClickListener(new OnListItemClickListener()); + mNotesListView.setOnItemLongClickListener(this); + mNotesListAdapter = new NotesListAdapter(this); + mNotesListView.setAdapter(mNotesListAdapter); + mAddNewNote = (Button) findViewById(R.id.btn_new_note);// 在activity中要获取该按钮 + mAddNewNote.setOnClickListener(this); + mAddNewNote.setOnTouchListener(new NewNoteOnTouchListener()); + mDispatch = false; + mDispatchY = 0; + mOriginY = 0; + mTitleBar = (TextView) findViewById(R.id.tv_title_bar); + mState = ListEditState.NOTE_LIST; + mModeCallBack = new ModeCallback(); + } + + // + /** + * @Package: net.micode.notes.ui + * @ClassName: ModeCallback + * @Description: 继承自ListView.MultiChoiceModeListener 和 OnMenuItemClickListener + * @Author: WUSHUXIAN + * @CreateDate: 2023/12/20 23:37 + * @Version: 1.0 + */ + private class ModeCallback implements ListView.MultiChoiceModeListener, OnMenuItemClickListener { + private DropdownMenu mDropDownMenu; + private ActionMode mActionMode; + private MenuItem mMoveMenu; + + public boolean onCreateActionMode(ActionMode mode, Menu menu) { + getMenuInflater().inflate(R.menu.note_list_options, menu); + menu.findItem(R.id.delete).setOnMenuItemClickListener(this); + mMoveMenu = menu.findItem(R.id.move); + if (mFocusNoteDataItem.getParentId() == Notes.ID_CALL_RECORD_FOLDER + || DataUtils.getUserFolderCount(mContentResolver) == 0) { + mMoveMenu.setVisible(false); + } else { + mMoveMenu.setVisible(true); + mMoveMenu.setOnMenuItemClickListener(this); + } + mActionMode = mode; + mNotesListAdapter.setChoiceMode(true); + mNotesListView.setLongClickable(false); + mAddNewNote.setVisibility(View.GONE); + + View customView = LayoutInflater.from(NotesListActivity.this).inflate( + R.layout.note_list_dropdown_menu, null); + mode.setCustomView(customView); + mDropDownMenu = new DropdownMenu(NotesListActivity.this, + (Button) customView.findViewById(R.id.selection_menu), + R.menu.note_list_dropdown); + mDropDownMenu.setOnDropdownMenuItemClickListener(new PopupMenu.OnMenuItemClickListener(){ + public boolean onMenuItemClick(final MenuItem item) { + mNotesListAdapter.selectAll(!mNotesListAdapter.isAllSelected()); + updateMenu(); + return true; + } + + }); + return true; + } + /** + * @method updateMenu + * @description 更新菜单 + * @date: 2023/12/16 23:36 + * @author: WUSHUXIAN + * @param + * @return + */ + private void updateMenu() { + int selectedCount = mNotesListAdapter.getSelectedCount(); + // Update dropdown menu + String format = getResources().getString(R.string.menu_select_title, selectedCount); + mDropDownMenu.setTitle(format); // 更改标题 + MenuItem item = mDropDownMenu.findItem(R.id.action_select_all); + if (item != null) { + if (mNotesListAdapter.isAllSelected()) { + item.setChecked(true); + item.setTitle(R.string.menu_deselect_all); + } else { + item.setChecked(false); + item.setTitle(R.string.menu_select_all); + } + } + } + + public boolean onPrepareActionMode(ActionMode mode, Menu menu) { + // TODO Auto-generated method stub + return false; + } + + public boolean onActionItemClicked(ActionMode mode, MenuItem item) { + // TODO Auto-generated method stub + return false; + } + + public void onDestroyActionMode(ActionMode mode) { + mNotesListAdapter.setChoiceMode(false); + mNotesListView.setLongClickable(true); + mAddNewNote.setVisibility(View.VISIBLE); + } + + public void finishActionMode() { + mActionMode.finish(); + } + + public void onItemCheckedStateChanged(ActionMode mode, int position, long id, + boolean checked) { + mNotesListAdapter.setCheckedItem(position, checked); + updateMenu(); + } + /** + * @method onMenuItemClick + * @description: + * @date: 2023/12/20 23:39 + * @author: WUSHUXIAN + * @param + * @return + */ + public boolean onMenuItemClick(MenuItem item) { + if (mNotesListAdapter.getSelectedCount() == 0) { + Toast.makeText(NotesListActivity.this, getString(R.string.menu_select_none), + Toast.LENGTH_SHORT).show(); + return true; + } + + int itemId = item.getItemId(); + if (itemId == R.id.delete) { + AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); + builder.setTitle(getString(R.string.alert_title_delete)); + builder.setIcon(android.R.drawable.ic_dialog_alert); + builder.setMessage(getString(R.string.alert_message_delete_notes, + mNotesListAdapter.getSelectedCount())); + builder.setPositiveButton(android.R.string.ok, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, + int which) { + batchDelete(); + } + }); + builder.setNegativeButton(android.R.string.cancel, null); + builder.show(); + } else if (itemId == R.id.move) { + startQueryDestinationFolders(); + } else { + return false; + } + return true; + } + } + //接口的实现 + private class NewNoteOnTouchListener implements OnTouchListener { + public boolean onTouch(View v, MotionEvent event) { + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: { + Display display = getWindowManager().getDefaultDisplay(); + int screenHeight = display.getHeight(); + int newNoteViewHeight = mAddNewNote.getHeight(); + int start = screenHeight - newNoteViewHeight; + int eventY = start + (int) event.getY(); + /** + * Minus TitleBar's height + */ + if (mState == ListEditState.SUB_FOLDER) { + eventY -= mTitleBar.getHeight(); + start -= mTitleBar.getHeight(); + } + /** + * HACKME:When click the transparent part of "New Note" button, dispatch + * the event to the list view behind this button. The transparent part of + * "New Note" button could be expressed by formula y=-0.12x+94锛圲nit:pixel锛� + * and the line top of the button. The coordinate based on left of the "New + * Note" button. The 94 represents maximum height of the transparent part. + * Notice that, if the background of the button changes, the formula should + * also change. This is very bad, just for the UI designer's strong requirement. + */ + if (event.getY() < (event.getX() * (-0.12) + 94)) { + View view = mNotesListView.getChildAt(mNotesListView.getChildCount() - 1 + - mNotesListView.getFooterViewsCount()); + if (view != null && view.getBottom() > start + && (view.getTop() < (start + 94))) { + mOriginY = (int) event.getY(); + mDispatchY = eventY; + event.setLocation(event.getX(), mDispatchY); + mDispatch = true; + return mNotesListView.dispatchTouchEvent(event); + } + } + break; + } + case MotionEvent.ACTION_MOVE: { + if (mDispatch) { + mDispatchY += (int) event.getY() - mOriginY; + event.setLocation(event.getX(), mDispatchY); + return mNotesListView.dispatchTouchEvent(event); + } + break; + } + default: { + if (mDispatch) { + event.setLocation(event.getX(), mDispatchY); + mDispatch = false; + return mNotesListView.dispatchTouchEvent(event); + } + break; + } + } + return false; + } + + }; + /** + * @method startAsyncNotesListQuery + * @description: 启动异步Notes列表查询 + * @date: 2023/12/20 23:45 + * @author: WUSHUXIAN + * @param + * @return + */ + private void startAsyncNotesListQuery() { + String selection = (mCurrentFolderId == Notes.ID_ROOT_FOLDER) ? ROOT_FOLDER_SELECTION : NORMAL_SELECTION; + mBackgroundQueryHandler.startQuery(FOLDER_NOTE_LIST_QUERY_TOKEN, null, + Notes.CONTENT_NOTE_URI, NoteItemData.PROJECTION, selection, new String[] {String.valueOf(mCurrentFolderId)}, + NoteColumns.TYPE + " DESC," + NoteColumns.MODIFIED_DATE + " DESC"); + } + /** + * @Package: net.micode.notes.ui + * @ClassName: BackgroundQueryHandler + * @Description: 对AsyncQueryHandler的拓展,背景查询处理 + * @Author: WUSHUXIAN + * @CreateDate: 2023/12/21 0:01 + * @Version: 1.0 + */ + private final class BackgroundQueryHandler extends AsyncQueryHandler { + public BackgroundQueryHandler(ContentResolver contentResolver) { + super(contentResolver); + } + + @Override + /** + * @method onQueryComplete + * @description: 在异步查询完成时调用。 + * @date: 2023/12/20 23:56 + * @author: WUSHUXIAN + * @param token 标识查询的令牌 + * @param cookie 传入的cookie对象 + * @param cursor 保存查询结果的光标 + * @return void + */ + protected void onQueryComplete(int token, Object cookie, Cursor cursor) { + switch (token) { + case FOLDER_NOTE_LIST_QUERY_TOKEN: + mNotesListAdapter.changeCursor(cursor); + break; + case FOLDER_LIST_QUERY_TOKEN: + if (cursor != null && cursor.getCount() > 0) { + showFolderListMenu(cursor); + } else { + Log.e(TAG, "Query folder failed"); + } + break; + default: + return; + } + } + } + + private void showFolderListMenu(Cursor cursor) { + AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); + builder.setTitle(R.string.menu_title_select_folder); + final FoldersListAdapter adapter = new FoldersListAdapter(this, cursor); + builder.setAdapter(adapter, new DialogInterface.OnClickListener() { + + public void onClick(DialogInterface dialog, int which) { + DataUtils.batchMoveToFolder(mContentResolver, + mNotesListAdapter.getSelectedItemIds(), adapter.getItemId(which)); + Toast.makeText( + NotesListActivity.this, + getString(R.string.format_move_notes_to_folder, + mNotesListAdapter.getSelectedCount(), + adapter.getFolderName(NotesListActivity.this, which)), + Toast.LENGTH_SHORT).show(); + mModeCallBack.finishActionMode(); + } + }); + builder.show(); + } + /** + * @method createNewNote + * @description 创建便签 + * @date: 12/21/2023 1:58 AM + * @author: YangYizhe + * @param + * @return + */ + private void createNewNote() { + Intent intent = new Intent(this, NoteEditActivity.class); + intent.setAction(Intent.ACTION_INSERT_OR_EDIT); + intent.putExtra(Notes.INTENT_EXTRA_FOLDER_ID, mCurrentFolderId); + this.startActivityForResult(intent, REQUEST_CODE_NEW_NODE); + } + /** + * @method batchDelete + * @description: + * 删除便签操作 + * @date: 2023/12/21 0:14 + * @author: WUSHUXIAN + * @param + * @return void + */ + @SuppressLint("StaticFieldLeak") + private void batchDelete() { + new AsyncTask>() { + protected HashSet doInBackground(Void... unused) { + HashSet widgets = mNotesListAdapter.getSelectedWidget(); + //如果没有同步,直接删除 + if (!isSyncMode()) { + if (DataUtils.batchDeleteNotes(mContentResolver, mNotesListAdapter + .getSelectedItemIds())) { + } else { + Log.e(TAG, "Delete notes error, should not happens"); + } + } + //已同步,将删除的便签移到垃圾桶 + else { + if (!DataUtils.batchMoveToFolder(mContentResolver, mNotesListAdapter + .getSelectedItemIds(), Notes.ID_TRASH_FOLER)) { + Log.e(TAG, "Move notes to trash folder error, should not happens"); + } + } + return widgets; + } + + @Override + protected void onPostExecute(HashSet widgets) { + if (widgets != null) { + for (AppWidgetAttribute widget : widgets) { + if (widget.widgetId != AppWidgetManager.INVALID_APPWIDGET_ID + && widget.widgetType != Notes.TYPE_WIDGET_INVALIDE) { + updateWidget(widget.widgetId, widget.widgetType); + } + } + } + mModeCallBack.finishActionMode(); + } + }.execute(); + } + /** + * @method deleteFolder + * @description: 删除文件夹 + * @date: 2023/12/21 0:32 + * @author: WUSHUXIAN + * @param folderId 文件标识 + * @return void + */ + private void deleteFolder(long folderId) { + if (folderId == Notes.ID_ROOT_FOLDER) { + Log.e(TAG, "Wrong folder id, should not happen " + folderId); + return; + } + + HashSet ids = new HashSet(); + ids.add(folderId); + HashSet widgets = DataUtils.getFolderNoteWidget(mContentResolver, + folderId); + if (!isSyncMode()) { + // if not synced, delete folder directly + DataUtils.batchDeleteNotes(mContentResolver, ids); + } else { + // in sync mode, we'll move the deleted folder into the trash folder + DataUtils.batchMoveToFolder(mContentResolver, ids, Notes.ID_TRASH_FOLER); + } + if (widgets != null) { + for (AppWidgetAttribute widget : widgets) { + if (widget.widgetId != AppWidgetManager.INVALID_APPWIDGET_ID + && widget.widgetType != Notes.TYPE_WIDGET_INVALIDE) { + updateWidget(widget.widgetId, widget.widgetType); + } + } + } + } + + /** + * @method openNode + * @description + * 打开便签的功能 + * @date: 12/21/2023 8:05 AM + * @author: YangYizhe + * @param data + */ + private void openNode(NoteItemData data) { + Intent intent = new Intent(this, NoteEditActivity.class); + intent.setAction(Intent.ACTION_VIEW); + intent.putExtra(Intent.EXTRA_UID, data.getId()); + this.startActivityForResult(intent, REQUEST_CODE_OPEN_NODE); + } + /** + * @method openFolder + * @description 打开文件夹功能 + * @date: 12/21/2023 8:05 AM + * @author: YangYizhe + * @param data + */ + private void openFolder(NoteItemData data) { + mCurrentFolderId = data.getId(); + startAsyncNotesListQuery(); + if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) { + mState = ListEditState.CALL_RECORD_FOLDER; + mAddNewNote.setVisibility(View.GONE); + } else { + mState = ListEditState.SUB_FOLDER; + } + if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) { + mTitleBar.setText(R.string.call_record_folder_name); + } else { + mTitleBar.setText(data.getSnippet()); + } + mTitleBar.setVisibility(View.VISIBLE); + } + /** + * @method onClick + * @description 如果碰到写便签按钮,新建便签 + * @date: 12/21/2023 8:06 AM + * @author: YangYizhe + * @param + */ + public void onClick(View v) { + if (v.getId() == R.id.btn_new_note) { + createNewNote(); + } + } + + private void showSoftInput() { + InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + if (inputMethodManager != null) { + inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); + } + } + + private void hideSoftInput(View view) { + InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0); + } + /** + * @method showCreateOrModifyFolderDialog + * @description: 创建或者修改文件夹时显示对话框 + * @date: 2023/12/21 0:39 + * @author: WUSHUXIAN + * @param create + * @return void + */ + private void showCreateOrModifyFolderDialog(final boolean create) { + final AlertDialog.Builder builder = new AlertDialog.Builder(this); + View view = LayoutInflater.from(this).inflate(R.layout.dialog_edit_text, null); + final EditText etName = (EditText) view.findViewById(R.id.et_foler_name); + showSoftInput(); + if (!create) { + if (mFocusNoteDataItem != null) { + etName.setText(mFocusNoteDataItem.getSnippet()); + builder.setTitle(getString(R.string.menu_folder_change_name)); + } else { + Log.e(TAG, "The long click data item is null"); + return; + } + } else { + etName.setText(""); + builder.setTitle(this.getString(R.string.menu_create_folder)); + } + + builder.setPositiveButton(android.R.string.ok, null); + builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + hideSoftInput(etName); + } + }); + + final Dialog dialog = builder.setView(view).show(); + final Button positive = (Button)dialog.findViewById(android.R.id.button1); + positive.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + hideSoftInput(etName); + String name = etName.getText().toString(); + if (DataUtils.checkVisibleFolderName(mContentResolver, name)) { + Toast.makeText(NotesListActivity.this, getString(R.string.folder_exist, name), + Toast.LENGTH_LONG).show(); + etName.setSelection(0, etName.length()); + return; + } + if (!create) { + if (!TextUtils.isEmpty(name)) { + ContentValues values = new ContentValues(); + values.put(NoteColumns.SNIPPET, name); + values.put(NoteColumns.TYPE, Notes.TYPE_FOLDER); + values.put(NoteColumns.LOCAL_MODIFIED, 1); + mContentResolver.update(Notes.CONTENT_NOTE_URI, values, NoteColumns.ID + + "=?", new String[] { + String.valueOf(mFocusNoteDataItem.getId()) + }); + } + } else if (!TextUtils.isEmpty(name)) { + ContentValues values = new ContentValues(); + values.put(NoteColumns.SNIPPET, name); + values.put(NoteColumns.TYPE, Notes.TYPE_FOLDER); + mContentResolver.insert(Notes.CONTENT_NOTE_URI, values); + } + dialog.dismiss(); + } + }); + //如果便签名是空的,则禁用确认按钮 + if (TextUtils.isEmpty(etName.getText())) { + positive.setEnabled(false); + } + etName.addTextChangedListener(new TextWatcher() { + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + // TODO Auto-generated method stub + + } + + public void onTextChanged(CharSequence s, int start, int before, int count) { + if (TextUtils.isEmpty(etName.getText())) { + positive.setEnabled(false); + } else { + positive.setEnabled(true); + } + } + + public void afterTextChanged(Editable s) { + // TODO Auto-generated method stub + + } + }); + } + + /* (non-Javadoc) + * @see android.app.Activity#onBackPressed() + * 按返回键时根据情况更改类中的数据 + */ + @Override + public void onBackPressed() { + switch (mState) { + case SUB_FOLDER: + mCurrentFolderId = Notes.ID_ROOT_FOLDER; + mState = ListEditState.NOTE_LIST; + startAsyncNotesListQuery(); + mTitleBar.setVisibility(View.GONE); + break; + case CALL_RECORD_FOLDER: + mCurrentFolderId = Notes.ID_ROOT_FOLDER; + mState = ListEditState.NOTE_LIST; + mAddNewNote.setVisibility(View.VISIBLE); + mTitleBar.setVisibility(View.GONE); + startAsyncNotesListQuery(); + break; + case NOTE_LIST: + super.onBackPressed(); + break; + default: + break; + } + } + + /** + * @param appWidgetId + * @param appWidgetType + * 根据不同类型的widget更新插件,通过intent传送数据 + */ + private void updateWidget(int appWidgetId, int appWidgetType) { + Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); + if (appWidgetType == Notes.TYPE_WIDGET_2X) { + intent.setClass(this, NoteWidgetProvider_2x.class); + } else if (appWidgetType == Notes.TYPE_WIDGET_4X) { + intent.setClass(this, NoteWidgetProvider_4x.class); + } else { + Log.e(TAG, "Unspported widget type"); + return; + } + + intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] { + appWidgetId + }); + + sendBroadcast(intent); + setResult(RESULT_OK, intent); + } + /** + * @method OnCreateContextMenuListener + * @description: 生成便签初始界面 + * @date: 2023/12/21 1:16 + * @author: WUSHUXIAN + */ + private final OnCreateContextMenuListener mFolderOnCreateContextMenuListener = new OnCreateContextMenuListener() { + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { + if (mFocusNoteDataItem != null) { + menu.setHeaderTitle(mFocusNoteDataItem.getSnippet()); + menu.add(0, MENU_FOLDER_VIEW, 0, R.string.menu_folder_view); + menu.add(0, MENU_FOLDER_DELETE, 0, R.string.menu_folder_delete); + menu.add(0, MENU_FOLDER_CHANGE_NAME, 0, R.string.menu_folder_change_name); + } + } + }; + + @Override + public void onContextMenuClosed(Menu menu) { + if (mNotesListView != null) { + mNotesListView.setOnCreateContextMenuListener(null); + } + super.onContextMenuClosed(menu); + } + + /* (non-Javadoc) + * @see android.app.Activity#onContextItemSelected(android.view.MenuItem) + * 针对menu中不同的选择进行不同的处理,里面详细注释 + */ + @Override + public boolean onContextItemSelected(MenuItem item) { + if (mFocusNoteDataItem == null) { + Log.e(TAG, "The long click data item is null"); + return false; + } + switch (item.getItemId()) { + case MENU_FOLDER_VIEW: + openFolder(mFocusNoteDataItem);//打开对应文件 + break; + case MENU_FOLDER_DELETE: + AlertDialog.Builder builder = new AlertDialog.Builder(this);//设置确认是否删除的对话框 + builder.setTitle(getString(R.string.alert_title_delete)); + builder.setIcon(android.R.drawable.ic_dialog_alert); + builder.setMessage(getString(R.string.alert_message_delete_folder)); + builder.setPositiveButton(android.R.string.ok, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + deleteFolder(mFocusNoteDataItem.getId()); + } + }); + builder.setNegativeButton(android.R.string.cancel, null); + builder.show();//显示对话框 + break; + case MENU_FOLDER_CHANGE_NAME: + showCreateOrModifyFolderDialog(false); + break; + default: + break; + } + + return true; + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + menu.clear(); + if (mState == ListEditState.NOTE_LIST) { + getMenuInflater().inflate(R.menu.note_list, menu); + // set sync or sync_cancel + menu.findItem(R.id.menu_sync).setTitle( + GTaskSyncService.isSyncing() ? R.string.menu_sync_cancel : R.string.menu_sync); + } else if (mState == ListEditState.SUB_FOLDER) { + getMenuInflater().inflate(R.menu.sub_folder, menu); + } else if (mState == ListEditState.CALL_RECORD_FOLDER) { + getMenuInflater().inflate(R.menu.call_record_folder, menu); + } else { + Log.e(TAG, "Wrong state:" + mState); + } + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + int itemId = item.getItemId(); + if (itemId == R.id.menu_new_folder) { + showCreateOrModifyFolderDialog(true); + } else if (itemId == R.id.menu_export_text) { + exportNoteToText(); + } else if (itemId == R.id.menu_sync) { + if (isSyncMode()) { + if (TextUtils.equals(item.getTitle(), getString(R.string.menu_sync))) { + GTaskSyncService.startSync(this); + } else { + GTaskSyncService.cancelSync(this); + } + } else { + startPreferenceActivity(); + } + } else if (itemId == R.id.menu_setting) { + startPreferenceActivity(); + } else if (itemId == R.id.menu_new_note) { + createNewNote(); + } else if (itemId == R.id.menu_search) { + onSearchRequested(); + } + return true; + } + + /* (non-Javadoc) + * @see android.app.Activity#onSearchRequested() + * 直接调用startSearch函数 + */ + @Override + public boolean onSearchRequested() { + startSearch(null, false, null /* appData */, false); + return true; + } + + /** + * @method exportNoteToText + * @description 将便签导出,export text按钮 + * @date: 12/21/2023 8:08 AM + * @author: YangYizhe + */ + private void exportNoteToText() { + final BackupUtils backup = BackupUtils.getInstance(NotesListActivity.this); + new AsyncTask() { + + @Override + protected Integer doInBackground(Void... unused) { + return backup.exportToText(); + } + + @Override + protected void onPostExecute(Integer result) { + if (result == BackupUtils.STATE_SD_CARD_UNMOUONTED) { + AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); + builder.setTitle(NotesListActivity.this + .getString(R.string.failed_sdcard_export)); + builder.setMessage(NotesListActivity.this + .getString(R.string.error_sdcard_unmounted)); + builder.setPositiveButton(android.R.string.ok, null); + builder.show(); + } else if (result == BackupUtils.STATE_SUCCESS) { + AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); + builder.setTitle(NotesListActivity.this + .getString(R.string.success_sdcard_export)); + builder.setMessage(NotesListActivity.this.getString( + R.string.format_exported_file_location, backup + .getExportedTextFileName(), backup.getExportedTextFileDir())); + builder.setPositiveButton(android.R.string.ok, null); + builder.show(); + } else if (result == BackupUtils.STATE_SYSTEM_ERROR) { + AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); + builder.setTitle(NotesListActivity.this + .getString(R.string.failed_sdcard_export)); + builder.setMessage(NotesListActivity.this + .getString(R.string.error_sdcard_export)); + builder.setPositiveButton(android.R.string.ok, null); + builder.show(); + } + } + + }.execute(); + } + + + private boolean isSyncMode() { + return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0; + } + + private void startPreferenceActivity() { + Activity from = getParent() != null ? getParent() : this; + Intent intent = new Intent(from, NotesPreferenceActivity.class); + from.startActivityIfNeeded(intent, -1); + } + + /** + * @Package: net.micode.notes.ui + * @ClassName: NotesListActivity + * @Description: 响应了按钮的点击 + * @Author: YangYizhe + * @CreateDate: 12/21/2023 8:09 AM + * @Version: 1.0 + */ + private class OnListItemClickListener implements OnItemClickListener { + + public void onItemClick(AdapterView parent, View view, int position, long id) { + if (view instanceof NotesListItem) { + NoteItemData item = ((NotesListItem) view).getItemData(); + if (mNotesListAdapter.isInChoiceMode()) { + if (item.getType() == Notes.TYPE_NOTE) { + position = position - mNotesListView.getHeaderViewsCount(); + mModeCallBack.onItemCheckedStateChanged(null, position, id, + !mNotesListAdapter.isSelectedItem(position)); + } + return; + } + + switch (mState) { + case NOTE_LIST: + if (item.getType() == Notes.TYPE_FOLDER + || item.getType() == Notes.TYPE_SYSTEM) { + openFolder(item); + } else if (item.getType() == Notes.TYPE_NOTE) { + openNode(item); + } else { + Log.e(TAG, "Wrong note type in NOTE_LIST"); + } + break; + case SUB_FOLDER: + case CALL_RECORD_FOLDER: + if (item.getType() == Notes.TYPE_NOTE) { + openNode(item); + } else { + Log.e(TAG, "Wrong note type in SUB_FOLDER"); + } + break; + default: + break; + } + } + } + + } + + /** + * 查询目标文件 + */ + private void startQueryDestinationFolders() { + String selection = NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>? AND " + NoteColumns.ID + "<>?"; + selection = (mState == ListEditState.NOTE_LIST) ? selection: + "(" + selection + ") OR (" + NoteColumns.ID + "=" + Notes.ID_ROOT_FOLDER + ")"; + + mBackgroundQueryHandler.startQuery(FOLDER_LIST_QUERY_TOKEN, + null, + Notes.CONTENT_NOTE_URI, + FoldersListAdapter.PROJECTION, + selection, + new String[] { + String.valueOf(Notes.TYPE_FOLDER), + String.valueOf(Notes.ID_TRASH_FOLER), + String.valueOf(mCurrentFolderId) + }, + NoteColumns.MODIFIED_DATE + " DESC"); + } + + /** + * @method onItemLongClick + * @description + * 长按某一项时进行的操作 + * 如果长按的是便签,则通过ActionMode菜单实现;如果长按的是文件夹,则通过ContextMenu菜单实现; + * @date: 12/21/2023 8:10 AM + * @author: YangYizhe + * @param + * @return + */ + public boolean onItemLongClick(AdapterView parent, View view, int position, long id) { + if (view instanceof NotesListItem) { + mFocusNoteDataItem = ((NotesListItem) view).getItemData(); + if (mFocusNoteDataItem.getType() == Notes.TYPE_NOTE && !mNotesListAdapter.isInChoiceMode()) { + if (mNotesListView.startActionMode(mModeCallBack) != null) { + mModeCallBack.onItemCheckedStateChanged(null, position, id, true); + mNotesListView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); + } else { + Log.e(TAG, "startActionMode fails"); + } + } else if (mFocusNoteDataItem.getType() == Notes.TYPE_FOLDER) { + mNotesListView.setOnCreateContextMenuListener(mFolderOnCreateContextMenuListener); + } + } + return false; + } +} \ No newline at end of file diff --git a/src/Notes/app/src/main/java/net/micode/notes/ui/NotesListItem.java b/src/Notes/app/src/main/java/net/micode/notes/ui/NotesListItem.java new file mode 100644 index 0000000..d32a37b --- /dev/null +++ b/src/Notes/app/src/main/java/net/micode/notes/ui/NotesListItem.java @@ -0,0 +1,123 @@ +package net.micode.notes.ui; + +import android.content.Context; +import android.text.format.DateUtils; +import android.view.View; +import android.widget.CheckBox; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import net.micode.notes.R; +import net.micode.notes.data.Notes; +import net.micode.notes.tool.DataUtils; +import net.micode.notes.tool.ResourceParser.NoteItemBgResources; + + +/** + * @Package: net.micode.notes.ui + * @ClassName: NotesListItem + * @Description: + * @Author: YangYizhe + * @CreateDate: 12/23/2023 11:35 PM + * @Version: 1.0 + */ +public class NotesListItem extends LinearLayout { + private ImageView mAlert;//闹钟图片 + private TextView mTitle; //标题 + private TextView mTime; //时间 + private TextView mCallName; // + private NoteItemData mItemData; //标签数据 + private CheckBox mCheckBox; //打钩框 + + /*初始化基本信息*/ + public NotesListItem(Context context) { + super(context); //super()它的主要作用是调整调用父类构造函数的顺序 + inflate(context, R.layout.note_item, this);//Inflate可用于将一个xml中定义的布局控件找出来,这里的xml是r。layout + //findViewById用于从contentView中查找指定ID的View,转换出来的形式根据需要而定; + mAlert = (ImageView) findViewById(R.id.iv_alert_icon); + mTitle = (TextView) findViewById(R.id.tv_title); + mTime = (TextView) findViewById(R.id.tv_time); + mCallName = (TextView) findViewById(R.id.tv_name); + mCheckBox = (CheckBox) findViewById(android.R.id.checkbox); + } + ///根据data的属性对各个控件的属性的控制,主要是可见性Visibility,内容setText,格式setTextAppearance + public void bind(Context context, NoteItemData data, boolean choiceMode, boolean checked) { + if (choiceMode && data.getType() == Notes.TYPE_NOTE) { + mCheckBox.setVisibility(View.VISIBLE); ///设置可见行为可见 + mCheckBox.setChecked(checked); ///格子打钩 + } else { + mCheckBox.setVisibility(View.GONE); + } + + mItemData = data; + ///设置控件属性,一共三种情况,由data的id和父id是否与保存到文件夹的id一致来决定 + if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) { + mCallName.setVisibility(View.GONE); + mAlert.setVisibility(View.VISIBLE); + //设置该textview的style + mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem); + //settext为设置内容 + mTitle.setText(context.getString(R.string.call_record_folder_name) + + context.getString(R.string.format_folder_files_count, data.getNotesCount())); + mAlert.setImageResource(R.drawable.call_record); + } else if (data.getParentId() == Notes.ID_CALL_RECORD_FOLDER) { + mCallName.setVisibility(View.VISIBLE); + mCallName.setText(data.getCallName()); + mTitle.setTextAppearance(context,R.style.TextAppearanceSecondaryItem); + mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet())); + ///关于闹钟的设置 + if (data.hasAlert()) { + mAlert.setImageResource(R.drawable.clock);//图片来源的设置 + mAlert.setVisibility(View.VISIBLE); + } else { + mAlert.setVisibility(View.GONE); + } + } else { + mCallName.setVisibility(View.GONE); + mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem); + ///设置title格式 + if (data.getType() == Notes.TYPE_FOLDER) { + mTitle.setText(data.getSnippet() + + context.getString(R.string.format_folder_files_count, + data.getNotesCount())); + mAlert.setVisibility(View.GONE); + } else { + mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet())); + if (data.hasAlert()) { + mAlert.setImageResource(R.drawable.clock);///设置图片来源 + mAlert.setVisibility(View.VISIBLE); + } else { + mAlert.setVisibility(View.GONE); + } + } + } + ///设置内容,获取相关时间,从data里编辑的日期中获取 + mTime. setText(DateUtils.getRelativeTimeSpanString(data.getModifiedDate())); + + setBackground(data); + } + //根据data的文件属性来设置背景 + private void setBackground(NoteItemData data) { + int id = data.getBgColorId(); + //,若是note型文件,则4种情况,对于4种不同情况的背景来源 + if (data.getType() == Notes.TYPE_NOTE) { + //单个数据并且只有一个子文件夹 + if (data.isSingle() || data.isOneFollowingFolder()) { + setBackgroundResource(NoteItemBgResources.getNoteBgSingleRes(id)); + } else if (data.isLast()) {//是最后一个数据 + setBackgroundResource(NoteItemBgResources.getNoteBgLastRes(id)); + } else if (data.isFirst() || data.isMultiFollowingFolder()) {//是一个数据并有多个子文件夹 + setBackgroundResource(NoteItemBgResources.getNoteBgFirstRes(id)); + } else { + setBackgroundResource(NoteItemBgResources.getNoteBgNormalRes(id)); + } + } else { + //若不是note直接调用文件夹的背景来源 + setBackgroundResource(NoteItemBgResources.getFolderBgRes()); + } + } + public NoteItemData getItemData() { + return mItemData; + } +} \ No newline at end of file diff --git a/src/Notes/app/src/main/java/net/micode/notes/ui/NotesPreferenceActivity.java b/src/Notes/app/src/main/java/net/micode/notes/ui/NotesPreferenceActivity.java new file mode 100644 index 0000000..e4109c6 --- /dev/null +++ b/src/Notes/app/src/main/java/net/micode/notes/ui/NotesPreferenceActivity.java @@ -0,0 +1,492 @@ +package net.micode.notes.ui; + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.app.ActionBar; +import android.app.AlertDialog; +import android.content.BroadcastReceiver; +import android.content.ContentValues; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.preference.Preference; +import android.preference.Preference.OnPreferenceClickListener; +import android.preference.PreferenceActivity; +import android.preference.PreferenceCategory; +import android.text.TextUtils; +import android.text.format.DateFormat; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.Button; +import android.widget.TextView; +import android.widget.Toast; + +import net.micode.notes.R; +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.gtask.remote.GTaskSyncService; + +/** + * @Package: net.micode.notes.ui + * @ClassName: NotesPreferenceActivity + * @Description: + * NotesPreferenceActivity,在小米便签中主要实现的是对背景颜色和字体大小的数据储存。 + * 继承了PreferenceActivity主要功能为对系统信息和配置进行自动保存的Activity + * @Author: YangYizhe + * @CreateDate: 12/23/2023 11:42 PM + * @Version: 1.0 + */ +public class NotesPreferenceActivity extends PreferenceActivity { + public static final String PREFERENCE_NAME = "notes_preferences";//优先名 + public static final String PREFERENCE_SYNC_ACCOUNT_NAME = "pref_key_account_name";//同步账号 + public static final String PREFERENCE_LAST_SYNC_TIME = "pref_last_sync_time";//同步时间 + public static final String PREFERENCE_SET_BG_COLOR_KEY = "pref_key_bg_random_appear"; + private static final String PREFERENCE_SYNC_ACCOUNT_KEY = "pref_sync_account_key";//同步密码 + private static final String AUTHORITIES_FILTER_KEY = "authorities"; + private PreferenceCategory mAccountCategory;//账户分组 + private GTaskReceiver mReceiver;//同步任务接收器 + private Account[] mOriAccounts;//账户 + private boolean mHasAddedAccount;//账户的hash标记 + + @Override + /** + * @method onCreate + * @description 创建一个activity,在函数里要完成所有的正常静态设置 + * @date: 12/23/2023 11:43 PM + * @author: YangYizhe + * @param [icicle] + * @return void + */ + protected void onCreate(Bundle icicle) { + super.onCreate(icicle); + /* using the app icon for navigation */ + getActionBar().setDisplayHomeAsUpEnabled(true);//给左上角图标的左边加上一个返回的图标 + addPreferencesFromResource(R.xml.preferences);//添加xml来源并显示 xml + mAccountCategory = (PreferenceCategory) findPreference(PREFERENCE_SYNC_ACCOUNT_KEY);//根据同步账户关键码来初始化分组 + mReceiver = new GTaskReceiver(); + IntentFilter filter = new IntentFilter(); + filter.addAction(GTaskSyncService.GTASK_SERVICE_BROADCAST_NAME); + registerReceiver(mReceiver, filter);//初始化同步组件 + mOriAccounts = null; + View header = LayoutInflater.from(this).inflate(R.layout.settings_header, null);//获取listvivew,ListView的作用:用于列出所有选择 + getListView().addHeaderView(header, null, true);//在listview组件上方添加其他组件 + } + + @Override + /** + * @method onResume + * @description activity交互功能的实现,用于接受用户的输入 + * @date: 12/23/2023 11:44 PM + * @author: YangYizhe + * @return void + */ + protected void onResume() { + super.onResume(); + + // need to set sync account automatically if user has added a new + // account + if (mHasAddedAccount) { + //若用户新加了账户则自动设置同步账户 + Account[] accounts = getGoogleAccounts();//获取google同步账户 + if (mOriAccounts != null && accounts.length > mOriAccounts.length) { + //若原账户不为空且当前账户有增加 + for (Account accountNew : accounts) { + boolean found = false; + for (Account accountOld : mOriAccounts) { + if (TextUtils.equals(accountOld.name, accountNew.name)) { + //更新账户 + found = true; + break; + } + } + if (!found) { + setSyncAccount(accountNew.name); + //若是没有找到旧的账户,那么同步账号中就只添加新账户 + break; + } + } + } + } + + refreshUI();//刷新标签界面 + } + + @Override + protected void onDestroy() { + if (mReceiver != null) { + unregisterReceiver(mReceiver);//注销接收器 + } + super.onDestroy();//执行父类的销毁动作 + } + + /** + * @method loadAccountPreference + * @description 重新设置账户信息 + * @date: 12/23/2023 11:45 PM + * @author: YangYizhe + * @param + * @return + */ + private void loadAccountPreference() { + mAccountCategory.removeAll();//销毁所有的分组 + Preference accountPref = new Preference(this);//建立首选项 + final String defaultAccount = getSyncAccountName(this); + accountPref.setTitle(getString(R.string.preferences_account_title)); + accountPref.setSummary(getString(R.string.preferences_account_summary));//设置首选项的大标题和小标题 + accountPref.setOnPreferenceClickListener(new OnPreferenceClickListener() { + public boolean onPreferenceClick(Preference preference) { + //建立监听器 + if (!GTaskSyncService.isSyncing()) { + if (TextUtils.isEmpty(defaultAccount)) { + // the first time to set account + //若是第一次建立账户显示选择账户提示对话框 + showSelectAccountAlertDialog(); + } else { + // if the account has already been set, we need to promp + // user about the risk + //若是已经建立则显示修改对话框并进行修改操作 + showChangeAccountConfirmAlertDialog(); + } + } else { + //若在没有同步的情况下,则在toast中显示不能修改 + Toast.makeText(NotesPreferenceActivity.this, + R.string.preferences_toast_cannot_change_account, Toast.LENGTH_SHORT) + .show(); + } + return true; + } + }); + mAccountCategory.addPreference(accountPref);//根据新建首选项编辑新的账户分组 + } + /** + * @method loadSyncButton + * @description 设置按键的状态和最后同步的时间 + * @date: 12/23/2023 11:46 PM + * @author: YangYizhe + * @param + * @return + */ + private void loadSyncButton() { + Button syncButton = (Button) findViewById(R.id.preference_sync_button); + TextView lastSyncTimeView = (TextView) findViewById(R.id.prefenerece_sync_status_textview); + //获取同步按钮控件和最终同步时间的的窗口 + // set button state + //设置按钮的状态 + if (GTaskSyncService.isSyncing()) { + //若是在同步状态下 + syncButton.setText(getString(R.string.preferences_button_sync_cancel)); + syncButton.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + GTaskSyncService.cancelSync(NotesPreferenceActivity.this); + } + });//设置按钮显示的文本为“取消同步”以及监听器 + } else { + syncButton.setText(getString(R.string.preferences_button_sync_immediately)); + syncButton.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + GTaskSyncService.startSync(NotesPreferenceActivity.this); + } + });//若是不同步则设置按钮显示的文本为“立即同步”以及对应监听器 + } + syncButton.setEnabled(!TextUtils.isEmpty(getSyncAccountName(this)));//设置按键可用还是不可用 + // set last sync time + // 设置最终同步时间 + if (GTaskSyncService.isSyncing()) { + //若是在同步的情况下 + lastSyncTimeView.setText(GTaskSyncService.getProgressString()); + lastSyncTimeView.setVisibility(View.VISIBLE); + // 根据当前同步服务器设置时间显示框的文本以及可见性 + } else { + //若是非同步情况 + long lastSyncTime = getLastSyncTime(this); + if (lastSyncTime != 0) { + lastSyncTimeView.setText(getString(R.string.preferences_last_sync_time, + DateFormat.format(getString(R.string.preferences_last_sync_time_format), + lastSyncTime))); + lastSyncTimeView.setVisibility(View.VISIBLE); + //则根据最后同步时间的信息来编辑时间显示框的文本内容和可见性 + } else { + //若时间为空直接设置为不可见状态 + lastSyncTimeView.setVisibility(View.GONE); + } + } + } + /** + * @method refreshUI + * @description + * 刷新标签界面 + * 调用上文设置账号和设置按键两个函数来实现 + * @date: 12/23/2023 11:46 PM + * @author: YangYizhe + */ + private void refreshUI() { + loadAccountPreference(); + loadSyncButton(); + } + /** + * @method showSelectAccountAlertDialog + * @description 显示账户选择的对话框并进行账户的设置 + * @date: 12/23/2023 11:47 PM + * @author: YangYizhe + * @param + * @return + */ + private void showSelectAccountAlertDialog() { + AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);//创建一个新的对话框 + + View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null); + TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title); + titleTextView.setText(getString(R.string.preferences_dialog_select_account_title)); + TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle); + subtitleTextView.setText(getString(R.string.preferences_dialog_select_account_tips));//设置标题以及子标题的内容 + dialogBuilder.setCustomTitle(titleView); + dialogBuilder.setPositiveButton(null, null);//设置对话框的自定义标题,建立一个YES的按钮 + Account[] accounts = getGoogleAccounts(); + String defAccount = getSyncAccountName(this);//获取同步账户信息 + mOriAccounts = accounts; + mHasAddedAccount = false; + + if (accounts.length > 0) { + //若账户不为空 + CharSequence[] items = new CharSequence[accounts.length]; + final CharSequence[] itemMapping = items; + int checkedItem = -1; + int index = 0; + for (Account account : accounts) { + if (TextUtils.equals(account.name, defAccount)) { + checkedItem = index;//在账户列表中查询到所需账户 + } + items[index++] = account.name; + } + dialogBuilder.setSingleChoiceItems(items, checkedItem, + //在对话框建立一个单选的复选框 + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + setSyncAccount(itemMapping[which].toString()); + dialog.dismiss();//取消对话框 + refreshUI(); + } + });//建立对话框网络版的监听器 + } + + View addAccountView = LayoutInflater.from(this).inflate(R.layout.add_account_text, null); + dialogBuilder.setView(addAccountView);//给新加账户对话框设置自定义样式 + + final AlertDialog dialog = dialogBuilder.show();//显示对话框 + addAccountView.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + mHasAddedAccount = true; + //将新加账户的hash置true + Intent intent = new Intent("android.settings.ADD_ACCOUNT_SETTINGS"); + //建立网络建立组件 + intent.putExtra(AUTHORITIES_FILTER_KEY, new String[] { + "gmail-ls" + }); + startActivityForResult(intent, -1); + //跳回上一个选项 + dialog.dismiss(); + } + });//建立新加账户对话框的监听器 + } + /** + * @method showChangeAccountConfirmAlertDialog + * @description 显示账户选择对话框和相关账户操作 + * @date: 12/23/2023 11:48 PM + * @author: YangYizhe + */ + private void showChangeAccountConfirmAlertDialog() { + AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);//创建一个新的对话框 + View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null); + TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title); + titleTextView.setText(getString(R.string.preferences_dialog_change_account_title, + getSyncAccountName(this))); + TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle); + subtitleTextView.setText(getString(R.string.preferences_dialog_change_account_warn_msg));//根据同步修改的账户信息设置标题以及子标题的内容 + dialogBuilder.setCustomTitle(titleView);//设置对话框的自定义标题 + CharSequence[] menuItemArray = new CharSequence[] { + getString(R.string.preferences_menu_change_account), + getString(R.string.preferences_menu_remove_account), + getString(R.string.preferences_menu_cancel) + }; + dialogBuilder.setItems(menuItemArray, new DialogInterface.OnClickListener() { + //设置对话框要显示的一个list,用于显示几个命令时,即change,remove,cancel + public void onClick(DialogInterface dialog, int which) { + //按键功能,由which来决定 + if (which == 0) { + //进入账户选择对话框 + showSelectAccountAlertDialog(); + } else if (which == 1) { + //删除账户并且跟新便签界面 + removeSyncAccount(); + refreshUI(); + } + } + }); + dialogBuilder.show();//显示对话框 + } + + /** + * @method getGoogleAccounts + * @description + * 获取谷歌账户 + * 通过账户管理器直接获取 + * @date: 12/23/2023 11:49 PM + * @author: YangYizhe + */ + private Account[] getGoogleAccounts() { + AccountManager accountManager = AccountManager.get(this); + return accountManager.getAccountsByType("com.google"); + } + + /** + * @method setSyncAccount + * @description 设置同步账户 + * @date: 12/23/2023 11:49 PM + * @author: YangYizhe + * @param account + */ + private void setSyncAccount(String account) { + if (!getSyncAccountName(this).equals(account)) { + //假如该账号不在同步账号列表中 + SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = settings.edit();//编辑共享的首选项 + if (account != null) { + editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, account); + } else { + editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, ""); + }//将该账号加入到首选项中 + + editor.commit();//提交修改的数据 + setLastSyncTime(this, 0);//将最后同步时间清零 + + // clean up local gtask related info + new Thread(new Runnable() { + public void run() { + ContentValues values = new ContentValues(); + values.put(NoteColumns.GTASK_ID, ""); + values.put(NoteColumns.SYNC_ID, 0); + getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null); + } + }).start(); + //重置当地同步任务的信息 + + Toast.makeText(NotesPreferenceActivity.this, + getString(R.string.preferences_toast_success_set_accout, account), + Toast.LENGTH_SHORT).show(); + //将toast的文本信息置为“设置账户成功”并显示出来 + } + } + /** + * @method removeSyncAccount + * @description 删除同步账户 + * @date: 12/23/2023 11:49 PM + * @author: YangYizhe + */ + private void removeSyncAccount() { + SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = settings.edit();//设置共享首选项 + + if (settings.contains(PREFERENCE_SYNC_ACCOUNT_NAME)) { + editor.remove(PREFERENCE_SYNC_ACCOUNT_NAME);//假如当前首选项中有账户就删除 + } + if (settings.contains(PREFERENCE_LAST_SYNC_TIME)) { + editor.remove(PREFERENCE_LAST_SYNC_TIME);//删除当前首选项中有账户时间 + } + editor.commit();//提交更新后的数据 + + // clean up local gtask related info + new Thread(new Runnable() { + public void run() { + ContentValues values = new ContentValues(); + values.put(NoteColumns.GTASK_ID, ""); + values.put(NoteColumns.SYNC_ID, 0); + getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null); + } + }).start(); + //重置当地同步任务的信息 + } + /** + * @method getSyncAccountName + * @description + * 获取同步账户名称 + * 通过共享的首选项里的信息直接获取 + * @date: 12/23/2023 11:50 PM + * @author: YangYizhe + * @param context + */ + public static String getSyncAccountName(Context context) { + SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, + Context.MODE_PRIVATE); + return settings.getString(PREFERENCE_SYNC_ACCOUNT_NAME, ""); + } + /** + * @method setLastSyncTime + * @description 设置最终同步的时间 + * @date: 12/23/2023 11:50 PM + * @author: YangYizhe + * @param + * @return + */ + public static void setLastSyncTime(Context context, long time) { + SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, + Context.MODE_PRIVATE); + SharedPreferences.Editor editor = settings.edit();// 从共享首选项中找到相关账户并获取其编辑器 + editor.putLong(PREFERENCE_LAST_SYNC_TIME, time); + editor.commit();//编辑最终同步时间并提交更新 + } + /** + * @method getLastSyncTime + * @description + * 获取最终同步时间 + * 通过共享的首选项里的信息直接获取 + * @date: 12/23/2023 11:51 PM + * @author: YangYizhe + * @param context + */ + public static long getLastSyncTime(Context context) { + SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, + Context.MODE_PRIVATE); + return settings.getLong(PREFERENCE_LAST_SYNC_TIME, 0); + } + + private class GTaskReceiver extends BroadcastReceiver { + + @Override + public void onReceive(Context context, Intent intent) { + refreshUI(); + if (intent.getBooleanExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_IS_SYNCING, false)) { + //获取随广播而来的Intent中的同步服务的数据 + TextView syncStatus = (TextView) findViewById(R.id.prefenerece_sync_status_textview); + syncStatus.setText(intent + .getStringExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_PROGRESS_MSG));//通过获取的数据在设置系统的状态 + } + + } + } + /** + * @method onOptionsItemSelected + * @description 处理菜单的选项 + * @date: 12/23/2023 11:52 PM + * @author: YangYizhe + * @param item + */ + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + //根据选项的id选择,这里只有一个主页 + case android.R.id.home: + Intent intent = new Intent(this, NotesListActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + return true; + //在主页情况下在创建连接组件intent,发出清空的信号并开始一个相应的activity + default: + return false; + } + } +} + \ No newline at end of file diff --git a/src/Notes/app/src/main/java/net/micode/notes/widget/NoteWidgetProvider.java b/src/Notes/app/src/main/java/net/micode/notes/widget/NoteWidgetProvider.java new file mode 100644 index 0000000..d8eb50b --- /dev/null +++ b/src/Notes/app/src/main/java/net/micode/notes/widget/NoteWidgetProvider.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.widget; +import android.app.PendingIntent; +import android.appwidget.AppWidgetManager; +import android.appwidget.AppWidgetProvider; +import android.content.ContentValues; +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; +import android.util.Log; +import android.widget.RemoteViews; + +import net.micode.notes.R; +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.tool.ResourceParser; +import net.micode.notes.ui.NoteEditActivity; +import net.micode.notes.ui.NotesListActivity; +/** + * @Package: net.micode.notes.widget + * @ClassName: NoteWidgetProvider + * @Description: 这个抽象类用作MiNote应用程序中笔记小部件的基础。它提供了在主屏幕上更新和管理笔记小部件的功能。 + * @Author: YangYizhe + * @CreateDate: 12/23/2023 11:53 PM + * @Version: 1.0 + */ +public abstract class NoteWidgetProvider extends AppWidgetProvider { + public static final String [] PROJECTION = new String [] { + NoteColumns.ID, + NoteColumns.BG_COLOR_ID, + NoteColumns.SNIPPET + }; + + public static final int COLUMN_ID = 0; + public static final int COLUMN_BG_COLOR_ID = 1; + public static final int COLUMN_SNIPPET = 2; + + private static final String TAG = "NoteWidgetProvider"; + + @Override + public void onDeleted(Context context, int[] appWidgetIds) { + ContentValues values = new ContentValues(); + values.put(NoteColumns.WIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); + for (int i = 0; i < appWidgetIds.length; i++) { + context.getContentResolver().update(Notes.CONTENT_NOTE_URI, + values, + NoteColumns.WIDGET_ID + "=?", + new String[] { String.valueOf(appWidgetIds[i])}); + } + } + + private Cursor getNoteWidgetInfo(Context context, int widgetId) { + return context.getContentResolver().query(Notes.CONTENT_NOTE_URI, + PROJECTION, + NoteColumns.WIDGET_ID + "=? AND " + NoteColumns.PARENT_ID + "<>?", + new String[] { String.valueOf(widgetId), String.valueOf(Notes.ID_TRASH_FOLER) }, + null); + } + + protected void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { + update(context, appWidgetManager, appWidgetIds, false); + } + + private void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds, + boolean privacyMode) { + for (int i = 0; i < appWidgetIds.length; i++) { + if (appWidgetIds[i] != AppWidgetManager.INVALID_APPWIDGET_ID) { + int bgId = ResourceParser.getDefaultBgId(context); + String snippet = ""; + Intent intent = new Intent(context, NoteEditActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); + intent.putExtra(Notes.INTENT_EXTRA_WIDGET_ID, appWidgetIds[i]); + intent.putExtra(Notes.INTENT_EXTRA_WIDGET_TYPE, getWidgetType()); + + Cursor c = getNoteWidgetInfo(context, appWidgetIds[i]); + if (c != null && c.moveToFirst()) { + if (c.getCount() > 1) { + Log.e(TAG, "Multiple message with same widget id:" + appWidgetIds[i]); + c.close(); + return; + } + snippet = c.getString(COLUMN_SNIPPET); + bgId = c.getInt(COLUMN_BG_COLOR_ID); + intent.putExtra(Intent.EXTRA_UID, c.getLong(COLUMN_ID)); + intent.setAction(Intent.ACTION_VIEW); + } else { + snippet = context.getResources().getString(R.string.widget_havenot_content); + intent.setAction(Intent.ACTION_INSERT_OR_EDIT); + } + + if (c != null) { + c.close(); + } + + RemoteViews rv = new RemoteViews(context.getPackageName(), getLayoutId()); + rv.setImageViewResource(R.id.widget_bg_image, getBgResourceId(bgId)); + intent.putExtra(Notes.INTENT_EXTRA_BACKGROUND_ID, bgId); + /** + * Generate the pending intent to start host for the widget + */ + PendingIntent pendingIntent = null; + if (privacyMode) { + rv.setTextViewText(R.id.widget_text, + context.getString(R.string.widget_under_visit_mode)); + pendingIntent = PendingIntent.getActivity(context, appWidgetIds[i], new Intent( + context, NotesListActivity.class), PendingIntent.FLAG_UPDATE_CURRENT); + } else { + rv.setTextViewText(R.id.widget_text, snippet); + pendingIntent = PendingIntent.getActivity(context, appWidgetIds[i], intent, + PendingIntent.FLAG_UPDATE_CURRENT); + } + + rv.setOnClickPendingIntent(R.id.widget_text, pendingIntent); + appWidgetManager.updateAppWidget(appWidgetIds[i], rv); + } + } + } + + protected abstract int getBgResourceId(int bgId); + + protected abstract int getLayoutId(); + + protected abstract int getWidgetType(); +} diff --git a/src/Notes/app/src/main/java/net/micode/notes/widget/NoteWidgetProvider_2x.java b/src/Notes/app/src/main/java/net/micode/notes/widget/NoteWidgetProvider_2x.java new file mode 100644 index 0000000..f3d5f1e --- /dev/null +++ b/src/Notes/app/src/main/java/net/micode/notes/widget/NoteWidgetProvider_2x.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.widget; + +import android.appwidget.AppWidgetManager; +import android.content.Context; + +import net.micode.notes.R; +import net.micode.notes.data.Notes; +import net.micode.notes.tool.ResourceParser; + +/** + * @Package: net.micode.notes.widget + * @ClassName: NoteWidgetProvider_2x + * @Description: + * 这个类是NoteWidgetProvider的一个具体实现,用于实现2x大小的笔记小部件。 + * 它继承自NoteWidgetProvider,并提供特定于2x小部件的布局ID、背景资源和小部件类型。 + * @Author: YangYizhe + * @CreateDate: 12/23/2023 11:55 PM + * @Version: 1.0 + */ +public class NoteWidgetProvider_2x extends NoteWidgetProvider { + @Override + public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { + super.update(context, appWidgetManager, appWidgetIds); + } + + @Override + protected int getLayoutId() { + return R.layout.widget_2x; + } + + @Override + protected int getBgResourceId(int bgId) { + return ResourceParser.WidgetBgResources.getWidget2xBgResource(bgId); + } + + @Override + protected int getWidgetType() { + return Notes.TYPE_WIDGET_2X; + } +} diff --git a/src/Notes/app/src/main/java/net/micode/notes/widget/NoteWidgetProvider_4x.java b/src/Notes/app/src/main/java/net/micode/notes/widget/NoteWidgetProvider_4x.java new file mode 100644 index 0000000..0c3ae50 --- /dev/null +++ b/src/Notes/app/src/main/java/net/micode/notes/widget/NoteWidgetProvider_4x.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.widget; + +import android.appwidget.AppWidgetManager; +import android.content.Context; + +import net.micode.notes.R; +import net.micode.notes.data.Notes; +import net.micode.notes.tool.ResourceParser; + +/** + * @Package: net.micode.notes.widget + * @ClassName: NoteWidgetProvider_4x + * @Description: + * 这个类是NoteWidgetProvider的一个具体实现,用于实现4x大小的笔记小部件。 + * 它继承自NoteWidgetProvider,并提供特定于4x小部件的布局ID、背景资源和小部件类型。 + * @Author: YangYizhe + * @CreateDate: 12/23/2023 11:56 PM + * @Version: 1.0 + */ +public class NoteWidgetProvider_4x extends NoteWidgetProvider { + @Override + public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { + super.update(context, appWidgetManager, appWidgetIds); + } + + protected int getLayoutId() { + return R.layout.widget_4x; + } + + @Override + protected int getBgResourceId(int bgId) { + return ResourceParser.WidgetBgResources.getWidget4xBgResource(bgId); + } + + @Override + protected int getWidgetType() { + return Notes.TYPE_WIDGET_4X; + } +} diff --git a/src/main/java/net/micode/notes/data/Contact.java b/src/main/java/net/micode/notes/data/Contact.java new file mode 100644 index 0000000..d65c239 --- /dev/null +++ b/src/main/java/net/micode/notes/data/Contact.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.data;//属于data这个包 + +import android.content.Context; +import android.database.Cursor; +import android.provider.ContactsContract.CommonDataKinds.Phone; +import android.provider.ContactsContract.Data; +import android.telephony.PhoneNumberUtils; +import android.util.Log; + +import java.util.HashMap; +/** + * @Package: net.micode.notes.data + * @ClassName: Contact + * @Description: + * Contact类用于查询联系人信息并进行缓存。 + * 该类包含一个静态的HashMap作为缓存,存储电话号码和对应的联系人名字。 + * 通过调用getContact方法可以根据给定的电话号码查询联系人名字。 + * 如果缓存中已经存在该电话号码对应的联系人名字,则直接返回缓存中的结果,否则通过查询数据库获取联系人名字并更新缓存。 + * 该类还定义了一个私有的SQL筛选语句用于查询联系人信息。 + * 注意:该类是线程不安全的,如果需要在多线程环境下使用,请做好同步控制 + * @Author: YangYizhe + * @CreateDate: 12/17/2023 10:10 AM + * @Version: 1.0 + */ +public class Contact { + /** + * 作为缓存,存储电话号码和对应的联系人名字 + */ + private static HashMap sContactCache; + private static final String TAG = "Contact";//设置日志TAG标签 + + //查询联系人的SQL筛选语句 + private static final String CALLER_ID_SELECTION = "PHONE_NUMBERS_EQUAL(" + Phone.NUMBER + + ",?) AND " + Data.MIMETYPE + "='" + Phone.CONTENT_ITEM_TYPE + "'" + + " AND " + Data.RAW_CONTACT_ID + " IN " + + "(SELECT raw_contact_id " + + " FROM phone_lookup" + + " WHERE min_match = '+')"; + //获取联系人 + public static String getContact(Context context, String phoneNumber) { + if(sContactCache == null) {/*如果缓存为空,就新建一个*/ + sContactCache = new HashMap(); + } + + if(sContactCache.containsKey(phoneNumber)) {/*如果缓存中已经有该电话号码对应的联系人名字,就直接返回*/ + return sContactCache.get(phoneNumber); + } + + String selection = CALLER_ID_SELECTION.replace("+", + PhoneNumberUtils.toCallerIDMinMatch(phoneNumber));//将电话号码转换为最小匹配模式,用于筛选 + Cursor cursor = context.getContentResolver().query( + Data.CONTENT_URI, + new String [] { Phone.DISPLAY_NAME }, + selection,//使用筛选条件 + new String[] { phoneNumber }, + null); + + if (cursor != null && cursor.moveToFirst()) {//如果找到了符合条件的联系人 + try { + String name = cursor.getString(0);//获取联系人名字 + sContactCache.put(phoneNumber, name);//将电话号码和联系人名字添加到缓存中 + return name;//返回联系人名字 + } catch (IndexOutOfBoundsException e) { + Log.e(TAG, " Cursor get string error " + e.toString()); + return null; + } finally { + cursor.close();//关闭游标 + } + } else {/*没找到*/ + Log.d(TAG, "No contact matched with number:" + phoneNumber); + return null; + } + } +} diff --git a/src/main/java/net/micode/notes/data/Notes.java b/src/main/java/net/micode/notes/data/Notes.java new file mode 100644 index 0000000..aba2dbb --- /dev/null +++ b/src/main/java/net/micode/notes/data/Notes.java @@ -0,0 +1,290 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.data; + +import android.net.Uri; +//Notes类就定义了很多常量,是小米便签的数据库 +public class Notes { + public static final String AUTHORITY = "micode_notes"; + public static final String TAG = "Notes"; + //三个type + public static final int TYPE_NOTE = 0; + public static final int TYPE_FOLDER = 1; + public static final int TYPE_SYSTEM = 2; + + /** + * Following IDs are system folders' identifiers + * {@link Notes#ID_ROOT_FOLDER } is default folder + * {@link Notes#ID_TEMPARAY_FOLDER } is for notes belonging no folder + * {@link Notes#ID_CALL_RECORD_FOLDER} is to store call records + */ + //不同种类的文件夹 + public static final int ID_ROOT_FOLDER = 0; + public static final int ID_TEMPARAY_FOLDER = -1; + public static final int ID_CALL_RECORD_FOLDER = -2; + public static final int ID_TRASH_FOLER = -3; + + public static final String INTENT_EXTRA_ALERT_DATE = "net.micode.notes.alert_date"; + public static final String INTENT_EXTRA_BACKGROUND_ID = "net.micode.notes.background_color_id"; + public static final String INTENT_EXTRA_WIDGET_ID = "net.micode.notes.widget_id"; + public static final String INTENT_EXTRA_WIDGET_TYPE = "net.micode.notes.widget_type"; + public static final String INTENT_EXTRA_FOLDER_ID = "net.micode.notes.folder_id"; + public static final String INTENT_EXTRA_CALL_DATE = "net.micode.notes.call_date"; + + public static final int TYPE_WIDGET_INVALIDE = -1; + public static final int TYPE_WIDGET_2X = 0; + public static final int TYPE_WIDGET_4X = 1; + //数据常量 包括普通note和call_note + public static class DataConstants { + public static final String NOTE = TextNote.CONTENT_ITEM_TYPE; + public static final String CALL_NOTE = CallNote.CONTENT_ITEM_TYPE; + } + //两个指针,一个找便签和文件夹,一个用来找数据 + /** + * Uri to query all notes and folders + */ + public static final Uri CONTENT_NOTE_URI = Uri.parse("content://" + AUTHORITY + "/note"); + + /** + * Uri to query data + */ + + public static final Uri CONTENT_DATA_URI = Uri.parse("content://" + AUTHORITY + "/data"); + //Notecolumns类,用于创建数据库的表头 + public interface NoteColumns { + //具体每一项都给了英文注释 + /** + * The unique ID for a row + *

Type: INTEGER (long)

+ */ + public static final String ID = "_id"; + + /** + * The parent's id for note or folder + *

Type: INTEGER (long)

+ */ + public static final String PARENT_ID = "parent_id"; + + /** + * Created data for note or folder + *

Type: INTEGER (long)

+ */ + public static final String CREATED_DATE = "created_date"; + + /** + * Latest modified date + *

Type: INTEGER (long)

+ */ + public static final String MODIFIED_DATE = "modified_date"; + + + /** + * Alert date + *

Type: INTEGER (long)

+ */ + public static final String ALERTED_DATE = "alert_date"; + + /** + * Folder's name or text content of note + *

Type: TEXT

+ */ + public static final String SNIPPET = "snippet"; + + /** + * Note's widget id + *

Type: INTEGER (long)

+ */ + public static final String WIDGET_ID = "widget_id"; + + /** + * Note's widget type + *

Type: INTEGER (long)

+ */ + public static final String WIDGET_TYPE = "widget_type"; + + /** + * Note's background color's id + *

Type: INTEGER (long)

+ */ + public static final String BG_COLOR_ID = "bg_color_id"; + + /** + * For text note, it doesn't has attachment, for multi-media + * note, it has at least one attachment + *

Type: INTEGER

+ */ + public static final String HAS_ATTACHMENT = "has_attachment"; + + /** + * Folder's count of notes + *

Type: INTEGER (long)

+ */ + public static final String NOTES_COUNT = "notes_count"; + + /** + * The file type: folder or note + *

Type: INTEGER

+ */ + public static final String TYPE = "type"; + + /** + * The last sync id + *

Type: INTEGER (long)

+ */ + public static final String SYNC_ID = "sync_id"; + + /** + * Sign to indicate local modified or not + *

Type: INTEGER

+ */ + public static final String LOCAL_MODIFIED = "local_modified"; + + /** + * Original parent id before moving into temporary folder + *

Type : INTEGER

+ */ + public static final String ORIGIN_PARENT_ID = "origin_parent_id"; + + /** + * The gtask id + *

Type : TEXT

+ */ + public static final String GTASK_ID = "gtask_id"; + + /** + * The version code + *

Type : INTEGER (long)

+ */ + public static final String VERSION = "version"; + + public static final String PASSWORD = "password"; + + public static final String IMPORTANCE = "importance"; + }//便签的各种属性 + /* + * 便签数据在数据库中的表头 + */ + public interface DataColumns { + /** + * The unique ID for a row + *

Type: INTEGER (long)

+ */ + public static final String ID = "_id"; + + /** + * The MIME type of the item represented by this row. + *

Type: Text

+ */ + public static final String MIME_TYPE = "mime_type"; + + /** + * The reference id to note that this data belongs to + *

Type: INTEGER (long)

+ */ + public static final String NOTE_ID = "note_id"; + + /** + * Created data for note or folder + *

Type: INTEGER (long)

+ */ + public static final String CREATED_DATE = "created_date"; + + /** + * Latest modified date + *

Type: INTEGER (long)

+ */ + public static final String MODIFIED_DATE = "modified_date"; + + /** + * Data's content + *

Type: TEXT

+ */ + public static final String CONTENT = "content"; + + + /** + * Generic data column, the meaning is {@link #MIMETYPE} specific, used for + * integer data type + *

Type: INTEGER

+ */ + public static final String DATA1 = "data1"; + + /** + * Generic data column, the meaning is {@link #MIMETYPE} specific, used for + * integer data type + *

Type: INTEGER

+ */ + public static final String DATA2 = "data2"; + + /** + * Generic data column, the meaning is {@link #MIMETYPE} specific, used for + * TEXT data type + *

Type: TEXT

+ */ + public static final String DATA3 = "data3"; + + /** + * Generic data column, the meaning is {@link #MIMETYPE} specific, used for + * TEXT data type + *

Type: TEXT

+ */ + public static final String DATA4 = "data4"; + + /** + * Generic data column, the meaning is {@link #MIMETYPE} specific, used for + * TEXT data type + *

Type: TEXT

+ */ + public static final String DATA5 = "data5"; + }//一个便签内部各种数据类型 + + public static final class TextNote implements DataColumns { + /** + * Mode to indicate the text in check list mode or not + *

Type: Integer 1:check list mode 0: normal mode

+ */ + public static final String MODE = DATA1; + + public static final int MODE_CHECK_LIST = 1; + + public static final String CONTENT_TYPE = "vnd.android.cursor.dir/text_note"; + + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/text_note"; + + public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/text_note"); + }//一种是文本textnote + + public static final class CallNote implements DataColumns { + /** + * Call date for this record + *

Type: INTEGER (long)

+ */ + public static final String CALL_DATE = DATA1; + + /** + * Phone number for this record + *

Type: TEXT

+ */ + public static final String PHONE_NUMBER = DATA3; + + public static final String CONTENT_TYPE = "vnd.android.cursor.dir/call_note"; + + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/call_note"; + + public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/call_note"); + }//另一种是通话类型的callnote +} diff --git a/src/main/java/net/micode/notes/data/NotesDatabaseHelper.java b/src/main/java/net/micode/notes/data/NotesDatabaseHelper.java new file mode 100644 index 0000000..bedbf65 --- /dev/null +++ b/src/main/java/net/micode/notes/data/NotesDatabaseHelper.java @@ -0,0 +1,362 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.data; + +import android.content.ContentValues; +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import android.util.Log; +//引用了同一个包中的另一个子包Notes中一些接口 +import net.micode.notes.data.Notes.DataColumns; +import net.micode.notes.data.Notes.DataConstants; +import net.micode.notes.data.Notes.NoteColumns; + +//数据库操作 +public class NotesDatabaseHelper extends SQLiteOpenHelper { + private static final String DB_NAME = "note.db"; + + private static final int DB_VERSION = 4; + //接口两部分一个Note一个DATA + public interface TABLE { + public static final String NOTE = "note"; + + public static final String DATA = "data"; + } + + private static final String TAG = "NotesDatabaseHelper"; + + private static NotesDatabaseHelper mInstance; + //基于NoteColumn创建一个NOTE_TABLE表格,并附上初始数据 + private static final String CREATE_NOTE_TABLE_SQL = + "CREATE TABLE " + TABLE.NOTE + "(" + + NoteColumns.ID + " INTEGER PRIMARY KEY," + + NoteColumns.PARENT_ID + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.ALERTED_DATE + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.BG_COLOR_ID + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.CREATED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + + NoteColumns.HAS_ATTACHMENT + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.MODIFIED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + + NoteColumns.NOTES_COUNT + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.SNIPPET + " TEXT NOT NULL DEFAULT ''," + + NoteColumns.TYPE + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.WIDGET_ID + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.WIDGET_TYPE + " INTEGER NOT NULL DEFAULT -1," + + NoteColumns.SYNC_ID + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.LOCAL_MODIFIED + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.ORIGIN_PARENT_ID + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.GTASK_ID + " TEXT NOT NULL DEFAULT ''," + + NoteColumns.VERSION + " INTEGER NOT NULL DEFAULT 0" + + ")"; + //主要基于datacolumn来创建DATA_TABLE + private static final String CREATE_DATA_TABLE_SQL = + "CREATE TABLE " + TABLE.DATA + "(" + + DataColumns.ID + " INTEGER PRIMARY KEY," + + DataColumns.MIME_TYPE + " TEXT NOT NULL," + + DataColumns.NOTE_ID + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.CREATED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + + NoteColumns.MODIFIED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + + DataColumns.CONTENT + " TEXT NOT NULL DEFAULT ''," + + DataColumns.DATA1 + " INTEGER," + + DataColumns.DATA2 + " INTEGER," + + DataColumns.DATA3 + " TEXT NOT NULL DEFAULT ''," + + DataColumns.DATA4 + " TEXT NOT NULL DEFAULT ''," + + DataColumns.DATA5 + " TEXT NOT NULL DEFAULT ''" + + ")"; + //这个数据是关于INDEX编号的 + private static final String CREATE_DATA_NOTE_ID_INDEX_SQL = + "CREATE INDEX IF NOT EXISTS note_id_index ON " + + TABLE.DATA + "(" + DataColumns.NOTE_ID + ");"; + + /** + * Increase folder's note count when move note to the folder + */ + private static final String NOTE_INCREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER = + "CREATE TRIGGER increase_folder_count_on_update "+ + " AFTER UPDATE OF " + NoteColumns.PARENT_ID + " ON " + TABLE.NOTE + + " BEGIN " + + " UPDATE " + TABLE.NOTE + + " SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + " + 1" + + " WHERE " + NoteColumns.ID + "=new." + NoteColumns.PARENT_ID + ";" + + " END"; + //移入note时触发,修改一系列数据,从哪来之类的 + /** + * Decrease folder's note count when move note from folder + */ + private static final String NOTE_DECREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER = + "CREATE TRIGGER decrease_folder_count_on_update " + + " AFTER UPDATE OF " + NoteColumns.PARENT_ID + " ON " + TABLE.NOTE + + " BEGIN " + + " UPDATE " + TABLE.NOTE + + " SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + "-1" + + " WHERE " + NoteColumns.ID + "=old." + NoteColumns.PARENT_ID + + " AND " + NoteColumns.NOTES_COUNT + ">0" + ";" + + " END"; + //移除Note时触发,与上面移入对应 + /** + * Increase folder's note count when insert new note to the folder + */ + private static final String NOTE_INCREASE_FOLDER_COUNT_ON_INSERT_TRIGGER = + "CREATE TRIGGER increase_folder_count_on_insert " + + " AFTER INSERT ON " + TABLE.NOTE + + " BEGIN " + + " UPDATE " + TABLE.NOTE + + " SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + " + 1" + + " WHERE " + NoteColumns.ID + "=new." + NoteColumns.PARENT_ID + ";" + + " END"; + //插入Note + /** + * Decrease folder's note count when delete note from the folder + */ + private static final String NOTE_DECREASE_FOLDER_COUNT_ON_DELETE_TRIGGER = + "CREATE TRIGGER decrease_folder_count_on_delete " + + " AFTER DELETE ON " + TABLE.NOTE + + " BEGIN " + + " UPDATE " + TABLE.NOTE + + " SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + "-1" + + " WHERE " + NoteColumns.ID + "=old." + NoteColumns.PARENT_ID + + " AND " + NoteColumns.NOTES_COUNT + ">0;" + + " END"; + //删除note + /** + * Update note's content when insert data with type {@link DataConstants#NOTE} + */ + private static final String DATA_UPDATE_NOTE_CONTENT_ON_INSERT_TRIGGER = + "CREATE TRIGGER update_note_content_on_insert " + + " AFTER INSERT ON " + TABLE.DATA + + " WHEN new." + DataColumns.MIME_TYPE + "='" + DataConstants.NOTE + "'" + + " BEGIN" + + " UPDATE " + TABLE.NOTE + + " SET " + NoteColumns.SNIPPET + "=new." + DataColumns.CONTENT + + " WHERE " + NoteColumns.ID + "=new." + DataColumns.NOTE_ID + ";" + + " END"; + //当给note插入新数据时触发 + /** + * Update note's content when data with {@link DataConstants#NOTE} type has changed + */ + private static final String DATA_UPDATE_NOTE_CONTENT_ON_UPDATE_TRIGGER = + "CREATE TRIGGER update_note_content_on_update " + + " AFTER UPDATE ON " + TABLE.DATA + + " WHEN old." + DataColumns.MIME_TYPE + "='" + DataConstants.NOTE + "'" + + " BEGIN" + + " UPDATE " + TABLE.NOTE + + " SET " + NoteColumns.SNIPPET + "=new." + DataColumns.CONTENT + + " WHERE " + NoteColumns.ID + "=new." + DataColumns.NOTE_ID + ";" + + " END";//note数据被修改update + + /** + * Update note's content when data with {@link DataConstants#NOTE} type has deleted + */ + private static final String DATA_UPDATE_NOTE_CONTENT_ON_DELETE_TRIGGER = + "CREATE TRIGGER update_note_content_on_delete " + + " AFTER delete ON " + TABLE.DATA + + " WHEN old." + DataColumns.MIME_TYPE + "='" + DataConstants.NOTE + "'" + + " BEGIN" + + " UPDATE " + TABLE.NOTE + + " SET " + NoteColumns.SNIPPET + "=''" + + " WHERE " + NoteColumns.ID + "=old." + DataColumns.NOTE_ID + ";" + + " END";//更新已经删除的便签的数据 + + /** + * Delete datas belong to note which has been deleted + */ + private static final String NOTE_DELETE_DATA_ON_DELETE_TRIGGER = + "CREATE TRIGGER delete_data_on_delete " + + " AFTER DELETE ON " + TABLE.NOTE + + " BEGIN" + + " DELETE FROM " + TABLE.DATA + + " WHERE " + DataColumns.NOTE_ID + "=old." + NoteColumns.ID + ";" + + " END";//删除 已经被删除的便签的数据 + + /** + * Delete notes belong to folder which has been deleted + */ + private static final String FOLDER_DELETE_NOTES_ON_DELETE_TRIGGER = + "CREATE TRIGGER folder_delete_notes_on_delete " + + " AFTER DELETE ON " + TABLE.NOTE + + " BEGIN" + + " DELETE FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.PARENT_ID + "=old." + NoteColumns.ID + ";" + + " END";//删除 已删除folder文件夹 中的便签要修改的数据 + + /** + * Move notes belong to folder which has been moved to trash folder + */ + private static final String FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER = + "CREATE TRIGGER folder_move_notes_on_trash " + + " AFTER UPDATE ON " + TABLE.NOTE + + " WHEN new." + NoteColumns.PARENT_ID + "=" + Notes.ID_TRASH_FOLER + + " BEGIN" + + " UPDATE " + TABLE.NOTE + + " SET " + NoteColumns.PARENT_ID + "=" + Notes.ID_TRASH_FOLER + + " WHERE " + NoteColumns.PARENT_ID + "=old." + NoteColumns.ID + ";" + + " END";//移动trash_folder中的便签 + + public NotesDatabaseHelper(Context context) { + super(context, DB_NAME, null, DB_VERSION); + } + //构造函数 + public void createNoteTable(SQLiteDatabase db) { + db.execSQL(CREATE_NOTE_TABLE_SQL); + reCreateNoteTableTriggers(db); + createSystemFolder(db); + Log.d(TAG, "note table has been created"); + } + + private void reCreateNoteTableTriggers(SQLiteDatabase db) { + db.execSQL("DROP TRIGGER IF EXISTS increase_folder_count_on_update"); + db.execSQL("DROP TRIGGER IF EXISTS decrease_folder_count_on_update"); + db.execSQL("DROP TRIGGER IF EXISTS decrease_folder_count_on_delete"); + db.execSQL("DROP TRIGGER IF EXISTS delete_data_on_delete"); + db.execSQL("DROP TRIGGER IF EXISTS increase_folder_count_on_insert"); + db.execSQL("DROP TRIGGER IF EXISTS folder_delete_notes_on_delete"); + db.execSQL("DROP TRIGGER IF EXISTS folder_move_notes_on_trash"); + + db.execSQL(NOTE_INCREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER); + db.execSQL(NOTE_DECREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER); + db.execSQL(NOTE_DECREASE_FOLDER_COUNT_ON_DELETE_TRIGGER); + db.execSQL(NOTE_DELETE_DATA_ON_DELETE_TRIGGER); + db.execSQL(NOTE_INCREASE_FOLDER_COUNT_ON_INSERT_TRIGGER); + db.execSQL(FOLDER_DELETE_NOTES_ON_DELETE_TRIGGER); + db.execSQL(FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER); + }//数据库操作的API,重新创建 + + private void createSystemFolder(SQLiteDatabase db) { + ContentValues values = new ContentValues(); + + /** + * call record foler for call notes + */ + values.put(NoteColumns.ID, Notes.ID_CALL_RECORD_FOLDER); + values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); + db.insert(TABLE.NOTE, null, values); + + /** + * root folder which is default folder + */ + values.clear(); + values.put(NoteColumns.ID, Notes.ID_ROOT_FOLDER); + values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); + db.insert(TABLE.NOTE, null, values); + + /** + * temporary folder which is used for moving note + */ + values.clear(); + values.put(NoteColumns.ID, Notes.ID_TEMPARAY_FOLDER); + values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); + db.insert(TABLE.NOTE, null, values); + + /** + * create trash folder + */ + values.clear(); + values.put(NoteColumns.ID, Notes.ID_TRASH_FOLER); + values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); + db.insert(TABLE.NOTE, null, values); + }//创建系统文件夹 + + public void createDataTable(SQLiteDatabase db) { + db.execSQL(CREATE_DATA_TABLE_SQL); + reCreateDataTableTriggers(db); + db.execSQL(CREATE_DATA_NOTE_ID_INDEX_SQL); + Log.d(TAG, "data table has been created"); + }//创建数据表格 + + private void reCreateDataTableTriggers(SQLiteDatabase db) { + db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_insert"); + db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_update"); + db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_delete"); + + db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_INSERT_TRIGGER); + db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_UPDATE_TRIGGER); + db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_DELETE_TRIGGER); + }//类似于recreatenotetable,重新创建触发器 + + static synchronized NotesDatabaseHelper getInstance(Context context) { + if (mInstance == null) { + mInstance = new NotesDatabaseHelper(context); + } + return mInstance; + }//sync同步,同一时刻只有一个线程执行 + + @Override + public void onCreate(SQLiteDatabase db) { + createNoteTable(db); + createDataTable(db); + }//创建Note Data两个表格 + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + boolean reCreateTriggers = false; + boolean skipV2 = false; + + if (oldVersion == 1) { + upgradeToV2(db); + skipV2 = true; // this upgrade including the upgrade from v2 to v3 + oldVersion++; + } + + if (oldVersion == 2 && !skipV2) { + upgradeToV3(db); + reCreateTriggers = true; + oldVersion++; + } + + if (oldVersion == 3) { + upgradeToV4(db); + oldVersion++; + } + + if (reCreateTriggers) { + reCreateNoteTableTriggers(db); + reCreateDataTableTriggers(db); + } + + if (oldVersion != newVersion) { + throw new IllegalStateException("Upgrade notes database to version " + newVersion + + "fails"); + } + }//数据库版本更新 + + private void upgradeToV2(SQLiteDatabase db) { + db.execSQL("DROP TABLE IF EXISTS " + TABLE.NOTE); + db.execSQL("DROP TABLE IF EXISTS " + TABLE.DATA); + createNoteTable(db); + createDataTable(db); + }//更新到V2 + + private void upgradeToV3(SQLiteDatabase db) { + // drop unused triggers + db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_insert"); + db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_delete"); + db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_update"); + // add a column for gtask id + db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.GTASK_ID + + " TEXT NOT NULL DEFAULT ''"); + // add a trash system folder + ContentValues values = new ContentValues(); + values.put(NoteColumns.ID, Notes.ID_TRASH_FOLER); + values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); + db.insert(TABLE.NOTE, null, values); + }//更新到V3 + + private void upgradeToV4(SQLiteDatabase db) { + db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.VERSION + + " INTEGER NOT NULL DEFAULT 0"); + }//更新到V4 +} diff --git a/src/main/java/net/micode/notes/data/NotesProvider.java b/src/main/java/net/micode/notes/data/NotesProvider.java new file mode 100644 index 0000000..6897999 --- /dev/null +++ b/src/main/java/net/micode/notes/data/NotesProvider.java @@ -0,0 +1,320 @@ +package net.micode.notes.data; + +import android.app.SearchManager; +import android.content.ContentProvider; +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.Intent; +import android.content.UriMatcher; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.net.Uri; +import android.text.TextUtils; +import android.util.Log; + +import net.micode.notes.R; +import net.micode.notes.data.Notes.DataColumns; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.data.NotesDatabaseHelper.TABLE; +//为存储和获取数据提供接口。可以在不同的应用程序之间共享数据 +//ContentProvider提供的方法 +//query:查询 +//insert:插入 +//update:更新 +//delete:删除 +//getType:得到数据类型 +public class NotesProvider extends ContentProvider { + // UriMatcher用于匹配Uri + private static final UriMatcher mMatcher; + + private NotesDatabaseHelper mHelper; + + private static final String TAG = "NotesProvider"; + + private static final int URI_NOTE = 1; + private static final int URI_NOTE_ITEM = 2; + private static final int URI_DATA = 3; + private static final int URI_DATA_ITEM = 4; + + private static final int URI_SEARCH = 5; + private static final int URI_SEARCH_SUGGEST = 6; + + static { + // 创建UriMatcher时,调用UriMatcher(UriMatcher.NO_MATCH)表示不匹配任何路径的返回码 + mMatcher = new UriMatcher(UriMatcher.NO_MATCH); + // 把需要匹配Uri路径全部给注册上 + mMatcher.addURI(Notes.AUTHORITY, "note", URI_NOTE); + mMatcher.addURI(Notes.AUTHORITY, "note/#", URI_NOTE_ITEM); + mMatcher.addURI(Notes.AUTHORITY, "data", URI_DATA); + mMatcher.addURI(Notes.AUTHORITY, "data/#", URI_DATA_ITEM); + mMatcher.addURI(Notes.AUTHORITY, "search", URI_SEARCH); + mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, URI_SEARCH_SUGGEST); + mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", URI_SEARCH_SUGGEST); + } + + /** + * x'0A' represents the '\n' character in sqlite. For title and content in the search result, + * we will trim '\n' and white space in order to show more information. + */ + // 声明 NOTES_SEARCH_PROJECTION + private static final String NOTES_SEARCH_PROJECTION = NoteColumns.ID + "," + + NoteColumns.ID + " AS " + SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA + "," + + "TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_1 + "," + + "TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_2 + "," + + R.drawable.search_result + " AS " + SearchManager.SUGGEST_COLUMN_ICON_1 + "," + + "'" + Intent.ACTION_VIEW + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_ACTION + "," + + "'" + Notes.TextNote.CONTENT_TYPE + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_DATA; + // 声明NOTES_SNIPPET_SEARCH_QUERY + private static String NOTES_SNIPPET_SEARCH_QUERY = "SELECT " + NOTES_SEARCH_PROJECTION + + " FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.SNIPPET + " LIKE ?" + + " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + + " AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE; + + @Override + // Context只有在onCreate()中才被初始化 + // 对mHelper进行实例化 + public boolean onCreate() { + mHelper = NotesDatabaseHelper.getInstance(getContext()); + return true; + } + + @Override + // 查询uri在数据库中对应的位置 + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, + String sortOrder) { + Cursor c = null; + // 获取可读数据库 + SQLiteDatabase db = mHelper.getReadableDatabase(); + String id = null; + // 匹配查找uri + switch (mMatcher.match(uri)) { + // 对于不同的匹配值,在数据库中查找相应的条目 + case URI_NOTE: + c = db.query(TABLE.NOTE, projection, selection, selectionArgs, null, null, + sortOrder); + break; + case URI_NOTE_ITEM: + id = uri.getPathSegments().get(1); + c = db.query(TABLE.NOTE, projection, NoteColumns.ID + "=" + id + + parseSelection(selection), selectionArgs, null, null, sortOrder); + break; + case URI_DATA: + c = db.query(TABLE.DATA, projection, selection, selectionArgs, null, null, + sortOrder); + break; + case URI_DATA_ITEM: + id = uri.getPathSegments().get(1); + c = db.query(TABLE.DATA, projection, DataColumns.ID + "=" + id + + parseSelection(selection), selectionArgs, null, null, sortOrder); + break; + case URI_SEARCH: + case URI_SEARCH_SUGGEST: + if (sortOrder != null || projection != null) { + // 不合法的参数异常 + throw new IllegalArgumentException( + "do not specify sortOrder, selection, selectionArgs, or projection" + "with this query"); + } + + String searchString = null; + if (mMatcher.match(uri) == URI_SEARCH_SUGGEST) { + if (uri.getPathSegments().size() > 1) { + // getPathSegments()方法得到一个String的List, + // 在uri.getPathSegments().get(1)为第2个元素 + searchString = uri.getPathSegments().get(1); + } + } else { + searchString = uri.getQueryParameter("pattern"); + } + + if (TextUtils.isEmpty(searchString)) { + return null; + } + + try { + searchString = String.format("%%%s%%", searchString); + c = db.rawQuery(NOTES_SNIPPET_SEARCH_QUERY, + new String[] { searchString }); + } catch (IllegalStateException ex) { + Log.e(TAG, "got exception: " + ex.toString()); + } + break; + default: + // 抛出异常 + throw new IllegalArgumentException("Unknown URI " + uri); + } + if (c != null) { + c.setNotificationUri(getContext().getContentResolver(), uri); + } + return c; + } + + @Override + // 插入一个uri + public Uri insert(Uri uri, ContentValues values) { + // 获得可写的数据库 + SQLiteDatabase db = mHelper.getWritableDatabase(); + long dataId = 0, noteId = 0, insertedId = 0; + switch (mMatcher.match(uri)) { + // 新增一个条目 + case URI_NOTE: + insertedId = noteId = db.insert(TABLE.NOTE, null, values); + break; + // 如果存在,查找NOTE_ID + case URI_DATA: + if (values.containsKey(DataColumns.NOTE_ID)) { + noteId = values.getAsLong(DataColumns.NOTE_ID); + } else { + Log.d(TAG, "Wrong data format without note id:" + values.toString()); + } + insertedId = dataId = db.insert(TABLE.DATA, null, values); + break; + default: + throw new IllegalArgumentException("Unknown URI " + uri); + } + // Notify the note uri + // notifyChange获得一个ContextResolver对象并且更新里面的内容 + if (noteId > 0) { + getContext().getContentResolver().notifyChange( + ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null); + } + + // Notify the data uri + if (dataId > 0) { + getContext().getContentResolver().notifyChange( + ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), null); + } + + // 返回插入的uri的路径 + return ContentUris.withAppendedId(uri, insertedId); + } + + @Override + // 删除一个uri + public int delete(Uri uri, String selection, String[] selectionArgs) { + //Uri代表要操作的数据,Android上可用的每种资源 -包括 图像、视频片段、音频资源等都可以用Uri来表示。 + int count = 0; + String id = null; + // 获得可写的数据库 + SQLiteDatabase db = mHelper.getWritableDatabase(); + boolean deleteData = false; + switch (mMatcher.match(uri)) { + case URI_NOTE: + selection = "(" + selection + ") AND " + NoteColumns.ID + ">0 "; + count = db.delete(TABLE.NOTE, selection, selectionArgs); + break; + case URI_NOTE_ITEM: + id = uri.getPathSegments().get(1); + /** + * ID that smaller than 0 is system folder which is not allowed to + * trash + */ + long noteId = Long.valueOf(id); + if (noteId <= 0) { + break; + } + count = db.delete(TABLE.NOTE, + NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs); + break; + case URI_DATA: + count = db.delete(TABLE.DATA, selection, selectionArgs); + deleteData = true; + break; + case URI_DATA_ITEM: + id = uri.getPathSegments().get(1); + count = db.delete(TABLE.DATA, + DataColumns.ID + "=" + id + parseSelection(selection), selectionArgs); + deleteData = true; + break; + default: + throw new IllegalArgumentException("Unknown URI " + uri); + } + if (count > 0) { + if (deleteData) { + getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null); + } + getContext().getContentResolver().notifyChange(uri, null); + } + return count; + } + + @Override + // 更新一个uri + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + int count = 0; + String id = null; + SQLiteDatabase db = mHelper.getWritableDatabase(); + boolean updateData = false; + switch (mMatcher.match(uri)) { + case URI_NOTE: + increaseNoteVersion(-1, selection, selectionArgs); + count = db.update(TABLE.NOTE, values, selection, selectionArgs); + break; + case URI_NOTE_ITEM: + id = uri.getPathSegments().get(1); + increaseNoteVersion(Long.valueOf(id), selection, selectionArgs); + count = db.update(TABLE.NOTE, values, NoteColumns.ID + "=" + id + + parseSelection(selection), selectionArgs); + break; + case URI_DATA: + count = db.update(TABLE.DATA, values, selection, selectionArgs); + updateData = true; + break; + case URI_DATA_ITEM: + id = uri.getPathSegments().get(1); + count = db.update(TABLE.DATA, values, DataColumns.ID + "=" + id + + parseSelection(selection), selectionArgs); + updateData = true; + break; + default: + throw new IllegalArgumentException("Unknown URI " + uri); + } + + if (count > 0) { + if (updateData) { + getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null); + } + getContext().getContentResolver().notifyChange(uri, null); + } + return count; + } + + // 将字符串解析成规定格式 + private String parseSelection(String selection) { + return (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""); + } + + //增加一个noteVersion + private void increaseNoteVersion(long id, String selection, String[] selectionArgs) { + StringBuilder sql = new StringBuilder(120); + sql.append("UPDATE "); + sql.append(TABLE.NOTE); + sql.append(" SET "); + sql.append(NoteColumns.VERSION); + sql.append("=" + NoteColumns.VERSION + "+1 "); + + if (id > 0 || !TextUtils.isEmpty(selection)) { + sql.append(" WHERE "); + } + if (id > 0) { + sql.append(NoteColumns.ID + "=" + String.valueOf(id)); + } + if (!TextUtils.isEmpty(selection)) { + String selectString = id > 0 ? parseSelection(selection) : selection; + for (String args : selectionArgs) { + selectString = selectString.replaceFirst("\\?", args); + } + sql.append(selectString); + } + + // execSQL()方法可以执行insert、delete、update和CREATE TABLE之类有更改行为的SQL语句 + mHelper.getWritableDatabase().execSQL(sql.toString()); + } + + @Override + public String getType(Uri uri) { + // TODO Auto-generated method stub + return null; + } + +} \ No newline at end of file diff --git a/src/main/java/net/micode/notes/gtask/data/MetaData.java b/src/main/java/net/micode/notes/gtask/data/MetaData.java new file mode 100644 index 0000000..3a2050b --- /dev/null +++ b/src/main/java/net/micode/notes/gtask/data/MetaData.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.gtask.data; + +import android.database.Cursor; +import android.util.Log; + +import net.micode.notes.tool.GTaskStringUtils; + +import org.json.JSONException; +import org.json.JSONObject; + + +public class MetaData extends Task { + private final static String TAG = MetaData.class.getSimpleName(); + + private String mRelatedGid = null; + + public void setMeta(String gid, JSONObject metaInfo) { + try { + metaInfo.put(GTaskStringUtils.META_HEAD_GTASK_ID, gid); + } catch (JSONException e) { + Log.e(TAG, "failed to put related gid"); + } + setNotes(metaInfo.toString()); + setName(GTaskStringUtils.META_NOTE_NAME); + } + + public String getRelatedGid() { + return mRelatedGid; + } + + @Override + public boolean isWorthSaving() { + return getNotes() != null; + } + + @Override + public void setContentByRemoteJSON(JSONObject js) { + super.setContentByRemoteJSON(js); + if (getNotes() != null) { + try { + JSONObject metaInfo = new JSONObject(getNotes().trim()); + mRelatedGid = metaInfo.getString(GTaskStringUtils.META_HEAD_GTASK_ID); + } catch (JSONException e) { + Log.w(TAG, "failed to get related gid"); + mRelatedGid = null; + } + } + } + + @Override + public void setContentByLocalJSON(JSONObject js) { + // this function should not be called + throw new IllegalAccessError("MetaData:setContentByLocalJSON should not be called"); + } + + @Override + public JSONObject getLocalJSONFromContent() { + throw new IllegalAccessError("MetaData:getLocalJSONFromContent should not be called"); + } + + @Override + public int getSyncAction(Cursor c) { + throw new IllegalAccessError("MetaData:getSyncAction should not be called"); + } + +} diff --git a/src/main/java/net/micode/notes/gtask/data/Node.java b/src/main/java/net/micode/notes/gtask/data/Node.java new file mode 100644 index 0000000..63950e0 --- /dev/null +++ b/src/main/java/net/micode/notes/gtask/data/Node.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.gtask.data; + +import android.database.Cursor; + +import org.json.JSONObject; + +public abstract class Node { + public static final int SYNC_ACTION_NONE = 0; + + public static final int SYNC_ACTION_ADD_REMOTE = 1; + + public static final int SYNC_ACTION_ADD_LOCAL = 2; + + public static final int SYNC_ACTION_DEL_REMOTE = 3; + + public static final int SYNC_ACTION_DEL_LOCAL = 4; + + public static final int SYNC_ACTION_UPDATE_REMOTE = 5; + + public static final int SYNC_ACTION_UPDATE_LOCAL = 6; + + public static final int SYNC_ACTION_UPDATE_CONFLICT = 7; + + public static final int SYNC_ACTION_ERROR = 8; + + private String mGid; + + private String mName; + + private long mLastModified; + + private boolean mDeleted; + + public Node() { + mGid = null; + mName = ""; + mLastModified = 0; + mDeleted = false; + } + + public abstract JSONObject getCreateAction(int actionId); + + public abstract JSONObject getUpdateAction(int actionId); + + public abstract void setContentByRemoteJSON(JSONObject js); + + public abstract void setContentByLocalJSON(JSONObject js); + + public abstract JSONObject getLocalJSONFromContent(); + + public abstract int getSyncAction(Cursor c); + + public void setGid(String gid) { + this.mGid = gid; + } + + public void setName(String name) { + this.mName = name; + } + + public void setLastModified(long lastModified) { + this.mLastModified = lastModified; + } + + public void setDeleted(boolean deleted) { + this.mDeleted = deleted; + } + + public String getGid() { + return this.mGid; + } + + public String getName() { + return this.mName; + } + + public long getLastModified() { + return this.mLastModified; + } + + public boolean getDeleted() { + return this.mDeleted; + } + +} diff --git a/src/main/java/net/micode/notes/gtask/data/SqlData.java b/src/main/java/net/micode/notes/gtask/data/SqlData.java new file mode 100644 index 0000000..d3ec3be --- /dev/null +++ b/src/main/java/net/micode/notes/gtask/data/SqlData.java @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.gtask.data; + +import android.content.ContentResolver; +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import android.util.Log; + +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.DataColumns; +import net.micode.notes.data.Notes.DataConstants; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.data.NotesDatabaseHelper.TABLE; +import net.micode.notes.gtask.exception.ActionFailureException; + +import org.json.JSONException; +import org.json.JSONObject; + + +public class SqlData { + private static final String TAG = SqlData.class.getSimpleName(); + + private static final int INVALID_ID = -99999; + + public static final String[] PROJECTION_DATA = new String[] { + DataColumns.ID, DataColumns.MIME_TYPE, DataColumns.CONTENT, DataColumns.DATA1, + DataColumns.DATA3 + }; + + public static final int DATA_ID_COLUMN = 0; + + public static final int DATA_MIME_TYPE_COLUMN = 1; + + public static final int DATA_CONTENT_COLUMN = 2; + + public static final int DATA_CONTENT_DATA_1_COLUMN = 3; + + public static final int DATA_CONTENT_DATA_3_COLUMN = 4; + + private ContentResolver mContentResolver; + + private boolean mIsCreate; + + private long mDataId; + + private String mDataMimeType; + + private String mDataContent; + + private long mDataContentData1; + + private String mDataContentData3; + + private ContentValues mDiffDataValues; + + public SqlData(Context context) { + mContentResolver = context.getContentResolver(); + mIsCreate = true; + mDataId = INVALID_ID; + mDataMimeType = DataConstants.NOTE; + mDataContent = ""; + mDataContentData1 = 0; + mDataContentData3 = ""; + mDiffDataValues = new ContentValues(); + } + + public SqlData(Context context, Cursor c) { + mContentResolver = context.getContentResolver(); + mIsCreate = false; + loadFromCursor(c); + mDiffDataValues = new ContentValues(); + } + + private void loadFromCursor(Cursor c) { + mDataId = c.getLong(DATA_ID_COLUMN); + mDataMimeType = c.getString(DATA_MIME_TYPE_COLUMN); + mDataContent = c.getString(DATA_CONTENT_COLUMN); + mDataContentData1 = c.getLong(DATA_CONTENT_DATA_1_COLUMN); + mDataContentData3 = c.getString(DATA_CONTENT_DATA_3_COLUMN); + } + + public void setContent(JSONObject js) throws JSONException { + long dataId = js.has(DataColumns.ID) ? js.getLong(DataColumns.ID) : INVALID_ID; + if (mIsCreate || mDataId != dataId) { + mDiffDataValues.put(DataColumns.ID, dataId); + } + mDataId = dataId; + + String dataMimeType = js.has(DataColumns.MIME_TYPE) ? js.getString(DataColumns.MIME_TYPE) + : DataConstants.NOTE; + if (mIsCreate || !mDataMimeType.equals(dataMimeType)) { + mDiffDataValues.put(DataColumns.MIME_TYPE, dataMimeType); + } + mDataMimeType = dataMimeType; + + String dataContent = js.has(DataColumns.CONTENT) ? js.getString(DataColumns.CONTENT) : ""; + if (mIsCreate || !mDataContent.equals(dataContent)) { + mDiffDataValues.put(DataColumns.CONTENT, dataContent); + } + mDataContent = dataContent; + + long dataContentData1 = js.has(DataColumns.DATA1) ? js.getLong(DataColumns.DATA1) : 0; + if (mIsCreate || mDataContentData1 != dataContentData1) { + mDiffDataValues.put(DataColumns.DATA1, dataContentData1); + } + mDataContentData1 = dataContentData1; + + String dataContentData3 = js.has(DataColumns.DATA3) ? js.getString(DataColumns.DATA3) : ""; + if (mIsCreate || !mDataContentData3.equals(dataContentData3)) { + mDiffDataValues.put(DataColumns.DATA3, dataContentData3); + } + mDataContentData3 = dataContentData3; + } + + public JSONObject getContent() throws JSONException { + if (mIsCreate) { + Log.e(TAG, "it seems that we haven't created this in database yet"); + return null; + } + JSONObject js = new JSONObject(); + js.put(DataColumns.ID, mDataId); + js.put(DataColumns.MIME_TYPE, mDataMimeType); + js.put(DataColumns.CONTENT, mDataContent); + js.put(DataColumns.DATA1, mDataContentData1); + js.put(DataColumns.DATA3, mDataContentData3); + return js; + } + + public void commit(long noteId, boolean validateVersion, long version) { + + if (mIsCreate) { + if (mDataId == INVALID_ID && mDiffDataValues.containsKey(DataColumns.ID)) { + mDiffDataValues.remove(DataColumns.ID); + } + + mDiffDataValues.put(DataColumns.NOTE_ID, noteId); + Uri uri = mContentResolver.insert(Notes.CONTENT_DATA_URI, mDiffDataValues); + try { + mDataId = Long.valueOf(uri.getPathSegments().get(1)); + } catch (NumberFormatException e) { + Log.e(TAG, "Get note id error :" + e.toString()); + throw new ActionFailureException("create note failed"); + } + } else { + if (mDiffDataValues.size() > 0) { + int result = 0; + if (!validateVersion) { + result = mContentResolver.update(ContentUris.withAppendedId( + Notes.CONTENT_DATA_URI, mDataId), mDiffDataValues, null, null); + } else { + result = mContentResolver.update(ContentUris.withAppendedId( + Notes.CONTENT_DATA_URI, mDataId), mDiffDataValues, + " ? in (SELECT " + NoteColumns.ID + " FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.VERSION + "=?)", new String[] { + String.valueOf(noteId), String.valueOf(version) + }); + } + if (result == 0) { + Log.w(TAG, "there is no update. maybe user updates note when syncing"); + } + } + } + + mDiffDataValues.clear(); + mIsCreate = false; + } + + public long getId() { + return mDataId; + } +} diff --git a/src/main/java/net/micode/notes/gtask/data/SqlNote.java b/src/main/java/net/micode/notes/gtask/data/SqlNote.java new file mode 100644 index 0000000..79a4095 --- /dev/null +++ b/src/main/java/net/micode/notes/gtask/data/SqlNote.java @@ -0,0 +1,505 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.gtask.data; + +import android.appwidget.AppWidgetManager; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import android.util.Log; + +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.DataColumns; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.gtask.exception.ActionFailureException; +import net.micode.notes.tool.GTaskStringUtils; +import net.micode.notes.tool.ResourceParser; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; + + +public class SqlNote { + private static final String TAG = SqlNote.class.getSimpleName(); + + private static final int INVALID_ID = -99999; + + public static final String[] PROJECTION_NOTE = new String[] { + NoteColumns.ID, NoteColumns.ALERTED_DATE, NoteColumns.BG_COLOR_ID, + NoteColumns.CREATED_DATE, NoteColumns.HAS_ATTACHMENT, NoteColumns.MODIFIED_DATE, + NoteColumns.NOTES_COUNT, NoteColumns.PARENT_ID, NoteColumns.SNIPPET, NoteColumns.TYPE, + NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE, NoteColumns.SYNC_ID, + NoteColumns.LOCAL_MODIFIED, NoteColumns.ORIGIN_PARENT_ID, NoteColumns.GTASK_ID, + NoteColumns.VERSION + }; + + public static final int ID_COLUMN = 0; + + public static final int ALERTED_DATE_COLUMN = 1; + + public static final int BG_COLOR_ID_COLUMN = 2; + + public static final int CREATED_DATE_COLUMN = 3; + + public static final int HAS_ATTACHMENT_COLUMN = 4; + + public static final int MODIFIED_DATE_COLUMN = 5; + + public static final int NOTES_COUNT_COLUMN = 6; + + public static final int PARENT_ID_COLUMN = 7; + + public static final int SNIPPET_COLUMN = 8; + + public static final int TYPE_COLUMN = 9; + + public static final int WIDGET_ID_COLUMN = 10; + + public static final int WIDGET_TYPE_COLUMN = 11; + + public static final int SYNC_ID_COLUMN = 12; + + public static final int LOCAL_MODIFIED_COLUMN = 13; + + public static final int ORIGIN_PARENT_ID_COLUMN = 14; + + public static final int GTASK_ID_COLUMN = 15; + + public static final int VERSION_COLUMN = 16; + + private Context mContext; + + private ContentResolver mContentResolver; + + private boolean mIsCreate; + + private long mId; + + private long mAlertDate; + + private int mBgColorId; + + private long mCreatedDate; + + private int mHasAttachment; + + private long mModifiedDate; + + private long mParentId; + + private String mSnippet; + + private int mType; + + private int mWidgetId; + + private int mWidgetType; + + private long mOriginParent; + + private long mVersion; + + private ContentValues mDiffNoteValues; + + private ArrayList mDataList; + + public SqlNote(Context context) { + mContext = context; + mContentResolver = context.getContentResolver(); + mIsCreate = true; + mId = INVALID_ID; + mAlertDate = 0; + mBgColorId = ResourceParser.getDefaultBgId(context); + mCreatedDate = System.currentTimeMillis(); + mHasAttachment = 0; + mModifiedDate = System.currentTimeMillis(); + mParentId = 0; + mSnippet = ""; + mType = Notes.TYPE_NOTE; + mWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID; + mWidgetType = Notes.TYPE_WIDGET_INVALIDE; + mOriginParent = 0; + mVersion = 0; + mDiffNoteValues = new ContentValues(); + mDataList = new ArrayList(); + } + + public SqlNote(Context context, Cursor c) { + mContext = context; + mContentResolver = context.getContentResolver(); + mIsCreate = false; + loadFromCursor(c); + mDataList = new ArrayList(); + if (mType == Notes.TYPE_NOTE) + loadDataContent(); + mDiffNoteValues = new ContentValues(); + } + + public SqlNote(Context context, long id) { + mContext = context; + mContentResolver = context.getContentResolver(); + mIsCreate = false; + loadFromCursor(id); + mDataList = new ArrayList(); + if (mType == Notes.TYPE_NOTE) + loadDataContent(); + mDiffNoteValues = new ContentValues(); + + } + + private void loadFromCursor(long id) { + Cursor c = null; + try { + c = mContentResolver.query(Notes.CONTENT_NOTE_URI, PROJECTION_NOTE, "(_id=?)", + new String[] { + String.valueOf(id) + }, null); + if (c != null) { + c.moveToNext(); + loadFromCursor(c); + } else { + Log.w(TAG, "loadFromCursor: cursor = null"); + } + } finally { + if (c != null) + c.close(); + } + } + + private void loadFromCursor(Cursor c) { + mId = c.getLong(ID_COLUMN); + mAlertDate = c.getLong(ALERTED_DATE_COLUMN); + mBgColorId = c.getInt(BG_COLOR_ID_COLUMN); + mCreatedDate = c.getLong(CREATED_DATE_COLUMN); + mHasAttachment = c.getInt(HAS_ATTACHMENT_COLUMN); + mModifiedDate = c.getLong(MODIFIED_DATE_COLUMN); + mParentId = c.getLong(PARENT_ID_COLUMN); + mSnippet = c.getString(SNIPPET_COLUMN); + mType = c.getInt(TYPE_COLUMN); + mWidgetId = c.getInt(WIDGET_ID_COLUMN); + mWidgetType = c.getInt(WIDGET_TYPE_COLUMN); + mVersion = c.getLong(VERSION_COLUMN); + } + + private void loadDataContent() { + Cursor c = null; + mDataList.clear(); + try { + c = mContentResolver.query(Notes.CONTENT_DATA_URI, SqlData.PROJECTION_DATA, + "(note_id=?)", new String[] { + String.valueOf(mId) + }, null); + if (c != null) { + if (c.getCount() == 0) { + Log.w(TAG, "it seems that the note has not data"); + return; + } + while (c.moveToNext()) { + SqlData data = new SqlData(mContext, c); + mDataList.add(data); + } + } else { + Log.w(TAG, "loadDataContent: cursor = null"); + } + } finally { + if (c != null) + c.close(); + } + } + + public boolean setContent(JSONObject js) { + try { + JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); + if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_SYSTEM) { + Log.w(TAG, "cannot set system folder"); + } else if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_FOLDER) { + // for folder we can only update the snnipet and type + String snippet = note.has(NoteColumns.SNIPPET) ? note + .getString(NoteColumns.SNIPPET) : ""; + if (mIsCreate || !mSnippet.equals(snippet)) { + mDiffNoteValues.put(NoteColumns.SNIPPET, snippet); + } + mSnippet = snippet; + + int type = note.has(NoteColumns.TYPE) ? note.getInt(NoteColumns.TYPE) + : Notes.TYPE_NOTE; + if (mIsCreate || mType != type) { + mDiffNoteValues.put(NoteColumns.TYPE, type); + } + mType = type; + } else if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_NOTE) { + JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA); + long id = note.has(NoteColumns.ID) ? note.getLong(NoteColumns.ID) : INVALID_ID; + if (mIsCreate || mId != id) { + mDiffNoteValues.put(NoteColumns.ID, id); + } + mId = id; + + long alertDate = note.has(NoteColumns.ALERTED_DATE) ? note + .getLong(NoteColumns.ALERTED_DATE) : 0; + if (mIsCreate || mAlertDate != alertDate) { + mDiffNoteValues.put(NoteColumns.ALERTED_DATE, alertDate); + } + mAlertDate = alertDate; + + int bgColorId = note.has(NoteColumns.BG_COLOR_ID) ? note + .getInt(NoteColumns.BG_COLOR_ID) : ResourceParser.getDefaultBgId(mContext); + if (mIsCreate || mBgColorId != bgColorId) { + mDiffNoteValues.put(NoteColumns.BG_COLOR_ID, bgColorId); + } + mBgColorId = bgColorId; + + long createDate = note.has(NoteColumns.CREATED_DATE) ? note + .getLong(NoteColumns.CREATED_DATE) : System.currentTimeMillis(); + if (mIsCreate || mCreatedDate != createDate) { + mDiffNoteValues.put(NoteColumns.CREATED_DATE, createDate); + } + mCreatedDate = createDate; + + int hasAttachment = note.has(NoteColumns.HAS_ATTACHMENT) ? note + .getInt(NoteColumns.HAS_ATTACHMENT) : 0; + if (mIsCreate || mHasAttachment != hasAttachment) { + mDiffNoteValues.put(NoteColumns.HAS_ATTACHMENT, hasAttachment); + } + mHasAttachment = hasAttachment; + + long modifiedDate = note.has(NoteColumns.MODIFIED_DATE) ? note + .getLong(NoteColumns.MODIFIED_DATE) : System.currentTimeMillis(); + if (mIsCreate || mModifiedDate != modifiedDate) { + mDiffNoteValues.put(NoteColumns.MODIFIED_DATE, modifiedDate); + } + mModifiedDate = modifiedDate; + + long parentId = note.has(NoteColumns.PARENT_ID) ? note + .getLong(NoteColumns.PARENT_ID) : 0; + if (mIsCreate || mParentId != parentId) { + mDiffNoteValues.put(NoteColumns.PARENT_ID, parentId); + } + mParentId = parentId; + + String snippet = note.has(NoteColumns.SNIPPET) ? note + .getString(NoteColumns.SNIPPET) : ""; + if (mIsCreate || !mSnippet.equals(snippet)) { + mDiffNoteValues.put(NoteColumns.SNIPPET, snippet); + } + mSnippet = snippet; + + int type = note.has(NoteColumns.TYPE) ? note.getInt(NoteColumns.TYPE) + : Notes.TYPE_NOTE; + if (mIsCreate || mType != type) { + mDiffNoteValues.put(NoteColumns.TYPE, type); + } + mType = type; + + int widgetId = note.has(NoteColumns.WIDGET_ID) ? note.getInt(NoteColumns.WIDGET_ID) + : AppWidgetManager.INVALID_APPWIDGET_ID; + if (mIsCreate || mWidgetId != widgetId) { + mDiffNoteValues.put(NoteColumns.WIDGET_ID, widgetId); + } + mWidgetId = widgetId; + + int widgetType = note.has(NoteColumns.WIDGET_TYPE) ? note + .getInt(NoteColumns.WIDGET_TYPE) : Notes.TYPE_WIDGET_INVALIDE; + if (mIsCreate || mWidgetType != widgetType) { + mDiffNoteValues.put(NoteColumns.WIDGET_TYPE, widgetType); + } + mWidgetType = widgetType; + + long originParent = note.has(NoteColumns.ORIGIN_PARENT_ID) ? note + .getLong(NoteColumns.ORIGIN_PARENT_ID) : 0; + if (mIsCreate || mOriginParent != originParent) { + mDiffNoteValues.put(NoteColumns.ORIGIN_PARENT_ID, originParent); + } + mOriginParent = originParent; + + for (int i = 0; i < dataArray.length(); i++) { + JSONObject data = dataArray.getJSONObject(i); + SqlData sqlData = null; + if (data.has(DataColumns.ID)) { + long dataId = data.getLong(DataColumns.ID); + for (SqlData temp : mDataList) { + if (dataId == temp.getId()) { + sqlData = temp; + } + } + } + + if (sqlData == null) { + sqlData = new SqlData(mContext); + mDataList.add(sqlData); + } + + sqlData.setContent(data); + } + } + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + return false; + } + return true; + } + + public JSONObject getContent() { + try { + JSONObject js = new JSONObject(); + + if (mIsCreate) { + Log.e(TAG, "it seems that we haven't created this in database yet"); + return null; + } + + JSONObject note = new JSONObject(); + if (mType == Notes.TYPE_NOTE) { + note.put(NoteColumns.ID, mId); + note.put(NoteColumns.ALERTED_DATE, mAlertDate); + note.put(NoteColumns.BG_COLOR_ID, mBgColorId); + note.put(NoteColumns.CREATED_DATE, mCreatedDate); + note.put(NoteColumns.HAS_ATTACHMENT, mHasAttachment); + note.put(NoteColumns.MODIFIED_DATE, mModifiedDate); + note.put(NoteColumns.PARENT_ID, mParentId); + note.put(NoteColumns.SNIPPET, mSnippet); + note.put(NoteColumns.TYPE, mType); + note.put(NoteColumns.WIDGET_ID, mWidgetId); + note.put(NoteColumns.WIDGET_TYPE, mWidgetType); + note.put(NoteColumns.ORIGIN_PARENT_ID, mOriginParent); + js.put(GTaskStringUtils.META_HEAD_NOTE, note); + + JSONArray dataArray = new JSONArray(); + for (SqlData sqlData : mDataList) { + JSONObject data = sqlData.getContent(); + if (data != null) { + dataArray.put(data); + } + } + js.put(GTaskStringUtils.META_HEAD_DATA, dataArray); + } else if (mType == Notes.TYPE_FOLDER || mType == Notes.TYPE_SYSTEM) { + note.put(NoteColumns.ID, mId); + note.put(NoteColumns.TYPE, mType); + note.put(NoteColumns.SNIPPET, mSnippet); + js.put(GTaskStringUtils.META_HEAD_NOTE, note); + } + + return js; + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + } + return null; + } + + public void setParentId(long id) { + mParentId = id; + mDiffNoteValues.put(NoteColumns.PARENT_ID, id); + } + + public void setGtaskId(String gid) { + mDiffNoteValues.put(NoteColumns.GTASK_ID, gid); + } + + public void setSyncId(long syncId) { + mDiffNoteValues.put(NoteColumns.SYNC_ID, syncId); + } + + public void resetLocalModified() { + mDiffNoteValues.put(NoteColumns.LOCAL_MODIFIED, 0); + } + + public long getId() { + return mId; + } + + public long getParentId() { + return mParentId; + } + + public String getSnippet() { + return mSnippet; + } + + public boolean isNoteType() { + return mType == Notes.TYPE_NOTE; + } + + public void commit(boolean validateVersion) { + if (mIsCreate) { + if (mId == INVALID_ID && mDiffNoteValues.containsKey(NoteColumns.ID)) { + mDiffNoteValues.remove(NoteColumns.ID); + } + + Uri uri = mContentResolver.insert(Notes.CONTENT_NOTE_URI, mDiffNoteValues); + try { + mId = Long.valueOf(uri.getPathSegments().get(1)); + } catch (NumberFormatException e) { + Log.e(TAG, "Get note id error :" + e.toString()); + throw new ActionFailureException("create note failed"); + } + if (mId == 0) { + throw new IllegalStateException("Create thread id failed"); + } + + if (mType == Notes.TYPE_NOTE) { + for (SqlData sqlData : mDataList) { + sqlData.commit(mId, false, -1); + } + } + } else { + if (mId <= 0 && mId != Notes.ID_ROOT_FOLDER && mId != Notes.ID_CALL_RECORD_FOLDER) { + Log.e(TAG, "No such note"); + throw new IllegalStateException("Try to update note with invalid id"); + } + if (mDiffNoteValues.size() > 0) { + mVersion ++; + int result = 0; + if (!validateVersion) { + result = mContentResolver.update(Notes.CONTENT_NOTE_URI, mDiffNoteValues, "(" + + NoteColumns.ID + "=?)", new String[] { + String.valueOf(mId) + }); + } else { + result = mContentResolver.update(Notes.CONTENT_NOTE_URI, mDiffNoteValues, "(" + + NoteColumns.ID + "=?) AND (" + NoteColumns.VERSION + "<=?)", + new String[] { + String.valueOf(mId), String.valueOf(mVersion) + }); + } + if (result == 0) { + Log.w(TAG, "there is no update. maybe user updates note when syncing"); + } + } + + if (mType == Notes.TYPE_NOTE) { + for (SqlData sqlData : mDataList) { + sqlData.commit(mId, validateVersion, mVersion); + } + } + } + + // refresh local info + loadFromCursor(mId); + if (mType == Notes.TYPE_NOTE) + loadDataContent(); + + mDiffNoteValues.clear(); + mIsCreate = false; + } +} diff --git a/src/main/java/net/micode/notes/gtask/data/Task.java b/src/main/java/net/micode/notes/gtask/data/Task.java new file mode 100644 index 0000000..6a19454 --- /dev/null +++ b/src/main/java/net/micode/notes/gtask/data/Task.java @@ -0,0 +1,351 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.gtask.data; + +import android.database.Cursor; +import android.text.TextUtils; +import android.util.Log; + +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.DataColumns; +import net.micode.notes.data.Notes.DataConstants; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.gtask.exception.ActionFailureException; +import net.micode.notes.tool.GTaskStringUtils; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + + +public class Task extends Node { + private static final String TAG = Task.class.getSimpleName(); + + private boolean mCompleted; + + private String mNotes; + + private JSONObject mMetaInfo; + + private Task mPriorSibling; + + private TaskList mParent; + + public Task() { + super(); + mCompleted = false; + mNotes = null; + mPriorSibling = null; + mParent = null; + mMetaInfo = null; + } + + public JSONObject getCreateAction(int actionId) { + JSONObject js = new JSONObject(); + + try { + // action_type + js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, + GTaskStringUtils.GTASK_JSON_ACTION_TYPE_CREATE); + + // action_id + js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); + + // index + js.put(GTaskStringUtils.GTASK_JSON_INDEX, mParent.getChildTaskIndex(this)); + + // entity_delta + JSONObject entity = new JSONObject(); + entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); + entity.put(GTaskStringUtils.GTASK_JSON_CREATOR_ID, "null"); + entity.put(GTaskStringUtils.GTASK_JSON_ENTITY_TYPE, + GTaskStringUtils.GTASK_JSON_TYPE_TASK); + if (getNotes() != null) { + entity.put(GTaskStringUtils.GTASK_JSON_NOTES, getNotes()); + } + js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity); + + // parent_id + js.put(GTaskStringUtils.GTASK_JSON_PARENT_ID, mParent.getGid()); + + // dest_parent_type + js.put(GTaskStringUtils.GTASK_JSON_DEST_PARENT_TYPE, + GTaskStringUtils.GTASK_JSON_TYPE_GROUP); + + // list_id + js.put(GTaskStringUtils.GTASK_JSON_LIST_ID, mParent.getGid()); + + // prior_sibling_id + if (mPriorSibling != null) { + js.put(GTaskStringUtils.GTASK_JSON_PRIOR_SIBLING_ID, mPriorSibling.getGid()); + } + + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("fail to generate task-create jsonobject"); + } + + return js; + } + + public JSONObject getUpdateAction(int actionId) { + JSONObject js = new JSONObject(); + + try { + // action_type + js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, + GTaskStringUtils.GTASK_JSON_ACTION_TYPE_UPDATE); + + // action_id + js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); + + // id + js.put(GTaskStringUtils.GTASK_JSON_ID, getGid()); + + // entity_delta + JSONObject entity = new JSONObject(); + entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); + if (getNotes() != null) { + entity.put(GTaskStringUtils.GTASK_JSON_NOTES, getNotes()); + } + entity.put(GTaskStringUtils.GTASK_JSON_DELETED, getDeleted()); + js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity); + + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("fail to generate task-update jsonobject"); + } + + return js; + } + + public void setContentByRemoteJSON(JSONObject js) { + if (js != null) { + try { + // id + if (js.has(GTaskStringUtils.GTASK_JSON_ID)) { + setGid(js.getString(GTaskStringUtils.GTASK_JSON_ID)); + } + + // last_modified + if (js.has(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)) { + setLastModified(js.getLong(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)); + } + + // name + if (js.has(GTaskStringUtils.GTASK_JSON_NAME)) { + setName(js.getString(GTaskStringUtils.GTASK_JSON_NAME)); + } + + // notes + if (js.has(GTaskStringUtils.GTASK_JSON_NOTES)) { + setNotes(js.getString(GTaskStringUtils.GTASK_JSON_NOTES)); + } + + // deleted + if (js.has(GTaskStringUtils.GTASK_JSON_DELETED)) { + setDeleted(js.getBoolean(GTaskStringUtils.GTASK_JSON_DELETED)); + } + + // completed + if (js.has(GTaskStringUtils.GTASK_JSON_COMPLETED)) { + setCompleted(js.getBoolean(GTaskStringUtils.GTASK_JSON_COMPLETED)); + } + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("fail to get task content from jsonobject"); + } + } + } + + public void setContentByLocalJSON(JSONObject js) { + if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE) + || !js.has(GTaskStringUtils.META_HEAD_DATA)) { + Log.w(TAG, "setContentByLocalJSON: nothing is avaiable"); + } + + try { + JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); + JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA); + + if (note.getInt(NoteColumns.TYPE) != Notes.TYPE_NOTE) { + Log.e(TAG, "invalid type"); + return; + } + + for (int i = 0; i < dataArray.length(); i++) { + JSONObject data = dataArray.getJSONObject(i); + if (TextUtils.equals(data.getString(DataColumns.MIME_TYPE), DataConstants.NOTE)) { + setName(data.getString(DataColumns.CONTENT)); + break; + } + } + + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + } + } + + public JSONObject getLocalJSONFromContent() { + String name = getName(); + try { + if (mMetaInfo == null) { + // new task created from web + if (name == null) { + Log.w(TAG, "the note seems to be an empty one"); + return null; + } + + JSONObject js = new JSONObject(); + JSONObject note = new JSONObject(); + JSONArray dataArray = new JSONArray(); + JSONObject data = new JSONObject(); + data.put(DataColumns.CONTENT, name); + dataArray.put(data); + js.put(GTaskStringUtils.META_HEAD_DATA, dataArray); + note.put(NoteColumns.TYPE, Notes.TYPE_NOTE); + js.put(GTaskStringUtils.META_HEAD_NOTE, note); + return js; + } else { + // synced task + JSONObject note = mMetaInfo.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); + JSONArray dataArray = mMetaInfo.getJSONArray(GTaskStringUtils.META_HEAD_DATA); + + for (int i = 0; i < dataArray.length(); i++) { + JSONObject data = dataArray.getJSONObject(i); + if (TextUtils.equals(data.getString(DataColumns.MIME_TYPE), DataConstants.NOTE)) { + data.put(DataColumns.CONTENT, getName()); + break; + } + } + + note.put(NoteColumns.TYPE, Notes.TYPE_NOTE); + return mMetaInfo; + } + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + return null; + } + } + + public void setMetaInfo(MetaData metaData) { + if (metaData != null && metaData.getNotes() != null) { + try { + mMetaInfo = new JSONObject(metaData.getNotes()); + } catch (JSONException e) { + Log.w(TAG, e.toString()); + mMetaInfo = null; + } + } + } + + public int getSyncAction(Cursor c) { + try { + JSONObject noteInfo = null; + if (mMetaInfo != null && mMetaInfo.has(GTaskStringUtils.META_HEAD_NOTE)) { + noteInfo = mMetaInfo.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); + } + + if (noteInfo == null) { + Log.w(TAG, "it seems that note meta has been deleted"); + return SYNC_ACTION_UPDATE_REMOTE; + } + + if (!noteInfo.has(NoteColumns.ID)) { + Log.w(TAG, "remote note id seems to be deleted"); + return SYNC_ACTION_UPDATE_LOCAL; + } + + // validate the note id now + if (c.getLong(SqlNote.ID_COLUMN) != noteInfo.getLong(NoteColumns.ID)) { + Log.w(TAG, "note id doesn't match"); + return SYNC_ACTION_UPDATE_LOCAL; + } + + if (c.getInt(SqlNote.LOCAL_MODIFIED_COLUMN) == 0) { + // there is no local update + if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { + // no update both side + return SYNC_ACTION_NONE; + } else { + // apply remote to local + return SYNC_ACTION_UPDATE_LOCAL; + } + } else { + // validate gtask id + if (!c.getString(SqlNote.GTASK_ID_COLUMN).equals(getGid())) { + Log.e(TAG, "gtask id doesn't match"); + return SYNC_ACTION_ERROR; + } + if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { + // local modification only + return SYNC_ACTION_UPDATE_REMOTE; + } else { + return SYNC_ACTION_UPDATE_CONFLICT; + } + } + } catch (Exception e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + } + + return SYNC_ACTION_ERROR; + } + + public boolean isWorthSaving() { + return mMetaInfo != null || (getName() != null && getName().trim().length() > 0) + || (getNotes() != null && getNotes().trim().length() > 0); + } + + public void setCompleted(boolean completed) { + this.mCompleted = completed; + } + + public void setNotes(String notes) { + this.mNotes = notes; + } + + public void setPriorSibling(Task priorSibling) { + this.mPriorSibling = priorSibling; + } + + public void setParent(TaskList parent) { + this.mParent = parent; + } + + public boolean getCompleted() { + return this.mCompleted; + } + + public String getNotes() { + return this.mNotes; + } + + public Task getPriorSibling() { + return this.mPriorSibling; + } + + public TaskList getParent() { + return this.mParent; + } + +} diff --git a/src/main/java/net/micode/notes/gtask/data/TaskList.java b/src/main/java/net/micode/notes/gtask/data/TaskList.java new file mode 100644 index 0000000..4ea21c5 --- /dev/null +++ b/src/main/java/net/micode/notes/gtask/data/TaskList.java @@ -0,0 +1,343 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.gtask.data; + +import android.database.Cursor; +import android.util.Log; + +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.gtask.exception.ActionFailureException; +import net.micode.notes.tool.GTaskStringUtils; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; + + +public class TaskList extends Node { + private static final String TAG = TaskList.class.getSimpleName(); + + private int mIndex; + + private ArrayList mChildren; + + public TaskList() { + super(); + mChildren = new ArrayList(); + mIndex = 1; + } + + public JSONObject getCreateAction(int actionId) { + JSONObject js = new JSONObject(); + + try { + // action_type + js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, + GTaskStringUtils.GTASK_JSON_ACTION_TYPE_CREATE); + + // action_id + js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); + + // index + js.put(GTaskStringUtils.GTASK_JSON_INDEX, mIndex); + + // entity_delta + JSONObject entity = new JSONObject(); + entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); + entity.put(GTaskStringUtils.GTASK_JSON_CREATOR_ID, "null"); + entity.put(GTaskStringUtils.GTASK_JSON_ENTITY_TYPE, + GTaskStringUtils.GTASK_JSON_TYPE_GROUP); + js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity); + + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("fail to generate tasklist-create jsonobject"); + } + + return js; + } + + public JSONObject getUpdateAction(int actionId) { + JSONObject js = new JSONObject(); + + try { + // action_type + js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, + GTaskStringUtils.GTASK_JSON_ACTION_TYPE_UPDATE); + + // action_id + js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); + + // id + js.put(GTaskStringUtils.GTASK_JSON_ID, getGid()); + + // entity_delta + JSONObject entity = new JSONObject(); + entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); + entity.put(GTaskStringUtils.GTASK_JSON_DELETED, getDeleted()); + js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity); + + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("fail to generate tasklist-update jsonobject"); + } + + return js; + } + + public void setContentByRemoteJSON(JSONObject js) { + if (js != null) { + try { + // id + if (js.has(GTaskStringUtils.GTASK_JSON_ID)) { + setGid(js.getString(GTaskStringUtils.GTASK_JSON_ID)); + } + + // last_modified + if (js.has(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)) { + setLastModified(js.getLong(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)); + } + + // name + if (js.has(GTaskStringUtils.GTASK_JSON_NAME)) { + setName(js.getString(GTaskStringUtils.GTASK_JSON_NAME)); + } + + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("fail to get tasklist content from jsonobject"); + } + } + } + + public void setContentByLocalJSON(JSONObject js) { + if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE)) { + Log.w(TAG, "setContentByLocalJSON: nothing is avaiable"); + } + + try { + JSONObject folder = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); + + if (folder.getInt(NoteColumns.TYPE) == Notes.TYPE_FOLDER) { + String name = folder.getString(NoteColumns.SNIPPET); + setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + name); + } else if (folder.getInt(NoteColumns.TYPE) == Notes.TYPE_SYSTEM) { + if (folder.getLong(NoteColumns.ID) == Notes.ID_ROOT_FOLDER) + setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT); + else if (folder.getLong(NoteColumns.ID) == Notes.ID_CALL_RECORD_FOLDER) + setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + + GTaskStringUtils.FOLDER_CALL_NOTE); + else + Log.e(TAG, "invalid system folder"); + } else { + Log.e(TAG, "error type"); + } + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + } + } + + public JSONObject getLocalJSONFromContent() { + try { + JSONObject js = new JSONObject(); + JSONObject folder = new JSONObject(); + + String folderName = getName(); + if (getName().startsWith(GTaskStringUtils.MIUI_FOLDER_PREFFIX)) + folderName = folderName.substring(GTaskStringUtils.MIUI_FOLDER_PREFFIX.length(), + folderName.length()); + folder.put(NoteColumns.SNIPPET, folderName); + if (folderName.equals(GTaskStringUtils.FOLDER_DEFAULT) + || folderName.equals(GTaskStringUtils.FOLDER_CALL_NOTE)) + folder.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); + else + folder.put(NoteColumns.TYPE, Notes.TYPE_FOLDER); + + js.put(GTaskStringUtils.META_HEAD_NOTE, folder); + + return js; + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + return null; + } + } + + public int getSyncAction(Cursor c) { + try { + if (c.getInt(SqlNote.LOCAL_MODIFIED_COLUMN) == 0) { + // there is no local update + if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { + // no update both side + return SYNC_ACTION_NONE; + } else { + // apply remote to local + return SYNC_ACTION_UPDATE_LOCAL; + } + } else { + // validate gtask id + if (!c.getString(SqlNote.GTASK_ID_COLUMN).equals(getGid())) { + Log.e(TAG, "gtask id doesn't match"); + return SYNC_ACTION_ERROR; + } + if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { + // local modification only + return SYNC_ACTION_UPDATE_REMOTE; + } else { + // for folder conflicts, just apply local modification + return SYNC_ACTION_UPDATE_REMOTE; + } + } + } catch (Exception e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + } + + return SYNC_ACTION_ERROR; + } + + public int getChildTaskCount() { + return mChildren.size(); + } + + public boolean addChildTask(Task task) { + boolean ret = false; + if (task != null && !mChildren.contains(task)) { + ret = mChildren.add(task); + if (ret) { + // need to set prior sibling and parent + task.setPriorSibling(mChildren.isEmpty() ? null : mChildren + .get(mChildren.size() - 1)); + task.setParent(this); + } + } + return ret; + } + + public boolean addChildTask(Task task, int index) { + if (index < 0 || index > mChildren.size()) { + Log.e(TAG, "add child task: invalid index"); + return false; + } + + int pos = mChildren.indexOf(task); + if (task != null && pos == -1) { + mChildren.add(index, task); + + // update the task list + Task preTask = null; + Task afterTask = null; + if (index != 0) + preTask = mChildren.get(index - 1); + if (index != mChildren.size() - 1) + afterTask = mChildren.get(index + 1); + + task.setPriorSibling(preTask); + if (afterTask != null) + afterTask.setPriorSibling(task); + } + + return true; + } + + public boolean removeChildTask(Task task) { + boolean ret = false; + int index = mChildren.indexOf(task); + if (index != -1) { + ret = mChildren.remove(task); + + if (ret) { + // reset prior sibling and parent + task.setPriorSibling(null); + task.setParent(null); + + // update the task list + if (index != mChildren.size()) { + mChildren.get(index).setPriorSibling( + index == 0 ? null : mChildren.get(index - 1)); + } + } + } + return ret; + } + + public boolean moveChildTask(Task task, int index) { + + if (index < 0 || index >= mChildren.size()) { + Log.e(TAG, "move child task: invalid index"); + return false; + } + + int pos = mChildren.indexOf(task); + if (pos == -1) { + Log.e(TAG, "move child task: the task should in the list"); + return false; + } + + if (pos == index) + return true; + return (removeChildTask(task) && addChildTask(task, index)); + } + + public Task findChildTaskByGid(String gid) { + for (int i = 0; i < mChildren.size(); i++) { + Task t = mChildren.get(i); + if (t.getGid().equals(gid)) { + return t; + } + } + return null; + } + + public int getChildTaskIndex(Task task) { + return mChildren.indexOf(task); + } + + public Task getChildTaskByIndex(int index) { + if (index < 0 || index >= mChildren.size()) { + Log.e(TAG, "getTaskByIndex: invalid index"); + return null; + } + return mChildren.get(index); + } + + public Task getChilTaskByGid(String gid) { + for (Task task : mChildren) { + if (task.getGid().equals(gid)) + return task; + } + return null; + } + + public ArrayList getChildTaskList() { + return this.mChildren; + } + + public void setIndex(int index) { + this.mIndex = index; + } + + public int getIndex() { + return this.mIndex; + } +} diff --git a/src/main/java/net/micode/notes/gtask/exception/ActionFailureException.java b/src/main/java/net/micode/notes/gtask/exception/ActionFailureException.java new file mode 100644 index 0000000..15504be --- /dev/null +++ b/src/main/java/net/micode/notes/gtask/exception/ActionFailureException.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.gtask.exception; + +public class ActionFailureException extends RuntimeException { + private static final long serialVersionUID = 4425249765923293627L; + + public ActionFailureException() { + super(); + } + + public ActionFailureException(String paramString) { + super(paramString); + } + + public ActionFailureException(String paramString, Throwable paramThrowable) { + super(paramString, paramThrowable); + } +} diff --git a/src/main/java/net/micode/notes/gtask/exception/NetworkFailureException.java b/src/main/java/net/micode/notes/gtask/exception/NetworkFailureException.java new file mode 100644 index 0000000..b08cfb1 --- /dev/null +++ b/src/main/java/net/micode/notes/gtask/exception/NetworkFailureException.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.gtask.exception; + +public class NetworkFailureException extends Exception { + private static final long serialVersionUID = 2107610287180234136L; + + public NetworkFailureException() { + super(); + } + + public NetworkFailureException(String paramString) { + super(paramString); + } + + public NetworkFailureException(String paramString, Throwable paramThrowable) { + super(paramString, paramThrowable); + } +} diff --git a/src/main/java/net/micode/notes/gtask/remote/GTaskASyncTask.java b/src/main/java/net/micode/notes/gtask/remote/GTaskASyncTask.java new file mode 100644 index 0000000..0e332f3 --- /dev/null +++ b/src/main/java/net/micode/notes/gtask/remote/GTaskASyncTask.java @@ -0,0 +1,129 @@ + +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.gtask.remote; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.os.AsyncTask; + +import net.micode.notes.R; +import net.micode.notes.ui.NotesListActivity; +import net.micode.notes.ui.NotesPreferenceActivity; + + +public class GTaskASyncTask extends AsyncTask { + + private static int GTASK_SYNC_NOTIFICATION_ID = 5234235; + + public interface OnCompleteListener { + void onComplete(); + } + + private Context mContext; + + private NotificationManager mNotifiManager; + + private GTaskManager mTaskManager; + + private OnCompleteListener mOnCompleteListener; + + public GTaskASyncTask(Context context, OnCompleteListener listener) { + mContext = context; + mOnCompleteListener = listener; + mNotifiManager = (NotificationManager) mContext + .getSystemService(Context.NOTIFICATION_SERVICE); + mTaskManager = GTaskManager.getInstance(); + } + + public void cancelSync() { + mTaskManager.cancelSync(); + } + + public void publishProgess(String message) { + publishProgress(new String[] { + message + }); + } + + private void showNotification(int tickerId, String content) { + PendingIntent pendingIntent; + if (tickerId != R.string.ticker_success) { + pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, + NotesPreferenceActivity.class), 0); + + } else { + pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, + NotesListActivity.class), 0); + } + + + Notification.Builder builder = new Notification.Builder(mContext) + .setAutoCancel(true) + .setContentTitle(mContext.getString(R.string.app_name)) + .setContentText(content) + .setContentIntent(pendingIntent) + .setWhen(System.currentTimeMillis()) + .setOngoing(true); + Notification notification=builder.getNotification(); + mNotifiManager.notify(GTASK_SYNC_NOTIFICATION_ID, notification); + } + + + + @Override + protected Integer doInBackground(Void... unused) { + publishProgess(mContext.getString(R.string.sync_progress_login, NotesPreferenceActivity + .getSyncAccountName(mContext))); + return mTaskManager.sync(mContext, this); + } + + @Override + protected void onProgressUpdate(String... progress) { + showNotification(R.string.ticker_syncing, progress[0]); + if (mContext instanceof GTaskSyncService) { + ((GTaskSyncService) mContext).sendBroadcast(progress[0]); + } + } + + @Override + protected void onPostExecute(Integer result) { + if (result == GTaskManager.STATE_SUCCESS) { + showNotification(R.string.ticker_success, mContext.getString( + R.string.success_sync_account, mTaskManager.getSyncAccount())); + NotesPreferenceActivity.setLastSyncTime(mContext, System.currentTimeMillis()); + } else if (result == GTaskManager.STATE_NETWORK_ERROR) { + showNotification(R.string.ticker_fail, mContext.getString(R.string.error_sync_network)); + } else if (result == GTaskManager.STATE_INTERNAL_ERROR) { + showNotification(R.string.ticker_fail, mContext.getString(R.string.error_sync_internal)); + } else if (result == GTaskManager.STATE_SYNC_CANCELLED) { + showNotification(R.string.ticker_cancel, mContext + .getString(R.string.error_sync_cancelled)); + } + if (mOnCompleteListener != null) { + new Thread(new Runnable() { + + public void run() { + mOnCompleteListener.onComplete(); + } + }).start(); + } + } +} diff --git a/src/main/java/net/micode/notes/gtask/remote/GTaskClient.java b/src/main/java/net/micode/notes/gtask/remote/GTaskClient.java new file mode 100644 index 0000000..c67dfdf --- /dev/null +++ b/src/main/java/net/micode/notes/gtask/remote/GTaskClient.java @@ -0,0 +1,585 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.gtask.remote; + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.accounts.AccountManagerFuture; +import android.app.Activity; +import android.os.Bundle; +import android.text.TextUtils; +import android.util.Log; + +import net.micode.notes.gtask.data.Node; +import net.micode.notes.gtask.data.Task; +import net.micode.notes.gtask.data.TaskList; +import net.micode.notes.gtask.exception.ActionFailureException; +import net.micode.notes.gtask.exception.NetworkFailureException; +import net.micode.notes.tool.GTaskStringUtils; +import net.micode.notes.ui.NotesPreferenceActivity; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.cookie.Cookie; +import org.apache.http.impl.client.BasicCookieStore; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.params.BasicHttpParams; +import org.apache.http.params.HttpConnectionParams; +import org.apache.http.params.HttpParams; +import org.apache.http.params.HttpProtocolParams; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.LinkedList; +import java.util.List; +import java.util.zip.GZIPInputStream; +import java.util.zip.Inflater; +import java.util.zip.InflaterInputStream; + + +public class GTaskClient { + private static final String TAG = GTaskClient.class.getSimpleName(); + + private static final String GTASK_URL = "https://mail.google.com/tasks/"; + + private static final String GTASK_GET_URL = "https://mail.google.com/tasks/ig"; + + private static final String GTASK_POST_URL = "https://mail.google.com/tasks/r/ig"; + + private static GTaskClient mInstance = null; + + private DefaultHttpClient mHttpClient; + + private String mGetUrl; + + private String mPostUrl; + + private long mClientVersion; + + private boolean mLoggedin; + + private long mLastLoginTime; + + private int mActionId; + + private Account mAccount; + + private JSONArray mUpdateArray; + + private GTaskClient() { + mHttpClient = null; + mGetUrl = GTASK_GET_URL; + mPostUrl = GTASK_POST_URL; + mClientVersion = -1; + mLoggedin = false; + mLastLoginTime = 0; + mActionId = 1; + mAccount = null; + mUpdateArray = null; + } + + public static synchronized GTaskClient getInstance() { + if (mInstance == null) { + mInstance = new GTaskClient(); + } + return mInstance; + } + + public boolean login(Activity activity) { + // we suppose that the cookie would expire after 5 minutes + // then we need to re-login + final long interval = 1000 * 60 * 5; + if (mLastLoginTime + interval < System.currentTimeMillis()) { + mLoggedin = false; + } + + // need to re-login after account switch + if (mLoggedin + && !TextUtils.equals(getSyncAccount().name, NotesPreferenceActivity + .getSyncAccountName(activity))) { + mLoggedin = false; + } + + if (mLoggedin) { + Log.d(TAG, "already logged in"); + return true; + } + + mLastLoginTime = System.currentTimeMillis(); + String authToken = loginGoogleAccount(activity, false); + if (authToken == null) { + Log.e(TAG, "login google account failed"); + return false; + } + + // login with custom domain if necessary + if (!(mAccount.name.toLowerCase().endsWith("gmail.com") || mAccount.name.toLowerCase() + .endsWith("googlemail.com"))) { + StringBuilder url = new StringBuilder(GTASK_URL).append("a/"); + int index = mAccount.name.indexOf('@') + 1; + String suffix = mAccount.name.substring(index); + url.append(suffix + "/"); + mGetUrl = url.toString() + "ig"; + mPostUrl = url.toString() + "r/ig"; + + if (tryToLoginGtask(activity, authToken)) { + mLoggedin = true; + } + } + + // try to login with google official url + if (!mLoggedin) { + mGetUrl = GTASK_GET_URL; + mPostUrl = GTASK_POST_URL; + if (!tryToLoginGtask(activity, authToken)) { + return false; + } + } + + mLoggedin = true; + return true; + } + + private String loginGoogleAccount(Activity activity, boolean invalidateToken) { + String authToken; + AccountManager accountManager = AccountManager.get(activity); + Account[] accounts = accountManager.getAccountsByType("com.google"); + + if (accounts.length == 0) { + Log.e(TAG, "there is no available google account"); + return null; + } + + String accountName = NotesPreferenceActivity.getSyncAccountName(activity); + Account account = null; + for (Account a : accounts) { + if (a.name.equals(accountName)) { + account = a; + break; + } + } + if (account != null) { + mAccount = account; + } else { + Log.e(TAG, "unable to get an account with the same name in the settings"); + return null; + } + + // get the token now + AccountManagerFuture accountManagerFuture = accountManager.getAuthToken(account, + "goanna_mobile", null, activity, null, null); + try { + Bundle authTokenBundle = accountManagerFuture.getResult(); + authToken = authTokenBundle.getString(AccountManager.KEY_AUTHTOKEN); + if (invalidateToken) { + accountManager.invalidateAuthToken("com.google", authToken); + loginGoogleAccount(activity, false); + } + } catch (Exception e) { + Log.e(TAG, "get auth token failed"); + authToken = null; + } + + return authToken; + } + + private boolean tryToLoginGtask(Activity activity, String authToken) { + if (!loginGtask(authToken)) { + // maybe the auth token is out of date, now let's invalidate the + // token and try again + authToken = loginGoogleAccount(activity, true); + if (authToken == null) { + Log.e(TAG, "login google account failed"); + return false; + } + + if (!loginGtask(authToken)) { + Log.e(TAG, "login gtask failed"); + return false; + } + } + return true; + } + + private boolean loginGtask(String authToken) { + int timeoutConnection = 10000; + int timeoutSocket = 15000; + HttpParams httpParameters = new BasicHttpParams(); + HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection); + HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket); + mHttpClient = new DefaultHttpClient(httpParameters); + BasicCookieStore localBasicCookieStore = new BasicCookieStore(); + mHttpClient.setCookieStore(localBasicCookieStore); + HttpProtocolParams.setUseExpectContinue(mHttpClient.getParams(), false); + + // login gtask + try { + String loginUrl = mGetUrl + "?auth=" + authToken; + HttpGet httpGet = new HttpGet(loginUrl); + HttpResponse response = null; + response = mHttpClient.execute(httpGet); + + // get the cookie now + List cookies = mHttpClient.getCookieStore().getCookies(); + boolean hasAuthCookie = false; + for (Cookie cookie : cookies) { + if (cookie.getName().contains("GTL")) { + hasAuthCookie = true; + } + } + if (!hasAuthCookie) { + Log.w(TAG, "it seems that there is no auth cookie"); + } + + // get the client version + String resString = getResponseContent(response.getEntity()); + String jsBegin = "_setup("; + String jsEnd = ")}"; + int begin = resString.indexOf(jsBegin); + int end = resString.lastIndexOf(jsEnd); + String jsString = null; + if (begin != -1 && end != -1 && begin < end) { + jsString = resString.substring(begin + jsBegin.length(), end); + } + JSONObject js = new JSONObject(jsString); + mClientVersion = js.getLong("v"); + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + return false; + } catch (Exception e) { + // simply catch all exceptions + Log.e(TAG, "httpget gtask_url failed"); + return false; + } + + return true; + } + + private int getActionId() { + return mActionId++; + } + + private HttpPost createHttpPost() { + HttpPost httpPost = new HttpPost(mPostUrl); + httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8"); + httpPost.setHeader("AT", "1"); + return httpPost; + } + + private String getResponseContent(HttpEntity entity) throws IOException { + String contentEncoding = null; + if (entity.getContentEncoding() != null) { + contentEncoding = entity.getContentEncoding().getValue(); + Log.d(TAG, "encoding: " + contentEncoding); + } + + InputStream input = entity.getContent(); + if (contentEncoding != null && contentEncoding.equalsIgnoreCase("gzip")) { + input = new GZIPInputStream(entity.getContent()); + } else if (contentEncoding != null && contentEncoding.equalsIgnoreCase("deflate")) { + Inflater inflater = new Inflater(true); + input = new InflaterInputStream(entity.getContent(), inflater); + } + + try { + InputStreamReader isr = new InputStreamReader(input); + BufferedReader br = new BufferedReader(isr); + StringBuilder sb = new StringBuilder(); + + while (true) { + String buff = br.readLine(); + if (buff == null) { + return sb.toString(); + } + sb = sb.append(buff); + } + } finally { + input.close(); + } + } + + private JSONObject postRequest(JSONObject js) throws NetworkFailureException { + if (!mLoggedin) { + Log.e(TAG, "please login first"); + throw new ActionFailureException("not logged in"); + } + + HttpPost httpPost = createHttpPost(); + try { + LinkedList list = new LinkedList(); + list.add(new BasicNameValuePair("r", js.toString())); + UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, "UTF-8"); + httpPost.setEntity(entity); + + // execute the post + HttpResponse response = mHttpClient.execute(httpPost); + String jsString = getResponseContent(response.getEntity()); + return new JSONObject(jsString); + + } catch (ClientProtocolException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new NetworkFailureException("postRequest failed"); + } catch (IOException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new NetworkFailureException("postRequest failed"); + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("unable to convert response content to jsonobject"); + } catch (Exception e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("error occurs when posting request"); + } + } + + public void createTask(Task task) throws NetworkFailureException { + commitUpdate(); + try { + JSONObject jsPost = new JSONObject(); + JSONArray actionList = new JSONArray(); + + // action_list + actionList.put(task.getCreateAction(getActionId())); + jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); + + // client_version + jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); + + // post + JSONObject jsResponse = postRequest(jsPost); + JSONObject jsResult = (JSONObject) jsResponse.getJSONArray( + GTaskStringUtils.GTASK_JSON_RESULTS).get(0); + task.setGid(jsResult.getString(GTaskStringUtils.GTASK_JSON_NEW_ID)); + + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("create task: handing jsonobject failed"); + } + } + + public void createTaskList(TaskList tasklist) throws NetworkFailureException { + commitUpdate(); + try { + JSONObject jsPost = new JSONObject(); + JSONArray actionList = new JSONArray(); + + // action_list + actionList.put(tasklist.getCreateAction(getActionId())); + jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); + + // client version + jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); + + // post + JSONObject jsResponse = postRequest(jsPost); + JSONObject jsResult = (JSONObject) jsResponse.getJSONArray( + GTaskStringUtils.GTASK_JSON_RESULTS).get(0); + tasklist.setGid(jsResult.getString(GTaskStringUtils.GTASK_JSON_NEW_ID)); + + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("create tasklist: handing jsonobject failed"); + } + } + + public void commitUpdate() throws NetworkFailureException { + if (mUpdateArray != null) { + try { + JSONObject jsPost = new JSONObject(); + + // action_list + jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, mUpdateArray); + + // client_version + jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); + + postRequest(jsPost); + mUpdateArray = null; + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("commit update: handing jsonobject failed"); + } + } + } + + public void addUpdateNode(Node node) throws NetworkFailureException { + if (node != null) { + // too many update items may result in an error + // set max to 10 items + if (mUpdateArray != null && mUpdateArray.length() > 10) { + commitUpdate(); + } + + if (mUpdateArray == null) + mUpdateArray = new JSONArray(); + mUpdateArray.put(node.getUpdateAction(getActionId())); + } + } + + public void moveTask(Task task, TaskList preParent, TaskList curParent) + throws NetworkFailureException { + commitUpdate(); + try { + JSONObject jsPost = new JSONObject(); + JSONArray actionList = new JSONArray(); + JSONObject action = new JSONObject(); + + // action_list + action.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, + GTaskStringUtils.GTASK_JSON_ACTION_TYPE_MOVE); + action.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, getActionId()); + action.put(GTaskStringUtils.GTASK_JSON_ID, task.getGid()); + if (preParent == curParent && task.getPriorSibling() != null) { + // put prioring_sibing_id only if moving within the tasklist and + // it is not the first one + action.put(GTaskStringUtils.GTASK_JSON_PRIOR_SIBLING_ID, task.getPriorSibling()); + } + action.put(GTaskStringUtils.GTASK_JSON_SOURCE_LIST, preParent.getGid()); + action.put(GTaskStringUtils.GTASK_JSON_DEST_PARENT, curParent.getGid()); + if (preParent != curParent) { + // put the dest_list only if moving between tasklists + action.put(GTaskStringUtils.GTASK_JSON_DEST_LIST, curParent.getGid()); + } + actionList.put(action); + jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); + + // client_version + jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); + + postRequest(jsPost); + + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("move task: handing jsonobject failed"); + } + } + + public void deleteNode(Node node) throws NetworkFailureException { + commitUpdate(); + try { + JSONObject jsPost = new JSONObject(); + JSONArray actionList = new JSONArray(); + + // action_list + node.setDeleted(true); + actionList.put(node.getUpdateAction(getActionId())); + jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); + + // client_version + jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); + + postRequest(jsPost); + mUpdateArray = null; + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("delete node: handing jsonobject failed"); + } + } + + public JSONArray getTaskLists() throws NetworkFailureException { + if (!mLoggedin) { + Log.e(TAG, "please login first"); + throw new ActionFailureException("not logged in"); + } + + try { + HttpGet httpGet = new HttpGet(mGetUrl); + HttpResponse response = null; + response = mHttpClient.execute(httpGet); + + // get the task list + String resString = getResponseContent(response.getEntity()); + String jsBegin = "_setup("; + String jsEnd = ")}"; + int begin = resString.indexOf(jsBegin); + int end = resString.lastIndexOf(jsEnd); + String jsString = null; + if (begin != -1 && end != -1 && begin < end) { + jsString = resString.substring(begin + jsBegin.length(), end); + } + JSONObject js = new JSONObject(jsString); + return js.getJSONObject("t").getJSONArray(GTaskStringUtils.GTASK_JSON_LISTS); + } catch (ClientProtocolException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new NetworkFailureException("gettasklists: httpget failed"); + } catch (IOException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new NetworkFailureException("gettasklists: httpget failed"); + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("get task lists: handing jasonobject failed"); + } + } + + public JSONArray getTaskList(String listGid) throws NetworkFailureException { + commitUpdate(); + try { + JSONObject jsPost = new JSONObject(); + JSONArray actionList = new JSONArray(); + JSONObject action = new JSONObject(); + + // action_list + action.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, + GTaskStringUtils.GTASK_JSON_ACTION_TYPE_GETALL); + action.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, getActionId()); + action.put(GTaskStringUtils.GTASK_JSON_LIST_ID, listGid); + action.put(GTaskStringUtils.GTASK_JSON_GET_DELETED, false); + actionList.put(action); + jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); + + // client_version + jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); + + JSONObject jsResponse = postRequest(jsPost); + return jsResponse.getJSONArray(GTaskStringUtils.GTASK_JSON_TASKS); + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("get task list: handing jsonobject failed"); + } + } + + public Account getSyncAccount() { + return mAccount; + } + + public void resetUpdateArray() { + mUpdateArray = null; + } +} diff --git a/src/main/java/net/micode/notes/gtask/remote/GTaskManager.java b/src/main/java/net/micode/notes/gtask/remote/GTaskManager.java new file mode 100644 index 0000000..d2b4082 --- /dev/null +++ b/src/main/java/net/micode/notes/gtask/remote/GTaskManager.java @@ -0,0 +1,800 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.gtask.remote; + +import android.app.Activity; +import android.content.ContentResolver; +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.util.Log; + +import net.micode.notes.R; +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.DataColumns; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.gtask.data.MetaData; +import net.micode.notes.gtask.data.Node; +import net.micode.notes.gtask.data.SqlNote; +import net.micode.notes.gtask.data.Task; +import net.micode.notes.gtask.data.TaskList; +import net.micode.notes.gtask.exception.ActionFailureException; +import net.micode.notes.gtask.exception.NetworkFailureException; +import net.micode.notes.tool.DataUtils; +import net.micode.notes.tool.GTaskStringUtils; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; + + +public class GTaskManager { + private static final String TAG = GTaskManager.class.getSimpleName(); + + public static final int STATE_SUCCESS = 0; + + public static final int STATE_NETWORK_ERROR = 1; + + public static final int STATE_INTERNAL_ERROR = 2; + + public static final int STATE_SYNC_IN_PROGRESS = 3; + + public static final int STATE_SYNC_CANCELLED = 4; + + private static GTaskManager mInstance = null; + + private Activity mActivity; + + private Context mContext; + + private ContentResolver mContentResolver; + + private boolean mSyncing; + + private boolean mCancelled; + + private HashMap mGTaskListHashMap; + + private HashMap mGTaskHashMap; + + private HashMap mMetaHashMap; + + private TaskList mMetaList; + + private HashSet mLocalDeleteIdMap; + + private HashMap mGidToNid; + + private HashMap mNidToGid; + + private GTaskManager() { + mSyncing = false; + mCancelled = false; + mGTaskListHashMap = new HashMap(); + mGTaskHashMap = new HashMap(); + mMetaHashMap = new HashMap(); + mMetaList = null; + mLocalDeleteIdMap = new HashSet(); + mGidToNid = new HashMap(); + mNidToGid = new HashMap(); + } + + public static synchronized GTaskManager getInstance() { + if (mInstance == null) { + mInstance = new GTaskManager(); + } + return mInstance; + } + + public synchronized void setActivityContext(Activity activity) { + // used for getting authtoken + mActivity = activity; + } + + public int sync(Context context, GTaskASyncTask asyncTask) { + if (mSyncing) { + Log.d(TAG, "Sync is in progress"); + return STATE_SYNC_IN_PROGRESS; + } + mContext = context; + mContentResolver = mContext.getContentResolver(); + mSyncing = true; + mCancelled = false; + mGTaskListHashMap.clear(); + mGTaskHashMap.clear(); + mMetaHashMap.clear(); + mLocalDeleteIdMap.clear(); + mGidToNid.clear(); + mNidToGid.clear(); + + try { + GTaskClient client = GTaskClient.getInstance(); + client.resetUpdateArray(); + + // login google task + if (!mCancelled) { + if (!client.login(mActivity)) { + throw new NetworkFailureException("login google task failed"); + } + } + + // get the task list from google + asyncTask.publishProgess(mContext.getString(R.string.sync_progress_init_list)); + initGTaskList(); + + // do content sync work + asyncTask.publishProgess(mContext.getString(R.string.sync_progress_syncing)); + syncContent(); + } catch (NetworkFailureException e) { + Log.e(TAG, e.toString()); + return STATE_NETWORK_ERROR; + } catch (ActionFailureException e) { + Log.e(TAG, e.toString()); + return STATE_INTERNAL_ERROR; + } catch (Exception e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + return STATE_INTERNAL_ERROR; + } finally { + mGTaskListHashMap.clear(); + mGTaskHashMap.clear(); + mMetaHashMap.clear(); + mLocalDeleteIdMap.clear(); + mGidToNid.clear(); + mNidToGid.clear(); + mSyncing = false; + } + + return mCancelled ? STATE_SYNC_CANCELLED : STATE_SUCCESS; + } + + private void initGTaskList() throws NetworkFailureException { + if (mCancelled) + return; + GTaskClient client = GTaskClient.getInstance(); + try { + JSONArray jsTaskLists = client.getTaskLists(); + + // init meta list first + mMetaList = null; + for (int i = 0; i < jsTaskLists.length(); i++) { + JSONObject object = jsTaskLists.getJSONObject(i); + String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID); + String name = object.getString(GTaskStringUtils.GTASK_JSON_NAME); + + if (name + .equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META)) { + mMetaList = new TaskList(); + mMetaList.setContentByRemoteJSON(object); + + // load meta data + JSONArray jsMetas = client.getTaskList(gid); + for (int j = 0; j < jsMetas.length(); j++) { + object = (JSONObject) jsMetas.getJSONObject(j); + MetaData metaData = new MetaData(); + metaData.setContentByRemoteJSON(object); + if (metaData.isWorthSaving()) { + mMetaList.addChildTask(metaData); + if (metaData.getGid() != null) { + mMetaHashMap.put(metaData.getRelatedGid(), metaData); + } + } + } + } + } + + // create meta list if not existed + if (mMetaList == null) { + mMetaList = new TaskList(); + mMetaList.setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + + GTaskStringUtils.FOLDER_META); + GTaskClient.getInstance().createTaskList(mMetaList); + } + + // init task list + for (int i = 0; i < jsTaskLists.length(); i++) { + JSONObject object = jsTaskLists.getJSONObject(i); + String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID); + String name = object.getString(GTaskStringUtils.GTASK_JSON_NAME); + + if (name.startsWith(GTaskStringUtils.MIUI_FOLDER_PREFFIX) + && !name.equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + + GTaskStringUtils.FOLDER_META)) { + TaskList tasklist = new TaskList(); + tasklist.setContentByRemoteJSON(object); + mGTaskListHashMap.put(gid, tasklist); + mGTaskHashMap.put(gid, tasklist); + + // load tasks + JSONArray jsTasks = client.getTaskList(gid); + for (int j = 0; j < jsTasks.length(); j++) { + object = (JSONObject) jsTasks.getJSONObject(j); + gid = object.getString(GTaskStringUtils.GTASK_JSON_ID); + Task task = new Task(); + task.setContentByRemoteJSON(object); + if (task.isWorthSaving()) { + task.setMetaInfo(mMetaHashMap.get(gid)); + tasklist.addChildTask(task); + mGTaskHashMap.put(gid, task); + } + } + } + } + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("initGTaskList: handing JSONObject failed"); + } + } + + private void syncContent() throws NetworkFailureException { + int syncType; + Cursor c = null; + String gid; + Node node; + + mLocalDeleteIdMap.clear(); + + if (mCancelled) { + return; + } + + // for local deleted note + try { + c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, + "(type<>? AND parent_id=?)", new String[] { + String.valueOf(Notes.TYPE_SYSTEM), String.valueOf(Notes.ID_TRASH_FOLER) + }, null); + if (c != null) { + while (c.moveToNext()) { + gid = c.getString(SqlNote.GTASK_ID_COLUMN); + node = mGTaskHashMap.get(gid); + if (node != null) { + mGTaskHashMap.remove(gid); + doContentSync(Node.SYNC_ACTION_DEL_REMOTE, node, c); + } + + mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN)); + } + } else { + Log.w(TAG, "failed to query trash folder"); + } + } finally { + if (c != null) { + c.close(); + c = null; + } + } + + // sync folder first + syncFolder(); + + // for note existing in database + try { + c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, + "(type=? AND parent_id<>?)", new String[] { + String.valueOf(Notes.TYPE_NOTE), String.valueOf(Notes.ID_TRASH_FOLER) + }, NoteColumns.TYPE + " DESC"); + if (c != null) { + while (c.moveToNext()) { + gid = c.getString(SqlNote.GTASK_ID_COLUMN); + node = mGTaskHashMap.get(gid); + if (node != null) { + mGTaskHashMap.remove(gid); + mGidToNid.put(gid, c.getLong(SqlNote.ID_COLUMN)); + mNidToGid.put(c.getLong(SqlNote.ID_COLUMN), gid); + syncType = node.getSyncAction(c); + } else { + if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) { + // local add + syncType = Node.SYNC_ACTION_ADD_REMOTE; + } else { + // remote delete + syncType = Node.SYNC_ACTION_DEL_LOCAL; + } + } + doContentSync(syncType, node, c); + } + } else { + Log.w(TAG, "failed to query existing note in database"); + } + + } finally { + if (c != null) { + c.close(); + c = null; + } + } + + // go through remaining items + Iterator> iter = mGTaskHashMap.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry entry = iter.next(); + node = entry.getValue(); + doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null); + } + + // mCancelled can be set by another thread, so we neet to check one by + // one + // clear local delete table + if (!mCancelled) { + if (!DataUtils.batchDeleteNotes(mContentResolver, mLocalDeleteIdMap)) { + throw new ActionFailureException("failed to batch-delete local deleted notes"); + } + } + + // refresh local sync id + if (!mCancelled) { + GTaskClient.getInstance().commitUpdate(); + refreshLocalSyncId(); + } + + } + + private void syncFolder() throws NetworkFailureException { + Cursor c = null; + String gid; + Node node; + int syncType; + + if (mCancelled) { + return; + } + + // for root folder + try { + c = mContentResolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, + Notes.ID_ROOT_FOLDER), SqlNote.PROJECTION_NOTE, null, null, null); + if (c != null) { + c.moveToNext(); + gid = c.getString(SqlNote.GTASK_ID_COLUMN); + node = mGTaskHashMap.get(gid); + if (node != null) { + mGTaskHashMap.remove(gid); + mGidToNid.put(gid, (long) Notes.ID_ROOT_FOLDER); + mNidToGid.put((long) Notes.ID_ROOT_FOLDER, gid); + // for system folder, only update remote name if necessary + if (!node.getName().equals( + GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT)) + doContentSync(Node.SYNC_ACTION_UPDATE_REMOTE, node, c); + } else { + doContentSync(Node.SYNC_ACTION_ADD_REMOTE, node, c); + } + } else { + Log.w(TAG, "failed to query root folder"); + } + } finally { + if (c != null) { + c.close(); + c = null; + } + } + + // for call-note folder + try { + c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, "(_id=?)", + new String[] { + String.valueOf(Notes.ID_CALL_RECORD_FOLDER) + }, null); + if (c != null) { + if (c.moveToNext()) { + gid = c.getString(SqlNote.GTASK_ID_COLUMN); + node = mGTaskHashMap.get(gid); + if (node != null) { + mGTaskHashMap.remove(gid); + mGidToNid.put(gid, (long) Notes.ID_CALL_RECORD_FOLDER); + mNidToGid.put((long) Notes.ID_CALL_RECORD_FOLDER, gid); + // for system folder, only update remote name if + // necessary + if (!node.getName().equals( + GTaskStringUtils.MIUI_FOLDER_PREFFIX + + GTaskStringUtils.FOLDER_CALL_NOTE)) + doContentSync(Node.SYNC_ACTION_UPDATE_REMOTE, node, c); + } else { + doContentSync(Node.SYNC_ACTION_ADD_REMOTE, node, c); + } + } + } else { + Log.w(TAG, "failed to query call note folder"); + } + } finally { + if (c != null) { + c.close(); + c = null; + } + } + + // for local existing folders + try { + c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, + "(type=? AND parent_id<>?)", new String[] { + String.valueOf(Notes.TYPE_FOLDER), String.valueOf(Notes.ID_TRASH_FOLER) + }, NoteColumns.TYPE + " DESC"); + if (c != null) { + while (c.moveToNext()) { + gid = c.getString(SqlNote.GTASK_ID_COLUMN); + node = mGTaskHashMap.get(gid); + if (node != null) { + mGTaskHashMap.remove(gid); + mGidToNid.put(gid, c.getLong(SqlNote.ID_COLUMN)); + mNidToGid.put(c.getLong(SqlNote.ID_COLUMN), gid); + syncType = node.getSyncAction(c); + } else { + if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) { + // local add + syncType = Node.SYNC_ACTION_ADD_REMOTE; + } else { + // remote delete + syncType = Node.SYNC_ACTION_DEL_LOCAL; + } + } + doContentSync(syncType, node, c); + } + } else { + Log.w(TAG, "failed to query existing folder"); + } + } finally { + if (c != null) { + c.close(); + c = null; + } + } + + // for remote add folders + Iterator> iter = mGTaskListHashMap.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry entry = iter.next(); + gid = entry.getKey(); + node = entry.getValue(); + if (mGTaskHashMap.containsKey(gid)) { + mGTaskHashMap.remove(gid); + doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null); + } + } + + if (!mCancelled) + GTaskClient.getInstance().commitUpdate(); + } + + private void doContentSync(int syncType, Node node, Cursor c) throws NetworkFailureException { + if (mCancelled) { + return; + } + + MetaData meta; + switch (syncType) { + case Node.SYNC_ACTION_ADD_LOCAL: + addLocalNode(node); + break; + case Node.SYNC_ACTION_ADD_REMOTE: + addRemoteNode(node, c); + break; + case Node.SYNC_ACTION_DEL_LOCAL: + meta = mMetaHashMap.get(c.getString(SqlNote.GTASK_ID_COLUMN)); + if (meta != null) { + GTaskClient.getInstance().deleteNode(meta); + } + mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN)); + break; + case Node.SYNC_ACTION_DEL_REMOTE: + meta = mMetaHashMap.get(node.getGid()); + if (meta != null) { + GTaskClient.getInstance().deleteNode(meta); + } + GTaskClient.getInstance().deleteNode(node); + break; + case Node.SYNC_ACTION_UPDATE_LOCAL: + updateLocalNode(node, c); + break; + case Node.SYNC_ACTION_UPDATE_REMOTE: + updateRemoteNode(node, c); + break; + case Node.SYNC_ACTION_UPDATE_CONFLICT: + // merging both modifications maybe a good idea + // right now just use local update simply + updateRemoteNode(node, c); + break; + case Node.SYNC_ACTION_NONE: + break; + case Node.SYNC_ACTION_ERROR: + default: + throw new ActionFailureException("unkown sync action type"); + } + } + + private void addLocalNode(Node node) throws NetworkFailureException { + if (mCancelled) { + return; + } + + SqlNote sqlNote; + if (node instanceof TaskList) { + if (node.getName().equals( + GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT)) { + sqlNote = new SqlNote(mContext, Notes.ID_ROOT_FOLDER); + } else if (node.getName().equals( + GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_CALL_NOTE)) { + sqlNote = new SqlNote(mContext, Notes.ID_CALL_RECORD_FOLDER); + } else { + sqlNote = new SqlNote(mContext); + sqlNote.setContent(node.getLocalJSONFromContent()); + sqlNote.setParentId(Notes.ID_ROOT_FOLDER); + } + } else { + sqlNote = new SqlNote(mContext); + JSONObject js = node.getLocalJSONFromContent(); + try { + if (js.has(GTaskStringUtils.META_HEAD_NOTE)) { + JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); + if (note.has(NoteColumns.ID)) { + long id = note.getLong(NoteColumns.ID); + if (DataUtils.existInNoteDatabase(mContentResolver, id)) { + // the id is not available, have to create a new one + note.remove(NoteColumns.ID); + } + } + } + + if (js.has(GTaskStringUtils.META_HEAD_DATA)) { + JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA); + for (int i = 0; i < dataArray.length(); i++) { + JSONObject data = dataArray.getJSONObject(i); + if (data.has(DataColumns.ID)) { + long dataId = data.getLong(DataColumns.ID); + if (DataUtils.existInDataDatabase(mContentResolver, dataId)) { + // the data id is not available, have to create + // a new one + data.remove(DataColumns.ID); + } + } + } + + } + } catch (JSONException e) { + Log.w(TAG, e.toString()); + e.printStackTrace(); + } + sqlNote.setContent(js); + + Long parentId = mGidToNid.get(((Task) node).getParent().getGid()); + if (parentId == null) { + Log.e(TAG, "cannot find task's parent id locally"); + throw new ActionFailureException("cannot add local node"); + } + sqlNote.setParentId(parentId.longValue()); + } + + // create the local node + sqlNote.setGtaskId(node.getGid()); + sqlNote.commit(false); + + // update gid-nid mapping + mGidToNid.put(node.getGid(), sqlNote.getId()); + mNidToGid.put(sqlNote.getId(), node.getGid()); + + // update meta + updateRemoteMeta(node.getGid(), sqlNote); + } + + private void updateLocalNode(Node node, Cursor c) throws NetworkFailureException { + if (mCancelled) { + return; + } + + SqlNote sqlNote; + // update the note locally + sqlNote = new SqlNote(mContext, c); + sqlNote.setContent(node.getLocalJSONFromContent()); + + Long parentId = (node instanceof Task) ? mGidToNid.get(((Task) node).getParent().getGid()) + : new Long(Notes.ID_ROOT_FOLDER); + if (parentId == null) { + Log.e(TAG, "cannot find task's parent id locally"); + throw new ActionFailureException("cannot update local node"); + } + sqlNote.setParentId(parentId.longValue()); + sqlNote.commit(true); + + // update meta info + updateRemoteMeta(node.getGid(), sqlNote); + } + + private void addRemoteNode(Node node, Cursor c) throws NetworkFailureException { + if (mCancelled) { + return; + } + + SqlNote sqlNote = new SqlNote(mContext, c); + Node n; + + // update remotely + if (sqlNote.isNoteType()) { + Task task = new Task(); + task.setContentByLocalJSON(sqlNote.getContent()); + + String parentGid = mNidToGid.get(sqlNote.getParentId()); + if (parentGid == null) { + Log.e(TAG, "cannot find task's parent tasklist"); + throw new ActionFailureException("cannot add remote task"); + } + mGTaskListHashMap.get(parentGid).addChildTask(task); + + GTaskClient.getInstance().createTask(task); + n = (Node) task; + + // add meta + updateRemoteMeta(task.getGid(), sqlNote); + } else { + TaskList tasklist = null; + + // we need to skip folder if it has already existed + String folderName = GTaskStringUtils.MIUI_FOLDER_PREFFIX; + if (sqlNote.getId() == Notes.ID_ROOT_FOLDER) + folderName += GTaskStringUtils.FOLDER_DEFAULT; + else if (sqlNote.getId() == Notes.ID_CALL_RECORD_FOLDER) + folderName += GTaskStringUtils.FOLDER_CALL_NOTE; + else + folderName += sqlNote.getSnippet(); + + Iterator> iter = mGTaskListHashMap.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry entry = iter.next(); + String gid = entry.getKey(); + TaskList list = entry.getValue(); + + if (list.getName().equals(folderName)) { + tasklist = list; + if (mGTaskHashMap.containsKey(gid)) { + mGTaskHashMap.remove(gid); + } + break; + } + } + + // no match we can add now + if (tasklist == null) { + tasklist = new TaskList(); + tasklist.setContentByLocalJSON(sqlNote.getContent()); + GTaskClient.getInstance().createTaskList(tasklist); + mGTaskListHashMap.put(tasklist.getGid(), tasklist); + } + n = (Node) tasklist; + } + + // update local note + sqlNote.setGtaskId(n.getGid()); + sqlNote.commit(false); + sqlNote.resetLocalModified(); + sqlNote.commit(true); + + // gid-id mapping + mGidToNid.put(n.getGid(), sqlNote.getId()); + mNidToGid.put(sqlNote.getId(), n.getGid()); + } + + private void updateRemoteNode(Node node, Cursor c) throws NetworkFailureException { + if (mCancelled) { + return; + } + + SqlNote sqlNote = new SqlNote(mContext, c); + + // update remotely + node.setContentByLocalJSON(sqlNote.getContent()); + GTaskClient.getInstance().addUpdateNode(node); + + // update meta + updateRemoteMeta(node.getGid(), sqlNote); + + // move task if necessary + if (sqlNote.isNoteType()) { + Task task = (Task) node; + TaskList preParentList = task.getParent(); + + String curParentGid = mNidToGid.get(sqlNote.getParentId()); + if (curParentGid == null) { + Log.e(TAG, "cannot find task's parent tasklist"); + throw new ActionFailureException("cannot update remote task"); + } + TaskList curParentList = mGTaskListHashMap.get(curParentGid); + + if (preParentList != curParentList) { + preParentList.removeChildTask(task); + curParentList.addChildTask(task); + GTaskClient.getInstance().moveTask(task, preParentList, curParentList); + } + } + + // clear local modified flag + sqlNote.resetLocalModified(); + sqlNote.commit(true); + } + + private void updateRemoteMeta(String gid, SqlNote sqlNote) throws NetworkFailureException { + if (sqlNote != null && sqlNote.isNoteType()) { + MetaData metaData = mMetaHashMap.get(gid); + if (metaData != null) { + metaData.setMeta(gid, sqlNote.getContent()); + GTaskClient.getInstance().addUpdateNode(metaData); + } else { + metaData = new MetaData(); + metaData.setMeta(gid, sqlNote.getContent()); + mMetaList.addChildTask(metaData); + mMetaHashMap.put(gid, metaData); + GTaskClient.getInstance().createTask(metaData); + } + } + } + + private void refreshLocalSyncId() throws NetworkFailureException { + if (mCancelled) { + return; + } + + // get the latest gtask list + mGTaskHashMap.clear(); + mGTaskListHashMap.clear(); + mMetaHashMap.clear(); + initGTaskList(); + + Cursor c = null; + try { + c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, + "(type<>? AND parent_id<>?)", new String[] { + String.valueOf(Notes.TYPE_SYSTEM), String.valueOf(Notes.ID_TRASH_FOLER) + }, NoteColumns.TYPE + " DESC"); + if (c != null) { + while (c.moveToNext()) { + String gid = c.getString(SqlNote.GTASK_ID_COLUMN); + Node node = mGTaskHashMap.get(gid); + if (node != null) { + mGTaskHashMap.remove(gid); + ContentValues values = new ContentValues(); + values.put(NoteColumns.SYNC_ID, node.getLastModified()); + mContentResolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, + c.getLong(SqlNote.ID_COLUMN)), values, null, null); + } else { + Log.e(TAG, "something is missed"); + throw new ActionFailureException( + "some local items don't have gid after sync"); + } + } + } else { + Log.w(TAG, "failed to query local note to refresh sync id"); + } + } finally { + if (c != null) { + c.close(); + c = null; + } + } + } + + public String getSyncAccount() { + return GTaskClient.getInstance().getSyncAccount().name; + } + + public void cancelSync() { + mCancelled = true; + } +} diff --git a/src/main/java/net/micode/notes/gtask/remote/GTaskSyncService.java b/src/main/java/net/micode/notes/gtask/remote/GTaskSyncService.java new file mode 100644 index 0000000..cca36f7 --- /dev/null +++ b/src/main/java/net/micode/notes/gtask/remote/GTaskSyncService.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.gtask.remote; + +import android.app.Activity; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.IBinder; + +public class GTaskSyncService extends Service { + public final static String ACTION_STRING_NAME = "sync_action_type"; + + public final static int ACTION_START_SYNC = 0; + + public final static int ACTION_CANCEL_SYNC = 1; + + public final static int ACTION_INVALID = 2; + + public final static String GTASK_SERVICE_BROADCAST_NAME = "net.micode.notes.gtask.remote.gtask_sync_service"; + + public final static String GTASK_SERVICE_BROADCAST_IS_SYNCING = "isSyncing"; + + public final static String GTASK_SERVICE_BROADCAST_PROGRESS_MSG = "progressMsg"; + + private static GTaskASyncTask mSyncTask = null; + + private static String mSyncProgress = ""; + + private void startSync() { + if (mSyncTask == null) { + mSyncTask = new GTaskASyncTask(this, new GTaskASyncTask.OnCompleteListener() { + public void onComplete() { + mSyncTask = null; + sendBroadcast(""); + stopSelf(); + } + }); + sendBroadcast(""); + mSyncTask.execute(); + } + } + + private void cancelSync() { + if (mSyncTask != null) { + mSyncTask.cancelSync(); + } + } + + @Override + public void onCreate() { + mSyncTask = null; + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + Bundle bundle = intent.getExtras(); + if (bundle != null && bundle.containsKey(ACTION_STRING_NAME)) { + switch (bundle.getInt(ACTION_STRING_NAME, ACTION_INVALID)) { + case ACTION_START_SYNC: + startSync(); + break; + case ACTION_CANCEL_SYNC: + cancelSync(); + break; + default: + break; + } + return START_STICKY; + } + return super.onStartCommand(intent, flags, startId); + } + + @Override + public void onLowMemory() { + if (mSyncTask != null) { + mSyncTask.cancelSync(); + } + } + + public IBinder onBind(Intent intent) { + return null; + } + + public void sendBroadcast(String msg) { + mSyncProgress = msg; + Intent intent = new Intent(GTASK_SERVICE_BROADCAST_NAME); + intent.putExtra(GTASK_SERVICE_BROADCAST_IS_SYNCING, mSyncTask != null); + intent.putExtra(GTASK_SERVICE_BROADCAST_PROGRESS_MSG, msg); + sendBroadcast(intent); + } + + public static void startSync(Activity activity) { + GTaskManager.getInstance().setActivityContext(activity); + Intent intent = new Intent(activity, GTaskSyncService.class); + intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_START_SYNC); + activity.startService(intent); + } + + public static void cancelSync(Context context) { + Intent intent = new Intent(context, GTaskSyncService.class); + intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_CANCEL_SYNC); + context.startService(intent); + } + + public static boolean isSyncing() { + return mSyncTask != null; + } + + public static String getProgressString() { + return mSyncProgress; + } +} diff --git a/src/main/java/net/micode/notes/model/Note.java b/src/main/java/net/micode/notes/model/Note.java new file mode 100644 index 0000000..864da2c --- /dev/null +++ b/src/main/java/net/micode/notes/model/Note.java @@ -0,0 +1,322 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.model; +import android.content.ContentProviderOperation; +import android.content.ContentProviderResult; +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.Context; +import android.content.OperationApplicationException; +import android.net.Uri; +import android.os.RemoteException; +import android.util.Log; + +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.CallNote; +import net.micode.notes.data.Notes.DataColumns; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.data.Notes.TextNote; + +import java.util.ArrayList; + +/** + * @Package: net.micode.notes.model + * @ClassName: Note + * @Description: 笔记类,用于操作和管理笔记数据 + * @Author: YangYizhe + * @CreateDate: 12/17/2023 10:06 AM + * @UpdateUser: none + * @UpdateDate: 12/17/2023 10:06 AM + * @UpdateRemark: none + * @Version: 1.0 + */ +public class Note { + private ContentValues mNoteDiffValues; + private NoteData mNoteData; + private static final String TAG = "Note"; + /** + * @method getNewNoteId + * @description 获取新的笔记ID,用于向数据库中添加新的笔记 + * @date: 12/20/2023 11:23 PM + * @author: YangYizhe + * @param folderId 文件夹Id + * @return noteId 新的笔记Id + */ + public static synchronized long getNewNoteId(Context context, long folderId) { + // Create a new note in the database + // 在数据库中创建一个新的笔记 + ContentValues values = new ContentValues(); + long createdTime = System.currentTimeMillis(); + values.put(NoteColumns.CREATED_DATE, createdTime); + values.put(NoteColumns.MODIFIED_DATE, createdTime); + values.put(NoteColumns.TYPE, Notes.TYPE_NOTE); + values.put(NoteColumns.LOCAL_MODIFIED, 1); + values.put(NoteColumns.PARENT_ID, folderId); + Uri uri = context.getContentResolver().insert(Notes.CONTENT_NOTE_URI, values); + + long noteId = 0; + try { + noteId = Long.valueOf(uri.getPathSegments().get(1)); + } catch (NumberFormatException e) { + Log.e(TAG, "Get note id error :" + e.toString()); + noteId = 0; + } + if (noteId == -1) { + throw new IllegalStateException("Wrong note id:" + noteId); + } + return noteId; + } + + public Note() { + mNoteDiffValues = new ContentValues(); + mNoteData = new NoteData(); + } + /** + * @method setNoteValue + * @description 设置笔记的值 + * @date: 12/20/2023 11:31 PM + * @author: YangYizhe + * @param key + * @param value + */ + public void setNoteValue(String key, String value) { + mNoteDiffValues.put(key, value); + mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); + mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); + } + /** + * @method setTextData + * @description 设置文本数据 + * @date: 12/20/2023 11:31 PM + * @author: YangYizhe + * @param key + * @param value + */ + public void setTextData(String key, String value) { + mNoteData.setTextData(key, value); + } + /** + * @method setTextDataId + * @description 设置文本数据ID + * @date: 12/20/2023 11:33 PM + * @author: YangYizhe + * @param id + */ + public void setTextDataId(long id) { + mNoteData.setTextDataId(id); + } + /** + * @method getTextDataId + * @description 获取文本数据Id + * @date: 12/20/2023 11:34 PM + * @author: YangYizhe + * @return mTextDataId 文本数据Id + */ + public long getTextDataId() { + return mNoteData.mTextDataId; + } + /** + * @method setCallDataId + * @description 设置通话记录数据ID + * @date: 12/20/2023 11:35 PM + * @author: YangYizhe + * @param id 通话记录数据id + */ + public void setCallDataId(long id) { + mNoteData.setCallDataId(id); + } + /** + * @method setCallData + * @description 设置通话记录数据 + * @date: 12/20/2023 11:36 PM + * @author: YangYizhe + * @param key + * @param value + */ + public void setCallData(String key, String value) { + mNoteData.setCallData(key, value); + } + /** + * @method isLocalModified + * @description 判断笔记是否有本地修改 + * @date: 12/20/2023 11:38 PM + * @author: YangYizhe + * @return bool 是否有本地修改 + */ + public boolean isLocalModified() { + return mNoteDiffValues.size() > 0 || mNoteData.isLocalModified(); + } + /** + * @method syncNote + * @description 同步笔记,将本地修改的数据同步到服务器 + * @date: 12/20/2023 11:42 PM + * @author: YangYizhe + * @param context 上下文对象 + * @param noteId 笔记ID + * @return bool 是否同步成功 + */ + public boolean syncNote(Context context, long noteId) { + if (noteId <= 0) { + throw new IllegalArgumentException("Wrong note id:" + noteId); + } + + if (!isLocalModified()) { + return true; + } + + /** + * In theory, once data changed, the note should be updated on {@link NoteColumns#LOCAL_MODIFIED} and + * {@link NoteColumns#MODIFIED_DATE}. For data safety, though update note fails, we also update the + * note data info + */ + if (context.getContentResolver().update( + ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), mNoteDiffValues, null, + null) == 0) { + Log.e(TAG, "Update note error, should not happen"); + // Do not return, fall through + } + mNoteDiffValues.clear(); + + if (mNoteData.isLocalModified() + && (mNoteData.pushIntoContentResolver(context, noteId) == null)) { + return false; + } + + return true; + } + + private class NoteData { + private long mTextDataId; + + private ContentValues mTextDataValues; + + private long mCallDataId; + + private ContentValues mCallDataValues; + + private static final String TAG = "NoteData"; + + public NoteData() { + mTextDataValues = new ContentValues(); + mCallDataValues = new ContentValues(); + mTextDataId = 0; + mCallDataId = 0; + } + + boolean isLocalModified() { + return mTextDataValues.size() > 0 || mCallDataValues.size() > 0; + } + + void setTextDataId(long id) { + if(id <= 0) { + throw new IllegalArgumentException("Text data id should larger than 0"); + } + mTextDataId = id; + } + + void setCallDataId(long id) { + if (id <= 0) { + throw new IllegalArgumentException("Call data id should larger than 0"); + } + mCallDataId = id; + } + + void setCallData(String key, String value) { + mCallDataValues.put(key, value); + mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); + mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); + } + + void setTextData(String key, String value) { + mTextDataValues.put(key, value); + mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); + mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); + } + + Uri pushIntoContentResolver(Context context, long noteId) { + /** + * Check for safety + */ + if (noteId <= 0) { + throw new IllegalArgumentException("Wrong note id:" + noteId); + } + + ArrayList operationList = new ArrayList(); + ContentProviderOperation.Builder builder = null; + + if(mTextDataValues.size() > 0) { + mTextDataValues.put(DataColumns.NOTE_ID, noteId); + if (mTextDataId == 0) { + mTextDataValues.put(DataColumns.MIME_TYPE, TextNote.CONTENT_ITEM_TYPE); + Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI, + mTextDataValues); + try { + setTextDataId(Long.valueOf(uri.getPathSegments().get(1))); + } catch (NumberFormatException e) { + Log.e(TAG, "Insert new text data fail with noteId" + noteId); + mTextDataValues.clear(); + return null; + } + } else { + builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId( + Notes.CONTENT_DATA_URI, mTextDataId)); + builder.withValues(mTextDataValues); + operationList.add(builder.build()); + } + mTextDataValues.clear(); + } + + if(mCallDataValues.size() > 0) { + mCallDataValues.put(DataColumns.NOTE_ID, noteId); + if (mCallDataId == 0) { + mCallDataValues.put(DataColumns.MIME_TYPE, CallNote.CONTENT_ITEM_TYPE); + Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI, + mCallDataValues); + try { + setCallDataId(Long.valueOf(uri.getPathSegments().get(1))); + } catch (NumberFormatException e) { + Log.e(TAG, "Insert new call data fail with noteId" + noteId); + mCallDataValues.clear(); + return null; + } + } else { + builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId( + Notes.CONTENT_DATA_URI, mCallDataId)); + builder.withValues(mCallDataValues); + operationList.add(builder.build()); + } + mCallDataValues.clear(); + } + + if (operationList.size() > 0) { + try { + ContentProviderResult[] results = context.getContentResolver().applyBatch( + Notes.AUTHORITY, operationList); + return (results == null || results.length == 0 || results[0] == null) ? null + : ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId); + } catch (RemoteException e) { + Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); + return null; + } catch (OperationApplicationException e) { + Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); + return null; + } + } + return null; + } + } +} diff --git a/src/main/java/net/micode/notes/model/WorkingNote.java b/src/main/java/net/micode/notes/model/WorkingNote.java new file mode 100644 index 0000000..31061f9 --- /dev/null +++ b/src/main/java/net/micode/notes/model/WorkingNote.java @@ -0,0 +1,417 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.model; + +import android.appwidget.AppWidgetManager; +import android.content.ContentUris; +import android.content.Context; +import android.database.Cursor; +import android.text.TextUtils; +import android.util.Log; + +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.CallNote; +import net.micode.notes.data.Notes.DataColumns; +import net.micode.notes.data.Notes.DataConstants; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.data.Notes.TextNote; +import net.micode.notes.tool.ResourceParser.NoteBgResources; + +/** + * @Package: net.micode.notes.model + * @ClassName: WorkingNote + * @Description: + * 工作笔记类,用于表示一条工作笔记的信息 + * Note 类是一个基本的笔记类,包含了笔记的基本信息,如标题、内容等 + * WorkingNote 类则是在 Note 类的基础上进行扩展,增加了一些与工作笔记相关的属性和方法,例如提醒日期、背景颜色、小部件等。 + * @Author: YangYizhe + * @CreateDate: 12/20/2023 11:48 PM + * @Version: 1.0 + */ +public class WorkingNote { + // Note for the working note + private Note mNote; + // Note Id + private long mNoteId; + // Note content + private String mContent; + // Note mode + private int mMode; + + private long mAlertDate; + + private long mModifiedDate; + + private int mBgColorId; + + private int mWidgetId; + + private int mWidgetType; + + private long mFolderId; + + private Context mContext; + + private static final String TAG = "WorkingNote"; + + private boolean mIsDeleted; + /** + * 笔记设置状态变化监听器 + */ + private NoteSettingChangedListener mNoteSettingStatusListener; + + public static final String[] DATA_PROJECTION = new String[] { + DataColumns.ID, + DataColumns.CONTENT, + DataColumns.MIME_TYPE, + DataColumns.DATA1, + DataColumns.DATA2, + DataColumns.DATA3, + DataColumns.DATA4, + }; + + public static final String[] NOTE_PROJECTION = new String[] { + NoteColumns.PARENT_ID, + NoteColumns.ALERTED_DATE, + NoteColumns.BG_COLOR_ID, + NoteColumns.WIDGET_ID, + NoteColumns.WIDGET_TYPE, + NoteColumns.MODIFIED_DATE + }; + + private static final int DATA_ID_COLUMN = 0; + + private static final int DATA_CONTENT_COLUMN = 1; + + private static final int DATA_MIME_TYPE_COLUMN = 2; + + private static final int DATA_MODE_COLUMN = 3; + + private static final int NOTE_PARENT_ID_COLUMN = 0; + + private static final int NOTE_ALERTED_DATE_COLUMN = 1; + + private static final int NOTE_BG_COLOR_ID_COLUMN = 2; + + private static final int NOTE_WIDGET_ID_COLUMN = 3; + + private static final int NOTE_WIDGET_TYPE_COLUMN = 4; + + private static final int NOTE_MODIFIED_DATE_COLUMN = 5; + + // New note construct + /** + * @method WorkingNote + * @description 构造方法,创建一个新的工作笔记 + * @date: 12/20/2023 11:54 PM + * @author: YangYizhe + * @param context 上下文环境 + * @param folderId 笔记所属的文件夹ID + */ + private WorkingNote(Context context, long folderId) { + mContext = context; + mAlertDate = 0; + mModifiedDate = System.currentTimeMillis(); + mFolderId = folderId; + mNote = new Note(); + mNoteId = 0; + mIsDeleted = false; + mMode = 0; + mWidgetType = Notes.TYPE_WIDGET_INVALIDE; + } + /** + * @method WorkingNote + * @description 构造方法,加载一个已存在的工作笔记 + * @date: 12/20/2023 11:54 PM + * @author: YangYizhe + * @param context 上下文环境 + * @param noteId 笔记的ID + * @param folderId 笔记所属的文件夹ID + */ + // Existing note construct + private WorkingNote(Context context, long noteId, long folderId) { + mContext = context; + mNoteId = noteId; + mFolderId = folderId; + mIsDeleted = false; + mNote = new Note(); + loadNote(); + } + /** + * @method loadNote + * @description 加载笔记的详细信息 + * @date: 12/20/2023 11:55 PM + * @author: YangYizhe + */ + private void loadNote() { + Cursor cursor = mContext.getContentResolver().query( + ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mNoteId), NOTE_PROJECTION, null, + null, null); + + if (cursor != null) { + if (cursor.moveToFirst()) { + mFolderId = cursor.getLong(NOTE_PARENT_ID_COLUMN); + mBgColorId = cursor.getInt(NOTE_BG_COLOR_ID_COLUMN); + mWidgetId = cursor.getInt(NOTE_WIDGET_ID_COLUMN); + mWidgetType = cursor.getInt(NOTE_WIDGET_TYPE_COLUMN); + mAlertDate = cursor.getLong(NOTE_ALERTED_DATE_COLUMN); + mModifiedDate = cursor.getLong(NOTE_MODIFIED_DATE_COLUMN); + } + cursor.close(); + } else { + Log.e(TAG, "No note with id:" + mNoteId); + throw new IllegalArgumentException("Unable to find note with id " + mNoteId); + } + loadNoteData(); + } + /** + * @method loadNoteData + * @description 加载笔记的Data + * @date: 12/20/2023 11:55 PM + * @author: YangYizhe + */ + private void loadNoteData() { + Cursor cursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, DATA_PROJECTION, + DataColumns.NOTE_ID + "=?", new String[] { + String.valueOf(mNoteId) + }, null); + + if (cursor != null) { + if (cursor.moveToFirst()) { + do { + String type = cursor.getString(DATA_MIME_TYPE_COLUMN); + if (DataConstants.NOTE.equals(type)) { + mContent = cursor.getString(DATA_CONTENT_COLUMN); + mMode = cursor.getInt(DATA_MODE_COLUMN); + mNote.setTextDataId(cursor.getLong(DATA_ID_COLUMN)); + } else if (DataConstants.CALL_NOTE.equals(type)) { + mNote.setCallDataId(cursor.getLong(DATA_ID_COLUMN)); + } else { + Log.d(TAG, "Wrong note type with type:" + type); + } + } while (cursor.moveToNext()); + } + cursor.close(); + } else { + Log.e(TAG, "No data with id:" + mNoteId); + throw new IllegalArgumentException("Unable to find note's data with id " + mNoteId); + } + } + /** + * @method createEmptyNote + * @description 创建一个空的工作笔记 + * @date: 12/20/2023 11:56 PM + * @author: YangYizhe + * @param context 上下文环境 + * @param folderId 笔记所属的文件夹ID + * @param widgetId 笔记的小部件ID + * @param widgetType 笔记的小部件类型 + * @param defaultBgColorId 笔记的默认背景颜色ID + * @return note 创建的空的工作笔记对象 + */ + public static WorkingNote createEmptyNote(Context context, long folderId, int widgetId, + int widgetType, int defaultBgColorId) { + WorkingNote note = new WorkingNote(context, folderId); + note.setBgColorId(defaultBgColorId); + note.setWidgetId(widgetId); + note.setWidgetType(widgetType); + return note; + } + + public static WorkingNote load(Context context, long id) { + return new WorkingNote(context, id, 0); + } + + public synchronized boolean saveNote() { + if (isWorthSaving()) { + if (!existInDatabase()) { + if ((mNoteId = Note.getNewNoteId(mContext, mFolderId)) == 0) { + Log.e(TAG, "Create new note fail with id:" + mNoteId); + return false; + } + } + + mNote.syncNote(mContext, mNoteId); + + /** + * Update widget content if there exist any widget of this note + */ + if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID + && mWidgetType != Notes.TYPE_WIDGET_INVALIDE + && mNoteSettingStatusListener != null) { + mNoteSettingStatusListener.onWidgetChanged(); + } + return true; + } else { + return false; + } + } + + public boolean existInDatabase() { + return mNoteId > 0; + } + + private boolean isWorthSaving() { + if (mIsDeleted || (!existInDatabase() && TextUtils.isEmpty(mContent)) + || (existInDatabase() && !mNote.isLocalModified())) { + return false; + } else { + return true; + } + } + + public void setOnSettingStatusChangedListener(NoteSettingChangedListener l) { + mNoteSettingStatusListener = l; + } + + public void setAlertDate(long date, boolean set) { + if (date != mAlertDate) { + mAlertDate = date; + mNote.setNoteValue(NoteColumns.ALERTED_DATE, String.valueOf(mAlertDate)); + } + if (mNoteSettingStatusListener != null) { + mNoteSettingStatusListener.onClockAlertChanged(date, set); + } + } + + public void markDeleted(boolean mark) { + mIsDeleted = mark; + if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID + && mWidgetType != Notes.TYPE_WIDGET_INVALIDE && mNoteSettingStatusListener != null) { + mNoteSettingStatusListener.onWidgetChanged(); + } + } + + public void setBgColorId(int id) { + if (id != mBgColorId) { + mBgColorId = id; + if (mNoteSettingStatusListener != null) { + mNoteSettingStatusListener.onBackgroundColorChanged(); + } + mNote.setNoteValue(NoteColumns.BG_COLOR_ID, String.valueOf(id)); + } + } + + public void setCheckListMode(int mode) { + if (mMode != mode) { + if (mNoteSettingStatusListener != null) { + mNoteSettingStatusListener.onCheckListModeChanged(mMode, mode); + } + mMode = mode; + mNote.setTextData(TextNote.MODE, String.valueOf(mMode)); + } + } + + public void setWidgetType(int type) { + if (type != mWidgetType) { + mWidgetType = type; + mNote.setNoteValue(NoteColumns.WIDGET_TYPE, String.valueOf(mWidgetType)); + } + } + + public void setWidgetId(int id) { + if (id != mWidgetId) { + mWidgetId = id; + mNote.setNoteValue(NoteColumns.WIDGET_ID, String.valueOf(mWidgetId)); + } + } + + public void setWorkingText(String text) { + if (!TextUtils.equals(mContent, text)) { + mContent = text; + mNote.setTextData(DataColumns.CONTENT, mContent); + } + } + + public void convertToCallNote(String phoneNumber, long callDate) { + mNote.setCallData(CallNote.CALL_DATE, String.valueOf(callDate)); + mNote.setCallData(CallNote.PHONE_NUMBER, phoneNumber); + mNote.setNoteValue(NoteColumns.PARENT_ID, String.valueOf(Notes.ID_CALL_RECORD_FOLDER)); + } + + public boolean hasClockAlert() { + return (mAlertDate > 0 ? true : false); + } + + public String getContent() { + return mContent; + } + + public long getAlertDate() { + return mAlertDate; + } + + public long getModifiedDate() { + return mModifiedDate; + } + + public int getBgColorResId() { + return NoteBgResources.getNoteBgResource(mBgColorId); + } + + public int getBgColorId() { + return mBgColorId; + } + + public int getTitleBgResId() { + return NoteBgResources.getNoteTitleBgResource(mBgColorId); + } + + public int getCheckListMode() { + return mMode; + } + + public long getNoteId() { + return mNoteId; + } + + public long getFolderId() { + return mFolderId; + } + + public int getWidgetId() { + return mWidgetId; + } + + public int getWidgetType() { + return mWidgetType; + } + + public interface NoteSettingChangedListener { + /** + * Called when the background color of current note has just changed + */ + void onBackgroundColorChanged(); + + /** + * Called when user set clock + */ + void onClockAlertChanged(long date, boolean set); + + /** + * Call when user create note from widget + */ + void onWidgetChanged(); + + /** + * Call when switch between check list mode and normal mode + * @param oldMode is previous mode before change + * @param newMode is new mode + */ + void onCheckListModeChanged(int oldMode, int newMode); + } +} diff --git a/src/main/java/net/micode/notes/tool/BackupUtils.java b/src/main/java/net/micode/notes/tool/BackupUtils.java new file mode 100644 index 0000000..39f6ec4 --- /dev/null +++ b/src/main/java/net/micode/notes/tool/BackupUtils.java @@ -0,0 +1,344 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.tool; + +import android.content.Context; +import android.database.Cursor; +import android.os.Environment; +import android.text.TextUtils; +import android.text.format.DateFormat; +import android.util.Log; + +import net.micode.notes.R; +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.DataColumns; +import net.micode.notes.data.Notes.DataConstants; +import net.micode.notes.data.Notes.NoteColumns; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; + + +public class BackupUtils { + private static final String TAG = "BackupUtils"; + // 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 final int STATE_SD_CARD_UNMOUONTED = 0; + // The backup file not exist + public static final int STATE_BACKUP_FILE_NOT_EXIST = 1; + // 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; + + private TextExport mTextExport; + + private BackupUtils(Context context) { + mTextExport = new TextExport(context); + } + + private static boolean externalStorageAvailable() { + return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()); + } + + public int exportToText() { + return mTextExport.exportToText(); + } + + public String getExportedTextFileName() { + return mTextExport.mFileName; + } + + public String getExportedTextFileDir() { + return mTextExport.mFileDirectory; + } + + private static class TextExport { + private static final String[] NOTE_PROJECTION = { + NoteColumns.ID, + NoteColumns.MODIFIED_DATE, + NoteColumns.SNIPPET, + NoteColumns.TYPE + }; + + 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 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; + + public TextExport(Context context) { + TEXT_FORMAT = context.getResources().getStringArray(R.array.format_for_exported_note); + mContext = context; + mFileName = ""; + mFileDirectory = ""; + } + + private String getFormat(int id) { + return TEXT_FORMAT[id]; + } + + /** + * Export the folder identified by folder id to text + */ + private void exportFolderToText(String folderId, PrintStream ps) { + // Query notes belong to this folder + Cursor notesCursor = mContext.getContentResolver().query(Notes.CONTENT_NOTE_URI, + NOTE_PROJECTION, NoteColumns.PARENT_ID + "=?", new String[] { + folderId + }, null); + + if (notesCursor != null) { + if (notesCursor.moveToFirst()) { + do { + // Print note's last modified date + ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format( + mContext.getString(R.string.format_datetime_mdhm), + notesCursor.getLong(NOTE_COLUMN_MODIFIED_DATE)))); + // Query data belong to this note + String noteId = notesCursor.getString(NOTE_COLUMN_ID); + exportNoteToText(noteId, ps); + } while (notesCursor.moveToNext()); + } + notesCursor.close(); + } + } + + /** + * Export note identified by id to a print stream + */ + private void exportNoteToText(String noteId, PrintStream ps) { + Cursor dataCursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, + DATA_PROJECTION, DataColumns.NOTE_ID + "=?", new String[] { + noteId + }, null); + + if (dataCursor != null) { + if (dataCursor.moveToFirst()) { + do { + String mimeType = dataCursor.getString(DATA_COLUMN_MIME_TYPE); + if (DataConstants.CALL_NOTE.equals(mimeType)) { + // Print phone number + String phoneNumber = dataCursor.getString(DATA_COLUMN_PHONE_NUMBER); + long callDate = dataCursor.getLong(DATA_COLUMN_CALL_DATE); + String location = dataCursor.getString(DATA_COLUMN_CONTENT); + + if (!TextUtils.isEmpty(phoneNumber)) { + ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), + phoneNumber)); + } + // Print call date + ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), DateFormat + .format(mContext.getString(R.string.format_datetime_mdhm), + callDate))); + // Print call attachment location + if (!TextUtils.isEmpty(location)) { + ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), + location)); + } + } else if (DataConstants.NOTE.equals(mimeType)) { + String content = dataCursor.getString(DATA_COLUMN_CONTENT); + if (!TextUtils.isEmpty(content)) { + ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), + content)); + } + } + } while (dataCursor.moveToNext()); + } + dataCursor.close(); + } + // print a line separator between note + try { + ps.write(new byte[] { + Character.LINE_SEPARATOR, Character.LETTER_NUMBER + }); + } catch (IOException e) { + Log.e(TAG, e.toString()); + } + } + + /** + * Note will be exported as text which is user readable + */ + public int exportToText() { + if (!externalStorageAvailable()) { + Log.d(TAG, "Media was not mounted"); + return STATE_SD_CARD_UNMOUONTED; + } + + PrintStream ps = getExportToTextPrintStream(); + if (ps == null) { + Log.e(TAG, "get print stream error"); + return STATE_SYSTEM_ERROR; + } + // First export folder and its notes + Cursor folderCursor = mContext.getContentResolver().query( + Notes.CONTENT_NOTE_URI, + NOTE_PROJECTION, + "(" + NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER + " AND " + + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + ") OR " + + NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER, null, null); + + if (folderCursor != null) { + if (folderCursor.moveToFirst()) { + do { + // Print folder's name + String folderName = ""; + if(folderCursor.getLong(NOTE_COLUMN_ID) == Notes.ID_CALL_RECORD_FOLDER) { + folderName = mContext.getString(R.string.call_record_folder_name); + } else { + folderName = folderCursor.getString(NOTE_COLUMN_SNIPPET); + } + if (!TextUtils.isEmpty(folderName)) { + ps.println(String.format(getFormat(FORMAT_FOLDER_NAME), folderName)); + } + String folderId = folderCursor.getString(NOTE_COLUMN_ID); + exportFolderToText(folderId, ps); + } while (folderCursor.moveToNext()); + } + folderCursor.close(); + } + + // Export notes in root's folder + Cursor noteCursor = mContext.getContentResolver().query( + Notes.CONTENT_NOTE_URI, + NOTE_PROJECTION, + NoteColumns.TYPE + "=" + +Notes.TYPE_NOTE + " AND " + NoteColumns.PARENT_ID + + "=0", null, null); + + if (noteCursor != null) { + if (noteCursor.moveToFirst()) { + do { + ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format( + mContext.getString(R.string.format_datetime_mdhm), + noteCursor.getLong(NOTE_COLUMN_MODIFIED_DATE)))); + // Query data belong to this note + String noteId = noteCursor.getString(NOTE_COLUMN_ID); + exportNoteToText(noteId, ps); + } while (noteCursor.moveToNext()); + } + noteCursor.close(); + } + ps.close(); + + return STATE_SUCCESS; + } + + /** + * Get a print stream pointed to the file {@generateExportedTextFile} + */ + private PrintStream getExportToTextPrintStream() { + 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"); + return null; + } + mFileName = file.getName(); + mFileDirectory = mContext.getString(R.string.file_path); + PrintStream ps = null; + try { + FileOutputStream fos = new FileOutputStream(file); + ps = new PrintStream(fos); + } catch (FileNotFoundException e) { + e.printStackTrace(); + return null; + } catch (NullPointerException e) { + e.printStackTrace(); + return null; + } + return ps; + } + } + + /** + * Generate the text file to store imported data + */ + private static File generateFileMountedOnSDcard(Context context, int filePathResId, int fileNameFormatResId) { + StringBuilder sb = new StringBuilder(); + sb.append(Environment.getExternalStorageDirectory()); + sb.append(context.getString(filePathResId)); + File filedir = new File(sb.toString()); + sb.append(context.getString( + fileNameFormatResId, + DateFormat.format(context.getString(R.string.format_date_ymd), + System.currentTimeMillis()))); + File file = new File(sb.toString()); + + try { + if (!filedir.exists()) { + filedir.mkdir(); + } + if (!file.exists()) { + file.createNewFile(); + } + return file; + } catch (SecurityException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + + return null; + } +} + + diff --git a/src/main/java/net/micode/notes/tool/DataUtils.java b/src/main/java/net/micode/notes/tool/DataUtils.java new file mode 100644 index 0000000..2a14982 --- /dev/null +++ b/src/main/java/net/micode/notes/tool/DataUtils.java @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.tool; + +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.os.RemoteException; +import android.util.Log; + +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.CallNote; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute; + +import java.util.ArrayList; +import java.util.HashSet; + + +public class DataUtils { + public static final String TAG = "DataUtils"; + public static boolean batchDeleteNotes(ContentResolver resolver, HashSet ids) { + if (ids == null) { + Log.d(TAG, "the ids is null"); + return true; + } + if (ids.size() == 0) { + Log.d(TAG, "no id is in the hashset"); + return true; + } + + ArrayList operationList = new ArrayList(); + for (long id : ids) { + if(id == Notes.ID_ROOT_FOLDER) { + Log.e(TAG, "Don't delete system folder root"); + continue; + } + ContentProviderOperation.Builder builder = ContentProviderOperation + .newDelete(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id)); + operationList.add(builder.build()); + } + try { + ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList); + if (results == null || results.length == 0 || results[0] == null) { + Log.d(TAG, "delete notes failed, ids:" + ids.toString()); + 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; + } + + public static void moveNoteToFoler(ContentResolver resolver, long id, long srcFolderId, long desFolderId) { + ContentValues values = new ContentValues(); + values.put(NoteColumns.PARENT_ID, desFolderId); + 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); + } + + public static boolean batchMoveToFolder(ContentResolver resolver, HashSet ids, + long folderId) { + if (ids == null) { + Log.d(TAG, "the ids is null"); + return true; + } + + ArrayList operationList = new ArrayList(); + for (long id : ids) { + ContentProviderOperation.Builder builder = ContentProviderOperation + .newUpdate(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id)); + builder.withValue(NoteColumns.PARENT_ID, folderId); + builder.withValue(NoteColumns.LOCAL_MODIFIED, 1); + operationList.add(builder.build()); + } + + try { + ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList); + if (results == null || results.length == 0 || results[0] == null) { + Log.d(TAG, "delete notes failed, ids:" + ids.toString()); + 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; + } + + /** + * Get the all folder count except system folders {@link Notes#TYPE_SYSTEM}} + */ + public static int getUserFolderCount(ContentResolver resolver) { + Cursor cursor =resolver.query(Notes.CONTENT_NOTE_URI, + new String[] { "COUNT(*)" }, + NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>?", + new String[] { String.valueOf(Notes.TYPE_FOLDER), String.valueOf(Notes.ID_TRASH_FOLER)}, + 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(); + } + } + } + return count; + } + + public static boolean visibleInNoteDatabase(ContentResolver resolver, long noteId, int type) { + Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), + null, + NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER, + new String [] {String.valueOf(type)}, + null); + + boolean exist = false; + if (cursor != null) { + if (cursor.getCount() > 0) { + exist = true; + } + cursor.close(); + } + return exist; + } + + public static boolean existInNoteDatabase(ContentResolver resolver, long noteId) { + Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), + null, null, null, null); + + boolean exist = false; + if (cursor != null) { + if (cursor.getCount() > 0) { + exist = true; + } + cursor.close(); + } + return exist; + } + + public static boolean existInDataDatabase(ContentResolver resolver, long dataId) { + Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), + null, null, null, null); + + boolean exist = false; + if (cursor != null) { + if (cursor.getCount() > 0) { + exist = true; + } + cursor.close(); + } + return exist; + } + + public static boolean checkVisibleFolderName(ContentResolver resolver, String name) { + Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, null, + NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER + + " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + + " AND " + NoteColumns.SNIPPET + "=?", + new String[] { name }, null); + boolean exist = false; + if(cursor != null) { + if(cursor.getCount() > 0) { + exist = true; + } + cursor.close(); + } + return exist; + } + + public static HashSet getFolderNoteWidget(ContentResolver resolver, long folderId) { + Cursor c = resolver.query(Notes.CONTENT_NOTE_URI, + new String[] { NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE }, + NoteColumns.PARENT_ID + "=?", + new String[] { String.valueOf(folderId) }, + null); + + HashSet set = null; + if (c != null) { + if (c.moveToFirst()) { + set = new HashSet(); + do { + try { + AppWidgetAttribute widget = new AppWidgetAttribute(); + widget.widgetId = c.getInt(0); + widget.widgetType = c.getInt(1); + set.add(widget); + } catch (IndexOutOfBoundsException e) { + Log.e(TAG, e.toString()); + } + } while (c.moveToNext()); + } + c.close(); + } + return set; + } + + public static String getCallNumberByNoteId(ContentResolver resolver, long noteId) { + Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI, + new String [] { CallNote.PHONE_NUMBER }, + CallNote.NOTE_ID + "=? AND " + CallNote.MIME_TYPE + "=?", + new String [] { String.valueOf(noteId), CallNote.CONTENT_ITEM_TYPE }, + null); + + if (cursor != null && cursor.moveToFirst()) { + try { + return cursor.getString(0); + } catch (IndexOutOfBoundsException e) { + Log.e(TAG, "Get call number fails " + e.toString()); + } finally { + cursor.close(); + } + } + return ""; + } + + public static long getNoteIdByPhoneNumberAndCallDate(ContentResolver resolver, String phoneNumber, long callDate) { + Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI, + new String [] { CallNote.NOTE_ID }, + CallNote.CALL_DATE + "=? AND " + CallNote.MIME_TYPE + "=? AND PHONE_NUMBERS_EQUAL(" + + 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; + } + + public static String getSnippetById(ContentResolver resolver, long noteId) { + Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, + new String [] { NoteColumns.SNIPPET }, + NoteColumns.ID + "=?", + new String [] { String.valueOf(noteId)}, + null); + + if (cursor != null) { + String snippet = ""; + if (cursor.moveToFirst()) { + snippet = cursor.getString(0); + } + cursor.close(); + return snippet; + } + throw new IllegalArgumentException("Note is not found with id: " + noteId); + } + + 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; + } +} diff --git a/src/main/java/net/micode/notes/tool/GTaskStringUtils.java b/src/main/java/net/micode/notes/tool/GTaskStringUtils.java new file mode 100644 index 0000000..666b729 --- /dev/null +++ b/src/main/java/net/micode/notes/tool/GTaskStringUtils.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.tool; + +public class GTaskStringUtils { + + public final static String GTASK_JSON_ACTION_ID = "action_id"; + + public final static String GTASK_JSON_ACTION_LIST = "action_list"; + + public final static String GTASK_JSON_ACTION_TYPE = "action_type"; + + public final static String GTASK_JSON_ACTION_TYPE_CREATE = "create"; + + public final static String GTASK_JSON_ACTION_TYPE_GETALL = "get_all"; + + public final static String GTASK_JSON_ACTION_TYPE_MOVE = "move"; + + public final static String GTASK_JSON_ACTION_TYPE_UPDATE = "update"; + + public final static String GTASK_JSON_CREATOR_ID = "creator_id"; + + public final static String GTASK_JSON_CHILD_ENTITY = "child_entity"; + + public final static String GTASK_JSON_CLIENT_VERSION = "client_version"; + + public final static String GTASK_JSON_COMPLETED = "completed"; + + public final static String GTASK_JSON_CURRENT_LIST_ID = "current_list_id"; + + public final static String GTASK_JSON_DEFAULT_LIST_ID = "default_list_id"; + + public final static String GTASK_JSON_DELETED = "deleted"; + + public final static String GTASK_JSON_DEST_LIST = "dest_list"; + + public final static String GTASK_JSON_DEST_PARENT = "dest_parent"; + + public final static String GTASK_JSON_DEST_PARENT_TYPE = "dest_parent_type"; + + public final static String GTASK_JSON_ENTITY_DELTA = "entity_delta"; + + public final static String GTASK_JSON_ENTITY_TYPE = "entity_type"; + + public final static String GTASK_JSON_GET_DELETED = "get_deleted"; + + public final static String GTASK_JSON_ID = "id"; + + public final static String GTASK_JSON_INDEX = "index"; + + public final static String GTASK_JSON_LAST_MODIFIED = "last_modified"; + + public final static String GTASK_JSON_LATEST_SYNC_POINT = "latest_sync_point"; + + public final static String GTASK_JSON_LIST_ID = "list_id"; + + public final static String GTASK_JSON_LISTS = "lists"; + + public final static String GTASK_JSON_NAME = "name"; + + public final static String GTASK_JSON_NEW_ID = "new_id"; + + public final static String GTASK_JSON_NOTES = "notes"; + + public final static String GTASK_JSON_PARENT_ID = "parent_id"; + + public final static String GTASK_JSON_PRIOR_SIBLING_ID = "prior_sibling_id"; + + public final static String GTASK_JSON_RESULTS = "results"; + + public final static String GTASK_JSON_SOURCE_LIST = "source_list"; + + public final static String GTASK_JSON_TASKS = "tasks"; + + public final static String GTASK_JSON_TYPE = "type"; + + public final static String GTASK_JSON_TYPE_GROUP = "GROUP"; + + public final static String GTASK_JSON_TYPE_TASK = "TASK"; + + public final static String GTASK_JSON_USER = "user"; + + public final static String MIUI_FOLDER_PREFFIX = "[MIUI_Notes]"; + + public final static String FOLDER_DEFAULT = "Default"; + + public final static String FOLDER_CALL_NOTE = "Call_Note"; + + public final static String FOLDER_META = "METADATA"; + + public final static String META_HEAD_GTASK_ID = "meta_gid"; + + public final static String META_HEAD_NOTE = "meta_note"; + + public final static String META_HEAD_DATA = "meta_data"; + + public final static String META_NOTE_NAME = "[META INFO] DON'T UPDATE AND DELETE"; + +} diff --git a/src/main/java/net/micode/notes/tool/ResourceParser.java b/src/main/java/net/micode/notes/tool/ResourceParser.java new file mode 100644 index 0000000..1ad3ad6 --- /dev/null +++ b/src/main/java/net/micode/notes/tool/ResourceParser.java @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.tool; + +import android.content.Context; +import android.preference.PreferenceManager; + +import net.micode.notes.R; +import net.micode.notes.ui.NotesPreferenceActivity; + +public class ResourceParser { + + public static final int YELLOW = 0; + public static final int BLUE = 1; + public static final int WHITE = 2; + public static final int GREEN = 3; + public static final int RED = 4; + + public static final int BG_DEFAULT_COLOR = YELLOW; + + public static final int TEXT_SMALL = 0; + public static final int TEXT_MEDIUM = 1; + public static final int TEXT_LARGE = 2; + public static final int TEXT_SUPER = 3; + + public static final int BG_DEFAULT_FONT_SIZE = TEXT_MEDIUM; + + public static class NoteBgResources { + private final static int [] BG_EDIT_RESOURCES = new int [] { + R.drawable.edit_yellow, + R.drawable.edit_blue, + R.drawable.edit_white, + R.drawable.edit_green, + R.drawable.edit_red + }; + + private final static int [] BG_EDIT_TITLE_RESOURCES = new int [] { + R.drawable.edit_title_yellow, + R.drawable.edit_title_blue, + R.drawable.edit_title_white, + R.drawable.edit_title_green, + R.drawable.edit_title_red + }; + + public static int getNoteBgResource(int id) { + return BG_EDIT_RESOURCES[id]; + } + + public static int getNoteTitleBgResource(int id) { + return BG_EDIT_TITLE_RESOURCES[id]; + } + } + + public static int getDefaultBgId(Context context) { + if (PreferenceManager.getDefaultSharedPreferences(context).getBoolean( + NotesPreferenceActivity.PREFERENCE_SET_BG_COLOR_KEY, false)) { + return (int) (Math.random() * NoteBgResources.BG_EDIT_RESOURCES.length); + } else { + return BG_DEFAULT_COLOR; + } + } + + public static class NoteItemBgResources { + private final static int [] BG_FIRST_RESOURCES = new int [] { + R.drawable.list_yellow_up, + R.drawable.list_blue_up, + R.drawable.list_white_up, + R.drawable.list_green_up, + R.drawable.list_red_up + }; + + private final static int [] BG_NORMAL_RESOURCES = new int [] { + R.drawable.list_yellow_middle, + R.drawable.list_blue_middle, + R.drawable.list_white_middle, + R.drawable.list_green_middle, + R.drawable.list_red_middle + }; + + private final static int [] BG_LAST_RESOURCES = new int [] { + R.drawable.list_yellow_down, + R.drawable.list_blue_down, + R.drawable.list_white_down, + R.drawable.list_green_down, + R.drawable.list_red_down, + }; + + private final static int [] BG_SINGLE_RESOURCES = new int [] { + R.drawable.list_yellow_single, + R.drawable.list_blue_single, + R.drawable.list_white_single, + R.drawable.list_green_single, + R.drawable.list_red_single + }; + + public static int getNoteBgFirstRes(int id) { + return BG_FIRST_RESOURCES[id]; + } + + public static int getNoteBgLastRes(int id) { + return BG_LAST_RESOURCES[id]; + } + + public static int getNoteBgSingleRes(int id) { + return BG_SINGLE_RESOURCES[id]; + } + + public static int getNoteBgNormalRes(int id) { + return BG_NORMAL_RESOURCES[id]; + } + + public static int getFolderBgRes() { + return R.drawable.list_folder; + } + } + + public static class WidgetBgResources { + private final static int [] BG_2X_RESOURCES = new int [] { + R.drawable.widget_2x_yellow, + R.drawable.widget_2x_blue, + R.drawable.widget_2x_white, + R.drawable.widget_2x_green, + R.drawable.widget_2x_red, + }; + + public static int getWidget2xBgResource(int id) { + return BG_2X_RESOURCES[id]; + } + + private final static int [] BG_4X_RESOURCES = new int [] { + R.drawable.widget_4x_yellow, + R.drawable.widget_4x_blue, + R.drawable.widget_4x_white, + R.drawable.widget_4x_green, + R.drawable.widget_4x_red + }; + + public static int getWidget4xBgResource(int id) { + return BG_4X_RESOURCES[id]; + } + } + + public static class TextAppearanceResources { + private final static int [] TEXTAPPEARANCE_RESOURCES = new int [] { + R.style.TextAppearanceNormal, + R.style.TextAppearanceMedium, + R.style.TextAppearanceLarge, + R.style.TextAppearanceSuper + }; + + public static int getTexAppearanceResource(int id) { + /** + * HACKME: Fix bug of store the resource id in shared preference. + * The id may larger than the length of resources, in this case, + * return the {@link ResourceParser#BG_DEFAULT_FONT_SIZE} + */ + if (id >= TEXTAPPEARANCE_RESOURCES.length) { + return BG_DEFAULT_FONT_SIZE; + } + return TEXTAPPEARANCE_RESOURCES[id]; + } + + public static int getResourcesSize() { + return TEXTAPPEARANCE_RESOURCES.length; + } + } +} diff --git a/src/main/java/net/micode/notes/ui/AlarmAlertActivity.java b/src/main/java/net/micode/notes/ui/AlarmAlertActivity.java new file mode 100644 index 0000000..96f28bf --- /dev/null +++ b/src/main/java/net/micode/notes/ui/AlarmAlertActivity.java @@ -0,0 +1,201 @@ +package net.micode.notes.ui; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; +import android.content.DialogInterface.OnDismissListener; +import android.content.Intent; +import android.media.AudioManager; +import android.media.MediaPlayer; +import android.media.RingtoneManager; +import android.net.Uri; +import android.os.Bundle; +import android.os.PowerManager; +import android.provider.Settings; +import android.view.Window; +import android.view.WindowManager; + +import net.micode.notes.R; +import net.micode.notes.data.Notes; +import net.micode.notes.tool.DataUtils; + +import java.io.IOException; +/** + * @Package: net.micode.notes.ui + * @ClassName: AlarmAlertActivity + * @Description: 闹钟提醒界面 + * @Author: YangYizhe + * @CreateDate: 12/21/2023 12:02 AM + * @Version: 1.0 + */ +public class AlarmAlertActivity extends Activity implements OnClickListener, OnDismissListener { + private long mNoteId; //文本在数据库存储中的ID号 + private String mSnippet; //闹钟提示时出现的文本片段 + private static final int SNIPPET_PREW_MAX_LEN = 60; + MediaPlayer mPlayer; + /** + * @method onCreate + * @description 当 Activity 创建时调用的生命周期方法 + * @date: 12/21/2023 12:05 AM + * @author: YangYizhe + * @param savedInstanceState 保存 Activity 状态的 Bundle 对象 + * @return null + */ + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + requestWindowFeature(Window.FEATURE_NO_TITLE);//设置界面显示——无标题 + + final Window win = getWindow(); + + win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);//设置窗体属性——在锁屏时显示 + + if (!isScreenOn()) { + win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON//保持点亮、点亮屏幕、允许点亮时解锁 + | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON + | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON + | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR); + } + + Intent intent = getIntent();//获取传递的 Intent + + try { + // 从 Intent 中获取数据并处理——获取标签 ID + mNoteId = Long.valueOf(intent.getData().getPathSegments().get(1)); + // 根据 ID 从数据库中获取标签内容 + mSnippet = DataUtils.getSnippetById(this.getContentResolver(), mNoteId); + // 判断标签片段是否达到符合长度 + mSnippet = mSnippet.length() > SNIPPET_PREW_MAX_LEN ? mSnippet.substring(0, + SNIPPET_PREW_MAX_LEN) + getResources().getString(R.string.notelist_string_info) + : mSnippet; + } catch (IllegalArgumentException e) { + e.printStackTrace(); + return; + } + + mPlayer = new MediaPlayer(); + if (DataUtils.visibleInNoteDatabase(getContentResolver(), mNoteId, Notes.TYPE_NOTE)) { + showActionDialog();// 显示对话框 + playAlarmSound();// 播放闹钟提示音 + } else { + finish();// 结束当前 Activity + } + } + /** + * @method isScreenOn + * @description + * 判断屏幕是否锁屏,调用系统函数判断,最后返回值是布尔类型 + * @date: 12/23/2023 11:21 PM + * @author: YangYizhe + * @param + * @return + */ + private boolean isScreenOn() { + PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); + return pm.isScreenOn(); + } + /** + * @method playAlarmSound + * @description + * 播放闹钟提示音 + * @date: 12/23/2023 11:21 PM + * @author: YangYizhe + */ + private void playAlarmSound() { + Uri url = RingtoneManager.getActualDefaultRingtoneUri(this, RingtoneManager.TYPE_ALARM); + //调用系统的铃声管理URI,得到闹钟提示音 + int silentModeStreams = Settings.System.getInt(getContentResolver(), + Settings.System.MODE_RINGER_STREAMS_AFFECTED, 0); + + if ((silentModeStreams & (1 << AudioManager.STREAM_ALARM)) != 0) { + mPlayer.setAudioStreamType(silentModeStreams); + } else { + mPlayer.setAudioStreamType(AudioManager.STREAM_ALARM); + } + try { + mPlayer.setDataSource(this, url); + mPlayer.prepare(); + mPlayer.setLooping(true);//设置是否循环播放 + mPlayer.start();//开始播放 + } catch (IllegalArgumentException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (SecurityException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IllegalStateException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + /** + * @method showActionDialog + * @description + * AlertDialog的构造方法全部是Protected的 + * 所以不能直接通过new一个AlertDialog来创建出一个AlertDialog。 + * 要创建一个AlertDialog,就要用到AlertDialog.Builder中的create()方法 + * 如这里的dialog就是新建了一个AlertDialog + * @date: 12/23/2023 11:28 PM + * @author: YangYizhe + */ + private void showActionDialog() { + AlertDialog.Builder dialog = new AlertDialog.Builder(this); + dialog.setTitle(R.string.app_name);//为对话框设置标题 + dialog.setMessage(mSnippet);//为对话框设置内容 + dialog.setPositiveButton(R.string.notealert_ok, this);//给对话框添加"Yes"按钮 + if (isScreenOn()) { + dialog.setNegativeButton(R.string.notealert_enter, this);//对话框添加"No"按钮 + } + dialog.show().setOnDismissListener(this); + } + /** + * @method onClick + * @description 处理点击事件 + * @date: 12/23/2023 11:27 PM + * @author: YangYizhe + * @param dialog + * @param which + */ + public void onClick(DialogInterface dialog, int which) { + switch (which) {//用which来选择click后下一步的操作 + case DialogInterface.BUTTON_NEGATIVE://这是取消操作 + Intent intent = new Intent(this, NoteEditActivity.class);//实现两个类间的数据传输 + intent.setAction(Intent.ACTION_VIEW);//设置动作属性 + intent.putExtra(Intent.EXTRA_UID, mNoteId);//实现key-value对 EXTRA_UID为key;mNoteId为键 + startActivity(intent);//开始动作 + break; + default: + break; + } + } + /** + * @method onDismiss + * @description 忽略 + * @date: 12/23/2023 11:25 PM + * @author: YangYizhe + * @param dialog + */ + public void onDismiss(DialogInterface dialog) { + stopAlarmSound();//停止闹钟声音 + finish(); + } + /** + * @method stopAlarmSound + * @description 停止闹钟的声音 + * @date: 12/23/2023 11:26 PM + * @author: YangYizhe + */ + private void stopAlarmSound() { + if (mPlayer != null) { + mPlayer.stop();//停止播放 + mPlayer.release();//释放MediaPlayer对象 + mPlayer = null; + } + } +} \ No newline at end of file diff --git a/src/main/java/net/micode/notes/ui/AlarmInitReceiver.java b/src/main/java/net/micode/notes/ui/AlarmInitReceiver.java new file mode 100644 index 0000000..1982fda --- /dev/null +++ b/src/main/java/net/micode/notes/ui/AlarmInitReceiver.java @@ -0,0 +1,57 @@ +package net.micode.notes.ui; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.ContentUris; +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; + +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.NoteColumns; + +/** + * @Package: net.micode.notes.ui + * @ClassName: AlarmInitReceiver + * @Description: + * AlarmInitReceiver 是一个广播接收器(BroadcastReceiver)的类 + * 广播接收器是 Android 中常用的一种组件,用于接收并处理系统或应用发送的广播消息 + * @Author: YangYizhe + * @CreateDate: 12/21/2023 12:09 AM + * @Version: 1.0 + */ +public class AlarmInitReceiver extends BroadcastReceiver { + private static final String [] PROJECTION = new String [] { + NoteColumns.ID, + NoteColumns.ALERTED_DATE + };//对数据库的操作,调用标签ID和闹钟时间 + private static final int COLUMN_ID = 0; + private static final int COLUMN_ALERTED_DATE = 1; + + @Override + public void onReceive(Context context, Intent intent) { + long currentDate = System.currentTimeMillis();//System.currentTimeMillis()产生一个当前的毫秒 + Cursor c = context.getContentResolver().query(Notes.CONTENT_NOTE_URI, + PROJECTION, + NoteColumns.ALERTED_DATE + ">? AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE, + new String[] { String.valueOf(currentDate) }, + null);//将long变量currentDate转化为字符串 + //Cursor在这里的作用是通过查找数据库中的标签内容,找到和当前系统时间相等的标签 + + if (c != null) { + if (c.moveToFirst()) { + do { + long alertDate = c.getLong(COLUMN_ALERTED_DATE); + Intent sender = new Intent(context, AlarmReceiver.class); + sender.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, c.getLong(COLUMN_ID))); + PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, sender, 0); + AlarmManager alermManager = (AlarmManager) context + .getSystemService(Context.ALARM_SERVICE); + alermManager.set(AlarmManager.RTC_WAKEUP, alertDate, pendingIntent); + } while (c.moveToNext()); + } + c.close(); + } + } +} \ No newline at end of file diff --git a/src/main/java/net/micode/notes/ui/DateTimePicker.java b/src/main/java/net/micode/notes/ui/DateTimePicker.java new file mode 100644 index 0000000..9cf4ba6 --- /dev/null +++ b/src/main/java/net/micode/notes/ui/DateTimePicker.java @@ -0,0 +1,518 @@ +package net.micode.notes.ui; + +import java.text.DateFormatSymbols; +import java.util.Calendar; + +import net.micode.notes.R; + + +import android.content.Context; +import android.text.format.DateFormat; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.NumberPicker; +/** + * @Package: net.micode.notes.ui + * @ClassName: DateTimePicker + * @Description: 继承自FrameLayout,实现了日期和时间的选择功能 + * 提供一个用户界面,让用户可以方便地选择日期和时间,并且能够监听用户对日期和时间的改变 + * 构造方法,包括默认构造方法和带参数的构造方法,用于初始化日期选择器的界面和属性; + * 一系列的回调方法,用于监听日期和时间的改变,包括日期选择、小时选择、分钟选择、上午/下午选择等; + * 一些公开的接口方法,用于设置当前日期、时间,设置24小时模式或12小时模式的切换等; + * 辅助方法,用于更新日期、小时、上午/下午选择器的界面显示和属性; + * 回调接口OnDateTimeChangedListener,用于监听日期和时间的改变事件。 + * @Author: YangYizhe + * @CreateDate: 12/21/2023 12:18 AM + * @Version: 1.0 + */ +public class DateTimePicker extends FrameLayout { + //FrameLayout是布局模板之一 + private static final boolean DEFAULT_ENABLE_STATE = true; + + private static final int HOURS_IN_HALF_DAY = 12; + private static final int HOURS_IN_ALL_DAY = 24; + private static final int DAYS_IN_ALL_WEEK = 7; + private static final int DATE_SPINNER_MIN_VAL = 0; + private static final int DATE_SPINNER_MAX_VAL = DAYS_IN_ALL_WEEK - 1; + private static final int HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW = 0; + private static final int HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW = 23; + private static final int HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW = 1; + private static final int HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW = 12; + private static final int MINUT_SPINNER_MIN_VAL = 0; + private static final int MINUT_SPINNER_MAX_VAL = 59; + private static final int AMPM_SPINNER_MIN_VAL = 0; + private static final int AMPM_SPINNER_MAX_VAL = 1; + /** + * 初始化控件 + * NumberPicker是数字选择器 + * 这里定义的四个变量全部是在设置闹钟时需要选择的变量(如日期、时、分、上午或者下午) + */ + private final NumberPicker mDateSpinner; + private final NumberPicker mHourSpinner; + private final NumberPicker mMinuteSpinner; + private final NumberPicker mAmPmSpinner; + private Calendar mDate;//定义了Calendar类型的变量mDate,用于操作时间 + private String[] mDateDisplayValues = new String[DAYS_IN_ALL_WEEK]; + + private boolean mIsAm; + + private boolean mIs24HourView; + + private boolean mIsEnabled = DEFAULT_ENABLE_STATE; + + private boolean mInitialising; + + private OnDateTimeChangedListener mOnDateTimeChangedListener; + + private NumberPicker.OnValueChangeListener mOnDateChangedListener = new NumberPicker.OnValueChangeListener() { + @Override + public void onValueChange(NumberPicker picker, int oldVal, int newVal) { + mDate.add(Calendar.DAY_OF_YEAR, newVal - oldVal); + updateDateControl(); + onDateTimeChanged(); + } + };//OnValueChangeListener,这是时间改变监听器,这里主要是对日期的监听 + + + private NumberPicker.OnValueChangeListener mOnHourChangedListener = new NumberPicker.OnValueChangeListener() { + //这里是对 小时(Hour) 的监听 + @Override + public void onValueChange(NumberPicker picker, int oldVal, int newVal) { + boolean isDateChanged = false; + Calendar cal = Calendar.getInstance(); + //声明一个Calendar的变量cal,便于后续的操作 + if (!mIs24HourView) { + if (!mIsAm && oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY) { + cal.setTimeInMillis(mDate.getTimeInMillis()); + cal.add(Calendar.DAY_OF_YEAR, 1); + isDateChanged = true; + //这里是对于12小时制时,晚上11点和12点交替时对日期的更改 + } else if (mIsAm && oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) { + cal.setTimeInMillis(mDate.getTimeInMillis()); + cal.add(Calendar.DAY_OF_YEAR, -1); + isDateChanged = true; + } + //这里是对于12小时制时,凌晨11点和12点交替时对日期的更改 + if (oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY || + oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) { + mIsAm = !mIsAm; + updateAmPmControl(); + }//这里是对于12小时制时,中午11点和12点交替时对AM和PM的更改 + } else { + if (oldVal == HOURS_IN_ALL_DAY - 1 && newVal == 0) { + cal.setTimeInMillis(mDate.getTimeInMillis()); + cal.add(Calendar.DAY_OF_YEAR, 1); + isDateChanged = true; + //这里是对于24小时制时,晚上11点和12点交替时对日期的更改 + } else if (oldVal == 0 && newVal == HOURS_IN_ALL_DAY - 1) { + cal.setTimeInMillis(mDate.getTimeInMillis()); + cal.add(Calendar.DAY_OF_YEAR, -1); + isDateChanged = true; + } + } //这里是对于12小时制时,凌晨11点和12点交替时对日期的更改 + int newHour = mHourSpinner.getValue() % HOURS_IN_HALF_DAY + (mIsAm ? 0 : HOURS_IN_HALF_DAY); + //通过数字选择器对newHour的赋值 + mDate.set(Calendar.HOUR_OF_DAY, newHour); + //通过set函数将新的Hour值传给mDate + onDateTimeChanged(); + if (isDateChanged) { + setCurrentYear(cal.get(Calendar.YEAR)); + setCurrentMonth(cal.get(Calendar.MONTH)); + setCurrentDay(cal.get(Calendar.DAY_OF_MONTH)); + } + } + }; + + private NumberPicker.OnValueChangeListener mOnMinuteChangedListener = new NumberPicker.OnValueChangeListener() { + @Override + //这里是对 分钟(Minute)改变的监听 + public void onValueChange(NumberPicker picker, int oldVal, int newVal) { + int minValue = mMinuteSpinner.getMinValue(); + int maxValue = mMinuteSpinner.getMaxValue(); + int offset = 0; + //设置offset,作为小时改变的一个记录数据 + if (oldVal == maxValue && newVal == minValue) { + offset += 1; + } else if (oldVal == minValue && newVal == maxValue) { + offset -= 1; + } + //如果原值为59,新值为0,则offset加1 + //如果原值为0,新值为59,则offset减1 + if (offset != 0) { + mDate.add(Calendar.HOUR_OF_DAY, offset); + mHourSpinner.setValue(getCurrentHour()); + updateDateControl(); + int newHour = getCurrentHourOfDay(); + if (newHour >= HOURS_IN_HALF_DAY) { + mIsAm = false; + updateAmPmControl(); + } else { + mIsAm = true; + updateAmPmControl(); + } + } + mDate.set(Calendar.MINUTE, newVal); + onDateTimeChanged(); + } + }; + + private NumberPicker.OnValueChangeListener mOnAmPmChangedListener = new NumberPicker.OnValueChangeListener() { + //对AM和PM的监听 + @Override + public void onValueChange(NumberPicker picker, int oldVal, int newVal) { + mIsAm = !mIsAm; + if (mIsAm) { + mDate.add(Calendar.HOUR_OF_DAY, -HOURS_IN_HALF_DAY); + } else { + mDate.add(Calendar.HOUR_OF_DAY, HOURS_IN_HALF_DAY); + } + updateAmPmControl(); + onDateTimeChanged(); + } + }; + + public interface OnDateTimeChangedListener { + void onDateTimeChanged(DateTimePicker view, int year, int month, + int dayOfMonth, int hourOfDay, int minute); + } + /** + * 构造方法 + */ + public DateTimePicker(Context context) { + this(context, System.currentTimeMillis());//通过对数据库的访问,获取当前的系统时间 + } + + public DateTimePicker(Context context, long date) { + this(context, date, DateFormat.is24HourFormat(context));//上面函数的得到的是一个天文数字(1970至今的秒数),需要DateFormat将其变得有意义 + } + + public DateTimePicker(Context context, long date, boolean is24HourView) { + super(context); + mDate = Calendar.getInstance();//获取系统时间 + mInitialising = true; + mIsAm = getCurrentHourOfDay() >= HOURS_IN_HALF_DAY; + inflate(context, R.layout.datetime_picker, this); + /* + * 如果当前Activity里用到别的layout,比如对话框layout + * 还要设置这个layout上的其他组件的内容,就必须用inflate()方法先将对话框的layout找出来 + * 然后再用findViewById()找到它上面的其它组件 + */ + mDateSpinner = (NumberPicker) findViewById(R.id.date); + mDateSpinner.setMinValue(DATE_SPINNER_MIN_VAL); + mDateSpinner.setMaxValue(DATE_SPINNER_MAX_VAL); + mDateSpinner.setOnValueChangedListener(mOnDateChangedListener); + + mHourSpinner = (NumberPicker) findViewById(R.id.hour); + mHourSpinner.setOnValueChangedListener(mOnHourChangedListener); + mMinuteSpinner = (NumberPicker) findViewById(R.id.minute); + mMinuteSpinner.setMinValue(MINUT_SPINNER_MIN_VAL); + mMinuteSpinner.setMaxValue(MINUT_SPINNER_MAX_VAL); + mMinuteSpinner.setOnLongPressUpdateInterval(100); + mMinuteSpinner.setOnValueChangedListener(mOnMinuteChangedListener); + + String[] stringsForAmPm = new DateFormatSymbols().getAmPmStrings(); + mAmPmSpinner = (NumberPicker) findViewById(R.id.amPm); + mAmPmSpinner.setMinValue(AMPM_SPINNER_MIN_VAL); + mAmPmSpinner.setMaxValue(AMPM_SPINNER_MAX_VAL); + mAmPmSpinner.setDisplayedValues(stringsForAmPm); + mAmPmSpinner.setOnValueChangedListener(mOnAmPmChangedListener); + + // update controls to initial state + updateDateControl(); + updateHourControl(); + updateAmPmControl(); + + set24HourView(is24HourView); + + // set to current time + setCurrentDate(date); + + setEnabled(isEnabled()); + + // set the content descriptions + mInitialising = false; + } + + /** + * @method setEnabled + * @description + * 用于设置是否启用日期选择器控件的功能 + * 先通过传入的参数enabled判断是否需要改变控件的启用状态。 + * 如果传入的参数和当前的启用状态相同,则直接返回,不进行任何操作 + * 如果传入的参数和当前的启用状态不同,则调用父类的setEnabled方法,来设置整个日期选择器控件的启用状态 + * 分别设置日期选择、分钟选择、小时选择、上午/下午选择这几个子控件的启用状态,即调用对应的setEnabled方法,并将enabled参数传入 + * @date: 12/21/2023 12:27 AM + * @author: YangYizhe + * @param enabled + */ + @Override + public void setEnabled(boolean enabled) { + if (mIsEnabled == enabled) { + return; + } + super.setEnabled(enabled); + mDateSpinner.setEnabled(enabled); + mMinuteSpinner.setEnabled(enabled); + mHourSpinner.setEnabled(enabled); + mAmPmSpinner.setEnabled(enabled); + mIsEnabled = enabled; + } + @Override + public boolean isEnabled() { + return mIsEnabled; + } + + /** + * Get the current date in millis + * + * @return the current date in millis + */ + public long getCurrentDateInTimeMillis() { + return mDate.getTimeInMillis(); + }//实现函数——得到当前的秒数 + + /** + * Set the current date + * + * @param date The current date in millis + */ + public void setCurrentDate(long date) { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(date); + setCurrentDate(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH), + cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE)); + } + + /** + * Set the current date + * + * @param year The current year + * @param month The current month + * @param dayOfMonth The current dayOfMonth + * @param hourOfDay The current hourOfDay + * @param minute The current minute + */ + public void setCurrentDate(int year, int month, + int dayOfMonth, int hourOfDay, int minute) { + setCurrentYear(year); + setCurrentMonth(month); + setCurrentDay(dayOfMonth); + setCurrentHour(hourOfDay); + setCurrentMinute(minute); + } + + /** + * Get current year + * + * @return The current year + */ + public int getCurrentYear() { + return mDate.get(Calendar.YEAR); + } + + /** + * Set current year + * + * @param year The current year + */ + public void setCurrentYear(int year) { + if (!mInitialising && year == getCurrentYear()) { + return; + } + mDate.set(Calendar.YEAR, year); + updateDateControl(); + onDateTimeChanged(); + } + + /** + * Get current month in the year + * + * @return The current month in the year + */ + public int getCurrentMonth() { + return mDate.get(Calendar.MONTH); + } + + /** + * Set current month in the year + * + * @param month The month in the year + */ + public void setCurrentMonth(int month) { + if (!mInitialising && month == getCurrentMonth()) { + return; + } + mDate.set(Calendar.MONTH, month); + updateDateControl(); + onDateTimeChanged(); + } + + /** + * Get current day of the month + * + * @return The day of the month + */ + public int getCurrentDay() { + return mDate.get(Calendar.DAY_OF_MONTH); + } + + /** + * Set current day of the month + * + * @param dayOfMonth The day of the month + */ + public void setCurrentDay(int dayOfMonth) { + if (!mInitialising && dayOfMonth == getCurrentDay()) { + return; + } + mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth); + updateDateControl(); + onDateTimeChanged(); + } + + /** + * Get current hour in 24 hour mode, in the range (0~23) + * @return The current hour in 24 hour mode + */ + public int getCurrentHourOfDay() { + return mDate.get(Calendar.HOUR_OF_DAY); + } + + private int getCurrentHour() { + if (mIs24HourView){ + return getCurrentHourOfDay(); + } else { + int hour = getCurrentHourOfDay(); + if (hour > HOURS_IN_HALF_DAY) { + return hour - HOURS_IN_HALF_DAY; + } else { + return hour == 0 ? HOURS_IN_HALF_DAY : hour; + } + } + } + + /** + * Set current hour in 24 hour mode, in the range (0~23) + * + * @param hourOfDay + */ + public void setCurrentHour(int hourOfDay) { + if (!mInitialising && hourOfDay == getCurrentHourOfDay()) { + return; + } + mDate.set(Calendar.HOUR_OF_DAY, hourOfDay); + if (!mIs24HourView) { + if (hourOfDay >= HOURS_IN_HALF_DAY) { + mIsAm = false; + if (hourOfDay > HOURS_IN_HALF_DAY) { + hourOfDay -= HOURS_IN_HALF_DAY; + } + } else { + mIsAm = true; + if (hourOfDay == 0) { + hourOfDay = HOURS_IN_HALF_DAY; + } + } + updateAmPmControl(); + } + mHourSpinner.setValue(hourOfDay); + onDateTimeChanged(); + } + + /** + * Get currentMinute + * + * @return The Current Minute + */ + public int getCurrentMinute() { + return mDate.get(Calendar.MINUTE); + } + + /** + * Set current minute + */ + public void setCurrentMinute(int minute) { + if (!mInitialising && minute == getCurrentMinute()) { + return; + } + mMinuteSpinner.setValue(minute); + mDate.set(Calendar.MINUTE, minute); + onDateTimeChanged(); + } + + /** + * @return true if this is in 24 hour view else false. + */ + public boolean is24HourView () { + return mIs24HourView; + } + + /** + * Set whether in 24 hour or AM/PM mode. + * + * @param is24HourView True for 24 hour mode. False for AM/PM mode. + */ + public void set24HourView(boolean is24HourView) { + if (mIs24HourView == is24HourView) { + return; + } + mIs24HourView = is24HourView; + mAmPmSpinner.setVisibility(is24HourView ? View.GONE : View.VISIBLE); + int hour = getCurrentHourOfDay(); + updateHourControl(); + setCurrentHour(hour); + updateAmPmControl(); + } + /** + * 几个辅助方法,用于更新日期、上午/下午选择和小时选择控件的显示和属性 + * 这些方法的作用是在选择器控件显示之前或用户改变了日期、时间模式时,更新相应控件的显示和属性,以保持界面的正确性和一致性 + */ + private void updateDateControl() { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(mDate.getTimeInMillis()); + cal.add(Calendar.DAY_OF_YEAR, -DAYS_IN_ALL_WEEK / 2 - 1); + mDateSpinner.setDisplayedValues(null); + for (int i = 0; i < DAYS_IN_ALL_WEEK; ++i) { + cal.add(Calendar.DAY_OF_YEAR, 1); + mDateDisplayValues[i] = (String) DateFormat.format("MM.dd EEEE", cal); + } + mDateSpinner.setDisplayedValues(mDateDisplayValues); + mDateSpinner.setValue(DAYS_IN_ALL_WEEK / 2); + mDateSpinner.invalidate(); + } + + private void updateAmPmControl() { + if (mIs24HourView) { + mAmPmSpinner.setVisibility(View.GONE); + } else { + int index = mIsAm ? Calendar.AM : Calendar.PM; + mAmPmSpinner.setValue(index); + mAmPmSpinner.setVisibility(View.VISIBLE); + } + } + + private void updateHourControl() { + if (mIs24HourView) { + mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW); + mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW); + } else { + mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW); + mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW); + } + } + + /** + * Set the callback that indicates the 'Set' button has been pressed. + * @param callback the callback, if null will do nothing + */ + public void setOnDateTimeChangedListener(OnDateTimeChangedListener callback) { + mOnDateTimeChangedListener = callback; + } + + private void onDateTimeChanged() { + if (mOnDateTimeChangedListener != null) { + mOnDateTimeChangedListener.onDateTimeChanged(this, getCurrentYear(), + getCurrentMonth(), getCurrentDay(), getCurrentHourOfDay(), getCurrentMinute()); + } + } +} \ No newline at end of file diff --git a/src/main/java/net/micode/notes/ui/DateTimePickerDialog.java b/src/main/java/net/micode/notes/ui/DateTimePickerDialog.java new file mode 100644 index 0000000..52640a8 --- /dev/null +++ b/src/main/java/net/micode/notes/ui/DateTimePickerDialog.java @@ -0,0 +1,126 @@ +package net.micode.notes.ui; + +import java.util.Calendar; + +import net.micode.notes.R; +import net.micode.notes.ui.DateTimePicker; +import net.micode.notes.ui.DateTimePicker.OnDateTimeChangedListener; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; +import android.text.format.DateFormat; +import android.text.format.DateUtils; +/** + * @Package: net.micode.notes.ui + * @ClassName: DateTimePickerDialog + * @Description: + * DateTimePickerDialog是一个自定义对话框,允许用户选择日期和时间。 + * 它继承自AlertDialog类,并实现OnClickListener接口。 + * 对话框包含一个DateTimePicker控件,用于选择日期和时间。 + * 通过OnDateTimeSetListener接口将选择的日期和时间传递给监听器。 + * 对话框还提供了设置24小时制和使用选择的日期更新对话框标题的方法。 + * onClick方法处理按钮点击事件,并在日期和时间设置完成时通知监听器。 + * @Author: YangYizhe + * @CreateDate: 12/21/2023 12:32 AM + * @Version: 1.0 + */ +public class DateTimePickerDialog extends AlertDialog implements OnClickListener { + + private Calendar mDate = Calendar.getInstance(); + private boolean mIs24HourView; + private OnDateTimeSetListener mOnDateTimeSetListener; + private DateTimePicker mDateTimePicker; + + /** + * 用于接收用户设置的日期和时间的接口。 + */ + public interface OnDateTimeSetListener { + void OnDateTimeSet(AlertDialog dialog, long date); + } + + /** + * @method DateTimePickerDialog + * @description + * 构造一个新的DateTimePickerDialog + * Remind me功能的对话框 + * @date: 12/21/2023 8:01 AM + * @author: YangYizhe + * @param context 上下文环境。 + * @param date 要在对话框中显示的初始日期。 + */ + public DateTimePickerDialog(Context context, long date) { + super(context); + mDateTimePicker = new DateTimePicker(context); + setView(mDateTimePicker); + + mDateTimePicker.setOnDateTimeChangedListener(new OnDateTimeChangedListener() { + public void onDateTimeChanged(DateTimePicker view, int year, int month, + int dayOfMonth, int hourOfDay, int minute) { + mDate.set(Calendar.YEAR, year); + mDate.set(Calendar.MONTH, month); + mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth); + mDate.set(Calendar.HOUR_OF_DAY, hourOfDay); + mDate.set(Calendar.MINUTE, minute); + updateTitle(mDate.getTimeInMillis()); + } + }); + + mDate.setTimeInMillis(date); + mDate.set(Calendar.SECOND, 0); + mDateTimePicker.setCurrentDate(mDate.getTimeInMillis()); + + setButton(context.getString(R.string.datetime_dialog_ok), this); + setButton2(context.getString(R.string.datetime_dialog_cancel), (OnClickListener) null); + + set24HourView(DateFormat.is24HourFormat(this.getContext())); + updateTitle(mDate.getTimeInMillis()); + } + + /** + * 设置对话框是否为24小时制。 + * + * @param is24HourView 如果对话框为24小时制,则为true;否则为false。 + */ + public void set24HourView(boolean is24HourView) { + mIs24HourView = is24HourView; + } + + /** + * 设置日期和时间设置监听器。 + * + * @param callBack 要通知的监听器。 + */ + public void setOnDateTimeSetListener(OnDateTimeSetListener callBack) { + mOnDateTimeSetListener = callBack; + } + + /** + * 使用指定的日期更新对话框标题。 + * + * @param date 要显示在标题中的日期。 + */ + private void updateTitle(long date) { + int flag = + DateUtils.FORMAT_SHOW_YEAR | + DateUtils.FORMAT_SHOW_DATE | + DateUtils.FORMAT_SHOW_TIME; + flag |= mIs24HourView ? DateUtils.FORMAT_24HOUR : DateUtils.FORMAT_24HOUR; + + setTitle(DateUtils.formatDateTime(this.getContext(), date, flag)); + } + + /** + * 处理按钮点击事件。 + * + * @param arg0 接收到点击事件的对话框。 + * @param arg1 被点击的按钮。 + */ + public void onClick(DialogInterface arg0, int arg1) { + if (mOnDateTimeSetListener != null) { + mOnDateTimeSetListener.OnDateTimeSet(this, mDate.getTimeInMillis()); + } + } + +} \ No newline at end of file diff --git a/src/main/java/net/micode/notes/ui/DropdownMenu.java b/src/main/java/net/micode/notes/ui/DropdownMenu.java new file mode 100644 index 0000000..b437d41 --- /dev/null +++ b/src/main/java/net/micode/notes/ui/DropdownMenu.java @@ -0,0 +1,62 @@ +package net.micode.notes.ui; + +import android.content.Context; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.Button; +import android.widget.PopupMenu; +import android.widget.PopupMenu.OnMenuItemClickListener; + +import net.micode.notes.R; +/** + * @Package: net.micode.notes.ui + * @ClassName: DropdownMenu + * @Description: + * DropdownMenu是一个自定义下拉菜单控件。 + * 它使用Button作为触发器,通过PopupMenu显示菜单选项。 + * 构造函数接收一个上下文环境、一个Button和菜单资源的ID。 + * 通过setOnDropdownMenuItemClickListener方法设置菜单选项的点击监听器。 + * 可以通过findItem方法查找特定的菜单选项。 + * 通过setTitle方法设置下拉菜单的标题 + * @Author: YangYizhe + * @CreateDate: 12/21/2023 12:36 AM + * @Version: 1.0 + */ +public class DropdownMenu { + private Button mButton; + private PopupMenu mPopupMenu; + //声明一个下拉菜单 + private Menu mMenu; + + public DropdownMenu(Context context, Button button, int menuId) { + mButton = button; + mButton.setBackgroundResource(R.drawable.dropdown_icon); + //设置这个view的背景 + mPopupMenu = new PopupMenu(context, mButton); + mMenu = mPopupMenu.getMenu(); + mPopupMenu.getMenuInflater().inflate(menuId, mMenu); + //MenuInflater是用来实例化Menu目录下的Menu布局文件 + //根据ID来确认menu的内容选项 + mButton.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + mPopupMenu.show(); + } + }); + } + + public void setOnDropdownMenuItemClickListener(OnMenuItemClickListener listener) { + if (mPopupMenu != null) { + mPopupMenu.setOnMenuItemClickListener(listener); + } + } + + public MenuItem findItem(int id) { + return mMenu.findItem(id); + }//对于菜单选项的初始化,根据索引搜索菜单需要的选项 + + public void setTitle(CharSequence title) { + mButton.setText(title); + }//布局文件,设置标题 +} \ No newline at end of file diff --git a/src/main/java/net/micode/notes/ui/FoldersListAdapter.java b/src/main/java/net/micode/notes/ui/FoldersListAdapter.java new file mode 100644 index 0000000..8c3a217 --- /dev/null +++ b/src/main/java/net/micode/notes/ui/FoldersListAdapter.java @@ -0,0 +1,83 @@ +package net.micode.notes.ui; + +import android.content.Context; +import android.database.Cursor; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CursorAdapter; +import android.widget.LinearLayout; +import android.widget.TextView; + +import net.micode.notes.R; +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.NoteColumns; + +/** + * @Package: net.micode.notes.ui + * @ClassName: FoldersListAdapter + * @Description: + * FoldersListAdapter是一个用于展示便签文件夹的列表适配器。 + * 它继承了CursorAdapter类,主要负责便签数据库和用户界面的交互。 + * 通过PROJECTION数组定义了需要从数据库中获取的数据列。 + * 它通过newView方法创建文件夹视图,并通过bindView方法将布局文件和数据绑定在一起。 + * getFolderName方法可以根据位置获取对应便签文件夹的名称 + * @Author: YangYizhe + * @CreateDate: 12/21/2023 12:37 AM + * @Version: 1.0 + */ +public class FoldersListAdapter extends CursorAdapter { + //CursorAdapter是Cursor和ListView的接口 + //FoldersListAdapter继承了CursorAdapter的类 + //主要作用是便签数据库和用户的交互 + //这里就是用folder(文件夹)的形式展现给用户 + public static final String [] PROJECTION = { + NoteColumns.ID, + NoteColumns.SNIPPET + };//调用数据库中便签的ID和片段 + + public static final int ID_COLUMN = 0; + public static final int NAME_COLUMN = 1; + + public FoldersListAdapter(Context context, Cursor c) { + super(context, c); + // TODO Auto-generated constructor stub + }//数据库操作 + + @Override + public View newView(Context context, Cursor cursor, ViewGroup parent) { + //ViewGroup是容器 + return new FolderListItem(context); + }//创建一个文件夹,对于各文件夹中子标签的初始化 + + @Override + public void bindView(View view, Context context, Cursor cursor) { + if (view instanceof FolderListItem) { + String folderName = (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context + .getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN); + ((FolderListItem) view).bind(folderName); + } + }//将各个布局文件绑定起来 + + public String getFolderName(Context context, int position) { + Cursor cursor = (Cursor) getItem(position); + return (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context + .getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN); + }//根据数据库中标签的ID得到标签的各项内容 + + private class FolderListItem extends LinearLayout { + private TextView mName; + + public FolderListItem(Context context) { + super(context); + //操作数据库 + inflate(context, R.layout.folder_list_item, this); + //根据布局文件的名字等信息将其找出来 + mName = (TextView) findViewById(R.id.tv_folder_name); + } + + public void bind(String name) { + mName.setText(name); + } + } + +} \ No newline at end of file diff --git a/src/main/java/net/micode/notes/ui/NoteEditActivity.java b/src/main/java/net/micode/notes/ui/NoteEditActivity.java index 54ce0a8..077f27c 100644 --- a/src/main/java/net/micode/notes/ui/NoteEditActivity.java +++ b/src/main/java/net/micode/notes/ui/NoteEditActivity.java @@ -720,6 +720,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, intent.putExtra(Notes.INTENT_EXTRA_FOLDER_ID, mWorkingNote.getFolderId()); startActivity(intent); } + private void deleteCurrentNote() { if (mWorkingNote.existInDatabase()) { HashSet ids = new HashSet(); @@ -994,11 +995,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, //遍历所有子编辑框的视图 NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text); if (!TextUtils.isEmpty(edit.getText())) { - /** - * 若文本不为空 - * 该选项框已打钩 - * 扩展字符串为已打钩并把标记置true - */ + //若文本不为空 if (((CheckBox) view.findViewById(R.id.cb_edit_item)).isChecked()) { //该选项框已打钩 sb.append(TAG_CHECKED).append(" ").append(edit.getText()).append("\n"); @@ -1011,6 +1008,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, } } mWorkingNote.setWorkingText(sb.toString());//利用编辑好的字符串设置运行便签的内容 + //利用编辑好的字符串设置运行便签的内容 } else {// 若不是该模式直接用编辑器中的内容设置运行中标签的内容 mWorkingNote.setWorkingText(mNoteEditor.getText().toString()); //若是获取到文本就改变其检查标记 diff --git a/src/main/java/net/micode/notes/ui/NoteEditText.java b/src/main/java/net/micode/notes/ui/NoteEditText.java new file mode 100644 index 0000000..c935358 --- /dev/null +++ b/src/main/java/net/micode/notes/ui/NoteEditText.java @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.ui; + +import android.content.Context; +import android.graphics.Rect; +import android.text.Layout; +import android.text.Selection; +import android.text.Spanned; +import android.text.TextUtils; +import android.text.style.URLSpan; +import android.util.AttributeSet; +import android.util.Log; +import android.view.ContextMenu; +import android.view.KeyEvent; +import android.view.MenuItem; +import android.view.MenuItem.OnMenuItemClickListener; +import android.view.MotionEvent; +import android.widget.EditText; + +import net.micode.notes.R; + +import java.util.HashMap; +import java.util.Map; + +/** + * @Package: net.micode.notes.ui + * @ClassName: NoteEditText + * @Description: + * @Author: YangYizhe + * @CreateDate: 12/21/2023 12:38 AM + * @Version: 1.0 + */ +public class NoteEditText extends EditText { + //常量标识 + private static final String TAG = "NoteEditText"; + //声明整型变量,文本索引 + private int mIndex; + //声明整型变量 + private int mSelectionStartBeforeDelete; + + //声明字符串常量,标志电话、网址、邮件 + private static final String SCHEME_TEL = "tel:" ; + private static final String SCHEME_HTTP = "http:" ; + private static final String SCHEME_EMAIL = "mailto:" ; + + //设置映射,将文本内容(电话、网址、邮件)做链接处理 + private static final Map sSchemaActionResMap = new HashMap(); + static { + sSchemaActionResMap.put(SCHEME_TEL, R.string.note_link_tel); + sSchemaActionResMap.put(SCHEME_HTTP, R.string.note_link_web); + sSchemaActionResMap.put(SCHEME_EMAIL, R.string.note_link_email); + } + + /** + * Call by the {@link NoteEditActivity} to delete or add edit text + * 该接口用于实现对TextView组件中的文字信息进行修改 + */ + public interface OnTextViewChangeListener { + /** + * Delete current edit text when {@link KeyEvent#KEYCODE_DEL} happens + * and the text is null + * 当delete键按下时删除当前编辑的文字块 + */ + void onEditTextDelete(int index, String text); + + /** + * Add edit text after current edit text when {@link KeyEvent#KEYCODE_ENTER} + * happen + * 当enter键按下时添加一个文字编辑块 + */ + void onEditTextEnter(int index, String text); + + /** + * Hide or show item option when text change + * 当文字发生变化时隐藏或者显示设置 + */ + void onTextChange(int index, boolean hasText); + } + + //声明文本视图变化监听器 + private OnTextViewChangeListener mOnTextViewChangeListener; + + /** + * 构造方法,实例化NoteEditText + */ + public NoteEditText(Context context) { + super(context, null); + mIndex = 0; + } + + //设置索引号 + public void setIndex(int index) { + mIndex = index; + } + + /** + * 设置文本视图变化监听器 + */ + public void setOnTextViewChangeListener(OnTextViewChangeListener listener) { + mOnTextViewChangeListener = listener; + } + + /** + * 构造方法,是由参数集(文本编辑风格)实例化NoteEditText + */ + public NoteEditText(Context context, AttributeSet attrs) { + super(context, attrs, android.R.attr.editTextStyle); + } + /** + * 构造方法,是由参数集(文本编辑风格、定义风格)实例化NoteEditText + */ + public NoteEditText(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + // TODO Auto-generated constructor stub + } + + /** + * @method onTouchEvent + * @description 处理触摸事件,根据触摸点的位置设置光标的位置 + * @date: 12/21/2023 12:41 AM + * @author: YangYizhe + * @param + * @return + */ + @Override + public boolean onTouchEvent(MotionEvent event) { + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + + int x = (int) event.getX(); + int y = (int) event.getY(); + x -= getTotalPaddingLeft(); + y -= getTotalPaddingTop(); + x += getScrollX(); + y += getScrollY(); + Layout layout = getLayout(); + int line = layout.getLineForVertical(y); + int off = layout.getOffsetForHorizontal(line, x); + Selection.setSelection(getText(), off); + break; + } + return super.onTouchEvent(event); + } + /** + * @method onKeyDown + * @description 监听键盘按键按下 + * @date: 12/21/2023 12:40 AM + * @author: YangYizhe + * @param keyCode 键盘按键的编码 + * @param event 按键事件 + * @return boolean + */ + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + switch (keyCode) { + case KeyEvent.KEYCODE_ENTER: + if (mOnTextViewChangeListener != null) { + return false; + } + break; + case KeyEvent.KEYCODE_DEL: + mSelectionStartBeforeDelete = getSelectionStart(); + break; + default: + break; + } + return super.onKeyDown(keyCode, event); + } + + /** + * @method onKeyUp + * @description 监听按键抬起 + * @date: 12/21/2023 12:39 AM + * @author: YangYizhe + * @param keyCode + * @param event + */ + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + switch(keyCode) { + case KeyEvent.KEYCODE_DEL: + if (mOnTextViewChangeListener != null) { + if (0 == mSelectionStartBeforeDelete && mIndex != 0) { + mOnTextViewChangeListener.onEditTextDelete(mIndex, getText().toString()); + return true; + } + } else { + Log.d(TAG, "OnTextViewChangeListener was not seted"); + } + break; + case KeyEvent.KEYCODE_ENTER: + if (mOnTextViewChangeListener != null) { + int selectionStart = getSelectionStart(); + String text = getText().subSequence(selectionStart, length()).toString(); + setText(getText().subSequence(0, selectionStart)); + mOnTextViewChangeListener.onEditTextEnter(mIndex + 1, text); + } else { + Log.d(TAG, "OnTextViewChangeListener was not seted"); + } + break; + default: + break; + } + return super.onKeyUp(keyCode, event); + } + + /** + * @method onFocusChanged + * @description 处理当前视图下的焦点改变事件 + * @date: 12/21/2023 12:39 AM + * @author: YangYizhe + * @param focused 代表获得或失去焦点 + * @param direction + * @param previouslyFocusedRect 上一个访问的焦点区域 + */ + @Override + protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { + if (mOnTextViewChangeListener != null) { + if (!focused && TextUtils.isEmpty(getText())) { + mOnTextViewChangeListener.onTextChange(mIndex, false); + } else { + mOnTextViewChangeListener.onTextChange(mIndex, true); + } + } + super.onFocusChanged(focused, direction, previouslyFocusedRect); + } + + /** + * @method onCreateContextMenu + * @description + * @date: 12/21/2023 12:39 AM + * @author: YangYizhe + * @param + * @return + */ + @Override + protected void onCreateContextMenu(ContextMenu menu) { + if (getText() instanceof Spanned) { + int selStart = getSelectionStart(); + int selEnd = getSelectionEnd(); + + int min = Math.min(selStart, selEnd); + int max = Math.max(selStart, selEnd); + + final URLSpan[] urls = ((Spanned) getText()).getSpans(min, max, URLSpan.class); + if (urls.length == 1) { + int defaultResId = 0; + for(String schema: sSchemaActionResMap.keySet()) { + if(urls[0].getURL().indexOf(schema) >= 0) { + defaultResId = sSchemaActionResMap.get(schema); + break; + } + } + + if (defaultResId == 0) { + defaultResId = R.string.note_link_other; + } + menu.add(0, 0, 0, defaultResId).setOnMenuItemClickListener( + new OnMenuItemClickListener() { + public boolean onMenuItemClick(MenuItem item) { + // goto a new intent + urls[0].onClick(NoteEditText.this); + return true; + } + }); + } + } + super.onCreateContextMenu(menu); + } +} diff --git a/src/main/java/net/micode/notes/ui/NoteItemData.java b/src/main/java/net/micode/notes/ui/NoteItemData.java new file mode 100644 index 0000000..0f5a878 --- /dev/null +++ b/src/main/java/net/micode/notes/ui/NoteItemData.java @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.ui; + +import android.content.Context; +import android.database.Cursor; +import android.text.TextUtils; + +import net.micode.notes.data.Contact; +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.tool.DataUtils; + + +public class NoteItemData { + static final String [] PROJECTION = new String [] { + NoteColumns.ID, + NoteColumns.ALERTED_DATE, + NoteColumns.BG_COLOR_ID, + NoteColumns.CREATED_DATE, + NoteColumns.HAS_ATTACHMENT, + NoteColumns.MODIFIED_DATE, + NoteColumns.NOTES_COUNT, + NoteColumns.PARENT_ID, + NoteColumns.SNIPPET, + NoteColumns.TYPE, + NoteColumns.WIDGET_ID, + NoteColumns.WIDGET_TYPE, + }; + + private static final int ID_COLUMN = 0; + private static final int ALERTED_DATE_COLUMN = 1; + private static final int BG_COLOR_ID_COLUMN = 2; + private static final int CREATED_DATE_COLUMN = 3; + private static final int HAS_ATTACHMENT_COLUMN = 4; + private static final int MODIFIED_DATE_COLUMN = 5; + private static final int NOTES_COUNT_COLUMN = 6; + private static final int PARENT_ID_COLUMN = 7; + private static final int SNIPPET_COLUMN = 8; + private static final int TYPE_COLUMN = 9; + private static final int WIDGET_ID_COLUMN = 10; + private static final int WIDGET_TYPE_COLUMN = 11; + + private long mId; + private long mAlertDate; + private int mBgColorId; + private long mCreatedDate; + private boolean mHasAttachment; + private long mModifiedDate; + private int mNotesCount; + private long mParentId; + private String mSnippet; + private int mType; + private int mWidgetId; + private int mWidgetType; + private String mName; + private String mPhoneNumber; + + private boolean mIsLastItem; + private boolean mIsFirstItem; + private boolean mIsOnlyOneItem; + private boolean mIsOneNoteFollowingFolder; + private boolean mIsMultiNotesFollowingFolder; + + public NoteItemData(Context context, Cursor cursor) { + mId = cursor.getLong(ID_COLUMN); + mAlertDate = cursor.getLong(ALERTED_DATE_COLUMN); + mBgColorId = cursor.getInt(BG_COLOR_ID_COLUMN); + mCreatedDate = cursor.getLong(CREATED_DATE_COLUMN); + mHasAttachment = (cursor.getInt(HAS_ATTACHMENT_COLUMN) > 0) ? true : false; + mModifiedDate = cursor.getLong(MODIFIED_DATE_COLUMN); + mNotesCount = cursor.getInt(NOTES_COUNT_COLUMN); + mParentId = cursor.getLong(PARENT_ID_COLUMN); + mSnippet = cursor.getString(SNIPPET_COLUMN); + mSnippet = mSnippet.replace(NoteEditActivity.TAG_CHECKED, "").replace( + NoteEditActivity.TAG_UNCHECKED, ""); + mType = cursor.getInt(TYPE_COLUMN); + mWidgetId = cursor.getInt(WIDGET_ID_COLUMN); + mWidgetType = cursor.getInt(WIDGET_TYPE_COLUMN); + + mPhoneNumber = ""; + if (mParentId == Notes.ID_CALL_RECORD_FOLDER) { + mPhoneNumber = DataUtils.getCallNumberByNoteId(context.getContentResolver(), mId); + if (!TextUtils.isEmpty(mPhoneNumber)) { + mName = Contact.getContact(context, mPhoneNumber); + if (mName == null) { + mName = mPhoneNumber; + } + } + } + + if (mName == null) { + mName = ""; + } + checkPostion(cursor); + } + + private void checkPostion(Cursor cursor) { + mIsLastItem = cursor.isLast() ? true : false; + mIsFirstItem = cursor.isFirst() ? true : false; + mIsOnlyOneItem = (cursor.getCount() == 1); + mIsMultiNotesFollowingFolder = false; + mIsOneNoteFollowingFolder = false; + + if (mType == Notes.TYPE_NOTE && !mIsFirstItem) { + int position = cursor.getPosition(); + if (cursor.moveToPrevious()) { + if (cursor.getInt(TYPE_COLUMN) == Notes.TYPE_FOLDER + || cursor.getInt(TYPE_COLUMN) == Notes.TYPE_SYSTEM) { + if (cursor.getCount() > (position + 1)) { + mIsMultiNotesFollowingFolder = true; + } else { + mIsOneNoteFollowingFolder = true; + } + } + if (!cursor.moveToNext()) { + throw new IllegalStateException("cursor move to previous but can't move back"); + } + } + } + } + + public boolean isOneFollowingFolder() { + return mIsOneNoteFollowingFolder; + } + + public boolean isMultiFollowingFolder() { + return mIsMultiNotesFollowingFolder; + } + + public boolean isLast() { + return mIsLastItem; + } + + public String getCallName() { + return mName; + } + + public boolean isFirst() { + return mIsFirstItem; + } + + public boolean isSingle() { + return mIsOnlyOneItem; + } + + public long getId() { + return mId; + } + + public long getAlertDate() { + return mAlertDate; + } + + public long getCreatedDate() { + return mCreatedDate; + } + + public boolean hasAttachment() { + return mHasAttachment; + } + + public long getModifiedDate() { + return mModifiedDate; + } + + public int getBgColorId() { + return mBgColorId; + } + + public long getParentId() { + return mParentId; + } + + public int getNotesCount() { + return mNotesCount; + } + + public long getFolderId () { + return mParentId; + } + + public int getType() { + return mType; + } + + public int getWidgetType() { + return mWidgetType; + } + + public int getWidgetId() { + return mWidgetId; + } + + public String getSnippet() { + return mSnippet; + } + + public boolean hasAlert() { + return (mAlertDate > 0); + } + + public boolean isCallRecord() { + return (mParentId == Notes.ID_CALL_RECORD_FOLDER && !TextUtils.isEmpty(mPhoneNumber)); + } + + public static int getNoteType(Cursor cursor) { + return cursor.getInt(TYPE_COLUMN); + } +} diff --git a/src/main/java/net/micode/notes/ui/NotesListActivity.java b/src/main/java/net/micode/notes/ui/NotesListActivity.java new file mode 100644 index 0000000..2b0e901 --- /dev/null +++ b/src/main/java/net/micode/notes/ui/NotesListActivity.java @@ -0,0 +1,1103 @@ +package net.micode.notes.ui; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.appwidget.AppWidgetManager; +import android.content.AsyncQueryHandler; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.SharedPreferences; +import android.database.Cursor; +import android.os.AsyncTask; +import android.os.Bundle; +import android.preference.PreferenceManager; +import android.text.Editable; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.util.Log; +import android.view.ActionMode; +import android.view.ContextMenu; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.Display; +import android.view.HapticFeedbackConstants; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.MenuItem.OnMenuItemClickListener; +import android.view.MotionEvent; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.View.OnCreateContextMenuListener; +import android.view.View.OnTouchListener; +import android.view.inputmethod.InputMethodManager; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.AdapterView.OnItemLongClickListener; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ListView; +import android.widget.PopupMenu; +import android.widget.TextView; +import android.widget.Toast; + +import net.micode.notes.R; +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.gtask.remote.GTaskSyncService; +import net.micode.notes.model.WorkingNote; +import net.micode.notes.tool.BackupUtils; +import net.micode.notes.tool.DataUtils; +import net.micode.notes.tool.ResourceParser; +import net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute; +import net.micode.notes.widget.NoteWidgetProvider_2x; +import net.micode.notes.widget.NoteWidgetProvider_4x; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.HashSet; +/** + * @Package: net.micode.notes.ui + * @ClassName: NotesListActivity + * @Description: + * 小米便签的主入口,提供了一系列便签操作主要功能 + * @Author: WUSHUXIAN + * @CreateDate: 2023/12/20 23:02 + * @Version: 1.0 + */ +public class NotesListActivity extends Activity implements OnClickListener, OnItemLongClickListener { + + private static final int FOLDER_NOTE_LIST_QUERY_TOKEN = 0; + + private static final int FOLDER_LIST_QUERY_TOKEN = 1; + + private static final int MENU_FOLDER_DELETE = 0; + + private static final int MENU_FOLDER_VIEW = 1; + + private static final int MENU_FOLDER_CHANGE_NAME = 2; + + private static final String PREFERENCE_ADD_INTRODUCTION = "net.micode.notes.introduction"; + + private enum ListEditState { + NOTE_LIST, SUB_FOLDER, CALL_RECORD_FOLDER + }; + + private ListEditState mState; + + private BackgroundQueryHandler mBackgroundQueryHandler; + + private NotesListAdapter mNotesListAdapter; + + private ListView mNotesListView; + + private Button mAddNewNote; + + private boolean mDispatch; + + private int mOriginY; + + private int mDispatchY; + + private TextView mTitleBar; + + private long mCurrentFolderId; + + private ContentResolver mContentResolver; + + private ModeCallback mModeCallBack; + + private static final String TAG = "NotesListActivity"; + + public static final int NOTES_LISTVIEW_SCROLL_RATE = 30; + + private NoteItemData mFocusNoteDataItem; + + private static final String NORMAL_SELECTION = NoteColumns.PARENT_ID + "=?"; + + private static final String ROOT_FOLDER_SELECTION = "(" + NoteColumns.TYPE + "<>" + + Notes.TYPE_SYSTEM + " AND " + NoteColumns.PARENT_ID + "=?)" + " OR (" + + NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER + " AND " + + NoteColumns.NOTES_COUNT + ">0)"; + + private final static int REQUEST_CODE_OPEN_NODE = 102; + private final static int REQUEST_CODE_NEW_NODE = 103; + @Override + /** + * @method onCreate + * @description: + * @date: 2023/12/21 1:52 + * @author: WUSHUXIAN + * @param void + * @return void + */ + protected void onCreate(final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); // 调用父类的onCreate函数 + setContentView(R.layout.note_list); + initResources(); + setAppInfoFromRawRes(); + } + + @Override + /** + * @method onActivityResult + * @description: + * 参数: + * requestCode: + * resultCode: + * data; + * @date: 2023/12/20 23:26 + * @author: WUSHUXIAN + * @param void + * @return void + */ + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (resultCode == RESULT_OK + && (requestCode == REQUEST_CODE_OPEN_NODE || requestCode == REQUEST_CODE_NEW_NODE)) { + mNotesListAdapter.changeCursor(null); + } else { + super.onActivityResult(requestCode, resultCode, data);// 调用 Activity 的onActivityResult() + } + } + /** + * @method setAppInfoFromRawRes + * @description: + * @date: 2023/12/20 23:24 + * @author: WUSHUXIAN + * @param + * @return + */ + private void setAppInfoFromRawRes() { + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);// Android平台给我们提供了一个SharedPreferences类,它是一个轻量级的存储类,特别适合用于保存软件配置参数。 + if (!sp.getBoolean(PREFERENCE_ADD_INTRODUCTION, false)) { + StringBuilder sb = new StringBuilder(); + InputStream in = null; + try { + in = getResources().openRawResource(R.raw.introduction); + if (in != null) { + InputStreamReader isr = new InputStreamReader(in); + BufferedReader br = new BufferedReader(isr); + char [] buf = new char[1024]; + int len = 0; + while ((len = br.read(buf)) > 0) { + sb.append(buf, 0, len); + } + } else { + Log.e(TAG, "Read introduction file error"); + return; + } + } catch (IOException e) { + e.printStackTrace(); + return; + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + + // 创建空的WorkingNote + WorkingNote note = WorkingNote.createEmptyNote(this, Notes.ID_ROOT_FOLDER, + AppWidgetManager.INVALID_APPWIDGET_ID, Notes.TYPE_WIDGET_INVALIDE, + ResourceParser.RED); + note.setWorkingText(sb.toString()); + if (note.saveNote()) { + // 更新保存note的信息 + sp.edit().putBoolean(PREFERENCE_ADD_INTRODUCTION, true).commit(); + } else { + Log.e(TAG, "Save introduction note error"); + return; + } + } + } + + @Override + protected void onStart() { + super.onStart(); + startAsyncNotesListQuery(); + } + /** + * @method initResources + * @description 初始化资源 + * @date: 2023/12/20 23:09 + * @author: WUSHUXIAN + * @param + * @return + */ + private void initResources() { + mContentResolver = this.getContentResolver(); // 获取应用程序的数据,得到类似数据表的东西 + mBackgroundQueryHandler = new BackgroundQueryHandler(this.getContentResolver()); + mCurrentFolderId = Notes.ID_ROOT_FOLDER; + + // findViewById 是安卓编程的定位函数,主要是引用.R文件里的引用名 + mNotesListView = (ListView) findViewById(R.id.notes_list); // 绑定XML中的ListView,作为Item的容器 + mNotesListView.addFooterView(LayoutInflater.from(this).inflate(R.layout.note_list_footer, null), + null, false); + mNotesListView.setOnItemClickListener(new OnListItemClickListener()); + mNotesListView.setOnItemLongClickListener(this); + mNotesListAdapter = new NotesListAdapter(this); + mNotesListView.setAdapter(mNotesListAdapter); + mAddNewNote = (Button) findViewById(R.id.btn_new_note);// 在activity中要获取该按钮 + mAddNewNote.setOnClickListener(this); + mAddNewNote.setOnTouchListener(new NewNoteOnTouchListener()); + mDispatch = false; + mDispatchY = 0; + mOriginY = 0; + mTitleBar = (TextView) findViewById(R.id.tv_title_bar); + mState = ListEditState.NOTE_LIST; + mModeCallBack = new ModeCallback(); + } + + // + /** + * @Package: net.micode.notes.ui + * @ClassName: ModeCallback + * @Description: 继承自ListView.MultiChoiceModeListener 和 OnMenuItemClickListener + * @Author: WUSHUXIAN + * @CreateDate: 2023/12/20 23:37 + * @Version: 1.0 + */ + private class ModeCallback implements ListView.MultiChoiceModeListener, OnMenuItemClickListener { + private DropdownMenu mDropDownMenu; + private ActionMode mActionMode; + private MenuItem mMoveMenu; + + public boolean onCreateActionMode(ActionMode mode, Menu menu) { + getMenuInflater().inflate(R.menu.note_list_options, menu); + menu.findItem(R.id.delete).setOnMenuItemClickListener(this); + mMoveMenu = menu.findItem(R.id.move); + if (mFocusNoteDataItem.getParentId() == Notes.ID_CALL_RECORD_FOLDER + || DataUtils.getUserFolderCount(mContentResolver) == 0) { + mMoveMenu.setVisible(false); + } else { + mMoveMenu.setVisible(true); + mMoveMenu.setOnMenuItemClickListener(this); + } + mActionMode = mode; + mNotesListAdapter.setChoiceMode(true); + mNotesListView.setLongClickable(false); + mAddNewNote.setVisibility(View.GONE); + + View customView = LayoutInflater.from(NotesListActivity.this).inflate( + R.layout.note_list_dropdown_menu, null); + mode.setCustomView(customView); + mDropDownMenu = new DropdownMenu(NotesListActivity.this, + (Button) customView.findViewById(R.id.selection_menu), + R.menu.note_list_dropdown); + mDropDownMenu.setOnDropdownMenuItemClickListener(new PopupMenu.OnMenuItemClickListener(){ + public boolean onMenuItemClick(final MenuItem item) { + mNotesListAdapter.selectAll(!mNotesListAdapter.isAllSelected()); + updateMenu(); + return true; + } + + }); + return true; + } + /** + * @method updateMenu + * @description 更新菜单 + * @date: 2023/12/16 23:36 + * @author: WUSHUXIAN + * @param + * @return + */ + private void updateMenu() { + int selectedCount = mNotesListAdapter.getSelectedCount(); + // Update dropdown menu + String format = getResources().getString(R.string.menu_select_title, selectedCount); + mDropDownMenu.setTitle(format); // 更改标题 + MenuItem item = mDropDownMenu.findItem(R.id.action_select_all); + if (item != null) { + if (mNotesListAdapter.isAllSelected()) { + item.setChecked(true); + item.setTitle(R.string.menu_deselect_all); + } else { + item.setChecked(false); + item.setTitle(R.string.menu_select_all); + } + } + } + + public boolean onPrepareActionMode(ActionMode mode, Menu menu) { + // TODO Auto-generated method stub + return false; + } + + public boolean onActionItemClicked(ActionMode mode, MenuItem item) { + // TODO Auto-generated method stub + return false; + } + + public void onDestroyActionMode(ActionMode mode) { + mNotesListAdapter.setChoiceMode(false); + mNotesListView.setLongClickable(true); + mAddNewNote.setVisibility(View.VISIBLE); + } + + public void finishActionMode() { + mActionMode.finish(); + } + + public void onItemCheckedStateChanged(ActionMode mode, int position, long id, + boolean checked) { + mNotesListAdapter.setCheckedItem(position, checked); + updateMenu(); + } + /** + * @method onMenuItemClick + * @description: + * @date: 2023/12/20 23:39 + * @author: WUSHUXIAN + * @param + * @return + */ + public boolean onMenuItemClick(MenuItem item) { + if (mNotesListAdapter.getSelectedCount() == 0) { + Toast.makeText(NotesListActivity.this, getString(R.string.menu_select_none), + Toast.LENGTH_SHORT).show(); + return true; + } + + int itemId = item.getItemId(); + if (itemId == R.id.delete) { + AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); + builder.setTitle(getString(R.string.alert_title_delete)); + builder.setIcon(android.R.drawable.ic_dialog_alert); + builder.setMessage(getString(R.string.alert_message_delete_notes, + mNotesListAdapter.getSelectedCount())); + builder.setPositiveButton(android.R.string.ok, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, + int which) { + batchDelete(); + } + }); + builder.setNegativeButton(android.R.string.cancel, null); + builder.show(); + } else if (itemId == R.id.move) { + startQueryDestinationFolders(); + } else { + return false; + } + return true; + } + } + //接口的实现 + private class NewNoteOnTouchListener implements OnTouchListener { + public boolean onTouch(View v, MotionEvent event) { + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: { + Display display = getWindowManager().getDefaultDisplay(); + int screenHeight = display.getHeight(); + int newNoteViewHeight = mAddNewNote.getHeight(); + int start = screenHeight - newNoteViewHeight; + int eventY = start + (int) event.getY(); + /** + * Minus TitleBar's height + */ + if (mState == ListEditState.SUB_FOLDER) { + eventY -= mTitleBar.getHeight(); + start -= mTitleBar.getHeight(); + } + /** + * HACKME:When click the transparent part of "New Note" button, dispatch + * the event to the list view behind this button. The transparent part of + * "New Note" button could be expressed by formula y=-0.12x+94锛圲nit:pixel锛� + * and the line top of the button. The coordinate based on left of the "New + * Note" button. The 94 represents maximum height of the transparent part. + * Notice that, if the background of the button changes, the formula should + * also change. This is very bad, just for the UI designer's strong requirement. + */ + if (event.getY() < (event.getX() * (-0.12) + 94)) { + View view = mNotesListView.getChildAt(mNotesListView.getChildCount() - 1 + - mNotesListView.getFooterViewsCount()); + if (view != null && view.getBottom() > start + && (view.getTop() < (start + 94))) { + mOriginY = (int) event.getY(); + mDispatchY = eventY; + event.setLocation(event.getX(), mDispatchY); + mDispatch = true; + return mNotesListView.dispatchTouchEvent(event); + } + } + break; + } + case MotionEvent.ACTION_MOVE: { + if (mDispatch) { + mDispatchY += (int) event.getY() - mOriginY; + event.setLocation(event.getX(), mDispatchY); + return mNotesListView.dispatchTouchEvent(event); + } + break; + } + default: { + if (mDispatch) { + event.setLocation(event.getX(), mDispatchY); + mDispatch = false; + return mNotesListView.dispatchTouchEvent(event); + } + break; + } + } + return false; + } + + }; + /** + * @method startAsyncNotesListQuery + * @description: 启动异步Notes列表查询 + * @date: 2023/12/20 23:45 + * @author: WUSHUXIAN + * @param + * @return + */ + private void startAsyncNotesListQuery() { + String selection = (mCurrentFolderId == Notes.ID_ROOT_FOLDER) ? ROOT_FOLDER_SELECTION : NORMAL_SELECTION; + mBackgroundQueryHandler.startQuery(FOLDER_NOTE_LIST_QUERY_TOKEN, null, + Notes.CONTENT_NOTE_URI, NoteItemData.PROJECTION, selection, new String[] {String.valueOf(mCurrentFolderId)}, + NoteColumns.TYPE + " DESC," + NoteColumns.MODIFIED_DATE + " DESC"); + } + /** + * @Package: net.micode.notes.ui + * @ClassName: BackgroundQueryHandler + * @Description: 对AsyncQueryHandler的拓展,背景查询处理 + * @Author: WUSHUXIAN + * @CreateDate: 2023/12/21 0:01 + * @Version: 1.0 + */ + private final class BackgroundQueryHandler extends AsyncQueryHandler { + public BackgroundQueryHandler(ContentResolver contentResolver) { + super(contentResolver); + } + + @Override + /** + * @method onQueryComplete + * @description: 在异步查询完成时调用。 + * @date: 2023/12/20 23:56 + * @author: WUSHUXIAN + * @param token 标识查询的令牌 + * @param cookie 传入的cookie对象 + * @param cursor 保存查询结果的光标 + * @return void + */ + protected void onQueryComplete(int token, Object cookie, Cursor cursor) { + switch (token) { + case FOLDER_NOTE_LIST_QUERY_TOKEN: + mNotesListAdapter.changeCursor(cursor); + break; + case FOLDER_LIST_QUERY_TOKEN: + if (cursor != null && cursor.getCount() > 0) { + showFolderListMenu(cursor); + } else { + Log.e(TAG, "Query folder failed"); + } + break; + default: + return; + } + } + } + + private void showFolderListMenu(Cursor cursor) { + AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); + builder.setTitle(R.string.menu_title_select_folder); + final FoldersListAdapter adapter = new FoldersListAdapter(this, cursor); + builder.setAdapter(adapter, new DialogInterface.OnClickListener() { + + public void onClick(DialogInterface dialog, int which) { + DataUtils.batchMoveToFolder(mContentResolver, + mNotesListAdapter.getSelectedItemIds(), adapter.getItemId(which)); + Toast.makeText( + NotesListActivity.this, + getString(R.string.format_move_notes_to_folder, + mNotesListAdapter.getSelectedCount(), + adapter.getFolderName(NotesListActivity.this, which)), + Toast.LENGTH_SHORT).show(); + mModeCallBack.finishActionMode(); + } + }); + builder.show(); + } + /** + * @method createNewNote + * @description 创建便签 + * @date: 12/21/2023 1:58 AM + * @author: YangYizhe + * @param + * @return + */ + private void createNewNote() { + Intent intent = new Intent(this, NoteEditActivity.class); + intent.setAction(Intent.ACTION_INSERT_OR_EDIT); + intent.putExtra(Notes.INTENT_EXTRA_FOLDER_ID, mCurrentFolderId); + this.startActivityForResult(intent, REQUEST_CODE_NEW_NODE); + } + /** + * @method batchDelete + * @description: + * 删除便签操作 + * @date: 2023/12/21 0:14 + * @author: WUSHUXIAN + * @param + * @return void + */ + @SuppressLint("StaticFieldLeak") + private void batchDelete() { + new AsyncTask>() { + protected HashSet doInBackground(Void... unused) { + HashSet widgets = mNotesListAdapter.getSelectedWidget(); + //如果没有同步,直接删除 + if (!isSyncMode()) { + if (DataUtils.batchDeleteNotes(mContentResolver, mNotesListAdapter + .getSelectedItemIds())) { + } else { + Log.e(TAG, "Delete notes error, should not happens"); + } + } + //已同步,将删除的便签移到垃圾桶 + else { + if (!DataUtils.batchMoveToFolder(mContentResolver, mNotesListAdapter + .getSelectedItemIds(), Notes.ID_TRASH_FOLER)) { + Log.e(TAG, "Move notes to trash folder error, should not happens"); + } + } + return widgets; + } + + @Override + protected void onPostExecute(HashSet widgets) { + if (widgets != null) { + for (AppWidgetAttribute widget : widgets) { + if (widget.widgetId != AppWidgetManager.INVALID_APPWIDGET_ID + && widget.widgetType != Notes.TYPE_WIDGET_INVALIDE) { + updateWidget(widget.widgetId, widget.widgetType); + } + } + } + mModeCallBack.finishActionMode(); + } + }.execute(); + } + /** + * @method deleteFolder + * @description: 删除文件夹 + * @date: 2023/12/21 0:32 + * @author: WUSHUXIAN + * @param folderId 文件标识 + * @return void + */ + private void deleteFolder(long folderId) { + if (folderId == Notes.ID_ROOT_FOLDER) { + Log.e(TAG, "Wrong folder id, should not happen " + folderId); + return; + } + + HashSet ids = new HashSet(); + ids.add(folderId); + HashSet widgets = DataUtils.getFolderNoteWidget(mContentResolver, + folderId); + if (!isSyncMode()) { + // if not synced, delete folder directly + DataUtils.batchDeleteNotes(mContentResolver, ids); + } else { + // in sync mode, we'll move the deleted folder into the trash folder + DataUtils.batchMoveToFolder(mContentResolver, ids, Notes.ID_TRASH_FOLER); + } + if (widgets != null) { + for (AppWidgetAttribute widget : widgets) { + if (widget.widgetId != AppWidgetManager.INVALID_APPWIDGET_ID + && widget.widgetType != Notes.TYPE_WIDGET_INVALIDE) { + updateWidget(widget.widgetId, widget.widgetType); + } + } + } + } + + /** + * @method openNode + * @description + * 打开便签的功能 + * @date: 12/21/2023 8:05 AM + * @author: YangYizhe + * @param data + */ + private void openNode(NoteItemData data) { + Intent intent = new Intent(this, NoteEditActivity.class); + intent.setAction(Intent.ACTION_VIEW); + intent.putExtra(Intent.EXTRA_UID, data.getId()); + this.startActivityForResult(intent, REQUEST_CODE_OPEN_NODE); + } + /** + * @method openFolder + * @description 打开文件夹功能 + * @date: 12/21/2023 8:05 AM + * @author: YangYizhe + * @param data + */ + private void openFolder(NoteItemData data) { + mCurrentFolderId = data.getId(); + startAsyncNotesListQuery(); + if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) { + mState = ListEditState.CALL_RECORD_FOLDER; + mAddNewNote.setVisibility(View.GONE); + } else { + mState = ListEditState.SUB_FOLDER; + } + if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) { + mTitleBar.setText(R.string.call_record_folder_name); + } else { + mTitleBar.setText(data.getSnippet()); + } + mTitleBar.setVisibility(View.VISIBLE); + } + /** + * @method onClick + * @description 如果碰到写便签按钮,新建便签 + * @date: 12/21/2023 8:06 AM + * @author: YangYizhe + * @param + */ + public void onClick(View v) { + if (v.getId() == R.id.btn_new_note) { + createNewNote(); + } + } + + private void showSoftInput() { + InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + if (inputMethodManager != null) { + inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); + } + } + + private void hideSoftInput(View view) { + InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0); + } + /** + * @method showCreateOrModifyFolderDialog + * @description: 创建或者修改文件夹时显示对话框 + * @date: 2023/12/21 0:39 + * @author: WUSHUXIAN + * @param create + * @return void + */ + private void showCreateOrModifyFolderDialog(final boolean create) { + final AlertDialog.Builder builder = new AlertDialog.Builder(this); + View view = LayoutInflater.from(this).inflate(R.layout.dialog_edit_text, null); + final EditText etName = (EditText) view.findViewById(R.id.et_foler_name); + showSoftInput(); + if (!create) { + if (mFocusNoteDataItem != null) { + etName.setText(mFocusNoteDataItem.getSnippet()); + builder.setTitle(getString(R.string.menu_folder_change_name)); + } else { + Log.e(TAG, "The long click data item is null"); + return; + } + } else { + etName.setText(""); + builder.setTitle(this.getString(R.string.menu_create_folder)); + } + + builder.setPositiveButton(android.R.string.ok, null); + builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + hideSoftInput(etName); + } + }); + + final Dialog dialog = builder.setView(view).show(); + final Button positive = (Button)dialog.findViewById(android.R.id.button1); + positive.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + hideSoftInput(etName); + String name = etName.getText().toString(); + if (DataUtils.checkVisibleFolderName(mContentResolver, name)) { + Toast.makeText(NotesListActivity.this, getString(R.string.folder_exist, name), + Toast.LENGTH_LONG).show(); + etName.setSelection(0, etName.length()); + return; + } + if (!create) { + if (!TextUtils.isEmpty(name)) { + ContentValues values = new ContentValues(); + values.put(NoteColumns.SNIPPET, name); + values.put(NoteColumns.TYPE, Notes.TYPE_FOLDER); + values.put(NoteColumns.LOCAL_MODIFIED, 1); + mContentResolver.update(Notes.CONTENT_NOTE_URI, values, NoteColumns.ID + + "=?", new String[] { + String.valueOf(mFocusNoteDataItem.getId()) + }); + } + } else if (!TextUtils.isEmpty(name)) { + ContentValues values = new ContentValues(); + values.put(NoteColumns.SNIPPET, name); + values.put(NoteColumns.TYPE, Notes.TYPE_FOLDER); + mContentResolver.insert(Notes.CONTENT_NOTE_URI, values); + } + dialog.dismiss(); + } + }); + //如果便签名是空的,则禁用确认按钮 + if (TextUtils.isEmpty(etName.getText())) { + positive.setEnabled(false); + } + etName.addTextChangedListener(new TextWatcher() { + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + // TODO Auto-generated method stub + + } + + public void onTextChanged(CharSequence s, int start, int before, int count) { + if (TextUtils.isEmpty(etName.getText())) { + positive.setEnabled(false); + } else { + positive.setEnabled(true); + } + } + + public void afterTextChanged(Editable s) { + // TODO Auto-generated method stub + + } + }); + } + + /* (non-Javadoc) + * @see android.app.Activity#onBackPressed() + * 按返回键时根据情况更改类中的数据 + */ + @Override + public void onBackPressed() { + switch (mState) { + case SUB_FOLDER: + mCurrentFolderId = Notes.ID_ROOT_FOLDER; + mState = ListEditState.NOTE_LIST; + startAsyncNotesListQuery(); + mTitleBar.setVisibility(View.GONE); + break; + case CALL_RECORD_FOLDER: + mCurrentFolderId = Notes.ID_ROOT_FOLDER; + mState = ListEditState.NOTE_LIST; + mAddNewNote.setVisibility(View.VISIBLE); + mTitleBar.setVisibility(View.GONE); + startAsyncNotesListQuery(); + break; + case NOTE_LIST: + super.onBackPressed(); + break; + default: + break; + } + } + + /** + * @param appWidgetId + * @param appWidgetType + * 根据不同类型的widget更新插件,通过intent传送数据 + */ + private void updateWidget(int appWidgetId, int appWidgetType) { + Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); + if (appWidgetType == Notes.TYPE_WIDGET_2X) { + intent.setClass(this, NoteWidgetProvider_2x.class); + } else if (appWidgetType == Notes.TYPE_WIDGET_4X) { + intent.setClass(this, NoteWidgetProvider_4x.class); + } else { + Log.e(TAG, "Unspported widget type"); + return; + } + + intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] { + appWidgetId + }); + + sendBroadcast(intent); + setResult(RESULT_OK, intent); + } + /** + * @method OnCreateContextMenuListener + * @description: 生成便签初始界面 + * @date: 2023/12/21 1:16 + * @author: WUSHUXIAN + */ + private final OnCreateContextMenuListener mFolderOnCreateContextMenuListener = new OnCreateContextMenuListener() { + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { + if (mFocusNoteDataItem != null) { + menu.setHeaderTitle(mFocusNoteDataItem.getSnippet()); + menu.add(0, MENU_FOLDER_VIEW, 0, R.string.menu_folder_view); + menu.add(0, MENU_FOLDER_DELETE, 0, R.string.menu_folder_delete); + menu.add(0, MENU_FOLDER_CHANGE_NAME, 0, R.string.menu_folder_change_name); + } + } + }; + + @Override + public void onContextMenuClosed(Menu menu) { + if (mNotesListView != null) { + mNotesListView.setOnCreateContextMenuListener(null); + } + super.onContextMenuClosed(menu); + } + + /* (non-Javadoc) + * @see android.app.Activity#onContextItemSelected(android.view.MenuItem) + * 针对menu中不同的选择进行不同的处理,里面详细注释 + */ + @Override + public boolean onContextItemSelected(MenuItem item) { + if (mFocusNoteDataItem == null) { + Log.e(TAG, "The long click data item is null"); + return false; + } + switch (item.getItemId()) { + case MENU_FOLDER_VIEW: + openFolder(mFocusNoteDataItem);//打开对应文件 + break; + case MENU_FOLDER_DELETE: + AlertDialog.Builder builder = new AlertDialog.Builder(this);//设置确认是否删除的对话框 + builder.setTitle(getString(R.string.alert_title_delete)); + builder.setIcon(android.R.drawable.ic_dialog_alert); + builder.setMessage(getString(R.string.alert_message_delete_folder)); + builder.setPositiveButton(android.R.string.ok, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + deleteFolder(mFocusNoteDataItem.getId()); + } + }); + builder.setNegativeButton(android.R.string.cancel, null); + builder.show();//显示对话框 + break; + case MENU_FOLDER_CHANGE_NAME: + showCreateOrModifyFolderDialog(false); + break; + default: + break; + } + + return true; + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + menu.clear(); + if (mState == ListEditState.NOTE_LIST) { + getMenuInflater().inflate(R.menu.note_list, menu); + // set sync or sync_cancel + menu.findItem(R.id.menu_sync).setTitle( + GTaskSyncService.isSyncing() ? R.string.menu_sync_cancel : R.string.menu_sync); + } else if (mState == ListEditState.SUB_FOLDER) { + getMenuInflater().inflate(R.menu.sub_folder, menu); + } else if (mState == ListEditState.CALL_RECORD_FOLDER) { + getMenuInflater().inflate(R.menu.call_record_folder, menu); + } else { + Log.e(TAG, "Wrong state:" + mState); + } + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + int itemId = item.getItemId(); + if (itemId == R.id.menu_new_folder) { + showCreateOrModifyFolderDialog(true); + } else if (itemId == R.id.menu_export_text) { + exportNoteToText(); + } else if (itemId == R.id.menu_sync) { + if (isSyncMode()) { + if (TextUtils.equals(item.getTitle(), getString(R.string.menu_sync))) { + GTaskSyncService.startSync(this); + } else { + GTaskSyncService.cancelSync(this); + } + } else { + startPreferenceActivity(); + } + } else if (itemId == R.id.menu_setting) { + startPreferenceActivity(); + } else if (itemId == R.id.menu_new_note) { + createNewNote(); + } else if (itemId == R.id.menu_search) { + onSearchRequested(); + } + return true; + } + + /* (non-Javadoc) + * @see android.app.Activity#onSearchRequested() + * 直接调用startSearch函数 + */ + @Override + public boolean onSearchRequested() { + startSearch(null, false, null /* appData */, false); + return true; + } + + /** + * @method exportNoteToText + * @description 将便签导出,export text按钮 + * @date: 12/21/2023 8:08 AM + * @author: YangYizhe + */ + private void exportNoteToText() { + final BackupUtils backup = BackupUtils.getInstance(NotesListActivity.this); + new AsyncTask() { + + @Override + protected Integer doInBackground(Void... unused) { + return backup.exportToText(); + } + + @Override + protected void onPostExecute(Integer result) { + if (result == BackupUtils.STATE_SD_CARD_UNMOUONTED) { + AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); + builder.setTitle(NotesListActivity.this + .getString(R.string.failed_sdcard_export)); + builder.setMessage(NotesListActivity.this + .getString(R.string.error_sdcard_unmounted)); + builder.setPositiveButton(android.R.string.ok, null); + builder.show(); + } else if (result == BackupUtils.STATE_SUCCESS) { + AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); + builder.setTitle(NotesListActivity.this + .getString(R.string.success_sdcard_export)); + builder.setMessage(NotesListActivity.this.getString( + R.string.format_exported_file_location, backup + .getExportedTextFileName(), backup.getExportedTextFileDir())); + builder.setPositiveButton(android.R.string.ok, null); + builder.show(); + } else if (result == BackupUtils.STATE_SYSTEM_ERROR) { + AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); + builder.setTitle(NotesListActivity.this + .getString(R.string.failed_sdcard_export)); + builder.setMessage(NotesListActivity.this + .getString(R.string.error_sdcard_export)); + builder.setPositiveButton(android.R.string.ok, null); + builder.show(); + } + } + + }.execute(); + } + + + private boolean isSyncMode() { + return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0; + } + + private void startPreferenceActivity() { + Activity from = getParent() != null ? getParent() : this; + Intent intent = new Intent(from, NotesPreferenceActivity.class); + from.startActivityIfNeeded(intent, -1); + } + + /** + * @Package: net.micode.notes.ui + * @ClassName: NotesListActivity + * @Description: 响应了按钮的点击 + * @Author: YangYizhe + * @CreateDate: 12/21/2023 8:09 AM + * @Version: 1.0 + */ + private class OnListItemClickListener implements OnItemClickListener { + + public void onItemClick(AdapterView parent, View view, int position, long id) { + if (view instanceof NotesListItem) { + NoteItemData item = ((NotesListItem) view).getItemData(); + if (mNotesListAdapter.isInChoiceMode()) { + if (item.getType() == Notes.TYPE_NOTE) { + position = position - mNotesListView.getHeaderViewsCount(); + mModeCallBack.onItemCheckedStateChanged(null, position, id, + !mNotesListAdapter.isSelectedItem(position)); + } + return; + } + + switch (mState) { + case NOTE_LIST: + if (item.getType() == Notes.TYPE_FOLDER + || item.getType() == Notes.TYPE_SYSTEM) { + openFolder(item); + } else if (item.getType() == Notes.TYPE_NOTE) { + openNode(item); + } else { + Log.e(TAG, "Wrong note type in NOTE_LIST"); + } + break; + case SUB_FOLDER: + case CALL_RECORD_FOLDER: + if (item.getType() == Notes.TYPE_NOTE) { + openNode(item); + } else { + Log.e(TAG, "Wrong note type in SUB_FOLDER"); + } + break; + default: + break; + } + } + } + + } + + /** + * 查询目标文件 + */ + private void startQueryDestinationFolders() { + String selection = NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>? AND " + NoteColumns.ID + "<>?"; + selection = (mState == ListEditState.NOTE_LIST) ? selection: + "(" + selection + ") OR (" + NoteColumns.ID + "=" + Notes.ID_ROOT_FOLDER + ")"; + + mBackgroundQueryHandler.startQuery(FOLDER_LIST_QUERY_TOKEN, + null, + Notes.CONTENT_NOTE_URI, + FoldersListAdapter.PROJECTION, + selection, + new String[] { + String.valueOf(Notes.TYPE_FOLDER), + String.valueOf(Notes.ID_TRASH_FOLER), + String.valueOf(mCurrentFolderId) + }, + NoteColumns.MODIFIED_DATE + " DESC"); + } + + /** + * @method onItemLongClick + * @description + * 长按某一项时进行的操作 + * 如果长按的是便签,则通过ActionMode菜单实现;如果长按的是文件夹,则通过ContextMenu菜单实现; + * @date: 12/21/2023 8:10 AM + * @author: YangYizhe + * @param + * @return + */ + public boolean onItemLongClick(AdapterView parent, View view, int position, long id) { + if (view instanceof NotesListItem) { + mFocusNoteDataItem = ((NotesListItem) view).getItemData(); + if (mFocusNoteDataItem.getType() == Notes.TYPE_NOTE && !mNotesListAdapter.isInChoiceMode()) { + if (mNotesListView.startActionMode(mModeCallBack) != null) { + mModeCallBack.onItemCheckedStateChanged(null, position, id, true); + mNotesListView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); + } else { + Log.e(TAG, "startActionMode fails"); + } + } else if (mFocusNoteDataItem.getType() == Notes.TYPE_FOLDER) { + mNotesListView.setOnCreateContextMenuListener(mFolderOnCreateContextMenuListener); + } + } + return false; + } +} \ No newline at end of file diff --git a/src/main/java/net/micode/notes/ui/NotesListItem.java b/src/main/java/net/micode/notes/ui/NotesListItem.java new file mode 100644 index 0000000..d32a37b --- /dev/null +++ b/src/main/java/net/micode/notes/ui/NotesListItem.java @@ -0,0 +1,123 @@ +package net.micode.notes.ui; + +import android.content.Context; +import android.text.format.DateUtils; +import android.view.View; +import android.widget.CheckBox; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import net.micode.notes.R; +import net.micode.notes.data.Notes; +import net.micode.notes.tool.DataUtils; +import net.micode.notes.tool.ResourceParser.NoteItemBgResources; + + +/** + * @Package: net.micode.notes.ui + * @ClassName: NotesListItem + * @Description: + * @Author: YangYizhe + * @CreateDate: 12/23/2023 11:35 PM + * @Version: 1.0 + */ +public class NotesListItem extends LinearLayout { + private ImageView mAlert;//闹钟图片 + private TextView mTitle; //标题 + private TextView mTime; //时间 + private TextView mCallName; // + private NoteItemData mItemData; //标签数据 + private CheckBox mCheckBox; //打钩框 + + /*初始化基本信息*/ + public NotesListItem(Context context) { + super(context); //super()它的主要作用是调整调用父类构造函数的顺序 + inflate(context, R.layout.note_item, this);//Inflate可用于将一个xml中定义的布局控件找出来,这里的xml是r。layout + //findViewById用于从contentView中查找指定ID的View,转换出来的形式根据需要而定; + mAlert = (ImageView) findViewById(R.id.iv_alert_icon); + mTitle = (TextView) findViewById(R.id.tv_title); + mTime = (TextView) findViewById(R.id.tv_time); + mCallName = (TextView) findViewById(R.id.tv_name); + mCheckBox = (CheckBox) findViewById(android.R.id.checkbox); + } + ///根据data的属性对各个控件的属性的控制,主要是可见性Visibility,内容setText,格式setTextAppearance + public void bind(Context context, NoteItemData data, boolean choiceMode, boolean checked) { + if (choiceMode && data.getType() == Notes.TYPE_NOTE) { + mCheckBox.setVisibility(View.VISIBLE); ///设置可见行为可见 + mCheckBox.setChecked(checked); ///格子打钩 + } else { + mCheckBox.setVisibility(View.GONE); + } + + mItemData = data; + ///设置控件属性,一共三种情况,由data的id和父id是否与保存到文件夹的id一致来决定 + if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) { + mCallName.setVisibility(View.GONE); + mAlert.setVisibility(View.VISIBLE); + //设置该textview的style + mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem); + //settext为设置内容 + mTitle.setText(context.getString(R.string.call_record_folder_name) + + context.getString(R.string.format_folder_files_count, data.getNotesCount())); + mAlert.setImageResource(R.drawable.call_record); + } else if (data.getParentId() == Notes.ID_CALL_RECORD_FOLDER) { + mCallName.setVisibility(View.VISIBLE); + mCallName.setText(data.getCallName()); + mTitle.setTextAppearance(context,R.style.TextAppearanceSecondaryItem); + mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet())); + ///关于闹钟的设置 + if (data.hasAlert()) { + mAlert.setImageResource(R.drawable.clock);//图片来源的设置 + mAlert.setVisibility(View.VISIBLE); + } else { + mAlert.setVisibility(View.GONE); + } + } else { + mCallName.setVisibility(View.GONE); + mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem); + ///设置title格式 + if (data.getType() == Notes.TYPE_FOLDER) { + mTitle.setText(data.getSnippet() + + context.getString(R.string.format_folder_files_count, + data.getNotesCount())); + mAlert.setVisibility(View.GONE); + } else { + mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet())); + if (data.hasAlert()) { + mAlert.setImageResource(R.drawable.clock);///设置图片来源 + mAlert.setVisibility(View.VISIBLE); + } else { + mAlert.setVisibility(View.GONE); + } + } + } + ///设置内容,获取相关时间,从data里编辑的日期中获取 + mTime. setText(DateUtils.getRelativeTimeSpanString(data.getModifiedDate())); + + setBackground(data); + } + //根据data的文件属性来设置背景 + private void setBackground(NoteItemData data) { + int id = data.getBgColorId(); + //,若是note型文件,则4种情况,对于4种不同情况的背景来源 + if (data.getType() == Notes.TYPE_NOTE) { + //单个数据并且只有一个子文件夹 + if (data.isSingle() || data.isOneFollowingFolder()) { + setBackgroundResource(NoteItemBgResources.getNoteBgSingleRes(id)); + } else if (data.isLast()) {//是最后一个数据 + setBackgroundResource(NoteItemBgResources.getNoteBgLastRes(id)); + } else if (data.isFirst() || data.isMultiFollowingFolder()) {//是一个数据并有多个子文件夹 + setBackgroundResource(NoteItemBgResources.getNoteBgFirstRes(id)); + } else { + setBackgroundResource(NoteItemBgResources.getNoteBgNormalRes(id)); + } + } else { + //若不是note直接调用文件夹的背景来源 + setBackgroundResource(NoteItemBgResources.getFolderBgRes()); + } + } + public NoteItemData getItemData() { + return mItemData; + } +} \ No newline at end of file diff --git a/src/main/java/net/micode/notes/ui/NotesPreferenceActivity.java b/src/main/java/net/micode/notes/ui/NotesPreferenceActivity.java new file mode 100644 index 0000000..e4109c6 --- /dev/null +++ b/src/main/java/net/micode/notes/ui/NotesPreferenceActivity.java @@ -0,0 +1,492 @@ +package net.micode.notes.ui; + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.app.ActionBar; +import android.app.AlertDialog; +import android.content.BroadcastReceiver; +import android.content.ContentValues; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.preference.Preference; +import android.preference.Preference.OnPreferenceClickListener; +import android.preference.PreferenceActivity; +import android.preference.PreferenceCategory; +import android.text.TextUtils; +import android.text.format.DateFormat; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.Button; +import android.widget.TextView; +import android.widget.Toast; + +import net.micode.notes.R; +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.gtask.remote.GTaskSyncService; + +/** + * @Package: net.micode.notes.ui + * @ClassName: NotesPreferenceActivity + * @Description: + * NotesPreferenceActivity,在小米便签中主要实现的是对背景颜色和字体大小的数据储存。 + * 继承了PreferenceActivity主要功能为对系统信息和配置进行自动保存的Activity + * @Author: YangYizhe + * @CreateDate: 12/23/2023 11:42 PM + * @Version: 1.0 + */ +public class NotesPreferenceActivity extends PreferenceActivity { + public static final String PREFERENCE_NAME = "notes_preferences";//优先名 + public static final String PREFERENCE_SYNC_ACCOUNT_NAME = "pref_key_account_name";//同步账号 + public static final String PREFERENCE_LAST_SYNC_TIME = "pref_last_sync_time";//同步时间 + public static final String PREFERENCE_SET_BG_COLOR_KEY = "pref_key_bg_random_appear"; + private static final String PREFERENCE_SYNC_ACCOUNT_KEY = "pref_sync_account_key";//同步密码 + private static final String AUTHORITIES_FILTER_KEY = "authorities"; + private PreferenceCategory mAccountCategory;//账户分组 + private GTaskReceiver mReceiver;//同步任务接收器 + private Account[] mOriAccounts;//账户 + private boolean mHasAddedAccount;//账户的hash标记 + + @Override + /** + * @method onCreate + * @description 创建一个activity,在函数里要完成所有的正常静态设置 + * @date: 12/23/2023 11:43 PM + * @author: YangYizhe + * @param [icicle] + * @return void + */ + protected void onCreate(Bundle icicle) { + super.onCreate(icicle); + /* using the app icon for navigation */ + getActionBar().setDisplayHomeAsUpEnabled(true);//给左上角图标的左边加上一个返回的图标 + addPreferencesFromResource(R.xml.preferences);//添加xml来源并显示 xml + mAccountCategory = (PreferenceCategory) findPreference(PREFERENCE_SYNC_ACCOUNT_KEY);//根据同步账户关键码来初始化分组 + mReceiver = new GTaskReceiver(); + IntentFilter filter = new IntentFilter(); + filter.addAction(GTaskSyncService.GTASK_SERVICE_BROADCAST_NAME); + registerReceiver(mReceiver, filter);//初始化同步组件 + mOriAccounts = null; + View header = LayoutInflater.from(this).inflate(R.layout.settings_header, null);//获取listvivew,ListView的作用:用于列出所有选择 + getListView().addHeaderView(header, null, true);//在listview组件上方添加其他组件 + } + + @Override + /** + * @method onResume + * @description activity交互功能的实现,用于接受用户的输入 + * @date: 12/23/2023 11:44 PM + * @author: YangYizhe + * @return void + */ + protected void onResume() { + super.onResume(); + + // need to set sync account automatically if user has added a new + // account + if (mHasAddedAccount) { + //若用户新加了账户则自动设置同步账户 + Account[] accounts = getGoogleAccounts();//获取google同步账户 + if (mOriAccounts != null && accounts.length > mOriAccounts.length) { + //若原账户不为空且当前账户有增加 + for (Account accountNew : accounts) { + boolean found = false; + for (Account accountOld : mOriAccounts) { + if (TextUtils.equals(accountOld.name, accountNew.name)) { + //更新账户 + found = true; + break; + } + } + if (!found) { + setSyncAccount(accountNew.name); + //若是没有找到旧的账户,那么同步账号中就只添加新账户 + break; + } + } + } + } + + refreshUI();//刷新标签界面 + } + + @Override + protected void onDestroy() { + if (mReceiver != null) { + unregisterReceiver(mReceiver);//注销接收器 + } + super.onDestroy();//执行父类的销毁动作 + } + + /** + * @method loadAccountPreference + * @description 重新设置账户信息 + * @date: 12/23/2023 11:45 PM + * @author: YangYizhe + * @param + * @return + */ + private void loadAccountPreference() { + mAccountCategory.removeAll();//销毁所有的分组 + Preference accountPref = new Preference(this);//建立首选项 + final String defaultAccount = getSyncAccountName(this); + accountPref.setTitle(getString(R.string.preferences_account_title)); + accountPref.setSummary(getString(R.string.preferences_account_summary));//设置首选项的大标题和小标题 + accountPref.setOnPreferenceClickListener(new OnPreferenceClickListener() { + public boolean onPreferenceClick(Preference preference) { + //建立监听器 + if (!GTaskSyncService.isSyncing()) { + if (TextUtils.isEmpty(defaultAccount)) { + // the first time to set account + //若是第一次建立账户显示选择账户提示对话框 + showSelectAccountAlertDialog(); + } else { + // if the account has already been set, we need to promp + // user about the risk + //若是已经建立则显示修改对话框并进行修改操作 + showChangeAccountConfirmAlertDialog(); + } + } else { + //若在没有同步的情况下,则在toast中显示不能修改 + Toast.makeText(NotesPreferenceActivity.this, + R.string.preferences_toast_cannot_change_account, Toast.LENGTH_SHORT) + .show(); + } + return true; + } + }); + mAccountCategory.addPreference(accountPref);//根据新建首选项编辑新的账户分组 + } + /** + * @method loadSyncButton + * @description 设置按键的状态和最后同步的时间 + * @date: 12/23/2023 11:46 PM + * @author: YangYizhe + * @param + * @return + */ + private void loadSyncButton() { + Button syncButton = (Button) findViewById(R.id.preference_sync_button); + TextView lastSyncTimeView = (TextView) findViewById(R.id.prefenerece_sync_status_textview); + //获取同步按钮控件和最终同步时间的的窗口 + // set button state + //设置按钮的状态 + if (GTaskSyncService.isSyncing()) { + //若是在同步状态下 + syncButton.setText(getString(R.string.preferences_button_sync_cancel)); + syncButton.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + GTaskSyncService.cancelSync(NotesPreferenceActivity.this); + } + });//设置按钮显示的文本为“取消同步”以及监听器 + } else { + syncButton.setText(getString(R.string.preferences_button_sync_immediately)); + syncButton.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + GTaskSyncService.startSync(NotesPreferenceActivity.this); + } + });//若是不同步则设置按钮显示的文本为“立即同步”以及对应监听器 + } + syncButton.setEnabled(!TextUtils.isEmpty(getSyncAccountName(this)));//设置按键可用还是不可用 + // set last sync time + // 设置最终同步时间 + if (GTaskSyncService.isSyncing()) { + //若是在同步的情况下 + lastSyncTimeView.setText(GTaskSyncService.getProgressString()); + lastSyncTimeView.setVisibility(View.VISIBLE); + // 根据当前同步服务器设置时间显示框的文本以及可见性 + } else { + //若是非同步情况 + long lastSyncTime = getLastSyncTime(this); + if (lastSyncTime != 0) { + lastSyncTimeView.setText(getString(R.string.preferences_last_sync_time, + DateFormat.format(getString(R.string.preferences_last_sync_time_format), + lastSyncTime))); + lastSyncTimeView.setVisibility(View.VISIBLE); + //则根据最后同步时间的信息来编辑时间显示框的文本内容和可见性 + } else { + //若时间为空直接设置为不可见状态 + lastSyncTimeView.setVisibility(View.GONE); + } + } + } + /** + * @method refreshUI + * @description + * 刷新标签界面 + * 调用上文设置账号和设置按键两个函数来实现 + * @date: 12/23/2023 11:46 PM + * @author: YangYizhe + */ + private void refreshUI() { + loadAccountPreference(); + loadSyncButton(); + } + /** + * @method showSelectAccountAlertDialog + * @description 显示账户选择的对话框并进行账户的设置 + * @date: 12/23/2023 11:47 PM + * @author: YangYizhe + * @param + * @return + */ + private void showSelectAccountAlertDialog() { + AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);//创建一个新的对话框 + + View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null); + TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title); + titleTextView.setText(getString(R.string.preferences_dialog_select_account_title)); + TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle); + subtitleTextView.setText(getString(R.string.preferences_dialog_select_account_tips));//设置标题以及子标题的内容 + dialogBuilder.setCustomTitle(titleView); + dialogBuilder.setPositiveButton(null, null);//设置对话框的自定义标题,建立一个YES的按钮 + Account[] accounts = getGoogleAccounts(); + String defAccount = getSyncAccountName(this);//获取同步账户信息 + mOriAccounts = accounts; + mHasAddedAccount = false; + + if (accounts.length > 0) { + //若账户不为空 + CharSequence[] items = new CharSequence[accounts.length]; + final CharSequence[] itemMapping = items; + int checkedItem = -1; + int index = 0; + for (Account account : accounts) { + if (TextUtils.equals(account.name, defAccount)) { + checkedItem = index;//在账户列表中查询到所需账户 + } + items[index++] = account.name; + } + dialogBuilder.setSingleChoiceItems(items, checkedItem, + //在对话框建立一个单选的复选框 + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + setSyncAccount(itemMapping[which].toString()); + dialog.dismiss();//取消对话框 + refreshUI(); + } + });//建立对话框网络版的监听器 + } + + View addAccountView = LayoutInflater.from(this).inflate(R.layout.add_account_text, null); + dialogBuilder.setView(addAccountView);//给新加账户对话框设置自定义样式 + + final AlertDialog dialog = dialogBuilder.show();//显示对话框 + addAccountView.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + mHasAddedAccount = true; + //将新加账户的hash置true + Intent intent = new Intent("android.settings.ADD_ACCOUNT_SETTINGS"); + //建立网络建立组件 + intent.putExtra(AUTHORITIES_FILTER_KEY, new String[] { + "gmail-ls" + }); + startActivityForResult(intent, -1); + //跳回上一个选项 + dialog.dismiss(); + } + });//建立新加账户对话框的监听器 + } + /** + * @method showChangeAccountConfirmAlertDialog + * @description 显示账户选择对话框和相关账户操作 + * @date: 12/23/2023 11:48 PM + * @author: YangYizhe + */ + private void showChangeAccountConfirmAlertDialog() { + AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);//创建一个新的对话框 + View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null); + TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title); + titleTextView.setText(getString(R.string.preferences_dialog_change_account_title, + getSyncAccountName(this))); + TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle); + subtitleTextView.setText(getString(R.string.preferences_dialog_change_account_warn_msg));//根据同步修改的账户信息设置标题以及子标题的内容 + dialogBuilder.setCustomTitle(titleView);//设置对话框的自定义标题 + CharSequence[] menuItemArray = new CharSequence[] { + getString(R.string.preferences_menu_change_account), + getString(R.string.preferences_menu_remove_account), + getString(R.string.preferences_menu_cancel) + }; + dialogBuilder.setItems(menuItemArray, new DialogInterface.OnClickListener() { + //设置对话框要显示的一个list,用于显示几个命令时,即change,remove,cancel + public void onClick(DialogInterface dialog, int which) { + //按键功能,由which来决定 + if (which == 0) { + //进入账户选择对话框 + showSelectAccountAlertDialog(); + } else if (which == 1) { + //删除账户并且跟新便签界面 + removeSyncAccount(); + refreshUI(); + } + } + }); + dialogBuilder.show();//显示对话框 + } + + /** + * @method getGoogleAccounts + * @description + * 获取谷歌账户 + * 通过账户管理器直接获取 + * @date: 12/23/2023 11:49 PM + * @author: YangYizhe + */ + private Account[] getGoogleAccounts() { + AccountManager accountManager = AccountManager.get(this); + return accountManager.getAccountsByType("com.google"); + } + + /** + * @method setSyncAccount + * @description 设置同步账户 + * @date: 12/23/2023 11:49 PM + * @author: YangYizhe + * @param account + */ + private void setSyncAccount(String account) { + if (!getSyncAccountName(this).equals(account)) { + //假如该账号不在同步账号列表中 + SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = settings.edit();//编辑共享的首选项 + if (account != null) { + editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, account); + } else { + editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, ""); + }//将该账号加入到首选项中 + + editor.commit();//提交修改的数据 + setLastSyncTime(this, 0);//将最后同步时间清零 + + // clean up local gtask related info + new Thread(new Runnable() { + public void run() { + ContentValues values = new ContentValues(); + values.put(NoteColumns.GTASK_ID, ""); + values.put(NoteColumns.SYNC_ID, 0); + getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null); + } + }).start(); + //重置当地同步任务的信息 + + Toast.makeText(NotesPreferenceActivity.this, + getString(R.string.preferences_toast_success_set_accout, account), + Toast.LENGTH_SHORT).show(); + //将toast的文本信息置为“设置账户成功”并显示出来 + } + } + /** + * @method removeSyncAccount + * @description 删除同步账户 + * @date: 12/23/2023 11:49 PM + * @author: YangYizhe + */ + private void removeSyncAccount() { + SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = settings.edit();//设置共享首选项 + + if (settings.contains(PREFERENCE_SYNC_ACCOUNT_NAME)) { + editor.remove(PREFERENCE_SYNC_ACCOUNT_NAME);//假如当前首选项中有账户就删除 + } + if (settings.contains(PREFERENCE_LAST_SYNC_TIME)) { + editor.remove(PREFERENCE_LAST_SYNC_TIME);//删除当前首选项中有账户时间 + } + editor.commit();//提交更新后的数据 + + // clean up local gtask related info + new Thread(new Runnable() { + public void run() { + ContentValues values = new ContentValues(); + values.put(NoteColumns.GTASK_ID, ""); + values.put(NoteColumns.SYNC_ID, 0); + getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null); + } + }).start(); + //重置当地同步任务的信息 + } + /** + * @method getSyncAccountName + * @description + * 获取同步账户名称 + * 通过共享的首选项里的信息直接获取 + * @date: 12/23/2023 11:50 PM + * @author: YangYizhe + * @param context + */ + public static String getSyncAccountName(Context context) { + SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, + Context.MODE_PRIVATE); + return settings.getString(PREFERENCE_SYNC_ACCOUNT_NAME, ""); + } + /** + * @method setLastSyncTime + * @description 设置最终同步的时间 + * @date: 12/23/2023 11:50 PM + * @author: YangYizhe + * @param + * @return + */ + public static void setLastSyncTime(Context context, long time) { + SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, + Context.MODE_PRIVATE); + SharedPreferences.Editor editor = settings.edit();// 从共享首选项中找到相关账户并获取其编辑器 + editor.putLong(PREFERENCE_LAST_SYNC_TIME, time); + editor.commit();//编辑最终同步时间并提交更新 + } + /** + * @method getLastSyncTime + * @description + * 获取最终同步时间 + * 通过共享的首选项里的信息直接获取 + * @date: 12/23/2023 11:51 PM + * @author: YangYizhe + * @param context + */ + public static long getLastSyncTime(Context context) { + SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, + Context.MODE_PRIVATE); + return settings.getLong(PREFERENCE_LAST_SYNC_TIME, 0); + } + + private class GTaskReceiver extends BroadcastReceiver { + + @Override + public void onReceive(Context context, Intent intent) { + refreshUI(); + if (intent.getBooleanExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_IS_SYNCING, false)) { + //获取随广播而来的Intent中的同步服务的数据 + TextView syncStatus = (TextView) findViewById(R.id.prefenerece_sync_status_textview); + syncStatus.setText(intent + .getStringExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_PROGRESS_MSG));//通过获取的数据在设置系统的状态 + } + + } + } + /** + * @method onOptionsItemSelected + * @description 处理菜单的选项 + * @date: 12/23/2023 11:52 PM + * @author: YangYizhe + * @param item + */ + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + //根据选项的id选择,这里只有一个主页 + case android.R.id.home: + Intent intent = new Intent(this, NotesListActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + return true; + //在主页情况下在创建连接组件intent,发出清空的信号并开始一个相应的activity + default: + return false; + } + } +} + \ No newline at end of file diff --git a/src/main/java/net/micode/notes/widget/NoteWidgetProvider.java b/src/main/java/net/micode/notes/widget/NoteWidgetProvider.java new file mode 100644 index 0000000..d8eb50b --- /dev/null +++ b/src/main/java/net/micode/notes/widget/NoteWidgetProvider.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.widget; +import android.app.PendingIntent; +import android.appwidget.AppWidgetManager; +import android.appwidget.AppWidgetProvider; +import android.content.ContentValues; +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; +import android.util.Log; +import android.widget.RemoteViews; + +import net.micode.notes.R; +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.tool.ResourceParser; +import net.micode.notes.ui.NoteEditActivity; +import net.micode.notes.ui.NotesListActivity; +/** + * @Package: net.micode.notes.widget + * @ClassName: NoteWidgetProvider + * @Description: 这个抽象类用作MiNote应用程序中笔记小部件的基础。它提供了在主屏幕上更新和管理笔记小部件的功能。 + * @Author: YangYizhe + * @CreateDate: 12/23/2023 11:53 PM + * @Version: 1.0 + */ +public abstract class NoteWidgetProvider extends AppWidgetProvider { + public static final String [] PROJECTION = new String [] { + NoteColumns.ID, + NoteColumns.BG_COLOR_ID, + NoteColumns.SNIPPET + }; + + public static final int COLUMN_ID = 0; + public static final int COLUMN_BG_COLOR_ID = 1; + public static final int COLUMN_SNIPPET = 2; + + private static final String TAG = "NoteWidgetProvider"; + + @Override + public void onDeleted(Context context, int[] appWidgetIds) { + ContentValues values = new ContentValues(); + values.put(NoteColumns.WIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); + for (int i = 0; i < appWidgetIds.length; i++) { + context.getContentResolver().update(Notes.CONTENT_NOTE_URI, + values, + NoteColumns.WIDGET_ID + "=?", + new String[] { String.valueOf(appWidgetIds[i])}); + } + } + + private Cursor getNoteWidgetInfo(Context context, int widgetId) { + return context.getContentResolver().query(Notes.CONTENT_NOTE_URI, + PROJECTION, + NoteColumns.WIDGET_ID + "=? AND " + NoteColumns.PARENT_ID + "<>?", + new String[] { String.valueOf(widgetId), String.valueOf(Notes.ID_TRASH_FOLER) }, + null); + } + + protected void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { + update(context, appWidgetManager, appWidgetIds, false); + } + + private void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds, + boolean privacyMode) { + for (int i = 0; i < appWidgetIds.length; i++) { + if (appWidgetIds[i] != AppWidgetManager.INVALID_APPWIDGET_ID) { + int bgId = ResourceParser.getDefaultBgId(context); + String snippet = ""; + Intent intent = new Intent(context, NoteEditActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); + intent.putExtra(Notes.INTENT_EXTRA_WIDGET_ID, appWidgetIds[i]); + intent.putExtra(Notes.INTENT_EXTRA_WIDGET_TYPE, getWidgetType()); + + Cursor c = getNoteWidgetInfo(context, appWidgetIds[i]); + if (c != null && c.moveToFirst()) { + if (c.getCount() > 1) { + Log.e(TAG, "Multiple message with same widget id:" + appWidgetIds[i]); + c.close(); + return; + } + snippet = c.getString(COLUMN_SNIPPET); + bgId = c.getInt(COLUMN_BG_COLOR_ID); + intent.putExtra(Intent.EXTRA_UID, c.getLong(COLUMN_ID)); + intent.setAction(Intent.ACTION_VIEW); + } else { + snippet = context.getResources().getString(R.string.widget_havenot_content); + intent.setAction(Intent.ACTION_INSERT_OR_EDIT); + } + + if (c != null) { + c.close(); + } + + RemoteViews rv = new RemoteViews(context.getPackageName(), getLayoutId()); + rv.setImageViewResource(R.id.widget_bg_image, getBgResourceId(bgId)); + intent.putExtra(Notes.INTENT_EXTRA_BACKGROUND_ID, bgId); + /** + * Generate the pending intent to start host for the widget + */ + PendingIntent pendingIntent = null; + if (privacyMode) { + rv.setTextViewText(R.id.widget_text, + context.getString(R.string.widget_under_visit_mode)); + pendingIntent = PendingIntent.getActivity(context, appWidgetIds[i], new Intent( + context, NotesListActivity.class), PendingIntent.FLAG_UPDATE_CURRENT); + } else { + rv.setTextViewText(R.id.widget_text, snippet); + pendingIntent = PendingIntent.getActivity(context, appWidgetIds[i], intent, + PendingIntent.FLAG_UPDATE_CURRENT); + } + + rv.setOnClickPendingIntent(R.id.widget_text, pendingIntent); + appWidgetManager.updateAppWidget(appWidgetIds[i], rv); + } + } + } + + protected abstract int getBgResourceId(int bgId); + + protected abstract int getLayoutId(); + + protected abstract int getWidgetType(); +} diff --git a/src/main/java/net/micode/notes/widget/NoteWidgetProvider_2x.java b/src/main/java/net/micode/notes/widget/NoteWidgetProvider_2x.java new file mode 100644 index 0000000..f3d5f1e --- /dev/null +++ b/src/main/java/net/micode/notes/widget/NoteWidgetProvider_2x.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.widget; + +import android.appwidget.AppWidgetManager; +import android.content.Context; + +import net.micode.notes.R; +import net.micode.notes.data.Notes; +import net.micode.notes.tool.ResourceParser; + +/** + * @Package: net.micode.notes.widget + * @ClassName: NoteWidgetProvider_2x + * @Description: + * 这个类是NoteWidgetProvider的一个具体实现,用于实现2x大小的笔记小部件。 + * 它继承自NoteWidgetProvider,并提供特定于2x小部件的布局ID、背景资源和小部件类型。 + * @Author: YangYizhe + * @CreateDate: 12/23/2023 11:55 PM + * @Version: 1.0 + */ +public class NoteWidgetProvider_2x extends NoteWidgetProvider { + @Override + public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { + super.update(context, appWidgetManager, appWidgetIds); + } + + @Override + protected int getLayoutId() { + return R.layout.widget_2x; + } + + @Override + protected int getBgResourceId(int bgId) { + return ResourceParser.WidgetBgResources.getWidget2xBgResource(bgId); + } + + @Override + protected int getWidgetType() { + return Notes.TYPE_WIDGET_2X; + } +} diff --git a/src/main/java/net/micode/notes/widget/NoteWidgetProvider_4x.java b/src/main/java/net/micode/notes/widget/NoteWidgetProvider_4x.java new file mode 100644 index 0000000..0c3ae50 --- /dev/null +++ b/src/main/java/net/micode/notes/widget/NoteWidgetProvider_4x.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.widget; + +import android.appwidget.AppWidgetManager; +import android.content.Context; + +import net.micode.notes.R; +import net.micode.notes.data.Notes; +import net.micode.notes.tool.ResourceParser; + +/** + * @Package: net.micode.notes.widget + * @ClassName: NoteWidgetProvider_4x + * @Description: + * 这个类是NoteWidgetProvider的一个具体实现,用于实现4x大小的笔记小部件。 + * 它继承自NoteWidgetProvider,并提供特定于4x小部件的布局ID、背景资源和小部件类型。 + * @Author: YangYizhe + * @CreateDate: 12/23/2023 11:56 PM + * @Version: 1.0 + */ +public class NoteWidgetProvider_4x extends NoteWidgetProvider { + @Override + public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { + super.update(context, appWidgetManager, appWidgetIds); + } + + protected int getLayoutId() { + return R.layout.widget_4x; + } + + @Override + protected int getBgResourceId(int bgId) { + return ResourceParser.WidgetBgResources.getWidget4xBgResource(bgId); + } + + @Override + protected int getWidgetType() { + return Notes.TYPE_WIDGET_4X; + } +} diff --git a/src/main/res/layout/note_edit.xml b/src/main/res/layout/note_edit.xml index 59415d7..10b2aa7 100644 --- a/src/main/res/layout/note_edit.xml +++ b/src/main/res/layout/note_edit.xml @@ -30,7 +30,6 @@ android:id="@+id/note_title" android:layout_width="fill_parent" android:layout_height="wrap_content"> - - - + android:textAppearance="@style/TextAppearancePrimaryItem" + android:lineSpacingMultiplier="1.2" /> + android:background="@drawable/list_background"> - - - - - - - - - - diff --git a/src/main/res/menu/sub_folder.xml b/src/main/res/menu/sub_folder.xml index d81e568..b00de26 100644 --- a/src/main/res/menu/sub_folder.xml +++ b/src/main/res/menu/sub_folder.xml @@ -21,20 +21,4 @@ - - - - - - - - \ No newline at end of file diff --git a/src/main/res/values-zh-rCN/strings.xml b/src/main/res/values-zh-rCN/strings.xml index dacfe89..09f75ed 100644 --- a/src/main/res/values-zh-rCN/strings.xml +++ b/src/main/res/values-zh-rCN/strings.xml @@ -119,14 +119,6 @@ 便签 设置 取消 - 亮背景 - 暗背景 - 私密模式 - 退出私密模式 - 字符数 - 恢复 - 欢迎页面 - EAZZY\nNOTE %1$s 条符合“%2$s”的搜索结果 diff --git a/src/main/res/values-zh-rTW/strings.xml b/src/main/res/values-zh-rTW/strings.xml index 1bfa120..e29b79b 100644 --- a/src/main/res/values-zh-rTW/strings.xml +++ b/src/main/res/values-zh-rTW/strings.xml @@ -120,14 +120,6 @@ 便籤 設置 取消 - 亮背景 - 暗背景 - 私密模式 - 退出私密模式 - 字符数 - 恢复 - 欢迎页面 - EAZZY\nNOTE %1$s 條符合”%2$s“的搜尋結果 diff --git a/src/main/res/values/colors.xml b/src/main/res/values/colors.xml index 2496c0c..123ffbf 100644 --- a/src/main/res/values/colors.xml +++ b/src/main/res/values/colors.xml @@ -17,9 +17,4 @@ #335b5b5b - #FF039BE5 - #FF01579B - #FF40C4FF - #FF00B0FF - #66000000 diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 469b04c..55df868 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -15,7 +15,8 @@ limitations under the License. --> - + Notes Notes 2x2 Notes 4x4 @@ -125,24 +126,10 @@ Notes set cancel - - light mode - dark mode - - secret mode - quit secret mode - - length - restore - - SplashActivity - EAZZY\nNOTE - - %1$s result for \"%2$s\" + %1$s result for \"%2$s\" - %1$s results for \"%2$s\" + %1$s results for \"%2$s\" - diff --git a/src/main/res/values/styles.xml b/src/main/res/values/styles.xml index db56d26..ad0d90d 100644 --- a/src/main/res/values/styles.xml +++ b/src/main/res/values/styles.xml @@ -16,22 +16,18 @@ --> - - - - @@ -70,13 +66,4 @@ visible - - - - \ No newline at end of file