针对给增添标签功能以及修复搜索功能疑修 #25

Merged
p82feo7wg merged 6 commits from wangjiaqi_branch into master 2 months ago

@ -42,6 +42,13 @@
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data
android:name="android.app.searchable"
android:resource="@xml/searchable" />
</activity>
@ -65,15 +72,6 @@
<data android:mimeType="vnd.android.cursor.item/text_note" />
<data android:mimeType="vnd.android.cursor.item/call_note" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data
android:name="android.app.searchable"
android:resource="@xml/searchable" />
</activity>
@ -162,7 +160,7 @@
<meta-data
android:name="android.app.default_searchable"
android:value=".ui.NoteEditActivity" />
android:value=".ui.NotesListActivity" />
<!-- <activity-->
<!-- android:name=".MainActivity"-->

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M2,20h20v4H2V20zM5.49,17h2.42l1.27,-3.58h5.65L16.09,17h2.42L13.25,3h-2.5L5.49,17zM9.91,11.39l2.03,-5.79h0.12l2.03,5.79H9.91z"/>
</vector>

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/black" android:strokeColor="@android:color/black" android:strokeWidth="2" android:pathData="M5,15 L19,15"/>
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 443 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 554 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M2,20h20v4H2V20zM5.49,17h2.42l1.27,-3.58h5.65L16.09,17h2.42L13.25,3h-2.5L5.49,17zM9.91,11.39l2.03,-5.79h0.12l2.03,5.79H9.91z"/>
</vector>

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M15.6,10.79c0.97,-0.67 1.65,-1.77 1.65,-2.79 0,-2.26 -1.75,-4 -4,-4L7,4v14h7.04c2.09,0 3.71,-1.7 3.71,-3.79 0,-1.52 -0.86,-2.82 -2.15,-3.42zM10,6.5h3c0.83,0 1.5,0.67 1.5,1.5s-0.67,1.5 -1.5,1.5h-3v-3zM13.5,15.5L10,15.5v-3h3.5c0.83,0 1.5,0.67 1.5,1.5s-0.67,1.5 -1.5,1.5z"/>
</vector>

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M10,4v3h2.21l-3.42,8H6v3h8v-3h-2.21l3.42,-8H18V4z"/>
</vector>

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M12.5,8c-2.65,0 -5.05,0.99 -6.9,2.6L2,7v9h9l-3.62,-3.62c1.39,-1.16 3.16,-1.88 5.12,-1.88 3.54,0 6.55,2.31 7.6,5.5l2.37,-0.78C21.08,11.03 17.15,8 12.5,8z"/>
</vector>

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<vector
android:height="108dp"
android:width="108dp"
android:viewportHeight="108"
android:viewportWidth="108"
xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z"/>
<path android:fillColor="#00000000" android:pathData="M9,0L9,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,0L19,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M29,0L29,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M39,0L39,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M49,0L49,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M59,0L59,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M69,0L69,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M79,0L79,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M89,0L89,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M99,0L99,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,9L108,9"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,19L108,19"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,29L108,29"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,39L108,39"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,49L108,49"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,59L108,59"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,69L108,69"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,79L108,79"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,89L108,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,99L108,99"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,29L89,29"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,39L89,39"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,49L89,49"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,59L89,59"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,69L89,69"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,79L89,79"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M29,19L29,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M39,19L39,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M49,19L49,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M59,19L59,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M69,19L69,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M79,19L79,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
</vector>

@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true"
android:drawable="@drawable/new_note_pressed" />
<item
android:drawable="@drawable/new_note_normal" />
</selector>

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<vector
android:height="108dp"
android:width="108dp"
android:viewportHeight="108"
android:viewportWidth="108"
xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z"/>
<path android:fillColor="#00000000" android:pathData="M9,0L9,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,0L19,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M29,0L29,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M39,0L39,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M49,0L49,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M59,0L59,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M69,0L69,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M79,0L79,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M89,0L89,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M99,0L99,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,9L108,9"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,19L108,19"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,29L108,29"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,39L108,39"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,49L108,49"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,59L108,59"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,69L108,69"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,79L108,79"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,89L108,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,99L108,99"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,29L89,29"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,39L89,39"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,49L89,49"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,59L89,59"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,69L89,69"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,79L89,79"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M29,19L29,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M39,19L39,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M49,19L49,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M59,19L59,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M69,19L69,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M79,19L79,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
</vector>

@ -64,8 +64,8 @@
android:layout_gravity="center"
android:background="@drawable/bg_btn_set_color" />
<!-- Insert image button OMO -->
<ImageButton
<!-- Insert image button OMO -->
<ImageButton
android:id="@+id/btn_insert_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@ -75,6 +75,71 @@
android:layout_marginRight="8dip" />
</LinearLayout>
<!-- Tag Management Section - Initially Hidden -->
<LinearLayout
android:id="@+id/tag_management_section"
android:layout_width="fill_parent"
android:layout_height="100dp"
android:orientation="vertical"
android:padding="8dp"
android:background="@drawable/list_background"
android:visibility="gone">
<!-- Existing Tags -->
<HorizontalScrollView
android:id="@+id/existing_tags_scroll"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:scrollbars="none"
android:minHeight="30dp">
<LinearLayout
android:id="@+id/existing_tags_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:padding="4dp"
android:layout_gravity="center_vertical">
</LinearLayout>
</HorizontalScrollView>
<!-- Add Tag Input -->
<LinearLayout
android:id="@+id/add_tag_input_layout"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:padding="4dp"
android:layout_marginTop="8dp"
android:minHeight="40dp">
<EditText
android:id="@+id/et_tag_input"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_weight="1"
android:hint="添加标签最多5个1-15字符"
android:inputType="text"
android:maxLength="15"
android:singleLine="true"
android:textAppearance="@style/TextAppearancePrimaryItem"
android:background="@android:drawable/editbox_background_normal"
android:gravity="center_vertical"
android:paddingHorizontal="8dp" />
<Button
android:id="@+id/btn_add_tag"
android:layout_width="80dp"
android:layout_height="40dp"
android:layout_marginLeft="8dp"
android:text="添加"
android:textAppearance="@style/TextAppearancePrimaryItem"
android:gravity="center" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/sv_note_edit"
android:layout_width="fill_parent"
@ -128,12 +193,17 @@
android:layout_height="7dip"
android:background="@drawable/bg_color_btn_mask" />
<!-- 横向滚动的富文本工具栏 -->
<!-- 横向滚动的富文本工具栏 -->
<HorizontalScrollView
android:id="@+id/floating_editor_toolbar"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_gravity="bottom|center"
android:background="#f5f5f5"
android:scrollbars="none">
android:scrollbars="none"
android:elevation="8dp"
android:layout_marginBottom="40dp">
<LinearLayout
android:layout_width="wrap_content"
@ -174,7 +244,7 @@
android:backgroundTint="@null"
android:focusable="false"
android:clickable="true"/>
<!-- 下划线按钮 -->
<Button
android:id="@+id/action_underline"
android:layout_width="40dp"
@ -189,6 +259,8 @@
</HorizontalScrollView>
</LinearLayout>
<!-- 富文本工具栏 - 放置在页面底部 -->
</LinearLayout>

@ -109,4 +109,4 @@
android:layout_gravity="top|right"
android:layout_marginEnd="24dp"
android:visibility="gone"/>
</FrameLayout>
</FrameLayout>

@ -40,35 +40,75 @@
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="10dip"
android:gravity="center_vertical|end">
android:orientation="vertical"
android:padding="10dip">
<!-- 菜单按钮 -->
<ImageButton
android:id="@+id/btn_menu"
android:layout_width="50dip"
android:layout_height="50dip"
android:src="@android:drawable/ic_menu_more"
android:background="@null"
android:onClick="showCustomMenu" />
<!-- 搜索图标按钮 -->
<ImageButton
android:id="@+id/btn_search"
android:layout_width="50dp"
<!-- 搜索栏 -->
<LinearLayout
android:id="@+id/search_bar"
android:layout_width="fill_parent"
android:layout_height="50dip"
android:layout_marginRight="10dip"
android:src="@android:drawable/ic_menu_search"
android:background="@null"
android:onClick="handleSearchClick" />
android:orientation="horizontal"
android:background="@drawable/bg_btn_set_color"
android:gravity="center_vertical"
android:visibility="gone"
android:layout_marginBottom="10dip">
<EditText
android:id="@+id/et_search"
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@null"
android:hint="搜索笔记和标签"
android:textColor="#FFFFFF"
android:paddingLeft="15dip"
android:paddingRight="10dip"
android:singleLine="true"
android:textSize="16sp"
android:imeOptions="actionSearch" />
<ImageButton
android:id="@+id/btn_clear_search"
android:layout_width="40dip"
android:layout_height="40dip"
android:src="@android:drawable/ic_menu_close_clear_cancel"
android:background="@null"
android:padding="10dip"
android:tint="#FFFFFF" />
</LinearLayout>
<!-- 正方形添加按钮 -->
<Button
android:id="@+id/btn_new_note"
android:background="@drawable/new_note"
android:layout_width="50dip"
android:layout_height="50dip"
android:focusable="false" />
<!-- 工具栏按钮 -->
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical|end">
<!-- 菜单按钮 -->
<ImageButton
android:id="@+id/btn_menu"
android:layout_width="50dip"
android:layout_height="50dip"
android:src="@android:drawable/ic_menu_more"
android:background="@null"
android:onClick="showCustomMenu" />
<!-- 搜索图标按钮 -->
<ImageButton
android:id="@+id/btn_search"
android:layout_width="50dp"
android:layout_height="50dip"
android:layout_marginRight="10dip"
android:src="@android:drawable/ic_menu_search"
android:background="@null"
android:onClick="handleSearchClick" />
<!-- 正方形添加按钮 -->
<Button
android:id="@+id/btn_new_note"
android:background="@drawable/new_note"
android:layout_width="50dip"
android:layout_height="50dip"
android:focusable="false" />
</LinearLayout>
</LinearLayout>
<ListView
@ -124,6 +164,4 @@
android:minWidth="0dip" />
</LinearLayout>
</LinearLayout>
</FrameLayout>
</FrameLayout>

@ -49,4 +49,8 @@
<item
android:id="@+id/menu_delete_remind"
android:title="@string/menu_remove_remind" />
<item
android:id="@+id/menu_tag_management"
android:title="标签管理" />
</menu>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- 定义退出搜索菜单项的ID -->
<item name="menu_exit_search" type="id" />
</resources>

@ -80,6 +80,8 @@ public class Notes {
public static final String NOTE = TextNote.CONTENT_ITEM_TYPE;
// 通话记录笔记的MIME类型
public static final String CALL_NOTE = CallNote.CONTENT_ITEM_TYPE;
// 标签的MIME类型
public static final String TAG = TagNote.CONTENT_ITEM_TYPE;
}
// URI 是 ContentProvider 的标准访问入口,用于统一访问应用的数据。
@ -251,4 +253,28 @@ public class Notes {
// 通话记录笔记的URI
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/call_note");
}
/**
* TagNote -
* URI
*/
public static final class TagNote implements DataColumns {
// 标签名称
public static final String TAG_NAME = CONTENT;
// 标签颜色
public static final String TAG_COLOR = DATA1;
// 标签使用次数
public static final String TAG_COUNT = DATA2;
// 标签的目录MIME类型
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/tag_note";
// 标签的单项MIME类型
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/tag_note";
// 标签的URI
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/tag_note");
}
}

@ -30,7 +30,7 @@ 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 = 7;
private static final int DB_VERSION = 8;
public interface TABLE {
public static final String NOTE = "note";
@ -326,6 +326,8 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
db.execSQL(CREATE_DATA_TABLE_SQL);
reCreateDataTableTriggers(db);
db.execSQL(CREATE_DATA_NOTE_ID_INDEX_SQL);
// 添加搜索相关的索引,优化搜索性能
db.execSQL("CREATE INDEX IF NOT EXISTS data_mime_type_content_index ON " + TABLE.DATA + "(" + DataColumns.MIME_TYPE + ", " + DataColumns.CONTENT + ");");
Log.d(TAG, "data table has been created");
}
@ -402,6 +404,11 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
oldVersion++;
}
if (oldVersion == 7) {
upgradeToV8(db);
oldVersion++;
}
if (reCreateTriggers) {
reCreateNoteTableTriggers(db);
reCreateDataTableTriggers(db);
@ -467,6 +474,12 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
createTrashDataTable(db);
Log.d(TAG, "Upgraded to version 7: created trash_data table");
}
private void upgradeToV8(SQLiteDatabase db) {
// 升级到版本8添加标签支持
// 标签数据将存储在现有的data表中使用新的MIME类型
Log.d(TAG, "Upgraded to version 8: added tag support");
}
public void createEncryptedNotePasswordTable(SQLiteDatabase db) {
db.execSQL(CREATE_ENCRYPTED_NOTE_PASSWORD_TABLE_SQL);

@ -79,9 +79,13 @@ public class NotesProvider extends ContentProvider {
private static String NOTES_SNIPPET_SEARCH_QUERY = "SELECT " + NOTES_SEARCH_PROJECTION
+ " FROM " + TABLE.NOTE
+ " WHERE " + NoteColumns.SNIPPET + " LIKE ?"
+ " LEFT JOIN " + TABLE.DATA + " ON " + TABLE.NOTE + "." + NoteColumns.ID + "=" + TABLE.DATA + "." + DataColumns.NOTE_ID
+ " WHERE (" + NoteColumns.SNIPPET + " LIKE ?"
+ " OR (" + TABLE.DATA + "." + DataColumns.MIME_TYPE + "=? AND " + TABLE.DATA + "." + DataColumns.CONTENT + " LIKE ?" + "))"
+ " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER
+ " AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE;
+ " AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE
+ " GROUP BY " + TABLE.NOTE + "." + NoteColumns.ID
+ " ORDER BY CASE WHEN " + TABLE.DATA + "." + DataColumns.MIME_TYPE + "=? AND " + TABLE.DATA + "." + DataColumns.CONTENT + " LIKE ?" + " THEN 0 ELSE 1 END, " + NoteColumns.MODIFIED_DATE + " DESC";
@Override
public boolean onCreate() {
@ -145,8 +149,9 @@ public class NotesProvider extends ContentProvider {
try {
searchString = String.format("%%%s%%", searchString);
String tagMimeType = Notes.DataConstants.TAG;
c = db.rawQuery(NOTES_SNIPPET_SEARCH_QUERY,
new String[] { searchString });
new String[] { searchString, tagMimeType, searchString, tagMimeType, searchString });
} catch (IllegalStateException ex) {
Log.e(TAG, "got exception: " + ex.toString());
}

@ -28,7 +28,9 @@ 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.TagNote;
import net.micode.notes.data.Notes.TextNote;
import java.util.ArrayList;
@ -149,6 +151,21 @@ public class Note {
public void setCallData(String key, String value) {
mNoteData.setCallData(key, value);
}
/**
*
* @param tagName
*/
public void addTagData(String tagName) {
mNoteData.addTagData(tagName);
}
/**
*
*/
public void clearTagData() {
mNoteData.clearTagData();
}
/**
*
@ -193,7 +210,7 @@ public class Note {
/**
*
*
*
*/
private class NoteData {
private long mTextDataId; // 文本笔记数据ID
@ -201,17 +218,23 @@ public class Note {
private long mCallDataId; // 通话笔记数据ID
private ContentValues mCallDataValues; // 通话笔记数据值
private static final String TAG = "NoteData"; // 日志标签
// 标签相关数据
private ArrayList<ContentValues> mTagDataValuesList; // 标签数据值列表
private ArrayList<Long> mTagDataIdList; // 标签数据ID列表
/** 构造函数:初始化两个数据集与对应 ID */
/** 构造函数:初始化数据集与对应 ID */
public NoteData() {
mTextDataValues = new ContentValues();
mCallDataValues = new ContentValues();
mTextDataId = 0;
mCallDataId = 0;
// 初始化标签相关数据
mTagDataValuesList = new ArrayList<>();
mTagDataIdList = new ArrayList<>();
}
/**
@ -219,7 +242,7 @@ public class Note {
* @return truefalse
*/
boolean isLocalModified() {
return mTextDataValues.size() > 0 || mCallDataValues.size() > 0;
return mTextDataValues.size() > 0 || mCallDataValues.size() > 0 || mTagDataValuesList.size() > 0;
}
/**
@ -267,6 +290,28 @@ public class Note {
mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1);
mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis());
}
/**
*
* @param tagName
*/
void addTagData(String tagName) {
ContentValues tagValues = new ContentValues();
tagValues.put(TagNote.TAG_NAME, tagName);
mTagDataValuesList.add(tagValues);
mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1);
mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis());
}
/**
*
*/
void clearTagData() {
mTagDataValuesList.clear();
mTagDataIdList.clear();
mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1);
mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis());
}
/**
*
@ -330,6 +375,29 @@ public class Note {
mCallDataValues.clear();
}
// 处理标签数据
if (mTagDataValuesList.size() > 0) {
// 先删除现有标签数据
String selection = DataColumns.NOTE_ID + "=? AND " + DataColumns.MIME_TYPE + "=?";
String[] selectionArgs = {String.valueOf(noteId), TagNote.CONTENT_ITEM_TYPE};
context.getContentResolver().delete(Notes.CONTENT_DATA_URI, selection, selectionArgs);
// 插入新标签数据
for (ContentValues tagValues : mTagDataValuesList) {
tagValues.put(DataColumns.NOTE_ID, noteId);
tagValues.put(DataColumns.MIME_TYPE, TagNote.CONTENT_ITEM_TYPE);
Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI, tagValues);
try {
long tagDataId = Long.valueOf(uri.getPathSegments().get(1));
mTagDataIdList.add(tagDataId);
} catch (NumberFormatException e) {
Log.e(TAG, "Insert new tag data fail with noteId" + noteId);
return null;
}
}
mTagDataValuesList.clear();
}
if (operationList.size() > 0) {
try {
ContentProviderResult[] results = context.getContentResolver().applyBatch(

@ -162,7 +162,7 @@ public class WorkingNote {
/**
* data
* <p>NOTECALL_NOTEID</p>
* <p>NOTECALL_NOTEIDTAG</p>
*/
private void loadNoteData() {
Cursor cursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, DATA_PROJECTION,
@ -180,6 +180,10 @@ public class WorkingNote {
mNote.setTextDataId(cursor.getLong(DATA_ID_COLUMN));
} else if (DataConstants.CALL_NOTE.equals(type)) {
mNote.setCallDataId(cursor.getLong(DATA_ID_COLUMN));
} else if (DataConstants.TAG.equals(type)) {
// 加载标签数据
String tagName = cursor.getString(DATA_CONTENT_COLUMN);
mNote.addTagData(tagName);
} else {
Log.d(TAG, "Wrong note type with type:" + type);
}
@ -529,6 +533,21 @@ public class WorkingNote {
}
return encrypted;
}
/**
*
* @param tagName
*/
public void addTag(String tagName) {
mNote.addTagData(tagName);
}
/**
*
*/
public void clearTags() {
mNote.clearTagData();
}
/**
*

@ -36,6 +36,7 @@ import android.view.WindowManager;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.tool.DataUtils;
import net.micode.notes.ui.NoteEditActivity;
import java.io.IOException;

@ -28,6 +28,7 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Paint;
import android.net.Uri;
import android.os.Build;
@ -46,9 +47,14 @@ import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.Gravity;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.util.DisplayMetrics;
import android.widget.HorizontalScrollView;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
@ -57,6 +63,7 @@ import android.util.Base64;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
@ -72,14 +79,21 @@ import android.content.res.Resources;
// import android.support.annotation.NonNull;
import android.Manifest;
import android.graphics.Color;
import java.io.InputStream;
import java.io.FileOutputStream;
import java.io.OutputStream;
import android.os.Environment;
import android.text.format.DateUtils;
import android.view.ViewGroup;
import android.content.ActivityNotFoundException;
import java.io.InputStream;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import jp.wasabeef.richeditor.RichEditor;
import net.micode.notes.R;
@ -96,12 +110,6 @@ import net.micode.notes.ui.NoteEditText.OnTextViewChangeListener;
import net.micode.notes.widget.NoteWidgetProvider_2x;
import net.micode.notes.widget.NoteWidgetProvider_4x;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
*
@ -181,6 +189,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
private View mFontSizeSelector; // 字体大小选择器
private RichEditor mNoteEditor; // 富文本编辑器
private View mNoteEditorPanel; // 笔记编辑器面板
private HorizontalScrollView mFloatingToolbar; // 浮动富文本工具栏
private WorkingNote mWorkingNote; // 工作笔记对象
private SharedPreferences mSharedPrefs; // 共享偏好设置
private int mFontSizeId; // 字体大小ID
@ -198,6 +207,18 @@ public class NoteEditActivity extends Activity implements OnClickListener,
private String mUserQuery; // 用户查询字符串
private Pattern mPattern; // 正则表达式模式(用于高亮查询结果)
private ChecklistManager mChecklistManager; // 清单管理器OMO
// 标签管理相关控件
private LinearLayout mTagManagementSection; // 标签管理区域
private LinearLayout mExistingTagsContainer; // 现有标签容器
private LinearLayout mBottomTagsContainer; // 底部标签显示容器
private EditText mTagInput; // 标签输入框
private Button mAddTagButton; // 添加标签按钮
private ArrayList<String> mTagsList; // 标签列表
// 搜索历史相关
private static final String PREFERENCE_SEARCH_HISTORY = "search_history"; // 搜索历史偏好设置键
private static final int MAX_SEARCH_HISTORY = 20; // 最大搜索历史记录数
/**
*
@ -275,6 +296,11 @@ public class NoteEditActivity extends Activity implements OnClickListener,
return false;
}
}
// 保存搜索历史
if (!TextUtils.isEmpty(mUserQuery)) {
saveSearchHistory(mUserQuery);
}
getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN
| WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
@ -503,6 +529,9 @@ public class NoteEditActivity extends Activity implements OnClickListener,
* is not ready
*/
showAlertHeader(); // 显示提醒头部
// 加载现有标签
loadExistingTags();
}
/**
@ -736,6 +765,12 @@ public class NoteEditActivity extends Activity implements OnClickListener,
Log.e(TAG, "RichEditor is null! Check layout file.");
return;
}
// 初始化浮动富文本工具栏
mFloatingToolbar = findViewById(R.id.floating_editor_toolbar);
if (mFloatingToolbar != null) {
setupFloatingToolbarDrag();
}
// 初始化富文本编辑器配置
initRichEditor();
@ -798,6 +833,9 @@ public class NoteEditActivity extends Activity implements OnClickListener,
mBtnInsertImage.setOnClickListener(this);
}
// 初始化标签管理相关控件
initTagManagement();
// 初始化富文本功能按钮
initRichEditorButtons();
}
@ -1001,11 +1039,28 @@ public class NoteEditActivity extends Activity implements OnClickListener,
case R.id.menu_delete_remind:
mWorkingNote.setAlertDate(0, false); // 删除提醒
break;
case R.id.menu_tag_management:
// 切换标签管理界面显示
toggleTagManagement();
break;
default:
break;
}
return true;
}
/**
* /
*/
private void toggleTagManagement() {
if (mTagManagementSection.getVisibility() == View.VISIBLE) {
// 隐藏标签管理界面
mTagManagementSection.setVisibility(View.GONE);
} else {
// 显示标签管理界面
mTagManagementSection.setVisibility(View.VISIBLE);
}
}
/**
*
@ -1403,6 +1458,10 @@ public class NoteEditActivity extends Activity implements OnClickListener,
*/
private boolean saveNote() {
getWorkingText(); // 获取当前工作文本
// 保存标签数据
saveTagsToNote();
boolean saved = mWorkingNote.saveNote(); // 保存笔记到数据库
if (saved) {
/**
@ -1416,6 +1475,69 @@ public class NoteEditActivity extends Activity implements OnClickListener,
}
return saved;
}
/**
*
*/
private void saveTagsToNote() {
// 清空现有标签
mWorkingNote.clearTags();
// 添加所有标签
for (String tag : mTagsList) {
mWorkingNote.addTag(tag);
}
}
/**
*
* @param query
*/
private void saveSearchHistory(String query) {
if (TextUtils.isEmpty(query)) {
return;
}
// 获取SharedPreferences
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
// 获取现有的搜索历史
String historyString = sp.getString(PREFERENCE_SEARCH_HISTORY, "");
// 将搜索历史拆分为列表
ArrayList<String> historyList = new ArrayList<>();
if (!TextUtils.isEmpty(historyString)) {
String[] historyArray = historyString.split(",");
for (String item : historyArray) {
if (!TextUtils.isEmpty(item) && !historyList.contains(item)) {
historyList.add(item);
}
}
}
// 如果搜索关键词已存在,先移除
historyList.remove(query);
// 将新的搜索关键词添加到列表开头
historyList.add(0, query);
// 限制搜索历史记录数量
if (historyList.size() > MAX_SEARCH_HISTORY) {
historyList = new ArrayList<>(historyList.subList(0, MAX_SEARCH_HISTORY));
}
// 将搜索历史列表转换为字符串,用逗号分隔
StringBuilder sb = new StringBuilder();
for (int i = 0; i < historyList.size(); i++) {
if (i > 0) {
sb.append(",");
}
sb.append(historyList.get(i));
}
// 保存到SharedPreferences
sp.edit().putString(PREFERENCE_SEARCH_HISTORY, sb.toString()).apply();
}
/**
*
@ -1685,7 +1807,264 @@ public class NoteEditActivity extends Activity implements OnClickListener,
mNoteEditor.setEditorFontSize(18); // 默认值
}
}
/**
*
*/
private void initTagManagement() {
// 初始化标签管理相关控件
mTagManagementSection = findViewById(R.id.tag_management_section);
mExistingTagsContainer = findViewById(R.id.existing_tags_container);
mTagInput = findViewById(R.id.et_tag_input);
mAddTagButton = findViewById(R.id.btn_add_tag);
// 初始化标签列表
mTagsList = new ArrayList<>();
// 设置添加标签按钮的点击事件
mAddTagButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
addTag();
}
});
// 加载现有标签
loadExistingTags();
}
/**
*
*/
private void addTag() {
String tagName = mTagInput.getText().toString().trim();
// 检查标签是否有效
if (isValidTag(tagName)) {
// 添加标签到列表
mTagsList.add(tagName);
// 更新UI显示
updateTagsDisplay();
// 清空输入框
mTagInput.setText("");
}
}
/**
*
* @param tagName
* @return
*/
private boolean isValidTag(String tagName) {
// 检查标签是否为空
if (TextUtils.isEmpty(tagName)) {
Toast.makeText(this, "标签不能为空", Toast.LENGTH_SHORT).show();
return false;
}
// 检查标签长度
if (tagName.length() < 1 || tagName.length() > 15) {
Toast.makeText(this, "标签长度必须在1-15个字符之间", Toast.LENGTH_SHORT).show();
return false;
}
// 检查标签数量是否超过限制
if (mTagsList.size() >= 5) {
Toast.makeText(this, "每个笔记最多只能添加5个标签", Toast.LENGTH_SHORT).show();
return false;
}
// 检查标签是否已存在
if (mTagsList.contains(tagName)) {
Toast.makeText(this, "该标签已存在", Toast.LENGTH_SHORT).show();
return false;
}
return true;
}
/**
*
*/
private void updateTagsDisplay() {
// 清空现有标签容器
mExistingTagsContainer.removeAllViews();
// 添加所有标签到容器
for (final String tag : mTagsList) {
// 创建标签管理区域的标签视图
LinearLayout tagView = new LinearLayout(this);
LinearLayout.LayoutParams tagViewParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
36);
tagViewParams.setMargins(0, 0, 8, 0);
tagView.setLayoutParams(tagViewParams);
tagView.setOrientation(LinearLayout.HORIZONTAL);
tagView.setGravity(Gravity.CENTER_VERTICAL);
tagView.setPadding(12, 0, 8, 0);
tagView.setBackgroundResource(R.drawable.bg_color_btn_mask);
tagView.setClickable(true);
tagView.setFocusable(true);
// 创建标签文本
TextView tagText = new TextView(this);
tagText.setText(tag);
tagText.setTextAppearance(this, R.style.TextAppearancePrimaryItem);
tagText.setTextColor(Color.WHITE);
tagText.setPadding(0, 0, 8, 0);
tagText.setSingleLine(true);
tagText.setTextSize(14);
// 创建删除按钮
ImageView deleteButton = new ImageView(this);
LinearLayout.LayoutParams deleteParams = new LinearLayout.LayoutParams(
24, 24);
deleteButton.setLayoutParams(deleteParams);
deleteButton.setImageResource(android.R.drawable.ic_delete);
deleteButton.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
deleteButton.setClickable(true);
deleteButton.setFocusable(true);
deleteButton.setTag(tag);
// 设置删除按钮点击事件
deleteButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
String tagToRemove = (String) v.getTag();
removeTag(tagToRemove);
}
});
// 添加控件到标签视图
tagView.addView(tagText);
tagView.addView(deleteButton);
// 添加标签视图到容器
mExistingTagsContainer.addView(tagView);
}
}
/**
*
* @param tagName
*/
private void removeTag(String tagName) {
mTagsList.remove(tagName);
updateTagsDisplay();
}
/**
*
*/
private void setupFloatingToolbarDrag() {
// 记录拖拽状态
final boolean[] isDragging = {false};
final int[] lastX = {0};
final int[] lastY = {0};
// 设置触摸监听器
mFloatingToolbar.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
int x = (int) event.getRawX();
int y = (int) event.getRawY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 记录初始触摸位置
isDragging[0] = false;
lastX[0] = x;
lastY[0] = y;
break;
case MotionEvent.ACTION_MOVE:
// 计算移动距离
int deltaX = x - lastX[0];
int deltaY = y - lastY[0];
// 判断是否开始拖拽
if (Math.abs(deltaX) > 5 || Math.abs(deltaY) > 5) {
isDragging[0] = true;
}
if (isDragging[0]) {
// 更新工具栏位置
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) v.getLayoutParams();
// 计算新的位置
int newLeftMargin = params.leftMargin + deltaX;
int newTopMargin = params.topMargin + deltaY;
// 限制位置在屏幕范围内
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int screenWidth = displayMetrics.widthPixels;
int screenHeight = displayMetrics.heightPixels;
// 计算工具栏宽度和高度
int toolbarWidth = v.getWidth();
int toolbarHeight = v.getHeight();
// 限制左边界
newLeftMargin = Math.max(0, Math.min(newLeftMargin, screenWidth - toolbarWidth));
// 限制上边界
newTopMargin = Math.max(0, Math.min(newTopMargin, screenHeight - toolbarHeight));
// 更新布局参数
params.leftMargin = newLeftMargin;
params.topMargin = newTopMargin;
v.setLayoutParams(params);
// 更新最后触摸位置
lastX[0] = x;
lastY[0] = y;
return true; // 消耗事件
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
// 结束拖拽
if (isDragging[0]) {
return true; // 消耗事件
}
break;
}
return false; // 不消耗事件,允许其他触摸事件处理
}
});
}
/**
*
*/
private void loadExistingTags() {
// 从数据库加载现有标签
if (mWorkingNote != null && mWorkingNote.getNoteId() > 0) {
Cursor cursor = getContentResolver().query(
Notes.CONTENT_DATA_URI,
new String[]{Notes.DataColumns.CONTENT},
Notes.DataColumns.NOTE_ID + "=? AND " + Notes.DataColumns.MIME_TYPE + "=?",
new String[]{String.valueOf(mWorkingNote.getNoteId()), Notes.DataConstants.TAG},
null);
if (cursor != null) {
while (cursor.moveToNext()) {
String tagName = cursor.getString(0);
if (!TextUtils.isEmpty(tagName) && !mTagsList.contains(tagName)) {
mTagsList.add(tagName);
}
}
cursor.close();
}
// 更新UI显示
updateTagsDisplay();
}
}
// 初始化富文本编辑器配置
private void initRichEditor() {
mNoteEditor.setEditorHeight(600); // 设置编辑器高度
@ -1699,7 +2078,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
// 添加富文本功能按钮初始化方法
private void initRichEditorButtons() {
// 撤销功能
findViewById(R.id.action_undo).setOnClickListener(new View.OnClickListener() {
findViewById(R.id.action_undo).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mNoteEditor.undo();
@ -1707,7 +2086,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
});
// 加粗功能
findViewById(R.id.action_bold).setOnClickListener(new View.OnClickListener() {
findViewById(R.id.action_bold).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mNoteEditor.setBold();
@ -1715,7 +2094,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
});
// 斜体功能
findViewById(R.id.action_italic).setOnClickListener(new View.OnClickListener() {
findViewById(R.id.action_italic).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mNoteEditor.setItalic();
@ -1723,7 +2102,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
});
// 下划线功能
findViewById(R.id.action_underline).setOnClickListener(new View.OnClickListener() {
findViewById(R.id.action_underline).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mNoteEditor.setUnderline();

@ -24,6 +24,7 @@ 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;
import net.micode.notes.ui.NoteEditActivity;
/**
*

@ -20,6 +20,7 @@ import android.app.Activity;
import net.micode.notes.ui.PasswordInputActivity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.SearchManager;
import android.appwidget.AppWidgetManager;
import android.content.AsyncQueryHandler;
import android.content.ContentResolver;
@ -41,6 +42,7 @@ import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Display;
import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
@ -50,12 +52,16 @@ import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnCreateContextMenuListener;
import android.view.View.OnTouchListener;
import android.widget.TextView.OnEditorActionListener;
import android.view.inputmethod.EditorInfo;
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.ImageButton;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.PopupMenu;
import android.widget.TextView;
@ -65,6 +71,7 @@ import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.data.Notes.TextNote;
import net.micode.notes.data.NotesDatabaseHelper;
import net.micode.notes.gtask.remote.GTaskSyncService;
import net.micode.notes.model.ChecklistManager;
import net.micode.notes.model.WorkingNote;
@ -80,9 +87,14 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashSet;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.Set;
import android.os.Build;
import android.view.Gravity;
import android.view.ViewConfiguration;
import android.graphics.Color;
import java.lang.reflect.Field;
/**
@ -99,6 +111,26 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
private static final int MENU_FOLDER_VIEW = 1;
// 标签云相关控件
private LinearLayout mTagCloudSection; // 标签云区域
private LinearLayout mTagCloudContainer; // 标签云容器
private Button mAllNotesTag; // 全部笔记标签
private String mCurrentFilterTag; // 当前过滤标签
// 搜索历史相关
private static final String PREFERENCE_SEARCH_HISTORY = "search_history"; // 搜索历史偏好设置键
private static final int MAX_SEARCH_HISTORY = 20; // 最大搜索历史记录数
private String mSearchQuery; // 当前搜索查询字符串
private boolean mIsSearchMode; // 是否处于搜索模式
// 实时搜索相关
private LinearLayout mSearchBar; // 搜索栏容器
private EditText mSearchEditText; // 搜索输入框
private ImageButton mClearSearchButton; // 清除搜索按钮
private static final int SEARCH_DEBOUNCE_DELAY = 300; // 搜索防抖延迟时间(毫秒)
private Runnable mSearchRunnable; // 搜索任务
private long mLastSearchTime; // 上次搜索时间
private static final int MENU_FOLDER_CHANGE_NAME = 2;
private static final String PREFERENCE_ADD_INTRODUCTION = "net.micode.notes.introduction";
@ -277,6 +309,103 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
mIsChecklistMode = false;
updateModeButtons();
mChecklistManager = new ChecklistManager(mContentResolver);
// 初始化搜索相关控件
mSearchBar = (LinearLayout) findViewById(R.id.search_bar);
mSearchEditText = findViewById(R.id.et_search);
mClearSearchButton = (ImageButton) findViewById(R.id.btn_clear_search);
// 设置搜索监听器
initSearchListeners();
// 标签云功能已移除,相关代码已注释
// mTagCloudSection = findViewById(R.id.tag_cloud_section);
// mTagCloudContainer = findViewById(R.id.tag_cloud_container);
// mAllNotesTag = findViewById(R.id.tag_all_notes);
// mCurrentFilterTag = null;
//
// // 加载标签云
// loadTagCloud();
}
/**
*
*/
private void initSearchListeners() {
// 初始化搜索任务
mSearchRunnable = new Runnable() {
@Override
public void run() {
// 执行搜索查询:如果已经处于搜索模式,只更新搜索结果,不退出搜索模式
// 这样可以确保搜索栏在用户输入时保持可见
if (mIsSearchMode) {
startAsyncNotesListQuery();
} else if (!TextUtils.isEmpty(mSearchQuery)) {
// 如果不在搜索模式但有搜索字符串,进入搜索模式
setSearchMode(true, mSearchQuery);
}
}
};
// 搜索输入框文本变化监听器
mSearchEditText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// 文本变化前的处理
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// 文本变化时的处理
mSearchQuery = s.toString();
Log.d(TAG, "Search text changed: '" + mSearchQuery + "'");
// 移除之前的搜索任务
mSearchEditText.removeCallbacks(mSearchRunnable);
// 延迟执行搜索任务
Log.d(TAG, "Posting search runnable with delay: " + SEARCH_DEBOUNCE_DELAY + "ms");
mSearchEditText.postDelayed(mSearchRunnable, SEARCH_DEBOUNCE_DELAY);
}
@Override
public void afterTextChanged(Editable s) {
// 文本变化后的处理
}
});
// 搜索输入框编辑器动作监听器(处理虚拟键盘上的搜索按钮)
mSearchEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
// 检查是否是搜索动作
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
// 获取搜索查询
String query = mSearchEditText.getText().toString().trim();
if (!TextUtils.isEmpty(query)) {
// 设置搜索模式并执行搜索
setSearchMode(true, query);
// 保存搜索历史
saveSearchHistory(query);
// 隐藏软键盘
hideSoftInput(mSearchEditText);
}
return true;
}
return false;
}
});
// 清除搜索按钮点击监听器
mClearSearchButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// 清除搜索内容
mSearchEditText.setText("");
// 移除未执行的搜索任务
mSearchEditText.removeCallbacks(mSearchRunnable);
// 退出搜索模式
exitSearchMode();
}
});
}
/**
@ -500,32 +629,189 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
String selection;
String[] selectionArgs;
if (mCurrentFolderId == Notes.ID_ROOT_FOLDER) {
if (mIsChecklistMode) {
// 根文件夹下的清单模式:只显示清单类型的笔记
selection = "(" + NoteColumns.TYPE + "=" + Notes.TYPE_CHECKLIST + " AND " + NoteColumns.PARENT_ID + "=?)" +
// 基础查询条件
if (mIsSearchMode) {
// 搜索模式:忽略清单模式,搜索所有笔记和清单
if (mCurrentFolderId == Notes.ID_ROOT_FOLDER) {
// 根文件夹下的搜索:显示所有类型的笔记(普通笔记、清单、文件夹、通话记录文件夹)
selection = "(" + NoteColumns.TYPE + " IN (" + Notes.TYPE_NOTE + ", " + Notes.TYPE_CHECKLIST + ", " + Notes.TYPE_FOLDER + ") AND " + NoteColumns.PARENT_ID + "=?)" +
" OR (" + NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER + " AND " + NoteColumns.NOTES_COUNT + ">0)";
} else {
// 根文件夹下的笔记模式:显示文件夹、普通笔记和非空通话记录文件夹
selection = "(" + NoteColumns.TYPE + "<>" + Notes.TYPE_SYSTEM + " AND " + NoteColumns.PARENT_ID + "=?)" +
" OR (" + NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER + " AND " + NoteColumns.NOTES_COUNT + ">0)";
// 普通文件夹下的搜索:显示所有类型的笔记(普通笔记和清单)
selection = NoteColumns.PARENT_ID + "=? AND " + NoteColumns.TYPE + " IN (" + Notes.TYPE_NOTE + ", " + Notes.TYPE_CHECKLIST + ")";
}
} else {
if (mIsChecklistMode) {
// 普通文件夹下的清单模式:只显示清单类型的笔记
selection = NoteColumns.PARENT_ID + "=? AND " + NoteColumns.TYPE + "=" + Notes.TYPE_CHECKLIST;
// 非搜索模式:使用原来的逻辑
if (mCurrentFolderId == Notes.ID_ROOT_FOLDER) {
if (mIsChecklistMode) {
// 根文件夹下的清单模式:只显示清单类型的笔记
selection = "(" + NoteColumns.TYPE + "=" + Notes.TYPE_CHECKLIST + " AND " + NoteColumns.PARENT_ID + "=?)" +
" OR (" + NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER + " AND " + NoteColumns.NOTES_COUNT + ">0)";
} else {
// 根文件夹下的笔记模式:显示文件夹、普通笔记和非空通话记录文件夹
selection = "(" + NoteColumns.TYPE + "<>" + Notes.TYPE_SYSTEM + " AND " + NoteColumns.PARENT_ID + "=?)" +
" OR (" + NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER + " AND " + NoteColumns.NOTES_COUNT + ">0)";
}
} else {
// 普通文件夹下的笔记模式:只显示普通笔记
selection = NoteColumns.PARENT_ID + "=? AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE;
if (mIsChecklistMode) {
// 普通文件夹下的清单模式:只显示清单类型的笔记
selection = NoteColumns.PARENT_ID + "=? AND " + NoteColumns.TYPE + "=" + Notes.TYPE_CHECKLIST;
} else {
// 普通文件夹下的笔记模式:只显示普通笔记
selection = NoteColumns.PARENT_ID + "=? AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE;
}
}
}
// 添加搜索过滤条件
if (mIsSearchMode && !TextUtils.isEmpty(mSearchQuery)) {
// 搜索过滤需要使用子查询因为内容存储在data表中
String searchFilter = " AND " + NoteColumns.ID + " IN (" +
"SELECT " + Notes.DataColumns.NOTE_ID +
" FROM " + NotesDatabaseHelper.TABLE.DATA +
" WHERE (" + Notes.DataColumns.MIME_TYPE + "=?" +
" AND " + Notes.DataColumns.CONTENT + " LIKE ?" +
") OR (" + Notes.DataColumns.MIME_TYPE + "=?" +
" AND " + Notes.DataColumns.CONTENT + " LIKE ?" +
"))";
// 将整个基础查询条件用括号括起来,确保搜索条件应用到所有笔记
// 这是修复搜索功能的关键解决SQL操作符优先级问题
selection = "(" + selection + ")";
selection += searchFilter;
}
// 添加标签过滤条件
if (mCurrentFilterTag != null) {
// 标签过滤需要使用子查询因为标签存储在data表中
String tagFilter = " AND " + NoteColumns.ID + " IN (" +
"SELECT " + Notes.DataColumns.NOTE_ID +
" FROM " + NotesDatabaseHelper.TABLE.DATA +
" WHERE " + Notes.DataColumns.MIME_TYPE + "=?" +
" AND " + Notes.DataColumns.CONTENT + "=?" +
")";
selection += tagFilter;
}
// 准备选择参数
ArrayList<String> argsList = new ArrayList<>();
argsList.add(String.valueOf(mCurrentFolderId));
// 添加搜索参数
if (mIsSearchMode && !TextUtils.isEmpty(mSearchQuery)) {
String searchPattern = "%" + mSearchQuery + "%";
// 搜索普通文本内容
argsList.add(Notes.DataConstants.NOTE);
argsList.add(searchPattern);
// 搜索标签
argsList.add(Notes.DataConstants.TAG);
argsList.add(searchPattern);
}
// 添加标签参数
if (mCurrentFilterTag != null) {
argsList.add(Notes.DataConstants.TAG);
argsList.add(mCurrentFilterTag);
}
selectionArgs = argsList.toArray(new String[argsList.size()]);
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");
Notes.CONTENT_NOTE_URI, NoteItemData.PROJECTION, selection, selectionArgs,
NoteColumns.TYPE + " DESC," + NoteColumns.MODIFIED_DATE + " DESC");
}
/**
*
*/
private void loadTagCloud() {
// 清空现有标签(保留"全部笔记"标签)
while (mTagCloudContainer.getChildCount() > 1) {
mTagCloudContainer.removeViewAt(1);
}
// 查询所有标签,按使用次数排序
String tagQuery = "SELECT " + Notes.DataColumns.CONTENT +
" FROM " + NotesDatabaseHelper.TABLE.DATA +
" WHERE " + Notes.DataColumns.MIME_TYPE + "=?" +
" GROUP BY " + Notes.DataColumns.CONTENT +
" ORDER BY COUNT(*) DESC";
Cursor cursor = mContentResolver.query(
Notes.CONTENT_DATA_URI,
new String[]{Notes.DataColumns.CONTENT},
Notes.DataColumns.MIME_TYPE + "=?",
new String[]{Notes.DataConstants.TAG},
Notes.DataColumns.CONTENT + " ASC");
if (cursor != null) {
while (cursor.moveToNext()) {
String tagName = cursor.getString(0);
if (!TextUtils.isEmpty(tagName)) {
// 创建标签按钮
Button tagButton = new Button(this);
tagButton.setText(tagName);
tagButton.setBackgroundResource(R.drawable.bg_btn_set_color);
tagButton.setTextColor(Color.WHITE);
tagButton.setTextSize(14);
tagButton.setSingleLine(true);
tagButton.setTag(tagName);
// tagButton.setOnClickListener(new OnClickListener() {
// @Override
// public void onClick(View v) {
// onTagClick(v);
// }
// });
// 设置标准化布局参数
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT, 36);
params.setMargins(0, 0, 8, 0);
params.gravity = Gravity.CENTER_VERTICAL;
tagButton.setLayoutParams(params);
// 设置标准化内边距
tagButton.setPadding(12, 0, 12, 0);
// 添加到标签云容器
mTagCloudContainer.addView(tagButton);
}
}
cursor.close();
}
}
// 标签云功能已移除onTagClick方法已删除
// public void onTagClick(View v) {
// if (v.getId() == R.id.tag_all_notes) {
// // 显示全部笔记
// mCurrentFilterTag = null;
// mAllNotesTag.setTextColor(Color.WHITE);
// mAllNotesTag.setBackgroundResource(R.drawable.bg_btn_set_color);
// } else {
// // 显示特定标签的笔记
// mCurrentFilterTag = (String) v.getTag();
// mAllNotesTag.setTextColor(Color.BLACK);
// mAllNotesTag.setBackgroundResource(android.R.drawable.btn_default);
// }
//
// // 更新其他标签的样式
// for (int i = 1; i < mTagCloudContainer.getChildCount(); i++) {
// Button tagButton = (Button) mTagCloudContainer.getChildAt(i);
// if (tagButton.getTag().equals(mCurrentFilterTag)) {
// tagButton.setTextColor(Color.WHITE);
// tagButton.setBackgroundResource(R.drawable.bg_btn_set_color);
// } else {
// tagButton.setTextColor(Color.BLACK);
// tagButton.setBackgroundResource(android.R.drawable.btn_default);
// }
// }
//
// // 重新查询笔记列表
// startAsyncNotesListQuery();
// }
/**
* -
*/
@ -540,9 +826,12 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
*/
@Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
Log.d(TAG, "onQueryComplete called with token: " + token + ", cursor: " + cursor + ", count: " + (cursor != null ? cursor.getCount() : -1));
switch (token) {
case FOLDER_NOTE_LIST_QUERY_TOKEN:
// 查询笔记列表完成,更新适配器
Log.d(TAG, "Updating notes list adapter with cursor, count: " + (cursor != null ? cursor.getCount() : -1));
mNotesListAdapter.changeCursor(cursor);
break;
case FOLDER_LIST_QUERY_TOKEN:
@ -615,7 +904,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
View view = LayoutInflater.from(this).inflate(R.layout.dialog_edit_text, null);
final EditText etPassword = (EditText) view.findViewById(R.id.et_foler_name);
etPassword.setHint(R.string.password_hint);
etPassword.setInputType(android.text.InputType.TYPE_CLASS_TEXT |
etPassword.setInputType(android.text.InputType.TYPE_CLASS_TEXT |
android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD);
builder.setTitle(R.string.password_set_dialog_title);
builder.setView(view);
@ -630,14 +919,14 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
final Dialog dialog = builder.create();
dialog.show();
final Button positive = (Button) dialog.findViewById(android.R.id.button1);
positive.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
String password = etPassword.getText().toString();
if (TextUtils.isEmpty(password)) {
Toast.makeText(NotesListActivity.this,
Toast.makeText(NotesListActivity.this,
R.string.password_empty, Toast.LENGTH_SHORT).show();
return;
}
@ -1175,12 +1464,71 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
startActivity(intent);
break;
}
case R.id.menu_exit_search:
// 退出搜索模式
exitSearchMode();
return true;
default:
break;
}
return true;
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
// 处理搜索意图
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
String query = intent.getStringExtra(SearchManager.QUERY);
if (!TextUtils.isEmpty(query)) {
setSearchMode(true, query);
// 添加到搜索历史
saveSearchHistory(query);
// 显示软键盘
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
imm.showSoftInput(mSearchEditText, InputMethodManager.SHOW_IMPLICIT);
}
}
}
}
/**
*
* @param isSearchMode
* @param searchQuery
*/
private void setSearchMode(boolean isSearchMode, String searchQuery) {
mIsSearchMode = isSearchMode;
mSearchQuery = searchQuery;
// 移除任何待执行的搜索任务,避免状态冲突
mSearchEditText.removeCallbacks(mSearchRunnable);
startAsyncNotesListQuery();
// 更新UI显示或隐藏搜索相关控件
if (isSearchMode) {
// 搜索模式下显示搜索栏
mSearchBar.setVisibility(View.VISIBLE);
// 设置搜索查询文本
mSearchEditText.setText(searchQuery);
// 将光标定位到文本末尾
mSearchEditText.setSelection(searchQuery.length());
} else {
// 退出搜索模式,隐藏搜索栏
mSearchBar.setVisibility(View.GONE);
}
}
/**
* 退
*/
public void exitSearchMode() {
setSearchMode(false, null);
}
@Override
public boolean onSearchRequested() {
startSearch(null, false, null /* appData */, false);
@ -1192,8 +1540,87 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
* OMO
*/
public void handleSearchClick(View view) {
// 调用系统搜索方法
onSearchRequested();
Log.d(TAG, "Search button clicked, showing search bar");
// 显示搜索栏并聚焦到搜索输入框
setSearchMode(true, mSearchQuery != null ? mSearchQuery : "");
mSearchEditText.requestFocus();
// 显示软键盘
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
imm.showSoftInput(mSearchEditText, InputMethodManager.SHOW_IMPLICIT);
}
Log.d(TAG, "Search bar displayed, search mode: " + mIsSearchMode);
}
/**
*
* @param query
*/
private void saveSearchHistory(String query) {
if (TextUtils.isEmpty(query)) {
return;
}
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
Set<String> historySet = prefs.getStringSet(PREFERENCE_SEARCH_HISTORY, new HashSet<String>());
// 创建新的集合以支持修改
Set<String> newHistorySet = new LinkedHashSet<String>(historySet);
// 如果已存在,先移除,然后添加到开头
newHistorySet.remove(query);
// 创建有序集合以保持插入顺序
LinkedHashSet<String> orderedHistorySet = new LinkedHashSet<String>();
orderedHistorySet.add(query);
orderedHistorySet.addAll(newHistorySet);
// 限制历史记录数量
if (orderedHistorySet.size() > MAX_SEARCH_HISTORY) {
// 移除最旧的记录
String[] historyArray = orderedHistorySet.toArray(new String[0]);
for (int i = MAX_SEARCH_HISTORY; i < historyArray.length; i++) {
orderedHistorySet.remove(historyArray[i]);
}
}
// 保存到偏好设置
prefs.edit().putStringSet(PREFERENCE_SEARCH_HISTORY, orderedHistorySet).apply();
}
/**
*
* @return
*/
private Set<String> getSearchHistory() {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
return prefs.getStringSet(PREFERENCE_SEARCH_HISTORY, new HashSet<String>());
}
/**
*
*/
private void clearSearchHistory() {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
prefs.edit().remove(PREFERENCE_SEARCH_HISTORY).apply();
}
/**
* 退
* @param menu
* @return
*/
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.note_list_options, menu);
// 在搜索模式下添加退出搜索菜单项
if (mIsSearchMode) {
MenuItem exitSearchItem = menu.add(Menu.NONE, R.id.menu_exit_search, Menu.NONE, "退出搜索");
exitSearchItem.setIcon(android.R.drawable.ic_menu_close_clear_cancel);
exitSearchItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
}
return true;
}
private void exportNoteToText() {
final BackupUtils backup = BackupUtils.getInstance(NotesListActivity.this);

@ -27,6 +27,7 @@ import android.widget.CursorAdapter;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.ui.NoteEditActivity;
import java.util.Collection;
import java.util.HashMap;

@ -28,6 +28,7 @@ import android.widget.Toast;
import net.micode.notes.R;
import net.micode.notes.tool.PasswordUtils;
import net.micode.notes.ui.NoteEditActivity;
/**
* Activity

Loading…
Cancel
Save