package com.appsimobile.appsihomeplugins.home; import android.app.DownloadManager; import android.app.PendingIntent; import android.app.Service; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Intent; import android.content.SharedPreferences; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.Uri; import android.net.wifi.SupplicantState; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.os.Build; import android.os.Handler; import android.preference.PreferenceManager; import android.provider.CallLog; import android.provider.ContactsContract; import android.provider.Settings; import android.text.format.Formatter; import android.util.Log; import android.util.SparseArray; import com.appsimobile.appsihomeplugins.DashClockHomeExtension; import com.appsimobile.appsihomeplugins.R; import com.appsimobile.appsihomeplugins.dashclock.calendar.CalendarExtension; import com.appsimobile.appsihomeplugins.dashclock.gmail.GmailExtension; import com.appsimobile.appsihomeplugins.dashclock.nextalarm.NextAlarmExtension; import com.appsimobile.appsihomeplugins.dashclock.phone.MissedCallsExtension; import com.appsimobile.appsihomeplugins.dashclock.phone.SmsExtension; import com.appsimobile.appsihomeplugins.dashclock.weather.WeatherExtension; import com.appsimobile.appsisupport.home.AppsiHomeServiceProvider; import com.appsimobile.appsisupport.home.FieldsBuilder; import com.appsimobile.appsisupport.home.HomeServiceContract; import com.appsimobile.appsisupport.internal.FieldValues; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Method; import java.util.Scanner; /** * Created by nick on 8/9/13. */ public class HomeServiceProvider extends AppsiHomeServiceProvider { // define constants, do not change after production. // these are used in Appsi to know the field being used final SparseArray<DashClockHomeExtension> mDashClockHomeExtensions = new SparseArray<DashClockHomeExtension>(); /** * This field opens the downloads list */ public static final int FIELD_DOWNLOADS = 1; /** * shows the user profile image */ public static final int FIELD_USER_PROFILE = 2; /** * This fields shows a toggle sample */ public static final int FIELD_TOGGLE_SAMPLE = 3; /** * Show a total missed call count * Note that this field is not being updated properly when * the call log is updated. */ public static final int FIELD_MISSED_CALLS_COUNT = 4; /** * Show a tethering info field */ public static final int FIELD_TETHERING_INFO = 5; public static final String ACTION_TOGGLE = "com.appsimobile.appsihomeplugins.home.ACTION_TOGGLE"; private Handler mHandler; @Override public void onCreate() { super.onCreate(); mDashClockHomeExtensions.put(DashClockHomeExtension.DASHCLOCK_EXTENSION_CALENDAR, new CalendarExtension(this)); mDashClockHomeExtensions.put(DashClockHomeExtension.DASHCLOCK_EXTENSION_GMAIL, new GmailExtension(this)); mDashClockHomeExtensions.put(DashClockHomeExtension.DASHCLOCK_EXTENSION_NEXT_ALARM, new NextAlarmExtension(this)); mDashClockHomeExtensions.put(DashClockHomeExtension.DASHCLOCK_EXTENSION_MISSED_CALLS, new MissedCallsExtension(this)); mDashClockHomeExtensions.put(DashClockHomeExtension.DASHCLOCK_EXTENSION_SMS, new SmsExtension(this)); mDashClockHomeExtensions.put(DashClockHomeExtension.DASHCLOCK_EXTENSION_WEATHER, new WeatherExtension(this)); mHandler = new Handler(); } /** * This is called when Appsi needs to update a specific field. Fill the * bundle with the values that need to be displayed * @param builder * @param fieldId */ @Override protected void updateBundleForField(FieldValues.Builder builder, int fieldId, int reason) { Log.i("HomeServiceProvider", "updating field: " + fieldId); switch (fieldId) { case FIELD_DOWNLOADS: createDownloadsValues(builder); break; case FIELD_USER_PROFILE: createUserProfileValues(builder); break; case FIELD_TOGGLE_SAMPLE: createToggleValues(builder); break; case FIELD_MISSED_CALLS_COUNT: createMissedCallCountValues(builder); break; case FIELD_TETHERING_INFO: createWifiFields(builder); break; default: mDashClockHomeExtensions.get(fieldId).onUpdateData(builder); } } private void createMissedCallCountValues(FieldValues.Builder builder) { int missedCallCount = getMissedCallsCount(); builder.text(getString(R.string.calls)); Intent showCallLog = new Intent(); showCallLog.setAction(Intent.ACTION_VIEW); showCallLog.setType(CallLog.Calls.CONTENT_TYPE); showCallLog.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); PendingIntent pi = PendingIntent.getActivity(this, 1, showCallLog, PendingIntent.FLAG_UPDATE_CURRENT); builder.intent(pi); builder.leftImageResId(R.drawable.ic_logo_missed_calls); builder.amount("" + missedCallCount); } private void createWifiFields(FieldValues.Builder builder) { WifiManager wifiManager = (WifiManager) getSystemService(WIFI_SERVICE); WifiInfo wifiInfo = wifiManager.getConnectionInfo(); String ssid = wifiInfo.getSSID(); builder.header(ssid); boolean wifiApEnabled = false; boolean wifiEnabled = wifiManager.isWifiEnabled(); try { Method method = WifiManager.class.getMethod("isWifiApEnabled"); wifiApEnabled = (Boolean) method.invoke(wifiManager); } catch (Exception e) { Log.e("HomeServiceProvider", "error getting AP state", e); } if (wifiApEnabled) { try { Scanner connectionsScanner = new Scanner(new File("/proc/net/arp")); Integer connectionsCount = 0; try { while (connectionsScanner.hasNextLine()) { connectionsScanner.nextLine(); connectionsCount++; } // remove header line connectionsCount = connectionsCount - 1; } finally { connectionsScanner.close(); } String text = getResources().getQuantityString(R.plurals.connection_count, connectionsCount, connectionsCount); builder.text(text); Method getWifiApConfiguration = WifiManager.class.getDeclaredMethod("getWifiApConfiguration"); builder.header(((WifiConfiguration) getWifiApConfiguration.invoke(wifiManager)).SSID); ComponentName comp = new ComponentName("com.android.settings", "com.android.settings.TetherSettings"); Intent intent = new Intent(); intent.setComponent(comp); PendingIntent pendingIntent = PendingIntent.getActivity(this, FIELD_TETHERING_INFO, intent, PendingIntent.FLAG_UPDATE_CURRENT); builder.intent(pendingIntent); } catch (IOException e) { Log.e("HomeServiceProvider", "error reading from /proc/net/arp", e); } catch (Exception e) { Log.e("HomeServiceProvider", "error updating hotspot configuration", e); } } else if (wifiEnabled) { SupplicantState supplicantState = wifiInfo.getSupplicantState(); int connectionStatusResId = 0; switch (supplicantState) { case ASSOCIATED: connectionStatusResId = R.string.network_state_associated; break; case ASSOCIATING: connectionStatusResId = R.string.network_state_associating; break; case AUTHENTICATING: connectionStatusResId = R.string.network_state_authenticating; break; case COMPLETED: connectionStatusResId = R.string.network_state_completed; break; case DISCONNECTED: connectionStatusResId = R.string.network_state_disconnected; break; case DORMANT: connectionStatusResId = R.string.network_state_doramnt; break; case FOUR_WAY_HANDSHAKE: connectionStatusResId = R.string.network_state_fwh; break; case GROUP_HANDSHAKE: connectionStatusResId = R.string.network_state_gh; break; case INACTIVE: connectionStatusResId = R.string.network_state_inactive; break; case INTERFACE_DISABLED: connectionStatusResId = R.string.network_state_interface_disabled; break; case INVALID: connectionStatusResId = R.string.network_state_invalid; break; case SCANNING: connectionStatusResId = R.string.network_state_scanning; break; case UNINITIALIZED: connectionStatusResId = R.string.network_state_uninitialized; break; } if (R.string.network_state_completed == connectionStatusResId) { int ip = wifiInfo.getIpAddress(); String ipAddress = Formatter.formatIpAddress(ip); builder.text(ipAddress); } else { if (connectionStatusResId != 0) { builder.text(getString(connectionStatusResId)); } } Intent intent = new Intent(Settings.ACTION_WIFI_SETTINGS); PendingIntent pendingIntent = PendingIntent.getActivity(this, FIELD_TETHERING_INFO, intent, PendingIntent.FLAG_UPDATE_CURRENT); builder.intent(pendingIntent); } else { Intent intent = new Intent(Settings.ACTION_WIRELESS_SETTINGS); PendingIntent pendingIntent = PendingIntent.getActivity(this, FIELD_TETHERING_INFO, intent, PendingIntent.FLAG_UPDATE_CURRENT); builder.intent(pendingIntent); ConnectivityManager conMgr = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE); NetworkInfo i = conMgr.getActiveNetworkInfo(); boolean isConnected = i != null && i.isConnected(); if (!isConnected) { builder.header(getString(R.string.no_connections)); builder.text("--"); builder.leftImageResId(R.drawable.ic_disconnected); return; } int titleResId = 0; int imageResId = 0; int textResId = 0; boolean useSubTypeAsDescription = false; switch (i.getType()) { case ConnectivityManager.TYPE_ETHERNET: titleResId = R.string.conn_eth; imageResId = R.drawable.ic_settings_data; break; case ConnectivityManager.TYPE_WIFI: titleResId = R.string.conn_wifi; imageResId = R.drawable.ic_mobile_conn; break; case ConnectivityManager.TYPE_MOBILE: titleResId = R.string.conn_mobile; imageResId = R.drawable.ic_settings_data; if (i.isRoaming()) { textResId = R.string.roaming; } else { useSubTypeAsDescription = true; } break; case ConnectivityManager.TYPE_WIMAX: titleResId = R.string.conn_wimax; imageResId = R.drawable.ic_wimax; break; case ConnectivityManager.TYPE_BLUETOOTH: titleResId = R.string.conn_bluetooth; imageResId = R.drawable.ic_settings_bluetooth2; break; } builder.leftImageResId(imageResId); builder.header(titleResId == 0 ? "--" : getString(titleResId)); String alt = useSubTypeAsDescription ? i.getSubtypeName() : "--"; builder.text(textResId == 0 ? alt : getString(textResId)); return; } int icon = wifiApEnabled ? R.drawable.ic_plugin_tethering : R.drawable.ic_settings_wireless; builder.leftImageResId(icon); } private void createUserProfileValues(FieldValues.Builder builder) { Object[] photoAndName = getUserProfileData(); Bitmap photo = (Bitmap) photoAndName[0]; String displayName = (String) photoAndName[1]; // we only show the image in Appsi, so we add no intent extra // add the photo and name builder.largeImage(photo); builder.header(displayName); StringBuffer text = new StringBuffer(); text.append("Android ").append(Build.VERSION.RELEASE).append("\n"); text.append(Build.MANUFACTURER).append("\n"); text.append(Build.MODEL); builder.text(text.toString()); } @Override public int onStartCommand(Intent intent, int flags, int startId) { String action = intent == null ? null : intent.getAction(); if (ACTION_TOGGLE.equals(action)) { SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); boolean isEnabled = preferences.getBoolean("sample_toggle", false); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("sample_toggle", !isEnabled); editor.apply(); HomeServiceContract.requestPluginUpdate(this, FIELD_TOGGLE_SAMPLE); } stopSelf(); return Service.START_NOT_STICKY; } private void createToggleValues(FieldValues.Builder builder) { SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); boolean isEnabled = preferences.getBoolean("sample_toggle", false); builder.text(getString(R.string.toggle_sample)); Intent intent = new Intent(this, HomeServiceProvider.class); intent.setClass(this, getClass()); intent.setAction(ACTION_TOGGLE); PendingIntent pi = PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); builder.intent(pi); builder.leftImageResId(R.drawable.ic_logo_toggle); builder.toggle(isEnabled ? 200 : 40, 0xdddddd); } private void createDownloadsValues(FieldValues.Builder builder) { Intent i = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS); updateDownloadBundle(builder, getString(R.string.downloads), R.drawable.ic_plugin_downloads, 12, i); } private void updateDownloadBundle(FieldValues.Builder builder, String name, int drawable, int count, Intent intent) { PendingIntent pi = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); builder.text(name) .amount("" + count) .intent(pi) .leftImageResId(drawable); } /** * Called when the service is created to setup the field you are using. Do not do complex computations * here. Simply register your fields. */ @Override protected void onRegisterFields(FieldsBuilder builder) { Log.d("HomeServiceProvider", "register fields"); builder.registerField(FIELD_DOWNLOADS, HomeServiceContract.FieldsResponse.DISPLAY_TYPE_SIMPLE, R.string.downloads, R.drawable.ic_plugin_downloads, null); builder.registerField(FIELD_MISSED_CALLS_COUNT, HomeServiceContract.FieldsResponse.DISPLAY_TYPE_MISSED_COUNT, R.string.calls, R.drawable.ic_logo_missed_calls, null, CallLog.CONTENT_URI.toString()); //builder.registerField(FIELD_TOGGLE_SAMPLE, HomeServiceContract.FieldsResponse.DISPLAY_TYPE_TOGGLE_STYLE, R.string.toggle_sample, R.drawable.ic_logo_toggle, null); builder.registerField(FIELD_USER_PROFILE, HomeServiceContract.FieldsResponse.DISPLAY_PROFILE_IMAGE_STYLE,R.string.profile_image, R.drawable.ic_logo_profile, null); builder.registerField(FIELD_TETHERING_INFO, HomeServiceContract.FieldsResponse.DISPLAY_TYPE_DASHCLOCK,R.string.wifi_info, R.drawable.ic_plugin_tethering, null); int count = mDashClockHomeExtensions.size(); for (int i = 0; i < count; i++) { int key = mDashClockHomeExtensions.keyAt(i); mDashClockHomeExtensions.get(key).onInitialize(builder); } } private int getMissedCallsCount() { String[] projection = { CallLog.Calls.TYPE }; String where = CallLog.Calls.TYPE + "=? AND " + CallLog.Calls.IS_READ + "=?"; Cursor c = getContentResolver().query( CallLog.Calls.CONTENT_URI, projection, where, new String[] { "" + CallLog.Calls.MISSED_TYPE, "0" }, null); if (c == null) return 0; try { return c.getCount(); } finally { c.close(); } } private Object[] getUserProfileData() { ContentResolver contentResolver = getContentResolver(); Cursor cursor = contentResolver.query( ContactsContract.Profile.CONTENT_URI, new String[]{ ContactsContract.Profile.PHOTO_URI, ContactsContract.Profile.DISPLAY_NAME, ContactsContract.Profile._ID, }, null, null, null); Bitmap photo = null; String name = null; if (cursor != null) { try { while (cursor.moveToNext()) { String uriString = cursor.getString(0); // index 0 = photo uri if (uriString != null) { Uri uri = Uri.parse(uriString); try { InputStream in = contentResolver.openInputStream(uri); photo = BitmapFactory.decodeStream(in); } catch (IOException e) { Log.w("HomeServiceProvider", "error loading profile image", e); } } name = cursor.getString(1); // index 1: displayName if (name != null && photo != null) break; } } finally { cursor.close(); } } return new Object[] {photo, name}; } }