1 /*
2  * Copyright (C) 2013 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.smssample;
18 
19 import android.content.Context;
20 import android.content.Intent;
21 import android.database.Cursor;
22 import android.net.Uri;
23 import android.os.Bundle;
24 import android.provider.Telephony.Sms.Inbox;
25 import android.support.v4.app.FragmentActivity;
26 import android.support.v4.app.LoaderManager.LoaderCallbacks;
27 import android.support.v4.content.CursorLoader;
28 import android.support.v4.content.Loader;
29 import android.support.v4.widget.SimpleCursorAdapter;
30 import android.telephony.TelephonyManager;
31 import android.text.TextUtils;
32 import android.view.Menu;
33 import android.view.View;
34 import android.view.View.OnClickListener;
35 import android.widget.Button;
36 import android.widget.EditText;
37 import android.widget.ListView;
38 import android.widget.RelativeLayout;
39 import android.widget.Toast;
40 
41 /**
42  * The main Activity that provides a sample of a few things:
43  *   -detecting if this app is the default SMS app and then showing/hiding UI and enabling/disabling
44  *    functionality. the UI that is shown has a button to prompt the user to set this app as the
45  *    default.
46  *   -a simple query to the SMS content provider to show a list of SMS messages in the inbox. even
47  *    though the query uses KitKat APIs this query should still work on earlier versions of Android
48  *    as the contract class and ContentProvider were still around (with essentially the same
49  *    structure) but were private.
50  *   -being triggered from another application when creating a new SMS. a good example is creating
51  *    a new SMS from the system People application. although nothing is done with the incoming
52  *    Intent in this case (just a Toast is displayed)
53  *
54  *  Obviously this is far from a full implementation and should just be used as a sample of how
55  *  an app could be set up to correctly integrate with the new Android 4.4 KitKat APIs while
56  *  running normally on earlier Android versions.
57  */
58 public class MainActivity extends FragmentActivity implements LoaderCallbacks<Cursor> {
59     private RelativeLayout mSetDefaultSmsLayout;
60     private Button mSendSmsButton;
61     private EditText mSendSmsEditText;
62     private SimpleCursorAdapter mAdapter;
63 
64     @Override
onCreate(Bundle savedInstanceState)65     protected void onCreate(Bundle savedInstanceState) {
66         super.onCreate(savedInstanceState);
67         setContentView(R.layout.activity_main);
68 
69         // Find some views
70         mSetDefaultSmsLayout = (RelativeLayout) findViewById(R.id.set_default_sms_layout);
71         mSendSmsEditText = (EditText) findViewById(R.id.send_sms_edittext);
72         ListView listView = (ListView) findViewById(android.R.id.list);
73         listView.setEmptyView(findViewById(android.R.id.empty));
74         mSendSmsButton = (Button) findViewById(R.id.send_sms_button);
75         mSendSmsButton.setOnClickListener(new OnClickListener() {
76             @Override
77             public void onClick(View view) {
78                 sendSms(mSendSmsEditText.getText().toString());
79             }
80         });
81 
82         // Create adapter and set it to our ListView
83         final String[] fromFields = new String[] {
84                 SmsQuery.PROJECTION[SmsQuery.ADDRESS], SmsQuery.PROJECTION[SmsQuery.BODY] };
85         final int[] toViews = new int[] { android.R.id.text1, android.R.id.text2 };
86         mAdapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_2, null,
87                 fromFields, toViews, 0);
88         listView.setAdapter(mAdapter);
89 
90         // Placeholder to process incoming SEND/SENDTO intents
91         String intentAction = getIntent() == null ? null : getIntent().getAction();
92         if (!TextUtils.isEmpty(intentAction) && (Intent.ACTION_SENDTO.equals(intentAction)
93                 || Intent.ACTION_SEND.equals(intentAction))) {
94             // TODO: Handle incoming SEND and SENDTO intents by pre-populating UI components
95             Toast.makeText(this, "Handle SEND and SENDTO intents: " + getIntent().getDataString(),
96                     Toast.LENGTH_SHORT).show();
97         }
98 
99         // Simple query to show the most recent SMS messages in the inbox
100         getSupportLoaderManager().initLoader(SmsQuery.TOKEN, null, this);
101     }
102 
103     /**
104      * Dummy sendSms method, would need the "to" address to actually send a message :)
105      */
sendSms(String smsText)106     private void sendSms(String smsText) {
107         if (!TextUtils.isEmpty(smsText)) {
108             if (Utils.isDefaultSmsApp(this)) {
109                 // TODO: Use SmsManager to send SMS and then record the message in the system SMS
110                 // ContentProvider
111                 Toast.makeText(this, "Sending text message: " + smsText, Toast.LENGTH_SHORT).show();
112             } else {
113                 // TODO: Notify the user the app is not default and provide a way to trigger
114                 // Utils.setDefaultSmsApp() so they can set it.
115                 Toast.makeText(this, "Not default", Toast.LENGTH_SHORT).show();
116             }
117         }
118     }
119 
120     @Override
onResume()121     protected void onResume() {
122         super.onResume();
123 
124         // Only do these checks/changes on KitKat+, the "mSetDefaultSmsLayout" has its visibility
125         // set to "gone" in the xml layout so it won't show at all on earlier Android versions.
126         if (Utils.hasKitKat()) {
127             if (Utils.isDefaultSmsApp(this)) {
128                 // This app is the default, remove the "make this app the default" layout and
129                 // enable message sending components.
130                 mSetDefaultSmsLayout.setVisibility(View.GONE);
131                 mSendSmsEditText.setHint(R.string.sms_send_new_hint);
132                 mSendSmsEditText.setEnabled(true);
133                 mSendSmsButton.setEnabled(true);
134             } else {
135                 // Not the default, show the "make this app the default" layout and disable
136                 // message sending components.
137                 mSetDefaultSmsLayout.setVisibility(View.VISIBLE);
138                 mSendSmsEditText.setText("");
139                 mSendSmsEditText.setHint(R.string.sms_send_disabled);
140                 mSendSmsEditText.setEnabled(false);
141                 mSendSmsButton.setEnabled(false);
142 
143                 Button button = (Button) findViewById(R.id.set_default_sms_button);
144                 button.setOnClickListener(new OnClickListener() {
145                     @Override
146                     public void onClick(View view) {
147                         Utils.setDefaultSmsApp(MainActivity.this);
148                     }
149                 });
150             }
151         }
152         String phoneNumberToast = "Unable to obtain phone number (not default SMS app?)";
153         final TelephonyManager telephony =
154                 (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
155         try {
156             phoneNumberToast = "Phone number: " + telephony.getLine1Number();
157         } catch (SecurityException e) {
158         }
159         Toast.makeText(this, phoneNumberToast, Toast.LENGTH_SHORT).show();
160     }
161 
162     @Override
onCreateOptionsMenu(Menu menu)163     public boolean onCreateOptionsMenu(Menu menu) {
164         // Inflate the menu; this adds items to the action bar if it is present.
165         getMenuInflater().inflate(R.menu.main, menu);
166         return true;
167     }
168 
169     @Override
onCreateLoader(int i, Bundle bundle)170     public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
171         if (i == SmsQuery.TOKEN) {
172             // This will fetch all SMS messages in the inbox, ordered by date desc
173             return new CursorLoader(this, SmsQuery.CONTENT_URI, SmsQuery.PROJECTION, null, null,
174                     SmsQuery.SORT_ORDER);
175         }
176         return null;
177     }
178 
179     @Override
onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor)180     public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
181         if (cursorLoader.getId() == SmsQuery.TOKEN && cursor != null) {
182             // Standard swap cursor in when load is done
183             mAdapter.swapCursor(cursor);
184         }
185     }
186 
187     @Override
onLoaderReset(Loader<Cursor> cursorLoader)188     public void onLoaderReset(Loader<Cursor> cursorLoader) {
189         // Standard swap cursor to null when loader is reset
190         mAdapter.swapCursor(null);
191     }
192 
193     /**
194      * A basic SmsQuery on android.provider.Telephony.Sms.Inbox
195      */
196     private interface SmsQuery {
197         int TOKEN = 1;
198 
199         static final Uri CONTENT_URI = Inbox.CONTENT_URI;
200 
201         static final String[] PROJECTION = {
202                 Inbox._ID,
203                 Inbox.ADDRESS,
204                 Inbox.BODY,
205         };
206 
207         static final String SORT_ORDER = Inbox.DEFAULT_SORT_ORDER;
208 
209         int ID = 0;
210         int ADDRESS = 1;
211         int BODY = 2;
212     }
213 }
214