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