split decoder code into Decoder class, made space for spectrum analyzer

pull/6/head
Ahmet Inan 2015-01-06 14:50:43 +01:00
rodzic 364b8f2209
commit eca5873fec
5 zmienionych plików z 323 dodań i 257 usunięć

Wyświetl plik

@ -0,0 +1,235 @@
/*
Copyright 2015 Ahmet Inan <xdsopl@googlemail.com>
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 xdsopl.robot36;
import android.graphics.Bitmap;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.support.v8.renderscript.Allocation;
import android.support.v8.renderscript.Element;
import android.support.v8.renderscript.RenderScript;
public class Decoder {
private boolean drawImage = true, quitThread = false;
private final MainActivity activity;
private final ImageView view;
private final AudioRecord audio;
private final int audioSource = MediaRecorder.AudioSource.MIC;
private final int channelConfig = AudioFormat.CHANNEL_IN_MONO;
private final int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
private final int sampleRate = 44100;
private final short[] audioBuffer;
private final int[] pixelBuffer;
private final int[] currentMode;
private final int[] savedBuffer;
private final int[] savedWidth;
private final int[] savedHeight;
private final RenderScript rs;
private final Allocation rsDecoderAudioBuffer;
private final Allocation rsDecoderPixelBuffer;
private final Allocation rsDecoderValueBuffer;
private final Allocation rsDecoderCurrentMode;
private final Allocation rsDecoderSavedBuffer;
private final Allocation rsDecoderSavedWidth;
private final Allocation rsDecoderSavedHeight;
private final ScriptC_decoder rsDecoder;
private final int mode_raw = 0;
private final int mode_robot36 = 1;
private final int mode_robot72 = 2;
private final int mode_martin1 = 3;
private final int mode_martin2 = 4;
private final int mode_scottie1 = 5;
private final int mode_scottie2 = 6;
private final int mode_scottieDX = 7;
private final int mode_wrasseSC2_180 = 8;
private final Thread thread = new Thread() {
@Override
public void run() {
while (true) {
synchronized (this) {
if (quitThread)
return;
if (drawImage)
view.drawCanvas();
}
decode();
}
}
};
public Decoder(ImageView view, MainActivity activity) {
this.view = view;
this.activity = activity;
pixelBuffer = new int[view.bitmap.getWidth() * view.bitmap.getHeight()];
int bufferSizeInBytes = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);
int bufferSizeInSamples = bufferSizeInBytes / 2;
int framesPerSecond = Math.max(1, sampleRate / bufferSizeInSamples);
audioBuffer = new short[framesPerSecond * bufferSizeInSamples];
audio = new AudioRecord(audioSource, sampleRate, channelConfig, audioFormat, audioBuffer.length * 2);
audio.startRecording();
int minValueBufferLength = 2 * sampleRate;
int valueBufferLength = Integer.highestOneBit(minValueBufferLength);
if (minValueBufferLength > valueBufferLength)
valueBufferLength <<= 1;
currentMode = new int[1];
savedWidth = new int[1];
savedHeight = new int[1];
savedBuffer = new int[pixelBuffer.length];
rs = RenderScript.create(activity.getApplicationContext());
rsDecoderAudioBuffer = Allocation.createSized(rs, Element.I16(rs), audioBuffer.length, Allocation.USAGE_SHARED | Allocation.USAGE_SCRIPT);
rsDecoderValueBuffer = Allocation.createSized(rs, Element.U8(rs), valueBufferLength, Allocation.USAGE_SCRIPT);
rsDecoderPixelBuffer = Allocation.createSized(rs, Element.I32(rs), pixelBuffer.length, Allocation.USAGE_SHARED | Allocation.USAGE_SCRIPT);
rsDecoderCurrentMode = Allocation.createSized(rs, Element.I32(rs), 1, Allocation.USAGE_SHARED | Allocation.USAGE_SCRIPT);
rsDecoderSavedWidth = Allocation.createSized(rs, Element.I32(rs), 1, Allocation.USAGE_SHARED | Allocation.USAGE_SCRIPT);
rsDecoderSavedHeight = Allocation.createSized(rs, Element.I32(rs), 1, Allocation.USAGE_SHARED | Allocation.USAGE_SCRIPT);
rsDecoderSavedBuffer = Allocation.createSized(rs, Element.I32(rs), savedBuffer.length, Allocation.USAGE_SHARED | Allocation.USAGE_SCRIPT);
rsDecoder = new ScriptC_decoder(rs);
rsDecoder.bind_audio_buffer(rsDecoderAudioBuffer);
rsDecoder.bind_value_buffer(rsDecoderValueBuffer);
rsDecoder.bind_pixel_buffer(rsDecoderPixelBuffer);
rsDecoder.bind_current_mode(rsDecoderCurrentMode);
rsDecoder.bind_saved_width(rsDecoderSavedWidth);
rsDecoder.bind_saved_height(rsDecoderSavedHeight);
rsDecoder.bind_saved_buffer(rsDecoderSavedBuffer);
rsDecoder.invoke_initialize(sampleRate, valueBufferLength, view.bitmap.getWidth(), view.bitmap.getHeight());
thread.start();
}
void toggle_scaling() { view.intScale ^= true; }
void softer_image() { rsDecoder.invoke_incr_blur(); }
void sharper_image() { rsDecoder.invoke_decr_blur(); }
void toggle_debug() { rsDecoder.invoke_toggle_debug(); }
void toggle_auto() { rsDecoder.invoke_toggle_auto(); }
void raw_mode() { rsDecoder.invoke_raw_mode(); }
void robot36_mode() { rsDecoder.invoke_robot36_mode(); }
void robot72_mode() { rsDecoder.invoke_robot72_mode(); }
void martin1_mode() { rsDecoder.invoke_martin1_mode(); }
void martin2_mode() { rsDecoder.invoke_martin2_mode(); }
void scottie1_mode() { rsDecoder.invoke_scottie1_mode(); }
void scottie2_mode() { rsDecoder.invoke_scottie2_mode(); }
void scottieDX_mode() { rsDecoder.invoke_scottieDX_mode(); }
void wrasseSC2_180_mode() { rsDecoder.invoke_wrasseSC2_180_mode(); }
void updateTitle(int id) { activity.updateTitle(activity.getString(id)); }
void switch_mode(int mode)
{
switch (mode) {
case mode_raw:
view.imageWidth = view.bitmap.getWidth();
view.imageHeight = view.bitmap.getHeight();
updateTitle(R.string.action_raw_mode);
break;
case mode_robot36:
view.imageWidth = 320;
view.imageHeight = 240;
updateTitle(R.string.action_robot36_mode);
break;
case mode_robot72:
view.imageWidth = 320;
view.imageHeight = 240;
updateTitle(R.string.action_robot72_mode);
break;
case mode_martin1:
view.imageWidth = 320;
view.imageHeight = 256;
updateTitle(R.string.action_martin1_mode);
break;
case mode_martin2:
view.imageWidth = 320;
view.imageHeight = 256;
updateTitle(R.string.action_martin2_mode);
break;
case mode_scottie1:
view.imageWidth = 320;
view.imageHeight = 256;
updateTitle(R.string.action_scottie1_mode);
break;
case mode_scottie2:
view.imageWidth = 320;
view.imageHeight = 256;
updateTitle(R.string.action_scottie2_mode);
break;
case mode_scottieDX:
view.imageWidth = 320;
view.imageHeight = 256;
updateTitle(R.string.action_scottieDX_mode);
break;
case mode_wrasseSC2_180:
view.imageWidth = 320;
view.imageHeight = 256;
updateTitle(R.string.action_wrasseSC2_180_mode);
break;
default:
break;
}
}
void pause() {
synchronized (thread) {
drawImage = false;
}
}
void resume() {
synchronized (thread) {
drawImage = true;
}
}
void destroy() {
synchronized (thread) {
quitThread = true;
}
try {
thread.join();
} catch (InterruptedException ignore) {
}
audio.stop();
audio.release();
}
void decode() {
int samples = audio.read(audioBuffer, 0, audioBuffer.length);
if (samples <= 0)
return;
rsDecoderAudioBuffer.copyFrom(audioBuffer);
rsDecoder.invoke_decode(samples);
rsDecoderPixelBuffer.copyTo(pixelBuffer);
view.bitmap.setPixels(pixelBuffer, 0, view.bitmap.getWidth(), 0, 0, view.bitmap.getWidth(), view.bitmap.getHeight());
rsDecoderCurrentMode.copyTo(currentMode);
switch_mode(currentMode[0]);
rsDecoderSavedHeight.copyTo(savedHeight);
if (savedHeight[0] > 0) {
rsDecoderSavedWidth.copyTo(savedWidth);
rsDecoderSavedBuffer.copyTo(savedBuffer);
activity.storeBitmap(Bitmap.createBitmap(savedBuffer, savedWidth[0], savedHeight[0], Bitmap.Config.ARGB_8888));
}
}
}

Wyświetl plik

@ -25,68 +25,16 @@ import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.support.v8.renderscript.Allocation;
import android.support.v8.renderscript.Element;
import android.support.v8.renderscript.RenderScript;
public class ImageView extends SurfaceView implements SurfaceHolder.Callback {
private int canvasWidth = -1, canvasHeight = -1;
private boolean takeABreak = true, cantTouchThis = true, quitThread = false;
private int imageWidth = 320;
private int imageHeight = 240;
private boolean intScale = false;
MainActivity activity;
private boolean cantTouchThis = true;
public int imageWidth = 320;
public int imageHeight = 240;
public boolean intScale = false;
private final SurfaceHolder holder;
private final Bitmap bitmap;
public final Bitmap bitmap;
private final Paint paint;
private final AudioRecord audio;
private final int audioSource = MediaRecorder.AudioSource.MIC;
private final int channelConfig = AudioFormat.CHANNEL_IN_MONO;
private final int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
private final int sampleRate = 44100;
private final short[] audioBuffer;
private final int[] pixelBuffer;
private final int[] currentMode;
private final int[] savedBuffer;
private final int[] savedWidth;
private final int[] savedHeight;
private final RenderScript rs;
private final Allocation rsDecoderAudioBuffer;
private final Allocation rsDecoderPixelBuffer;
private final Allocation rsDecoderValueBuffer;
private final Allocation rsDecoderCurrentMode;
private final Allocation rsDecoderSavedBuffer;
private final Allocation rsDecoderSavedWidth;
private final Allocation rsDecoderSavedHeight;
private final ScriptC_decoder rsDecoder;
private final int mode_raw = 0;
private final int mode_robot36 = 1;
private final int mode_robot72 = 2;
private final int mode_martin1 = 3;
private final int mode_martin2 = 4;
private final int mode_scottie1 = 5;
private final int mode_scottie2 = 6;
private final int mode_scottieDX = 7;
private final int mode_wrasseSC2_180 = 8;
private final Thread thread = new Thread() {
@Override
public void run() {
while (true) {
synchronized (this) {
if (quitThread)
return;
drawCanvas();
}
decode();
}
}
};
public ImageView(Context context, AttributeSet attrs) {
super(context, attrs);
@ -95,163 +43,40 @@ public class ImageView extends SurfaceView implements SurfaceHolder.Callback {
paint = new Paint(Paint.FILTER_BITMAP_FLAG);
bitmap = Bitmap.createBitmap(320, 384, Bitmap.Config.ARGB_8888);
pixelBuffer = new int[bitmap.getWidth() * bitmap.getHeight()];
int bufferSizeInBytes = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);
int bufferSizeInSamples = bufferSizeInBytes / 2;
int framesPerSecond = Math.max(1, sampleRate / bufferSizeInSamples);
audioBuffer = new short[framesPerSecond * bufferSizeInSamples];
audio = new AudioRecord(audioSource, sampleRate, channelConfig, audioFormat, audioBuffer.length * 2);
audio.startRecording();
int minValueBufferLength = 2 * sampleRate;
int valueBufferLength = Integer.highestOneBit(minValueBufferLength);
if (minValueBufferLength > valueBufferLength)
valueBufferLength <<= 1;
currentMode = new int[1];
savedWidth = new int[1];
savedHeight = new int[1];
savedBuffer = new int[pixelBuffer.length];
rs = RenderScript.create(context);
rsDecoderAudioBuffer = Allocation.createSized(rs, Element.I16(rs), audioBuffer.length, Allocation.USAGE_SHARED | Allocation.USAGE_SCRIPT);
rsDecoderValueBuffer = Allocation.createSized(rs, Element.U8(rs), valueBufferLength, Allocation.USAGE_SCRIPT);
rsDecoderPixelBuffer = Allocation.createSized(rs, Element.I32(rs), pixelBuffer.length, Allocation.USAGE_SHARED | Allocation.USAGE_SCRIPT);
rsDecoderCurrentMode = Allocation.createSized(rs, Element.I32(rs), 1, Allocation.USAGE_SHARED | Allocation.USAGE_SCRIPT);
rsDecoderSavedWidth = Allocation.createSized(rs, Element.I32(rs), 1, Allocation.USAGE_SHARED | Allocation.USAGE_SCRIPT);
rsDecoderSavedHeight = Allocation.createSized(rs, Element.I32(rs), 1, Allocation.USAGE_SHARED | Allocation.USAGE_SCRIPT);
rsDecoderSavedBuffer = Allocation.createSized(rs, Element.I32(rs), savedBuffer.length, Allocation.USAGE_SHARED | Allocation.USAGE_SCRIPT);
rsDecoder = new ScriptC_decoder(rs);
rsDecoder.bind_audio_buffer(rsDecoderAudioBuffer);
rsDecoder.bind_value_buffer(rsDecoderValueBuffer);
rsDecoder.bind_pixel_buffer(rsDecoderPixelBuffer);
rsDecoder.bind_current_mode(rsDecoderCurrentMode);
rsDecoder.bind_saved_width(rsDecoderSavedWidth);
rsDecoder.bind_saved_height(rsDecoderSavedHeight);
rsDecoder.bind_saved_buffer(rsDecoderSavedBuffer);
rsDecoder.invoke_initialize(sampleRate, valueBufferLength, bitmap.getWidth(), bitmap.getHeight());
thread.start();
}
void toggle_scaling() { intScale ^= true; }
void softer_image() { rsDecoder.invoke_incr_blur(); }
void sharper_image() { rsDecoder.invoke_decr_blur(); }
void toggle_debug() { rsDecoder.invoke_toggle_debug(); }
void toggle_auto() { rsDecoder.invoke_toggle_auto(); }
void raw_mode() { rsDecoder.invoke_raw_mode(); }
void robot36_mode() { rsDecoder.invoke_robot36_mode(); }
void robot72_mode() { rsDecoder.invoke_robot72_mode(); }
void martin1_mode() { rsDecoder.invoke_martin1_mode(); }
void martin2_mode() { rsDecoder.invoke_martin2_mode(); }
void scottie1_mode() { rsDecoder.invoke_scottie1_mode(); }
void scottie2_mode() { rsDecoder.invoke_scottie2_mode(); }
void scottieDX_mode() { rsDecoder.invoke_scottieDX_mode(); }
void wrasseSC2_180_mode() { rsDecoder.invoke_wrasseSC2_180_mode(); }
void updateTitle(int id) { activity.updateTitle(activity.getString(id)); }
void switch_mode(int mode)
{
switch (mode) {
case mode_raw:
imageWidth = bitmap.getWidth();
imageHeight = bitmap.getHeight();
updateTitle(R.string.action_raw_mode);
break;
case mode_robot36:
imageWidth = 320;
imageHeight = 240;
updateTitle(R.string.action_robot36_mode);
break;
case mode_robot72:
imageWidth = 320;
imageHeight = 240;
updateTitle(R.string.action_robot72_mode);
break;
case mode_martin1:
imageWidth = 320;
imageHeight = 256;
updateTitle(R.string.action_martin1_mode);
break;
case mode_martin2:
imageWidth = 320;
imageHeight = 256;
updateTitle(R.string.action_martin2_mode);
break;
case mode_scottie1:
imageWidth = 320;
imageHeight = 256;
updateTitle(R.string.action_scottie1_mode);
break;
case mode_scottie2:
imageWidth = 320;
imageHeight = 256;
updateTitle(R.string.action_scottie2_mode);
break;
case mode_scottieDX:
imageWidth = 320;
imageHeight = 256;
updateTitle(R.string.action_scottieDX_mode);
break;
case mode_wrasseSC2_180:
imageWidth = 320;
imageHeight = 256;
updateTitle(R.string.action_wrasseSC2_180_mode);
break;
default:
break;
}
}
void pause() {
synchronized (thread) {
takeABreak = true;
}
}
void resume() {
synchronized (thread) {
takeABreak = false;
}
}
void destroy() {
synchronized (thread) {
quitThread = true;
}
try {
thread.join();
} catch (InterruptedException ignore) {
}
audio.stop();
audio.release();
}
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
synchronized (thread) {
synchronized (holder) {
canvasWidth = width;
canvasHeight = height;
drawCanvas();
}
drawCanvas();
}
public void surfaceCreated(SurfaceHolder holder) {
synchronized (thread) {
synchronized (holder) {
cantTouchThis = false;
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
synchronized (thread) {
synchronized (holder) {
cantTouchThis = true;
}
}
void drawCanvas() {
if (cantTouchThis || takeABreak)
return;
Canvas canvas = null;
try {
canvas = holder.lockCanvas(null);
drawBitmap(canvas);
} finally {
if (canvas != null)
holder.unlockCanvasAndPost(canvas);
synchronized (holder) {
if (cantTouchThis)
return;
Canvas canvas = null;
try {
canvas = holder.lockCanvas(null);
drawBitmap(canvas);
} finally {
if (canvas != null)
holder.unlockCanvasAndPost(canvas);
}
}
}
@ -276,24 +101,4 @@ public class ImageView extends SurfaceView implements SurfaceHolder.Callback {
canvas.drawBitmap(bitmap, px, py, paint);
canvas.restore();
}
void decode() {
int samples = audio.read(audioBuffer, 0, audioBuffer.length);
if (samples <= 0)
return;
rsDecoderAudioBuffer.copyFrom(audioBuffer);
rsDecoder.invoke_decode(samples);
rsDecoderPixelBuffer.copyTo(pixelBuffer);
bitmap.setPixels(pixelBuffer, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
rsDecoderCurrentMode.copyTo(currentMode);
switch_mode(currentMode[0]);
rsDecoderSavedHeight.copyTo(savedHeight);
if (savedHeight[0] > 0) {
rsDecoderSavedWidth.copyTo(savedWidth);
rsDecoderSavedBuffer.copyTo(savedBuffer);
activity.storeBitmap(Bitmap.createBitmap(savedBuffer, savedWidth[0], savedHeight[0], Bitmap.Config.ARGB_8888));
}
}
}

Wyświetl plik

@ -22,6 +22,7 @@ import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
@ -30,6 +31,8 @@ import android.os.Environment;
import android.support.v4.app.NotificationCompat;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.LinearLayout;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
@ -37,7 +40,7 @@ import java.text.SimpleDateFormat;
import java.util.Date;
public class MainActivity extends Activity {
private ImageView view;
private Decoder decoder;
private Bitmap bitmap;
private NotificationManager manager;
private int notifyID = 1;
@ -101,91 +104,99 @@ public class MainActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
view = (ImageView)findViewById(R.id.image);
view.activity = this;
changeLayoutOrientation(getResources().getConfiguration());
decoder = new Decoder((ImageView)findViewById(R.id.image), this);
manager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
showNotification();
}
@Override
protected void onDestroy () {
view.destroy();
decoder.destroy();
manager.cancel(notifyID);
super.onDestroy();
}
@Override
protected void onPause() {
view.pause();
decoder.pause();
super.onPause();
}
@Override
protected void onResume() {
view.resume();
decoder.resume();
super.onResume();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
public void onConfigurationChanged(Configuration config) {
super.onConfigurationChanged(config);
changeLayoutOrientation(config);
}
//noinspection SimplifiableIfStatement
switch (id) {
private void changeLayoutOrientation(Configuration config) {
boolean horizontal = config.orientation == Configuration.ORIENTATION_LANDSCAPE;
findViewById(R.id.spectrum).setLayoutParams(
new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT, horizontal ? 1.0f : 10.0f));
int orientation = horizontal ? LinearLayout.HORIZONTAL : LinearLayout.VERTICAL;
((LinearLayout)findViewById(R.id.content)).setOrientation(orientation);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_softer_image:
view.softer_image();
decoder.softer_image();
return true;
case R.id.action_sharper_image:
view.sharper_image();
decoder.sharper_image();
return true;
case R.id.action_toggle_scaling:
view.toggle_scaling();
decoder.toggle_scaling();
return true;
case R.id.action_toggle_debug:
view.toggle_debug();
decoder.toggle_debug();
return true;
case R.id.action_toggle_auto:
view.toggle_auto();
decoder.toggle_auto();
return true;
case R.id.action_raw_mode:
view.raw_mode();
decoder.raw_mode();
return true;
case R.id.action_robot36_mode:
view.robot36_mode();
decoder.robot36_mode();
return true;
case R.id.action_robot72_mode:
view.robot72_mode();
decoder.robot72_mode();
return true;
case R.id.action_martin1_mode:
view.martin1_mode();
decoder.martin1_mode();
return true;
case R.id.action_martin2_mode:
view.martin2_mode();
decoder.martin2_mode();
return true;
case R.id.action_scottie1_mode:
view.scottie1_mode();
decoder.scottie1_mode();
return true;
case R.id.action_scottie2_mode:
view.scottie2_mode();
decoder.scottie2_mode();
return true;
case R.id.action_scottieDX_mode:
view.scottieDX_mode();
decoder.scottieDX_mode();
return true;
case R.id.action_wrasseSC2_180_mode:
view.wrasseSC2_180_mode();
decoder.wrasseSC2_180_mode();
return true;
}
return super.onOptionsItemSelected(item);
}
}

Wyświetl plik

@ -1,15 +1,28 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">
tools:context=".MainActivity"
android:orientation="vertical">
<xdsopl.robot36.ImageView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:id="@+id/image" />
</RelativeLayout>
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:contentDescription="@string/decoder_view" />
<ImageView
android:id="@+id/spectrum"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="10"
android:contentDescription="@string/spectrum_view" />
</LinearLayout>

Wyświetl plik

@ -17,5 +17,7 @@
<string name="action_softer_image">Softer Image</string>
<string name="action_sharper_image">Sharper Image</string>
<string name="action_toggle_scaling">Toggle Scaling</string>
<string name="decoder_view">Decoder View</string>
<string name="spectrum_view">Spectrum View</string>
</resources>