1 /*
2  * Copyright (C) 2007 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.example.android.notepad;
18 
19 import android.app.Activity;
20 import android.content.ContentValues;
21 import android.database.Cursor;
22 import android.net.Uri;
23 import android.os.Bundle;
24 import android.text.TextUtils;
25 import android.view.View;
26 import android.widget.EditText;
27 import android.widget.Toast;
28 
29 /**
30  * This Activity allows the user to edit a note's title. It displays a floating window
31  * containing an EditText.
32  *
33  * NOTE: Notice that the provider operations in this Activity are taking place on the UI thread.
34  * This is not a good practice. It is only done here to make the code more readable. A real
35  * application should use the {@link android.content.AsyncQueryHandler}
36  * or {@link android.os.AsyncTask} object to perform operations asynchronously on a separate thread.
37  */
38 public class TitleEditor extends Activity {
39 
40     /**
41      * This is a special intent action that means "edit the title of a note".
42      */
43     public static final String EDIT_TITLE_ACTION = "com.android.notepad.action.EDIT_TITLE";
44 
45     // Creates a projection that returns the note ID and the note contents.
46     private static final String[] PROJECTION = new String[] {
47             NotePad.Notes._ID, // 0
48             NotePad.Notes.COLUMN_NAME_TITLE, // 1
49     };
50 
51     // The position of the title column in a Cursor returned by the provider.
52     private static final int COLUMN_INDEX_TITLE = 1;
53 
54     // An EditText object for preserving the edited title.
55     private EditText mText;
56 
57     // A URI object for the note whose title is being edited.
58     private Uri mUri;
59 
60     // The title that was last saved.
61     private String mSavedTitle;
62 
63     /**
64      * This method is called by Android when the Activity is first started. From the incoming
65      * Intent, it determines what kind of editing is desired, and then does it.
66      */
67     @Override
onCreate(Bundle savedInstanceState)68     public void onCreate(Bundle savedInstanceState) {
69         super.onCreate(savedInstanceState);
70 
71         // Set the View for this Activity object's UI.
72         setContentView(R.layout.title_editor);
73 
74         // Gets the View ID for the EditText box
75         mText = (EditText) this.findViewById(R.id.title);
76 
77         // Get the Intent that activated this Activity, and from it get the URI of the note whose
78         // title we need to edit.
79         mUri = getIntent().getData();
80 
81         /*
82          * Using the URI passed in with the triggering Intent, gets the note.
83          *
84          * Note: This is being done on the UI thread. It will block the thread until the query
85          * completes. In a sample app, going against a simple provider based on a local database,
86          * the block will be momentary, but in a real app you should use
87          * android.content.AsyncQueryHandler or android.os.AsyncTask.
88          */
89 
90         Cursor cursor = getContentResolver().query(
91             mUri,        // The URI for the note that is to be retrieved.
92             PROJECTION,  // The columns to retrieve
93             null,        // No selection criteria are used, so no where columns are needed.
94             null,        // No where columns are used, so no where values are needed.
95             null         // No sort order is needed.
96         );
97 
98         if (cursor != null) {
99 
100             // The Cursor was just retrieved, so its index is set to one record *before* the first
101             // record retrieved. This moves it to the first record.
102             cursor.moveToFirst();
103 
104             // Displays the current title text in the EditText object.
105             mText.setText(cursor.getString(COLUMN_INDEX_TITLE));
106         }
107     }
108 
109     /**
110      * This method is called when the Activity is about to come to the foreground. This happens
111      * when the Activity comes to the top of the task stack, OR when it is first starting.
112      *
113      * Displays the current title for the selected note.
114      */
115     @Override
onResume()116     protected void onResume() {
117         super.onResume();
118     }
119 
120     /**
121      * This method is called when the Activity loses focus.
122      *
123      * While there is no need to override this method in this app, it is shown here to highlight
124      * that we are not saving any state in onPause, but have moved app state saving to onStop
125      * callback.
126      * In earlier versions of this app and popular literature it had been shown that onPause is good
127      * place to persist any unsaved work, however, this is not really a good practice because of how
128      * application and process lifecycle behave.
129      * As a general guideline apps should have a way of saving their business logic that does not
130      * solely rely on Activity (or other component) lifecyle state transitions.
131      * As a backstop you should save any app state, not saved during lifetime of the Activity, in
132      * onStop().
133      * For a more detailed explanation of this recommendation please read
134      * <a href = "https://developer.android.com/guide/topics/processes/process-lifecycle.html">
135      * Processes and Application Life Cycle </a>.
136      * <a href="https://developer.android.com/training/basics/activity-lifecycle/pausing.html">
137      * Pausing and Resuming an Activity </a>.
138      */
139     @Override
onPause()140     protected void onPause() {
141         super.onPause();
142     }
143 
144     /**
145      * This method is called when the Activity becomes invisible.
146      *
147      * For Activity objects that edit information, onStop() may be the one place where changes are
148      * saved.
149      * Updates the note with the text currently in the text box.
150      */
151     @Override
onStop()152     protected void onStop() {
153         super.onStop();
154         saveTitle();
155     }
156 
onClickOk(View v)157     public void onClickOk(View v) {
158         saveTitle();
159         finish();
160     }
161 
162     // Saves the title if required
saveTitle()163     private void saveTitle() {
164 
165         if (!TextUtils.isEmpty(mText.getText())) {
166 
167             String newTitle = mText.getText().toString();
168 
169             if (!newTitle.equals(mSavedTitle)) {
170                 // Creates a values map for updating the provider.
171                 ContentValues values = new ContentValues();
172 
173                 // In the values map, sets the title to the current contents of the edit box.
174                 values.put(NotePad.Notes.COLUMN_NAME_TITLE, newTitle);
175 
176                 /*
177                  * Updates the provider with the note's new title.
178                  *
179                  * Note: This is being done on the UI thread. It will block the thread until the
180                  * update completes. In a sample app, going against a simple provider based on a
181                  * local database, the block will be momentary, but in a real app you should use
182                  * android.content.AsyncQueryHandler or android.os.AsyncTask.
183                  */
184                 getContentResolver().update(
185                     mUri,    // The URI for the note to update.
186                     values,
187                     // The values map containing the columns to update and the values to use.
188                     null,    // No selection criteria is used, so no "where" columns are needed.
189                     null     // No "where" columns are used, so no "where" values are needed.
190                 );
191                 mSavedTitle = newTitle;
192             }
193         } else {
194             Toast.makeText(this, R.string.title_blank, Toast.LENGTH_SHORT).show();
195         }
196     }
197 }
198