diff --git a/res/drawable-hdpi/ic_qs_nfc_off.png b/res/drawable-hdpi/ic_qs_nfc_off.png new file mode 100644 index 0000000000..9f5ad63fc4 Binary files /dev/null and b/res/drawable-hdpi/ic_qs_nfc_off.png differ diff --git a/res/drawable-hdpi/ic_qs_nfc_on.png b/res/drawable-hdpi/ic_qs_nfc_on.png new file mode 100644 index 0000000000..1ff664f610 Binary files /dev/null and b/res/drawable-hdpi/ic_qs_nfc_on.png differ diff --git a/res/drawable-mdpi/ic_qs_nfc_off.png b/res/drawable-mdpi/ic_qs_nfc_off.png new file mode 100644 index 0000000000..0e2bea8fe1 Binary files /dev/null and b/res/drawable-mdpi/ic_qs_nfc_off.png differ diff --git a/res/drawable-mdpi/ic_qs_nfc_on.png b/res/drawable-mdpi/ic_qs_nfc_on.png new file mode 100644 index 0000000000..e728ab18bc Binary files /dev/null and b/res/drawable-mdpi/ic_qs_nfc_on.png differ diff --git a/res/drawable-xhdpi/ic_qs_nfc_off.png b/res/drawable-xhdpi/ic_qs_nfc_off.png new file mode 100644 index 0000000000..d660b8780e Binary files /dev/null and b/res/drawable-xhdpi/ic_qs_nfc_off.png differ diff --git a/res/drawable-xhdpi/ic_qs_nfc_on.png b/res/drawable-xhdpi/ic_qs_nfc_on.png new file mode 100644 index 0000000000..fc1093c381 Binary files /dev/null and b/res/drawable-xhdpi/ic_qs_nfc_on.png differ diff --git a/res/layout/quick_settings_tile_nfc.xml b/res/layout/quick_settings_tile_nfc.xml new file mode 100644 index 0000000000..d10d58bbb9 --- /dev/null +++ b/res/layout/quick_settings_tile_nfc.xml @@ -0,0 +1,24 @@ + + + \ No newline at end of file diff --git a/res/values/arrays.xml b/res/values/arrays.xml index b2b341719e..d3bcef12cf 100644 --- a/res/values/arrays.xml +++ b/res/values/arrays.xml @@ -83,6 +83,7 @@ @string/qs_tile_bluetooth @string/qs_tile_gps @string/qs_tile_gps_alt + @string/qs_tile_nfc @string/qs_tile_mobile_data @string/qs_tile_network_mode @string/qs_tile_data_usage @@ -113,6 +114,7 @@ bluetooth_textview gps_textview gps_tileview + nfc_tileview data_conn_textview network_mode_tileview rssi_textview @@ -145,6 +147,7 @@ @string/qs_tile_airplane_mode @string/qs_tile_bluetooth @string/qs_tile_gps + @string/qs_tile_nfc @string/qs_tile_ringer_mode @string/qs_tile_volume @string/qs_tile_network_mode @@ -171,6 +174,7 @@ airplane_mode_textview bluetooth_textview gps_tileview + nfc_tileview ringer_mode_tileview volume_tileview network_mode_tileview diff --git a/res/values/strings.xml b/res/values/strings.xml index 3c2abedda6..1ac7ea34bf 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -99,6 +99,8 @@ Wi-Fi off Not connected Turning on… + NFC + NFC off Statusbar QuickSettings tiles @@ -129,6 +131,7 @@ Expanded desktop Stay awake Screenshot + NFC Color Picker diff --git a/src/com/ceco/gm2/gravitybox/ModQuickSettings.java b/src/com/ceco/gm2/gravitybox/ModQuickSettings.java index a60648192b..1d5ed7f201 100644 --- a/src/com/ceco/gm2/gravitybox/ModQuickSettings.java +++ b/src/com/ceco/gm2/gravitybox/ModQuickSettings.java @@ -31,6 +31,7 @@ import com.ceco.gm2.gravitybox.quicksettings.ExpandedDesktopTile; import com.ceco.gm2.gravitybox.quicksettings.GpsTile; import com.ceco.gm2.gravitybox.quicksettings.NetworkModeTile; +import com.ceco.gm2.gravitybox.quicksettings.NfcTile; import com.ceco.gm2.gravitybox.quicksettings.QuickAppTile; import com.ceco.gm2.gravitybox.quicksettings.QuickRecordTile; import com.ceco.gm2.gravitybox.quicksettings.RingerModeTile; @@ -152,7 +153,8 @@ public class ModQuickSettings { R.id.stay_awake_tileview, R.id.screenshot_tileview, R.id.gps_tileview, - R.id.ringer_mode_tileview + R.id.ringer_mode_tileview, + R.id.nfc_tileview )); if (Utils.isMtkDevice()) { mCustomGbTileKeys.add(R.id.wifi_tileview); @@ -594,6 +596,12 @@ protected void afterHookedMethod(final MethodHookParam param) throws Throwable { gpsTile.setupQuickSettingsTile(mContainerView, inflater, mPrefs); mTiles.add(gpsTile); } + + if (Utils.hasNFC(mContext)) { + NfcTile nfcTile = new NfcTile(mContext, mGbContext, mStatusBar, mPanelBar); + nfcTile.setupQuickSettingsTile(mContainerView, inflater); + mTiles.add(nfcTile); + } RingerModeTile rmTile = new RingerModeTile(mContext, mGbContext, mStatusBar, mPanelBar); rmTile.setupQuickSettingsTile(mContainerView, inflater, mPrefs); diff --git a/src/com/ceco/gm2/gravitybox/Utils.java b/src/com/ceco/gm2/gravitybox/Utils.java index e1ba819b88..84da6a5edd 100644 --- a/src/com/ceco/gm2/gravitybox/Utils.java +++ b/src/com/ceco/gm2/gravitybox/Utils.java @@ -52,6 +52,7 @@ public class Utils { private static Boolean mHasVibrator = null; private static Boolean mHasFlash = null; private static Boolean mHasGPS = null; + private static Boolean mHasNFC = null; // Supported MTK devices private static final Set MTK_DEVICES = new HashSet(Arrays.asList( @@ -214,6 +215,20 @@ public static boolean hasGPS(Context con) { } } + public static boolean hasNFC(Context con) { + if (mHasNFC != null) return mHasNFC; + PackageManager pm = con.getPackageManager(); + if (pm == null) { + if (DEBUG) log("Cannot get package manager, assuming no NFC feature"); + mHasNFC = null; + return false; + } + + mHasNFC = pm.hasSystemFeature(PackageManager.FEATURE_NFC); + + return mHasNFC; + } + public static String getDeviceCharacteristics() { if (mDeviceCharacteristics != null) return mDeviceCharacteristics; diff --git a/src/com/ceco/gm2/gravitybox/quicksettings/NfcTile.java b/src/com/ceco/gm2/gravitybox/quicksettings/NfcTile.java new file mode 100644 index 0000000000..257f2e4f02 --- /dev/null +++ b/src/com/ceco/gm2/gravitybox/quicksettings/NfcTile.java @@ -0,0 +1,160 @@ +package com.ceco.gm2.gravitybox.quicksettings; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.nfc.NfcAdapter; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.TextView; + +import com.ceco.gm2.gravitybox.R; + +import de.robv.android.xposed.XposedHelpers; + +public class NfcTile extends AQuickSettingsTile { + + private static final String INTENT_ADAPTER_STATE_CHANGED = "android.nfc.action.ADAPTER_STATE_CHANGED"; + private static final String INTENT_NFC_SETTINGS = "android.settings.NFC_SETTINGS"; + + private static final int STATE_TURNING_ON = XposedHelpers.getStaticIntField(NfcAdapter.class, "STATE_TURNING_ON"); + private static final int STATE_ON = XposedHelpers.getStaticIntField(NfcAdapter.class, "STATE_ON"); + @SuppressWarnings("unused") + private static final int STATE_TURNING_OFF = XposedHelpers.getStaticIntField(NfcAdapter.class, "STATE_TURNING_OFF"); + private static final int STATE_OFF = XposedHelpers.getStaticIntField(NfcAdapter.class, "STATE_OFF"); + private static final int NFC_ADAPTER_UNKNOWN = -100; + + @SuppressWarnings("unused") + private static final String TAG = "GB:NfcTile"; + + private static NfcAdapter mNfcAdapter = null; + + private BroadcastReceiver mNfcStateChangedReceiver = new BroadcastReceiver() { + + @Override + public void onReceive(Context context, Intent intent) { + if (INTENT_ADAPTER_STATE_CHANGED.equals(intent.getAction())) { + if (mNfcAdapter == null) + getNfcAdapter(true); + updateTile(getNfcState()); + } + } + }; + + private TextView mTextView; + + public NfcTile(Context context, Context gbContext, Object statusBar, Object panelBar) { + super(context, gbContext, statusBar, panelBar); + + mOnClick = new View.OnClickListener() { + + @Override + public void onClick(View v) { + toggleState(); + } + }; + + mOnLongClick = new View.OnLongClickListener() { + + @Override + public boolean onLongClick(View v) { + Intent intent = new Intent(INTENT_NFC_SETTINGS); + intent.addCategory(Intent.CATEGORY_DEFAULT); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(intent); + return true; + } + }; + } + + private void toggleState() { + int state = getNfcState(); + /* For some reason, a switch-case way of handling this didn't work */ + + if (mNfcAdapter == null) + getNfcAdapter(true); + + if (mNfcAdapter == null) { + return; + } + + if (state == STATE_TURNING_ON || state == STATE_ON) + XposedHelpers.callMethod(mNfcAdapter, "disable"); + else + XposedHelpers.callMethod(mNfcAdapter, "enable"); + + updateTile(); + } + + @Override + protected void onTileCreate() { + LayoutInflater inflater = LayoutInflater.from(mGbContext); + inflater.inflate(R.layout.quick_settings_tile_nfc, mTile); + mTextView = (TextView) mTile.findViewById(R.id.nfc_tileview); + + mTextView.setText(mLabel); + mTextView.setCompoundDrawablesWithIntrinsicBounds(0, mDrawableId, 0, 0); + } + + @Override + protected void updateTile() { + updateTile(getNfcState()); + } + + private void updateTile(int state) { + if (state == STATE_TURNING_ON || state == STATE_ON) { + mDrawableId = R.drawable.ic_qs_nfc_on; + mLabel = mGbContext.getString(R.string.quick_settings_nfc); + } else { + mDrawableId = R.drawable.ic_qs_nfc_off; + mLabel = mGbContext.getString(R.string.quick_settings_nfc_off); + } + + if (mTextView != null) { + mTextView.setText(mLabel); + mTextView.setCompoundDrawablesWithIntrinsicBounds(0, mDrawableId, 0, 0); + } + } + + private int getNfcState() { + int state = STATE_OFF; + try { + getNfcAdapter(false); + } catch (UnsupportedOperationException e) { + state = NFC_ADAPTER_UNKNOWN; + } + + if (mNfcAdapter != null) + state = (Integer) XposedHelpers.callMethod(mNfcAdapter, "getAdapterState"); + + return state; + } + + @Override + protected void onTilePostCreate() { + mContext.registerReceiver(mNfcStateChangedReceiver, + new IntentFilter(INTENT_ADAPTER_STATE_CHANGED)); + + super.onTilePostCreate(); + } + + private void getNfcAdapter(boolean suppressThrow) throws UnsupportedOperationException { + Class ServiceManager; + try { + ServiceManager = Class.forName("android.os.ServiceManager"); + } catch (ClassNotFoundException e1) { + e1.printStackTrace(); + return; + } + + if (mNfcAdapter == null && XposedHelpers.callStaticMethod(ServiceManager, "getService", "nfc") != null) { + try { + mNfcAdapter = NfcAdapter.getDefaultAdapter(mContext); + } catch (UnsupportedOperationException e) { + if (!suppressThrow) + throw e; + } + } + } +}