From cb61c3e279904861f3b566f57db13e7987cf09f7 Mon Sep 17 00:00:00 2001 From: kbillore Date: Wed, 1 Jul 2020 11:13:59 +0530 Subject: [PATCH] Application shows dialog when USB camera hot plug When application opened and user plug\unplug USB camera then application start operation on wrong device id's because when hot plug happends device id's gets realign because of this many stability issues observed on USB hot plug Solution : Whenever USB hot plug detected application will show dialog to close application. Closing and opening application will make application works on right device nodes. Tracked-On : OAM-91443 Signed-off-by: Shiva Kumara Signed-off-by: kbillore --- .../intel/multicamera/FullScreenActivity.java | 69 +++-- .../com/intel/multicamera/MultiCamera.java | 9 + .../intel/multicamera/MultiViewActivity.java | 47 +++ .../com/intel/multicamera/PhotoPreview.java | 7 +- .../multicamera/SingleCameraActivity.java | 56 +++- .../intel/multicamera/USBChangeDialog.java | 289 ++++++++++++++++++ .../res/values/strings.xml | 1 + 7 files changed, 449 insertions(+), 29 deletions(-) create mode 100644 camera/MultiCameraApplication/java/com/intel/multicamera/USBChangeDialog.java diff --git a/camera/MultiCameraApplication/java/com/intel/multicamera/FullScreenActivity.java b/camera/MultiCameraApplication/java/com/intel/multicamera/FullScreenActivity.java index e8f30db..ce954fc 100644 --- a/camera/MultiCameraApplication/java/com/intel/multicamera/FullScreenActivity.java +++ b/camera/MultiCameraApplication/java/com/intel/multicamera/FullScreenActivity.java @@ -22,6 +22,7 @@ import androidx.core.app.ActivityCompat; import android.Manifest; +import android.app.Dialog; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -36,6 +37,7 @@ import android.os.SystemClock; import android.util.Log; import android.view.View; +import android.view.Window; import android.view.WindowManager; import android.widget.FrameLayout; import android.widget.ImageButton; @@ -58,6 +60,7 @@ public class FullScreenActivity extends AppCompatActivity { private int numOfCameras; private AutoFitTextureView mCamera_BackView, mCamera_FrontView; + private int isDialogShown; private ImageButton mCameraSwitch, mCameraPicture, mCameraRecord, mCameraSplit, mSettings; private ImageButton mSettingClose; @@ -78,6 +81,7 @@ protected void onCreate(Bundle savedInstanceState) { getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); + isDialogShown = 0; ActionBar actionBar = getSupportActionBar(); if (actionBar != null) { actionBar.setHomeButtonEnabled(true); @@ -99,28 +103,51 @@ protected void onCreate(Bundle savedInstanceState) { checkPermissions(); openBackCamera(); + try { + IntentFilter filter = new IntentFilter(); + filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED); + filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED); + + // BroadcastReceiver when insert/remove the device USB plug into/from a USB port + mUsbReceiver = new BroadcastReceiver() { + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) { + System.out.println(TAG+"BroadcastReceiver USB Connected"); + if(isDialogShown == 0) { + Dialog detailDialog = USBChangeDialog.create(FullScreenActivity.this); + detailDialog.setCancelable(false); + detailDialog.setCanceledOnTouchOutside(false); + detailDialog.requestWindowFeature(Window.FEATURE_NO_TITLE); + detailDialog.show(); + isDialogShown = 1; + mCameraSwitch.setVisibility(View.GONE); + mCameraRecord.setVisibility(View.GONE); + mCameraPicture.setVisibility(View.GONE); + mCameraSplit.setVisibility(View.GONE); + } - IntentFilter filter = new IntentFilter(); - filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED); - filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED); - - // BroadcastReceiver when insert/remove the device USB plug into/from a USB port - mUsbReceiver = new BroadcastReceiver() { - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - System.out.println("BroadcastReceiver Event"); - if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) { - System.out.println(TAG+"BroadcastReceiver USB Connected"); - openBackCamera(); - - } else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) { - System.out.println(TAG+"BroadcastReceiver USB Disconnected"); - openBackCamera(); + } else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) { + System.out.println(TAG + "BroadcastReceiver USB Disconnected"); + if(isDialogShown == 0) { + Dialog detailDialog = USBChangeDialog.create(FullScreenActivity.this); + detailDialog.setCancelable(false); + detailDialog.setCanceledOnTouchOutside(false); + detailDialog.requestWindowFeature(Window.FEATURE_NO_TITLE); + detailDialog.show(); + isDialogShown = 1; + mCameraSwitch.setVisibility(View.GONE); + mCameraRecord.setVisibility(View.GONE); + mCameraPicture.setVisibility(View.GONE); + mCameraSplit.setVisibility(View.GONE); + } + } } - } - }; + }; - registerReceiver(mUsbReceiver , filter); + registerReceiver(mUsbReceiver , filter); + }catch (Exception e) { + } mCameraSwitch.setOnClickListener(new View.OnClickListener() { @Override @@ -254,12 +281,11 @@ public void onClick(View view) { mIsRecordingVideo =true; if (mCameraInst.getWhichCamera() == 0) { mCameraBack.getmRecord().startRecordingVideo(); - mCameraBack.getmRecord().showRecordingUI(true); } else { mCameraFront.getmRecord().startRecordingVideo(); - mCameraFront.getmRecord().showRecordingUI(true); } + mCameraBack.getmRecord().showRecordingUI(true); mSettings.setVisibility(View.GONE); mCameraSwitch.setVisibility(View.GONE); mCameraPicture.setVisibility(View.GONE); @@ -431,6 +457,7 @@ private void checkPermissions() { @Override protected void onPause() { + unregisterReceiver(mUsbReceiver); super.onPause(); } diff --git a/camera/MultiCameraApplication/java/com/intel/multicamera/MultiCamera.java b/camera/MultiCameraApplication/java/com/intel/multicamera/MultiCamera.java index 68a6ae8..d05e088 100644 --- a/camera/MultiCameraApplication/java/com/intel/multicamera/MultiCamera.java +++ b/camera/MultiCameraApplication/java/com/intel/multicamera/MultiCamera.java @@ -32,10 +32,12 @@ public class MultiCamera { private Uri mCurrentUri; private ContentValues mCurrentFileInfo; + private int isPopupShown; MultiCamera() { mWhichCamera = 0; mIsCameraOrSurveillance = 0; mOpenCameraId = -1; + isPopupShown = 0; } public static MultiCamera getInstance() { if (ic_instance == null) { @@ -120,4 +122,11 @@ public int getOpenCameraId() { public void setOpenCameraId(int openCameraId) { mOpenCameraId = openCameraId; } + public int getIsPopupShown() { + return isPopupShown; + } + + public void setIsPopupShown(int isPopupShown) { + this.isPopupShown = isPopupShown; + } } diff --git a/camera/MultiCameraApplication/java/com/intel/multicamera/MultiViewActivity.java b/camera/MultiCameraApplication/java/com/intel/multicamera/MultiViewActivity.java index ebe7c81..5ebbb45 100644 --- a/camera/MultiCameraApplication/java/com/intel/multicamera/MultiViewActivity.java +++ b/camera/MultiCameraApplication/java/com/intel/multicamera/MultiViewActivity.java @@ -18,6 +18,7 @@ package com.intel.multicamera; import android.Manifest; +import android.app.Dialog; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -67,6 +68,8 @@ public class MultiViewActivity extends AppCompatActivity { private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); private FrameLayout frameView0, frameView1, frameView2, frameView3; + private int isDialogShown; + private SettingsPrefUtil Fragment, Fragment1, Fragment2, Fragment3; public String[] CameraIds; @@ -100,6 +103,7 @@ protected void onCreate(Bundle savedInstanceState) { actionBar.setHomeButtonEnabled(true); } + isDialogShown = 0; setContentView(R.layout.activity_multiview); Log.e(TAG, "onCreate"); @@ -128,6 +132,47 @@ protected void onCreate(Bundle savedInstanceState) { FullScrn2.setVisibility(View.GONE); FullScrn3.setVisibility(View.GONE); startCamera(); + try { + IntentFilter filter = new IntentFilter(); + filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED); + filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED); + + // BroadcastReceiver when insert/remove the device USB plug into/from a USB port + mUsbReceiver = new BroadcastReceiver() { + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) { + System.out.println(TAG + "BroadcastReceiver USB Connected"); + if(isDialogShown == 0) { + Dialog detailDialog = USBChangeDialog.create(MultiViewActivity.this); + detailDialog.setCancelable(false); + detailDialog.setCanceledOnTouchOutside(false); + detailDialog.requestWindowFeature(Window.FEATURE_NO_TITLE); + detailDialog.show(); + isDialogShown = 1; + } + + } else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) { + System.out.println(TAG + "BroadcastReceiver USB Disconnected"); + if(isDialogShown == 0) { + Dialog detailDialog = USBChangeDialog.create(MultiViewActivity.this); + detailDialog.setCancelable(false); + detailDialog.setCanceledOnTouchOutside(false); + detailDialog.requestWindowFeature(Window.FEATURE_NO_TITLE); + detailDialog.show(); + isDialogShown = 1; + } + } + } + }; + registerReceiver(mUsbReceiver , filter); + }catch (Exception e) { + //there is race condition when camera connection app is trying to open during that + // time is camera is disconnect suddently then its trying to access non exist device + //nodes hence crash is observed + System.out.println(TAG + " Exception occured during USB attach and detach"); + } + } void startCamera() { @@ -610,6 +655,8 @@ protected void onPause() { System.out.println("onPause"); super.onPause(); + unregisterReceiver(mUsbReceiver); + // closeCamera(); } public void settingView(View view) { diff --git a/camera/MultiCameraApplication/java/com/intel/multicamera/PhotoPreview.java b/camera/MultiCameraApplication/java/com/intel/multicamera/PhotoPreview.java index d73655e..70854e8 100644 --- a/camera/MultiCameraApplication/java/com/intel/multicamera/PhotoPreview.java +++ b/camera/MultiCameraApplication/java/com/intel/multicamera/PhotoPreview.java @@ -113,11 +113,8 @@ public void onClick(View v) { Intent scanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); scanIntent.setData(Uri.fromFile(file)); mActivity.sendBroadcast(scanIntent); - FrameLayout previewLayout; - previewLayout = mActivity.findViewById(R.id.previewLayout); - previewLayout.setVisibility(View.GONE); - - mRoundedThumbnailView.hideThumbnail(); + preView.setImageResource(android.R.color.background_dark); + details.setVisibility(View.INVISIBLE); } } }); diff --git a/camera/MultiCameraApplication/java/com/intel/multicamera/SingleCameraActivity.java b/camera/MultiCameraApplication/java/com/intel/multicamera/SingleCameraActivity.java index e18b90b..57f0c14 100644 --- a/camera/MultiCameraApplication/java/com/intel/multicamera/SingleCameraActivity.java +++ b/camera/MultiCameraApplication/java/com/intel/multicamera/SingleCameraActivity.java @@ -18,6 +18,7 @@ package com.intel.multicamera; import android.Manifest; +import android.app.Dialog; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -32,6 +33,7 @@ import android.os.SystemClock; import android.util.Log; import android.view.View; +import android.view.Window; import android.view.WindowManager; import android.widget.ImageButton; import android.widget.ImageView; @@ -50,7 +52,7 @@ public class SingleCameraActivity extends AppCompatActivity { private static final String TAG = "SingleCameraActivity"; private boolean mHasCriticalPermissions; - + private int isDialogShown; private int numOfCameras; private AutoFitTextureView mCamera_BackView, mCamera_FrontView; @@ -76,7 +78,7 @@ protected void onCreate(Bundle savedInstanceState) { if (actionBar != null) { actionBar.setHomeButtonEnabled(true); } - + isDialogShown = 0; setContentView(R.layout.activity_full_screen); mIsRecordingVideo = false; @@ -94,7 +96,54 @@ protected void onCreate(Bundle savedInstanceState) { checkPermissions(); OpenCamera(); - + try { + IntentFilter filter = new IntentFilter(); + filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED); + filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED); + + // BroadcastReceiver when insert/remove the device USB plug into/from a USB port + mUsbReceiver = new BroadcastReceiver() { + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) { + System.out.println(TAG+"BroadcastReceiver USB Connected"); + if(isDialogShown == 0) { + Dialog detailDialog = USBChangeDialog.create(SingleCameraActivity.this); + detailDialog.setCancelable(false); + detailDialog.setCanceledOnTouchOutside(false); + detailDialog.requestWindowFeature(Window.FEATURE_NO_TITLE); + detailDialog.show(); + isDialogShown = 1; + mCameraSwitch.setVisibility(View.GONE); + mCameraRecord.setVisibility(View.GONE); + mCameraPicture.setVisibility(View.GONE); + mCameraSplit.setVisibility(View.GONE); + } + + } else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) { + System.out.println(TAG + "BroadcastReceiver USB Disconnected"); + if(isDialogShown == 0) { + Dialog detailDialog = USBChangeDialog.create(SingleCameraActivity.this); + detailDialog.setCancelable(false); + detailDialog.setCanceledOnTouchOutside(false); + detailDialog.requestWindowFeature(Window.FEATURE_NO_TITLE); + detailDialog.show(); + isDialogShown = 1; + mCameraSwitch.setVisibility(View.GONE); + mCameraRecord.setVisibility(View.GONE); + mCameraPicture.setVisibility(View.GONE); + mCameraSplit.setVisibility(View.GONE); + } + } + } + }; + registerReceiver(mUsbReceiver , filter); + }catch (Exception e) { + //there is race condition when camera connection app is trying to open during that + // time is camera is disconnect suddently then its trying to access non exist device + //nodes hence crash is observed + System.out.println(TAG + " Exception occured during USB attach and detach"); + } mCameraSwitch.setOnClickListener(new View.OnClickListener() { @Override @@ -299,6 +348,7 @@ private void checkPermissions() { @Override protected void onPause() { + unregisterReceiver(mUsbReceiver); super.onPause(); } diff --git a/camera/MultiCameraApplication/java/com/intel/multicamera/USBChangeDialog.java b/camera/MultiCameraApplication/java/com/intel/multicamera/USBChangeDialog.java new file mode 100644 index 0000000..18cb12f --- /dev/null +++ b/camera/MultiCameraApplication/java/com/intel/multicamera/USBChangeDialog.java @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.intel.multicamera; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.text.format.Formatter; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ListView; +import android.widget.TextView; + +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Locale; +import java.util.Map.Entry; + +/** + * Displays details (such as Exif) of a local media item. + */ +public class USBChangeDialog { + /** + * Creates a dialog for showing media data. + * + * @param context the Android context. + * @return A dialog that can be made visible to show the media details. + */ + public static Dialog create(final Context context) { + ListView detailsList = + (ListView)LayoutInflater.from(context).inflate(R.layout.details_list, null, false); + detailsList.setAdapter(new DetailsAdapter(context)); + + final AlertDialog.Builder builder = new AlertDialog.Builder(context); + return builder.setTitle(R.string.usb_change) + .setView(detailsList) + .setPositiveButton(R.string.close, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, + int whichButton) { + Activity activity = (Activity) context; + activity.finish(); + //dialog.dismiss(); + } + }) + .create(); + } + + /** + * An adapter for feeding a details list view with the contents of a + * {@link MediaDetails} instance. + */ + private static class DetailsAdapter extends BaseAdapter { + private final Context mContext; + //private final MediaDetails mMediaDetails; + //private final ArrayList mItems; + private final Locale mDefaultLocale = Locale.getDefault(); + private final DecimalFormat mDecimalFormat = new DecimalFormat(".####"); + private int mWidthIndex = -1; + private int mHeightIndex = -1; + + public DetailsAdapter(Context context) { + mContext = context; + //mMediaDetails = details; + //mItems = new ArrayList(details.size()); + // setDetails(context, details); + } +/* + private void setDetails(Context context, MediaDetails details) { + boolean resolutionIsValid = true; + String path = null; + for (Entry detail : details) { + String value; + switch (detail.getKey()) { + case MediaDetails.INDEX_SIZE: { + value = Formatter.formatFileSize(context, (Long)detail.getValue()); + break; + } + + case MediaDetails.INDEX_WIDTH: + mWidthIndex = mItems.size(); + if (detail.getValue().toString().equalsIgnoreCase("0")) { + value = context.getString(R.string.unknown); + resolutionIsValid = false; + } else { + value = toLocalInteger(detail.getValue()); + } + break; + case MediaDetails.INDEX_HEIGHT: { + mHeightIndex = mItems.size(); + if (detail.getValue().toString().equalsIgnoreCase("0")) { + value = context.getString(R.string.unknown); + resolutionIsValid = false; + } else { + value = toLocalInteger(detail.getValue()); + } + break; + } + case MediaDetails.INDEX_PATH: + // Prepend the new-line as a) paths are usually long, so + // the formatting is better and b) an RTL UI will see it + // as a separate section and interpret it for what it + // is, rather than trying to make it RTL (which messes + // up the path). + value = "\n" + detail.getValue().toString(); + path = detail.getValue().toString(); + break; + case MediaDetails.INDEX_ORIENTATION: + value = toLocalInteger(detail.getValue()); + break; + default: { + Object valueObj = detail.getValue(); + // This shouldn't happen, log its key to help us + // diagnose the problem. + if (valueObj == null) { + fail("%s's value is Null", getDetailsName(context, detail.getKey())); + } + value = valueObj.toString(); + } + } + int key = detail.getKey(); + if (details.hasUnit(key)) { + value = String.format("%s: %s %s", getDetailsName(context, key), value, + context.getString(details.getUnit(key))); + } else { + value = String.format("%s: %s", getDetailsName(context, key), value); + } + mItems.add(value); + } + if (!resolutionIsValid) { + resolveResolution(path); + } + } +*/ + public void resolveResolution(String path) { + Bitmap bitmap = BitmapFactory.decodeFile(path); + if (bitmap == null) return; + onResolutionAvailable(bitmap.getWidth(), bitmap.getHeight()); + } + + @Override + public boolean areAllItemsEnabled() { + return false; + } + + @Override + public boolean isEnabled(int position) { + return false; + } + + @Override + public int getCount() { + return 0; + } + + @Override + public Object getItem(int position) { + return null; //mMediaDetails.getDetail(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + TextView tv; + if (convertView == null) { + tv = (TextView)LayoutInflater.from(mContext).inflate(R.layout.details, parent, + false); + } else { + tv = (TextView)convertView; + } + //tv.setText(mItems.get(position)); + return tv; + } + + public void onResolutionAvailable(int width, int height) { + if (width == 0 || height == 0) return; + // Update the resolution with the new width and height + String widthString = + String.format(mDefaultLocale, "%s: %d", + getDetailsName(mContext, MediaDetails.INDEX_WIDTH), width); + String heightString = + String.format(mDefaultLocale, "%s: %d", + getDetailsName(mContext, MediaDetails.INDEX_HEIGHT), height); + //mItems.set(mWidthIndex, String.valueOf(widthString)); + //mItems.set(mHeightIndex, String.valueOf(heightString)); + notifyDataSetChanged(); + } + + /** + * Converts the given integer (given as String or Integer object) to a + * localized String version. + */ + private String toLocalInteger(Object valueObj) { + if (valueObj instanceof Integer) { + return toLocalNumber((Integer)valueObj); + } else { + String value = valueObj.toString(); + try { + value = toLocalNumber(Integer.parseInt(value)); + } catch (NumberFormatException ex) { + // Just keep the current "value" if we cannot + // parse it as a fallback. + } + return value; + } + } + + /** Converts the given integer to a localized String version. */ + private String toLocalNumber(int n) { + return String.format(mDefaultLocale, "%d", n); + } + + } + + public static String getDetailsName(Context context, int key) { + switch (key) { + case MediaDetails.INDEX_TYPE: + return context.getString(R.string.type); + case MediaDetails.INDEX_TITLE: + return context.getString(R.string.title); + case MediaDetails.INDEX_DESCRIPTION: + return context.getString(R.string.description); + case MediaDetails.INDEX_DATETIME: + return context.getString(R.string.time); + case MediaDetails.INDEX_DATEMODIFIED: + return context.getString(R.string.datemodified); + case MediaDetails.INDEX_LOCATION: + return context.getString(R.string.location); + case MediaDetails.INDEX_PATH: + return context.getString(R.string.path); + case MediaDetails.INDEX_WIDTH: + return context.getString(R.string.width); + case MediaDetails.INDEX_HEIGHT: + return context.getString(R.string.height); + case MediaDetails.INDEX_DIMENSIONS: + return context.getString(R.string.dimensions); + case MediaDetails.INDEX_ORIENTATION: + return context.getString(R.string.orientation); + case MediaDetails.INDEX_DURATION: + return context.getString(R.string.duration); + case MediaDetails.INDEX_MIMETYPE: + return context.getString(R.string.mimetype); + case MediaDetails.INDEX_SIZE: + return context.getString(R.string.file_size); + case MediaDetails.INDEX_MAKE: + return context.getString(R.string.maker); + case MediaDetails.INDEX_MODEL: + return context.getString(R.string.model); + default: + return "Unknown key" + key; + } + } + + /** + * Throw an assertion error wit the given message. + * + * @param message the message, can contain placeholders. + * @param args if he message contains placeholders, these values will be + * used to fill them. + */ + private static void fail(String message, Object... args) { + throw new AssertionError(args.length == 0 ? message : String.format(message, args)); + } +} diff --git a/camera/MultiCameraApplication/res/values/strings.xml b/camera/MultiCameraApplication/res/values/strings.xml index 4a2dfb3..392809d 100644 --- a/camera/MultiCameraApplication/res/values/strings.xml +++ b/camera/MultiCameraApplication/res/values/strings.xml @@ -141,6 +141,7 @@ Unknown Details + USB Plug\Unplug Detected Close