How to play a song opened from file manager? - android

I have developed a music player for Android and the app works perfectly. When I try to open a song from file manager it opens the music player but not playing the song.
I tried to fetch the uri using getintent().getdata() but don't know how convert the uri to my required format.
Help me to solve the issue.
Class to play songs
public static void playAll(final Context context, final long[] list, int position,
final long sourceId, final IdType sourceType,
final boolean forceShuffle) {
if (list == null || list.length == 0 || mService == null) {
return;
}
try {
if (forceShuffle) {
mService.setShuffleMode(MusicService.SHUFFLE_NORMAL);
}
final long currentId = mService.getAudioId();
final int currentQueuePosition = getQueuePosition();
if (position != -1 && currentQueuePosition == position && currentId == list[position]) {
final long[] playlist = getQueue();
if (Arrays.equals(list, playlist)) {
mService.play();
return;
}
}
if (position < 0) {
position = 0;
}
mService.open(list, forceShuffle ? -1 : position, sourceId, sourceType.mId);
mService.play();
} catch (final RemoteException ignored) {
} catch (IllegalStateException e) {
e.printStackTrace();
}
}

Related

Bass library on android : how to save audio to file?

I'm developing an app using bass audio library. about voice change function, I can play audio succeessfully like this,
however , I cannot save it to custom file:
BASS.BASS_MusicFree(chan);
BASS.BASS_StreamFree(chan);
if ((chan = BASS.BASS_StreamCreateFile(new BASS.Asset(getAssets(), "test.mp3"), 0, 0, BASS.BASS_MUSIC_DECODE)) == 0
&& (chan = BASS.BASS_MusicLoad(new BASS.Asset(getAssets(), "test.mp3"), 0, 0, BASS.BASS_SAMPLE_LOOP | BASS.BASS_MUSIC_RAMP | floatable, 1)) == 0) {
// whatever it is, it ain't playable
((Button) findViewById(R.id.open)).setText("press here to open a file");
Error("Can't play the file");
return;
}
chan = BASS_FX.BASS_FX_TempoCreate(chan, BASS.BASS_SAMPLE_MONO);//enable pitch
chanFX = BASS_FX.BASS_FX_TempoGetSource(chan);
((Button) findViewById(R.id.open)).setText("test");
setupFX(mPitch, mRate, mIdistortionListener);
BASS.BASS_ChannelPlay(chan, false);
BASS.BASS_SetVolume(0.9f);
I'm developing an app using bass audio library. about voice change function,
however , I cannot save it to custom file:
mBtnSave.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String path = testPath();
savePath(path);
Toast.makeText(TextActivity.this, "save successfully" + path, Toast.LENGTH_SHORT).show();
}
});
private String testPath() {
File localFile = new File(Environment.getExternalStorageDirectory().getPath(), "testVoid");
if (!localFile.exists()) {
localFile.mkdirs();
}
String path = localFile.getAbsolutePath() + "/record" + ".wav";
Log.d("mvn", path);
return path;
}
public void savePath(String filePath) {
long len = BASS.BASS_ChannelGetLength(chan, BASS.BASS_POS_BYTE);
double time = BASS.BASS_ChannelBytes2Seconds(chan, len);
File localFile = new File(filePath);
//flag=262208
if ((!isEmpty(filePath)) && (chan != 0) && (BASSenc.BASS_Encode_Start(chan, filePath, 262208, null, Integer.valueOf(0)) != 0)) {
int i1;
try {
ByteBuffer localByteBuffer = ByteBuffer.allocateDirect(20000);
do {
i1 = BASS.BASS_ChannelGetData(chan, localByteBuffer, localByteBuffer.capacity());
} while ((i1 != -1) && (i1 != 0));
return;
} catch (Exception localException) {
localException.printStackTrace();
}
}
}

ExoPlayer Hls quality

I have ExoPlayer which plays HLS videos, the thing is i need to give user ability to change video quality(auto/1080/720/480).
I figured out that playing around with AdaptiveTrackSelection.Factory does set the quality, but it remains till the object is killed.
I have also tried using MappingTrackSelector, i know that my video has 4 tracks, but i did not get how to select any of it manually. Will this selection make it work?
Thanks for any ideas.
MappingTrackSelector.MappedTrackInfo trackInfo = mDefaultTrackSelector.getCurrentMappedTrackInfo();
mDefaultTrackSelector.selectTracks(
//what should go here?
, trackInfo.getTrackGroups(4));
Regarding this thread :https://github.com/google/ExoPlayer/issues/2250, I managed to change exo player video quality while playing previous one, so it does not getting in buffering instantly.
So I have next classes :
public enum HLSQuality {
Auto, Quality1080, Quality720, Quality480, NoValue
}
class HLSUtil {
private HLSUtil() {
}
#NonNull
static HLSQuality getQuality(#NonNull Format format) {
switch (format.height) {
case 1080: {
return HLSQuality.Quality1080;
}
case 720: {
return HLSQuality.Quality720;
}
case 480:
case 486: {
return HLSQuality.Quality480;
}
default: {
return HLSQuality.NoValue;
}
}
}
static boolean isQualityPlayable(#NonNull Format format) {
return format.height <= 1080;
}
}
public class ClassAdaptiveTrackSelection extends BaseTrackSelection {
public static final class Factory implements TrackSelection.Factory {
private final BandwidthMeter bandwidthMeter;
private final int maxInitialBitrate = 2000000;
private final int minDurationForQualityIncreaseMs = 10000;
private final int maxDurationForQualityDecreaseMs = 25000;
private final int minDurationToRetainAfterDiscardMs = 25000;
private final float bandwidthFraction = 0.75f;
private final float bufferedFractionToLiveEdgeForQualityIncrease = 0.75f;
public Factory(BandwidthMeter bandwidthMeter) {
this.bandwidthMeter = bandwidthMeter;
}
#Override
public ClassAdaptiveTrackSelection createTrackSelection(TrackGroup group, int... tracks) {
Log.d(ClassAdaptiveTrackSelection.class.getSimpleName(), " Video player quality reset to Auto");
sHLSQuality = HLSQuality.Auto;
return new ClassAdaptiveTrackSelection(
group,
tracks,
bandwidthMeter,
maxInitialBitrate,
minDurationForQualityIncreaseMs,
maxDurationForQualityDecreaseMs,
minDurationToRetainAfterDiscardMs,
bandwidthFraction,
bufferedFractionToLiveEdgeForQualityIncrease
);
}
}
private static HLSQuality sHLSQuality = HLSQuality.Auto;
private final BandwidthMeter bandwidthMeter;
private final int maxInitialBitrate;
private final long minDurationForQualityIncreaseUs;
private final long maxDurationForQualityDecreaseUs;
private final long minDurationToRetainAfterDiscardUs;
private final float bandwidthFraction;
private final float bufferedFractionToLiveEdgeForQualityIncrease;
private int selectedIndex;
private int reason;
private ClassAdaptiveTrackSelection(TrackGroup group,
int[] tracks,
BandwidthMeter bandwidthMeter,
int maxInitialBitrate,
long minDurationForQualityIncreaseMs,
long maxDurationForQualityDecreaseMs,
long minDurationToRetainAfterDiscardMs,
float bandwidthFraction,
float bufferedFractionToLiveEdgeForQualityIncrease) {
super(group, tracks);
this.bandwidthMeter = bandwidthMeter;
this.maxInitialBitrate = maxInitialBitrate;
this.minDurationForQualityIncreaseUs = minDurationForQualityIncreaseMs * 1000L;
this.maxDurationForQualityDecreaseUs = maxDurationForQualityDecreaseMs * 1000L;
this.minDurationToRetainAfterDiscardUs = minDurationToRetainAfterDiscardMs * 1000L;
this.bandwidthFraction = bandwidthFraction;
this.bufferedFractionToLiveEdgeForQualityIncrease = bufferedFractionToLiveEdgeForQualityIncrease;
selectedIndex = determineIdealSelectedIndex(Long.MIN_VALUE);
reason = C.SELECTION_REASON_INITIAL;
}
#Override
public void updateSelectedTrack(long playbackPositionUs, long bufferedDurationUs, long availableDurationUs) {
long nowMs = SystemClock.elapsedRealtime();
// Stash the current selection, then make a new one.
int currentSelectedIndex = selectedIndex;
selectedIndex = determineIdealSelectedIndex(nowMs);
if (selectedIndex == currentSelectedIndex) {
return;
}
if (!isBlacklisted(currentSelectedIndex, nowMs)) {
// Revert back to the current selection if conditions are not suitable for switching.
Format currentFormat = getFormat(currentSelectedIndex);
Format selectedFormat = getFormat(selectedIndex);
if (selectedFormat.bitrate > currentFormat.bitrate
&& bufferedDurationUs < minDurationForQualityIncreaseUs(availableDurationUs)) {
// The selected track is a higher quality, but we have insufficient buffer to safely switch
// up. Defer switching up for now.
selectedIndex = currentSelectedIndex;
} else if (selectedFormat.bitrate < currentFormat.bitrate
&& bufferedDurationUs >= maxDurationForQualityDecreaseUs) {
// The selected track is a lower quality, but we have sufficient buffer to defer switching
// down for now.
selectedIndex = currentSelectedIndex;
}
}
// If we adapted, update the trigger.
if (selectedIndex != currentSelectedIndex) {
reason = C.SELECTION_REASON_ADAPTIVE;
}
}
#Override
public int getSelectedIndex() {
return selectedIndex;
}
#Override
public int getSelectionReason() {
return reason;
}
#Override
public Object getSelectionData() {
return null;
}
#Override
public int evaluateQueueSize(long playbackPositionUs, List<? extends MediaChunk> queue) {
if (queue.isEmpty()) {
return 0;
}
int queueSize = queue.size();
long bufferedDurationUs = queue.get(queueSize - 1).endTimeUs - playbackPositionUs;
if (bufferedDurationUs < minDurationToRetainAfterDiscardUs) {
return queueSize;
}
int idealSelectedIndex = determineIdealSelectedIndex(SystemClock.elapsedRealtime());
Format idealFormat = getFormat(idealSelectedIndex);
// If the chunks contain video, discard from the first SD chunk beyond
// minDurationToRetainAfterDiscardUs whose resolution and bitrate are both lower than the ideal
// track.
for (int i = 0; i < queueSize; i++) {
MediaChunk chunk = queue.get(i);
Format format = chunk.trackFormat;
long durationBeforeThisChunkUs = chunk.startTimeUs - playbackPositionUs;
if (durationBeforeThisChunkUs >= minDurationToRetainAfterDiscardUs
&& format.bitrate < idealFormat.bitrate
&& format.height != Format.NO_VALUE && format.height < 720
&& format.width != Format.NO_VALUE && format.width < 1280
&& format.height < idealFormat.height) {
return i;
}
}
return queueSize;
}
private int determineIdealSelectedIndex(long nowMs) {
if (sHLSQuality != HLSQuality.Auto) {
Log.d(ClassAdaptiveTrackSelection.class.getSimpleName(), " Video player quality seeking for " + String.valueOf(sHLSQuality));
for (int i = 0; i < length; i++) {
Format format = getFormat(i);
if (HLSUtil.getQuality(format) == sHLSQuality) {
Log.d(ClassAdaptiveTrackSelection.class.getSimpleName(), " Video player quality set to " + String.valueOf(sHLSQuality));
return i;
}
}
}
Log.d(ClassAdaptiveTrackSelection.class.getSimpleName(), " Video player quality seeking for auto quality " + String.valueOf(sHLSQuality));
long bitrateEstimate = bandwidthMeter.getBitrateEstimate();
long effectiveBitrate = bitrateEstimate == BandwidthMeter.NO_ESTIMATE
? maxInitialBitrate : (long) (bitrateEstimate * bandwidthFraction);
int lowestBitrateNonBlacklistedIndex = 0;
for (int i = 0; i < length; i++) {
if (nowMs == Long.MIN_VALUE || !isBlacklisted(i, nowMs)) {
Format format = getFormat(i);
if (format.bitrate <= effectiveBitrate && HLSUtil.isQualityPlayable(format)) {
Log.d(ClassAdaptiveTrackSelection.class.getSimpleName(), " Video player quality auto quality found " + String.valueOf(sHLSQuality));
return i;
} else {
lowestBitrateNonBlacklistedIndex = i;
}
}
}
return lowestBitrateNonBlacklistedIndex;
}
private long minDurationForQualityIncreaseUs(long availableDurationUs) {
boolean isAvailableDurationTooShort = availableDurationUs != C.TIME_UNSET
&& availableDurationUs <= minDurationForQualityIncreaseUs;
return isAvailableDurationTooShort
? (long) (availableDurationUs * bufferedFractionToLiveEdgeForQualityIncrease)
: minDurationForQualityIncreaseUs;
}
static void setHLSQuality(HLSQuality HLSQuality) {
sHLSQuality = HLSQuality;
}
}
Hope it helps someone.
You can check out ExoPlayer_TrackSelection from github for changing video quality manually.

App crashes sometimes when starting videoview

I am using VideoView to play live streams and it works fine most of the time, but sometimes the app freezes for a moment and either continues to play or crash and send me to home screen.
This is what happens when i select an item from the listview or click KEY_DOWN / KEY_UP:
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
if (mVideoView != null && mListView == null) {
if (listViewNext != listViewCurrent) { playChannel(listViewNext, 0); }
return true;
}
}
}
private void playChannel(int channel, int id) {
listViewCurrent = channel;
listViewNext = listViewCurrent + 1;
listViewPrev = listViewCurrent - 1;
if (mListViewChannels == "Danish") {
if (listViewNext >= arrayDanishChannels.size()) { listViewNext = 0; }
if (listViewPrev < 0) { listViewPrev = arrayDanishChannels.size() - 1; }
mListViewLink = arrayDanishLinks.get(listViewCurrent);
} else if (mListViewChannels == "World") {
if (listViewNext >= arrayWorldChannels.size()) { listViewNext = 0; }
if (listViewPrev < 0) { listViewPrev = arrayWorldChannels.size() - 1; }
mListViewLink = arrayWorldLinks.get(listViewCurrent);
}
String listViewName = (String) listView.getItemAtPosition(listViewCurrent);
String[] links = mListViewLink.split(";"); int temp = id + 1;
if (id < 0 || temp > links.length) { id = 0; temp = 1; }
mListViewLink = links[id]; listViewLinkId = temp; listViewLinkIds = links.length;
startVideo(listViewName+" ("+listViewLinkId+"/"+listViewLinkIds+")", mListViewLink);
mListViewCurrent = mListViewChannels;
}
private void startVideo(String title, String link) {
toastDisplay.cancel();
setInfoView(title);
imageView.setVisibility(ImageView.INVISIBLE);
videoView.removeCallbacks(videoRunnable);
videoView.stopPlayback();
MediaController media = new MediaController(MainActivity.this); media.setAnchorView(videoView); media.setMediaPlayer(videoView);
videoView.setMediaController(media);
videoView.setVideoPath(link);
videoView.setOnPreparedListener(videoViewPreparedListener);
videoView.setOnErrorListener(videoViewErrorListener);
videoView.setVisibility(VideoView.VISIBLE);
videoView.postDelayed(videoRunnable, videoViewTimeOut);
videoView.start();
mVideoView = "true";
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(MainActivity.this);
SharedPreferences.Editor edit = pref.edit();
edit.putString("setLastStream", title+";"+mListViewChannels);
edit.apply();
}
private void setInfoView(String title) {
infoView.removeCallbacks(infoRunnable);
infoView.setText(title);
infoView.setVisibility(TextView.VISIBLE);
progressBar.setVisibility(ProgressBar.VISIBLE);
}
Just a note: I have payed for the streams and some are free to access for anyone, no illegal content.
I am using an android set top box with firmware 4.4.2
I have tried to add try { .. } finally { .. } everywhere and it still freezes sometimes at random times.
Please help, im new in making apps and this is really driving me crazy.
Sorry for my bad english.

Android Camera2 API RAW capture with flash

I tried to take flashed images and RAW with camera2 API and save JPEG and DNG files. When I make first shoot, the result image (jpeg) is black. On second shot everything is fine! I make photos in completely dark room. Flash fires for both shots. And each time flash fires twice (precapture and capture).
Also similar issue happens, when I try to capture just JPEG image (without RAW) and exposure is set to 0.
Here is code of creation of capture requests:
public static void CreateRequests(final int format) throws CameraAccessException
{
final boolean isRAWCapture = (format == CameraController.RAW);
stillRequestBuilder = HALv3.getInstance().camDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
precaptureRequestBuilder = HALv3.getInstance().camDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
rawRequestBuilder = HALv3.getInstance().camDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
if (format == CameraController.YUV_RAW)
{
stillRequestBuilder.set(CaptureRequest.EDGE_MODE, CaptureRequest.EDGE_MODE_OFF);
stillRequestBuilder.set(CaptureRequest.NOISE_REDUCTION_MODE, CaptureRequest.NOISE_REDUCTION_MODE_OFF);
precaptureRequestBuilder.set(CaptureRequest.EDGE_MODE, CaptureRequest.EDGE_MODE_OFF);
precaptureRequestBuilder.set(CaptureRequest.NOISE_REDUCTION_MODE, CaptureRequest.NOISE_REDUCTION_MODE_OFF);
} else if (isRAWCapture)
{
stillRequestBuilder.set(CaptureRequest.EDGE_MODE, CaptureRequest.EDGE_MODE_HIGH_QUALITY);
stillRequestBuilder.set(CaptureRequest.NOISE_REDUCTION_MODE,
CaptureRequest.NOISE_REDUCTION_MODE_HIGH_QUALITY);
precaptureRequestBuilder.set(CaptureRequest.EDGE_MODE, CaptureRequest.EDGE_MODE_HIGH_QUALITY);
precaptureRequestBuilder.set(CaptureRequest.NOISE_REDUCTION_MODE,
CaptureRequest.NOISE_REDUCTION_MODE_HIGH_QUALITY);
rawRequestBuilder.set(CaptureRequest.EDGE_MODE, CaptureRequest.EDGE_MODE_OFF);
rawRequestBuilder.set(CaptureRequest.NOISE_REDUCTION_MODE, CaptureRequest.NOISE_REDUCTION_MODE_OFF);
} else
{
stillRequestBuilder.set(CaptureRequest.EDGE_MODE, CaptureRequest.EDGE_MODE_HIGH_QUALITY);
stillRequestBuilder.set(CaptureRequest.NOISE_REDUCTION_MODE,
CaptureRequest.NOISE_REDUCTION_MODE_HIGH_QUALITY);
precaptureRequestBuilder.set(CaptureRequest.EDGE_MODE, CaptureRequest.EDGE_MODE_HIGH_QUALITY);
precaptureRequestBuilder.set(CaptureRequest.NOISE_REDUCTION_MODE,
CaptureRequest.NOISE_REDUCTION_MODE_HIGH_QUALITY);
}
stillRequestBuilder.set(CaptureRequest.TONEMAP_MODE, CaptureRequest.TONEMAP_MODE_HIGH_QUALITY);
precaptureRequestBuilder.set(CaptureRequest.TONEMAP_MODE, CaptureRequest.TONEMAP_MODE_HIGH_QUALITY);
if (isRAWCapture)
rawRequestBuilder.set(CaptureRequest.TONEMAP_MODE, CaptureRequest.TONEMAP_MODE_HIGH_QUALITY);
if ((zoomLevel > 1.0f) && (format != CameraController.YUV_RAW))
{
zoomCropCapture = getZoomRect(zoomLevel, activeRect.width(), activeRect.height());
stillRequestBuilder.set(CaptureRequest.SCALER_CROP_REGION, zoomCropCapture);
precaptureRequestBuilder.set(CaptureRequest.SCALER_CROP_REGION, zoomCropCapture);
if (isRAWCapture)
rawRequestBuilder.set(CaptureRequest.SCALER_CROP_REGION, zoomCropCapture);
}
int focusMode = PreferenceManager.getDefaultSharedPreferences(MainScreen.getMainContext()).getInt(
CameraController.isFrontCamera() ? MainScreen.sRearFocusModePref : MainScreen.sFrontFocusModePref, -1);
stillRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, focusMode);
precaptureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, focusMode);
if (isRAWCapture)
rawRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, focusMode);
if (format == CameraController.JPEG)
{
stillRequestBuilder.addTarget(MainScreen.getJPEGImageReader().getSurface());
} else if (format == CameraController.YUV || format == CameraController.YUV_RAW)
{
stillRequestBuilder.addTarget(MainScreen.getYUVImageReader().getSurface());
} else if (format == CameraController.RAW)
{
rawRequestBuilder.addTarget(MainScreen.getRAWImageReader().getSurface());
stillRequestBuilder.addTarget(MainScreen.getJPEGImageReader().getSurface());
}
precaptureRequestBuilder.addTarget(MainScreen.getPreviewYUVImageReader().getSurface());
int flashMode = PreferenceManager.getDefaultSharedPreferences(MainScreen.getMainContext()).getInt(
MainScreen.sFlashModePref, -1);
Integer previewFlash = null;
if (flashMode == CameraParameters.FLASH_MODE_OFF) {
previewFlash = CaptureRequest.FLASH_MODE_OFF;
} else if (flashMode == CameraParameters.FLASH_MODE_SINGLE) {
previewFlash = CaptureRequest.FLASH_MODE_SINGLE;
} else if (flashMode == CameraParameters.FLASH_MODE_TORCH) {
previewFlash = CaptureRequest.FLASH_MODE_TORCH;
}
HALv3.stillRequestBuilder.set(CaptureRequest.FLASH_MODE, previewFlash);
HALv3.precaptureRequestBuilder.set(CaptureRequest.FLASH_MODE, previewFlash);
HALv3.rawRequestBuilder.set(CaptureRequest.FLASH_MODE, previewFlash);
if(flashMode == CameraParameters.FLASH_MODE_SINGLE || flashMode == CameraParameters.FLASH_MODE_AUTO || flashMode == CameraParameters.FLASH_MODE_REDEYE)
{
if(flashMode == CameraParameters.FLASH_MODE_SINGLE)
flashMode = CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH;
else if(flashMode == CameraParameters.FLASH_MODE_AUTO )
flashMode = CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH;
else
flashMode = CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE;
HALv3.stillRequestBuilder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);
HALv3.stillRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, flashMode);
HALv3.precaptureRequestBuilder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);
HALv3.precaptureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, flashMode);
HALv3.rawRequestBuilder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);
HALv3.rawRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, flashMode);
}
}
and capture methods:
public static int captureImageWithParamsHALv3Simple(final int nFrames, final int format, final int[] pause,
final int[] evRequested, final int[] gain, final long[] exposure, final boolean resInHeap,
final boolean playShutter)
{
int requestID;
try
{
requestID = HALv3.getInstance().mCaptureSession.capture(stillRequestBuilder.build(), stillCaptureCallback,
null);
if (isRAWCapture)
HALv3.getInstance().mCaptureSession.capture(rawRequestBuilder.build(), stillCaptureCallback, null);
} catch (CameraAccessException e)
{
e.printStackTrace();
}
return 0;
}
public static int captureImageWithParamsHALv3(final int nFrames, final int format, final int[] pause,
final int[] evRequested, final int[] gain, final long[] exposure, final boolean resInHeap,
final boolean playShutter)
{
int requestID = -1;
try
{
CreateRequests(format);
precaptureRequestBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
requestID = HALv3.getInstance().mCaptureSession.capture(precaptureRequestBuilder.build(),
new CameraCaptureSession.CaptureCallback()
{
#Override
public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
TotalCaptureResult result)
{
precaptureRequestBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_IDLE);
captureImageWithParamsHALv3Simple(nFrames, format, pause, evRequested, gain, exposure,
resInHeap, playShutter);
}
}, null);
} catch (CameraAccessException e)
{
Log.e(TAG, "setting up still image capture request failed");
e.printStackTrace();
throw new RuntimeException();
}
return requestID;
}
enter code here
Also you may see full code of project here: https://github.com/almalence/OpenCamera
And camera2 part:
https://github.com/almalence/OpenCamera/blob/master/src/com/almalence/opencam/cameracontroller/HALv3.java

Android - decryption issues with AES encrypted string in QR code

I've built a QR code generator and a QR code scanner for passing data about phones and their users between phones (the phones are being loaned out so there will be a master phone with the scanner app and the rest with the generator app). The QR code generated is a JSON format string containing a persons name/number/imei of their phone but for security I have tried to encrypt the string before encoding to QR, but the scanned QR code throws up a 'pad block corrupted' error.
The JSON data encodes into QR/decodes from QR fine as plain text, and I checked the encryption/decryption before encoding to QR and the data encrypts/decrypts fine, so it's something to do with when the encrypted text is encoded into QR but I've no idea where to begin with it!
Does anyone know how i can sort the issue? Or if theres any QR friendly encryption methods?!!
I took the QRCodeEncoder straight from ZXings source and placed it into my activity:
/**QR ENCODER CLASS****************************************************/
public class QRCodeEncoder
{
private final String TAG = QRCodeEncoder.class.getSimpleName();
private static final int WHITE = 0xFFFFFFFF;
private static final int BLACK = 0xFF000000;
private final Activity activity;
private String contents;
private String displayContents;
private String title;
private BarcodeFormat format;
private final int dimension;
QRCodeEncoder(Activity activity, Intent intent, int dimension) {
this.activity = activity;
if (intent == null) {
throw new IllegalArgumentException("No valid data to encode. intent is null");
}
String action = intent.getAction();
if (action.equals(Intents.Encode.ACTION)) {
if (!encodeContentsFromZXingIntent(intent)) {
throw new IllegalArgumentException("No valid data to encode. Zxing intent returned false");
}
} else if (action.equals(Intent.ACTION_SEND)) {
if (!encodeContentsFromShareIntent(intent)) {
throw new IllegalArgumentException("No valid data to encode. Share Intent returned false");
}
}
this.dimension = dimension;
}
public String getContents() {
return contents;
}
public String getDisplayContents() {
return displayContents;
}
public String getTitle() {
return title;
}
// It would be nice if the string encoding lived in the core ZXing library,
// but we use platform specific code like PhoneNumberUtils, so it can't.
private boolean encodeContentsFromZXingIntent(Intent intent) {
// Default to QR_CODE if no format given.
String formatString = intent.getStringExtra(Intents.Encode.FORMAT);
try {
format = BarcodeFormat.valueOf(formatString);
} catch (IllegalArgumentException iae) {
// Ignore it then
format = null;
}
if (format == null || BarcodeFormat.QR_CODE.equals(format)) {
String type = intent.getStringExtra(Intents.Encode.TYPE);
if (type == null || type.length() == 0) {
return false;
}
this.format = BarcodeFormat.QR_CODE;
encodeQRCodeContents(intent, type);
} else {
String data = intent.getStringExtra(Intents.Encode.DATA);
if (data != null && data.length() > 0) {
contents = data;
displayContents = data;
title = "QR Encoder";
}
}
return contents != null && contents.length() > 0;
}
// Handles send intents from multitude of Android applications
private boolean encodeContentsFromShareIntent(Intent intent) {
// Check if this is a plain text encoding, or contact
if (intent.hasExtra(Intent.EXTRA_TEXT)) {
return encodeContentsFromShareIntentPlainText(intent);
}
// Attempt default sharing.
return encodeContentsFromShareIntentDefault(intent);
}
private boolean encodeContentsFromShareIntentPlainText(Intent intent) {
// Notice: Google Maps shares both URL and details in one text, bummer!
contents = intent.getStringExtra(Intent.EXTRA_TEXT);
Toast.makeText(getApplicationContext(),"contents read = "+contents,Toast.LENGTH_SHORT).show();
// We only support non-empty and non-blank texts.
// Trim text to avoid URL breaking.
if (contents == null) {
return false;
}
contents = contents.trim();
if (contents.length() == 0) {
return false;
}
// We only do QR code.
format = BarcodeFormat.QR_CODE;
if (intent.hasExtra(Intent.EXTRA_SUBJECT)) {
displayContents = intent.getStringExtra(Intent.EXTRA_SUBJECT);
} else if (intent.hasExtra(Intent.EXTRA_TITLE)) {
displayContents = intent.getStringExtra(Intent.EXTRA_TITLE);
} else {
displayContents = contents;
}
title = "QR Encoder";
return true;
}
// Handles send intents from the Contacts app, retrieving a contact as a VCARD.
// Note: Does not work on HTC devices due to broken custom Contacts application.
private boolean encodeContentsFromShareIntentDefault(Intent intent) {
format = BarcodeFormat.QR_CODE;
try {
Uri uri = (Uri)intent.getExtras().getParcelable(Intent.EXTRA_STREAM);
InputStream stream = activity.getContentResolver().openInputStream(uri);
int length = stream.available();
if (length <= 0) {
Log.w(TAG, "Content stream is empty");
return false;
}
byte[] vcard = new byte[length];
int bytesRead = stream.read(vcard, 0, length);
if (bytesRead < length) {
Log.w(TAG, "Unable to fully read available bytes from content stream");
return false;
}
String vcardString = new String(vcard, 0, bytesRead, "UTF-8");
Log.d(TAG, "Encoding share intent content:");
Log.d(TAG, vcardString);
Result result = new Result(vcardString, vcard, null, BarcodeFormat.QR_CODE);
ParsedResult parsedResult = ResultParser.parseResult(result);
if (!(parsedResult instanceof AddressBookParsedResult)) {
Log.d(TAG, "Result was not an address");
return false;
}
if (!encodeQRCodeContents((AddressBookParsedResult) parsedResult)) {
Log.d(TAG, "Unable to encode contents");
return false;
}
} catch (IOException e) {
Log.w(TAG, e);
return false;
} catch (NullPointerException e) {
Log.w(TAG, e);
// In case the uri was not found in the Intent.
return false;
}
return contents != null && contents.length() > 0;
}
private void encodeQRCodeContents(Intent intent, String type) {
if (type.equals(Contents.Type.TEXT)) {
String data = intent.getStringExtra(Intents.Encode.DATA);
if (data != null && data.length() > 0) {
contents = data;
displayContents = data;
title = "QR Encoder";
}
} else if (type.equals(Contents.Type.EMAIL)) {
String data = trim(intent.getStringExtra(Intents.Encode.DATA));
if (data != null) {
contents = "mailto:" + data;
displayContents = data;
title = "QR Encoder";
}
} else if (type.equals(Contents.Type.PHONE)) {
String data = trim(intent.getStringExtra(Intents.Encode.DATA));
if (data != null) {
contents = "tel:" + data;
displayContents = PhoneNumberUtils.formatNumber(data);
title = "QR Encoder";
}
} else if (type.equals(Contents.Type.SMS)) {
String data = trim(intent.getStringExtra(Intents.Encode.DATA));
if (data != null) {
contents = "sms:" + data;
displayContents = PhoneNumberUtils.formatNumber(data);
title = "QR Encoder";
}
} else if (type.equals(Contents.Type.CONTACT)) {
Bundle bundle = intent.getBundleExtra(Intents.Encode.DATA);
if (bundle != null) {
StringBuilder newContents = new StringBuilder(100);
StringBuilder newDisplayContents = new StringBuilder(100);
newContents.append("MECARD:");
String name = trim(bundle.getString(Contacts.Intents.Insert.NAME));
if (name != null) {
newContents.append("N:").append(escapeMECARD(name)).append(';');
newDisplayContents.append(name);
}
String address = trim(bundle.getString(Contacts.Intents.Insert.POSTAL));
if (address != null) {
newContents.append("ADR:").append(escapeMECARD(address)).append(';');
newDisplayContents.append('\n').append(address);
}
for (int x = 0; x < Contents.PHONE_KEYS.length; x++) {
String phone = trim(bundle.getString(Contents.PHONE_KEYS[x]));
if (phone != null) {
newContents.append("TEL:").append(escapeMECARD(phone)).append(';');
newDisplayContents.append('\n').append(PhoneNumberUtils.formatNumber(phone));
}
}
for (int x = 0; x < Contents.EMAIL_KEYS.length; x++) {
String email = trim(bundle.getString(Contents.EMAIL_KEYS[x]));
if (email != null) {
newContents.append("EMAIL:").append(escapeMECARD(email)).append(';');
newDisplayContents.append('\n').append(email);
}
}
// Make sure we've encoded at least one field.
if (newDisplayContents.length() > 0) {
newContents.append(';');
contents = newContents.toString();
displayContents = newDisplayContents.toString();
title = "QR Encoder";
} else {
contents = null;
displayContents = null;
}
}
} else if (type.equals(Contents.Type.LOCATION)) {
Bundle bundle = intent.getBundleExtra(Intents.Encode.DATA);
if (bundle != null) {
// These must use Bundle.getFloat(), not getDouble(), it's part of the API.
float latitude = bundle.getFloat("LAT", Float.MAX_VALUE);
float longitude = bundle.getFloat("LONG", Float.MAX_VALUE);
if (latitude != Float.MAX_VALUE && longitude != Float.MAX_VALUE) {
contents = "geo:" + latitude + ',' + longitude;
displayContents = latitude + "," + longitude;
title = "QR Encoder";
}
}
}
}
private boolean encodeQRCodeContents(AddressBookParsedResult contact) {
StringBuilder newContents = new StringBuilder(100);
StringBuilder newDisplayContents = new StringBuilder(100);
newContents.append("MECARD:");
String[] names = contact.getNames();
if (names != null && names.length > 0) {
String name = trim(names[0]);
if (name != null) {
newContents.append("N:").append(escapeMECARD(name)).append(';');
newDisplayContents.append(name);
}
}
String[] addresses = contact.getAddresses();
if (addresses != null) {
for (String address : addresses) {
address = trim(address);
if (address != null) {
newContents.append("ADR:").append(escapeMECARD(address)).append(';');
newDisplayContents.append('\n').append(address);
}
}
}
String[] phoneNumbers = contact.getPhoneNumbers();
if (phoneNumbers != null) {
for (String phone : phoneNumbers) {
phone = trim(phone);
if (phone != null) {
newContents.append("TEL:").append(escapeMECARD(phone)).append(';');
newDisplayContents.append('\n').append(PhoneNumberUtils.formatNumber(phone));
}
}
}
String[] emails = contact.getEmails();
if (emails != null) {
for (String email : emails) {
email = trim(email);
if (email != null) {
newContents.append("EMAIL:").append(escapeMECARD(email)).append(';');
newDisplayContents.append('\n').append(email);
}
}
}
String url = trim(contact.getURL());
if (url != null) {
newContents.append("URL:").append(escapeMECARD(url)).append(';');
newDisplayContents.append('\n').append(url);
}
// Make sure we've encoded at least one field.
if (newDisplayContents.length() > 0) {
newContents.append(';');
contents = newContents.toString();
displayContents = newDisplayContents.toString();
title = "QR Encoder";
return true;
} else {
contents = null;
displayContents = null;
return false;
}
}
Bitmap encodeAsBitmap() throws WriterException {
Hashtable<EncodeHintType,Object> hints = null;
String encoding = guessAppropriateEncoding(contents);
if (encoding != null) {
hints = new Hashtable<EncodeHintType,Object>(2);
hints.put(EncodeHintType.CHARACTER_SET, encoding);
}
MultiFormatWriter writer = new MultiFormatWriter();
BitMatrix result = writer.encode(contents, format, dimension, dimension, hints);
int width = result.getWidth();
int height = result.getHeight();
int[] pixels = new int[width * height];
// All are 0, or black, by default
for (int y = 0; y < height; y++) {
int offset = y * width;
for (int x = 0; x < width; x++) {
pixels[offset + x] = result.get(x, y) ? BLACK : WHITE;
}
}
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
return bitmap;
}
private String guessAppropriateEncoding(CharSequence contents) {
// Very crude at the moment
for (int i = 0; i < contents.length(); i++) {
if (contents.charAt(i) > 0xFF) {
return "UTF-8";
}
}
return null;
}
private String trim(String s) {
if (s == null) {
return null;
}
s = s.trim();
return s.length() == 0 ? null : s;
}
private String escapeMECARD(String input) {
if (input == null || (input.indexOf(':') < 0 && input.indexOf(';') < 0)) {
return input;
}
int length = input.length();
StringBuilder result = new StringBuilder(length);
for (int i = 0; i < length; i++) {
char c = input.charAt(i);
if (c == ':' || c == ';') {
result.append('\\');
}
result.append(c);
}
return result.toString();
}
}
And the encryption/decryption class from this website (unedited)
Here's a snippet of the onCreate() method in my activity:
QRCodeEncoder myQRCodeEncoder;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.qr_view);
ImageView imageView = (ImageView)findViewById(R.id.qr_image);
extStorageDirectory = Environment.getExternalStorageDirectory().toString();
try
{
//JSON data is passed from another activity to this one
qrMessage = getIntent().getStringExtra("QR_JSON");
Intent encode = new Intent(Intents.Encode.ACTION);
encode.putExtra(Intents.Encode.TYPE, Contents.Type.TEXT);
encode.putExtra(Intents.Encode.FORMAT, "QR_CODE");
//This is the original plain text way that works:
//encode.putExtra(Intents.Encode.DATA, qrMessage);
//This is the encyption way
String encMessage = SimpleCrypto.encrypt("my s3cr3t k3y", qrMessage);
encode.putExtra(Intents.Encode.DATA,encMessage);
myQRCodeEncoder = new QRCodeEncoder(this, encode, 200);
}
catch(Exception e)
{
Toast.makeText(getApplicationContext(),"Could not encode:"+e.getMessage(),Toast.LENGTH_SHORT).show();
}
catch(Error e)
{
Toast.makeText(getApplicationContext(),"Could not encode:"+e.getMessage(),Toast.LENGTH_SHORT).show();
}
try {
Bitmap qrBitmap = myQRCodeEncoder.encodeAsBitmap();
imageView.setImageBitmap(qrBitmap);
} catch (Exception e) {
Toast.makeText(getApplicationContext(),"Could not set image:"+e.getMessage(),Toast.LENGTH_SHORT).show();
}
}
And here's the onActivityResult method from the scanner (I use ZXing's barcode scanner to retrieve the data)
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
if (requestCode == 0) {
if (resultCode == RESULT_OK) {
String contents = intent.getStringExtra("SCAN_RESULT");//contents of the scan
String format = intent.getStringExtra("SCAN_RESULT_FORMAT");
// Handle successful scan
/* display the scanned persons info*/
try {
String decryptedcontents = SimpleCrypto.decrypt("my s3cr3t k3y",contents);
String result = getJSONFromScanData(decryptedcontents);
} catch (Exception e) {
// TODO Auto-generated catch block
Toast.makeText(this, "Scanned data could not be decrypted:"+e.getMessage(), Toast.LENGTH_SHORT).show();//says 'pad block corrupted' as the message
}
} else if (resultCode == RESULT_CANCELED) {
// Handle cancel
Toast.makeText(this, "Scan cancelled", Toast.LENGTH_SHORT).show();
}
}
}
EDIT: after some further investigation it seems that the encyption/decyption process seems to 'shave off' part of the data:
JSONObject example = new JSONObject("{\"user_firstname\":\"Ben\",\"user_lastname\":\" Ten\",\"user_login\":\"benten\",\"user_pass\":\"password\",\"user_email\":\"benten#domain.com\"}");
String mess = SimpleCrypto.encrypt("my s3cr3t k3y",example.toString());
String decrmess = SimpleCrypto.decrypt("my s3cr3t k3y",mess));
//decypts as:{"user_pass":"password","user_email":"benten#domain.com","user_login":"benten","user_lastname":"
as you can see only 96 characters are decrypted, theres no user_firstname or the users actual last name, the data is missing, but this number is inconsistent, I changed the user_email to "benbenten#domain.com" and the user_firstname to "benben" and 112 characters were decrypted...I am completely stumped
EDIT 2: Yngve Ã…dlandsvik has kindly pointed me in the right direction (many thanks again!) that the string length needed to be a multiple of 16, so I set the Cipher.getInstance in both the encrypt and decrypt methods to:
Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding","BC");
and in my main activity set a loop to add 0's on the end of my string as custom padding before encrypting:
boolean carryOn = true;
while(carryOn)
{
int paddedLength = qrMessage.getBytes().length;
int checkMultiple16 = paddedLength%16;
if(checkMultiple16==0)
{
carryOn = false;
}
else
qrMessage+="0";
}
EDIT 3: It looks like QR encoding still screws with the encryption, I can't decrypt the scanned in data properly, looks like QR encoding does something with strings before it encodes to QR which seems to break the thing, guess I'll have to stick to unencrypted text in the QR...
I haven't read the code closely, but I assume this happens because AES only operates on blocks of 16 bytes at once. So my guess is you need to manually apply some form of reversible padding to your string before encryption so it becomes a multiple of 16, and then reverse the padding after decryption.
You could also change the Cipher.getInstance() strings in the crypto code so the encryption will support padding natively, though I don't know which padding types and cipher modes are available on Android.

Categories

Resources