diff --git a/app/src/main/java/xdsopl/robot36/Decoder.java b/app/src/main/java/xdsopl/robot36/Decoder.java new file mode 100644 index 0000000..9291587 --- /dev/null +++ b/app/src/main/java/xdsopl/robot36/Decoder.java @@ -0,0 +1,235 @@ +/* + Copyright 2015 Ahmet Inan + + 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)); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/xdsopl/robot36/ImageView.java b/app/src/main/java/xdsopl/robot36/ImageView.java index 0195963..a60395d 100644 --- a/app/src/main/java/xdsopl/robot36/ImageView.java +++ b/app/src/main/java/xdsopl/robot36/ImageView.java @@ -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)); - } - } } diff --git a/app/src/main/java/xdsopl/robot36/MainActivity.java b/app/src/main/java/xdsopl/robot36/MainActivity.java index 59afd27..0195236 100644 --- a/app/src/main/java/xdsopl/robot36/MainActivity.java +++ b/app/src/main/java/xdsopl/robot36/MainActivity.java @@ -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); } } diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 8a36e16..99de13b 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,15 +1,28 @@ - + tools:context=".MainActivity" + android:orientation="vertical"> - + android:id="@+id/image" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_weight="1" + android:contentDescription="@string/decoder_view" /> + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 522ef17..2b27f91 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -17,5 +17,7 @@ Softer Image Sharper Image Toggle Scaling + Decoder View + Spectrum View