diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c6cbe56 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..ad59d57 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +Tubes1-Android \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..96cc43e --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml new file mode 100644 index 0000000..e7bedf3 --- /dev/null +++ b/.idea/copyright/profiles_settings.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..39139a6 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..7158618 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1.8 + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..b0ce46e --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 0000000..7f68460 --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index 5ce4793..b92c336 100644 --- a/README.md +++ b/README.md @@ -1 +1,115 @@ -# IF3111-2016-Tugas1-Android \ No newline at end of file +# Project 1 - Android - IF3111 Platform-based Application Development +## 13513021 - Erick Chandra + +In IF3111 course, all students have to accomplish the app completion within certain deadline. The original specification is available [here] in Bahasa Indonesia. + +This application mainly have the functionality of guessing the places in Bandung Institute of Technology. There are 10 places for guessing: +- GKU Barat +- GKU Timur +- Intel +- CC Barat +- CC Timur +- DPR +- Oktagon (changed; previously Sunken) +- Perpustakaan +- PAU +- Kubus + +Inside the application, it must communicate with the server (provided by lab assistants) and have certain communication convention such as follows. + +**Location Request** + +These are the client and server communication via JSON string passed by socket connection. + +***First Client Request*** +```sh +{“com”:”req_loc”,”nim”:”13513021”} +``` + +***First Server Request*** +```sh +{“status”:”ok”,”nim”:”13513021”,”longitude”:”6.234123132”,”latitude”:”0.1234123412”,”token”:”21nu2f2n3rh23diefef23hr23ew”} +``` + +***Send Answer (Client to Server)*** +```sh +{“com”:”answer”,”nim”:”13513021”,”answer”:”labtek_v”, ”longitude”:”6.234123132”,”latitude”:”0.1234123412”,”token”:”21nu2f2n3rh23diefef23hr23ew”} +``` + +***Server Response (if the answer is correct)*** +```sh +{“status”:”ok”,”nim”:”13513021”,”longitude”:”8.13215123214”,”latitude”:”9.1234123412”,”token”:”124fewfm32r32ifmwder42”} +``` + +***Server Response (if the answer is incorrect)*** +```sh +{“status”:”wrong_answer”,”nim”:”13513021”,”token”:”124fewfm32r32ifmwder42”} +``` + +***Server Response (if the client has reached the third correct answer therefore finish)*** +```sh +{“status”:”finish”,”nim”:”13513021”,”token”:”124fewfm32r32ifmwder42”,”check”:1} +``` + +> Important: The given token must be used when making the next reply. Otherwise it will be an error. + +## Detailed Application Specification +* Application must be able to receive message from the server with JSON format, containing location (latitude and longitude) and token. +* Application must be able to calculate location point (latitude and longitude) and show the location indocator on the map view. The map will be implemented with Google Map API. +* There is a navigation arrow located on the map. The default direction is North. In order to implement this, sensors might be utilised. +* Application must be able to send camera intent. +* Photo could be taken but it is not compulsory to upload the photo to the server. +* Applicationmust be able to send message to the server with JSON format containing location information (latitude, longitude), Student ID, and token. +* Use of supporting SDK for this project. +* For some view, there are some differences when rotating the orientation (landscape vs portrait). +* Colours, fonts, and styling are not included in major marking. However, button position on the screen is important. +* Server replies must be shown with Toast or Alert Dialogue. +* Whenever the app uses the sensor, it must release all sensors by the time the user leaves the activity to reduce unnecessary battery consumption and to avoid some performance issues. + +## Screenshots + +**App Launcher** + +![App Launcher](screenshots/01-Launcher.png) + +**Home Activity** + +![Home Activity](screenshots/02-Home.png) + +**Map Activity** + +![Map Activity](screenshots/03-Map.png) + +**Map Activity (Landscape Mode)** + +![Map Activity Landscape](screenshots/03-b-MapLandscape.jpg) + +**Calling Camera Intent** + +![Camera Intent](screenshots/04-Intent.png) + +**Submission Activity** + +![Submission Activity](screenshots/05-Submit.png) + +**Submission Spinner Drop Down** + +![Submission Spinner Drop Down](screenshots/06-SubmitSpinner.png) + +**Message Toast** + +![Message Toast](screenshots/07-ReceivedMsgToast.png) + +## Source Code Folder Path + +```sh +~/Tubes1-Android/app/src/main/ +``` + +## Binary Built APK Folder Path + +```sh +~/Tubes1-Android/apk/ +``` + + [here]: diff --git a/apk/app-debug-unaligned.apk b/apk/app-debug-unaligned.apk new file mode 100644 index 0000000..875bdf6 Binary files /dev/null and b/apk/app-debug-unaligned.apk differ diff --git a/apk/app-debug.apk b/apk/app-debug.apk new file mode 100644 index 0000000..d1c7735 Binary files /dev/null and b/apk/app-debug.apk differ diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..4c579b2 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,29 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 23 + buildToolsVersion "23.0.2" + + defaultConfig { + applicationId "com.example.erickchandra.tubes1_android" + minSdkVersion 19 + targetSdkVersion 23 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + testCompile 'junit:junit:4.12' + compile 'com.android.support:appcompat-v7:23.2.1' + compile 'com.android.support:design:23.2.1' + compile 'com.google.android.gms:play-services:8.4.0' + compile 'com.android.support:support-v4:23.2.1' +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..cda4b0e --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /home/erickchandra/Android/Sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/app/src/androidTest/java/com/example/erickchandra/tubes1_android/ApplicationTest.java b/app/src/androidTest/java/com/example/erickchandra/tubes1_android/ApplicationTest.java new file mode 100644 index 0000000..fca4e29 --- /dev/null +++ b/app/src/androidTest/java/com/example/erickchandra/tubes1_android/ApplicationTest.java @@ -0,0 +1,13 @@ +package com.example.erickchandra.tubes1_android; + +import android.app.Application; +import android.test.ApplicationTestCase; + +/** + * Testing Fundamentals + */ +public class ApplicationTest extends ApplicationTestCase { + public ApplicationTest() { + super(Application.class); + } +} \ No newline at end of file diff --git a/app/src/debug/res/values/google_maps_api.xml b/app/src/debug/res/values/google_maps_api.xml new file mode 100644 index 0000000..eb89921 --- /dev/null +++ b/app/src/debug/res/values/google_maps_api.xml @@ -0,0 +1,21 @@ + + + + AIzaSyCKsgsskyCM5msZiN_VfLFmRp5HfxbO6Es + + diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..5820536 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/ic_logo_launcher-web.png b/app/src/main/ic_logo_launcher-web.png new file mode 100644 index 0000000..c8eed37 Binary files /dev/null and b/app/src/main/ic_logo_launcher-web.png differ diff --git a/app/src/main/java/com/example/erickchandra/tubes1_android/AsyncResponse.java b/app/src/main/java/com/example/erickchandra/tubes1_android/AsyncResponse.java new file mode 100644 index 0000000..b5d68eb --- /dev/null +++ b/app/src/main/java/com/example/erickchandra/tubes1_android/AsyncResponse.java @@ -0,0 +1,8 @@ +package com.example.erickchandra.tubes1_android; + +/** + * Created by erickchandra on 3/26/16. + */ +public interface AsyncResponse { + void processFinish(String output); +} diff --git a/app/src/main/java/com/example/erickchandra/tubes1_android/Client.java b/app/src/main/java/com/example/erickchandra/tubes1_android/Client.java new file mode 100644 index 0000000..09f08e7 --- /dev/null +++ b/app/src/main/java/com/example/erickchandra/tubes1_android/Client.java @@ -0,0 +1,145 @@ +package com.example.erickchandra.tubes1_android; + +/** + * Created by erickchandra on 3/25/16. + */ +import android.app.Activity; +import android.app.ProgressDialog; +import android.os.AsyncTask; +import android.widget.Toast; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.net.Socket; +import java.net.UnknownHostException; + +public class Client extends AsyncTask { + + String dstAddress; + int dstPort; + String response = ""; + Activity parentActivity; + String messageIn, messageOut; + + private ProgressDialog progressDialog; + + Client(String addr, int port, String messageIn, String messageOut) { + // this.parentActivity = parentActivity; + dstAddress = addr; + dstPort = port; + progressDialog = new ProgressDialog(parentActivity); + + this.messageIn = messageIn; + this.messageOut = messageOut; + } + + @Override + protected void onPreExecute() { + this.progressDialog.setMessage("Connecting Server"); + this.progressDialog.show(); + } + + @Override + protected Void doInBackground(Void... arg0) { + + Socket socket = null; + + System.out.println("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); + + try { + socket = new Socket(dstAddress, dstPort); + System.out.println("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"); + +// SendMessageThread sendMessage = new SendMessageThread(socket, messageOut); +// Thread smt = new Thread(sendMessage).start(); + +// final Socket finalSocket = socket; +// Thread sendMsgThread = new Thread() { +// @Override +// public void run() { +// try { +// finalSocket.getOutputStream().write(messageOut.getBytes()); +// } catch (IOException e) { +// e.printStackTrace(); +// } +// } +// }; +// sendMsgThread.start(); + +// socket.getOutputStream().write(messageOut.getBytes()); + PrintWriter output = new PrintWriter(new OutputStreamWriter(socket.getOutputStream())); + output.print(messageOut + "\n"); + output.flush(); + + System.out.println("CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"); + +// ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream( +// 1024); +// byte[] buffer = new byte[1024]; +// +// int bytesRead; +// InputStream inputStream = socket.getInputStream(); + + /* + * notice: inputStream.read() will block if no data return + */ + System.out.println("DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD"); +// while ((bytesRead = inputStream.read(buffer)) != -1) { +// byteArrayOutputStream.write(buffer, 0, bytesRead); +// response += byteArrayOutputStream.toString("UTF-8"); +// System.out.println("######################################"); +// } + + BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream())); + messageIn = input.readLine(); + + System.out.println("Response: " + messageIn); + + System.out.println("EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE"); + + if (this.progressDialog.isShowing()) { + this.progressDialog.dismiss(); + System.out.println("**************************************"); + } + + messageIn = response; + + // Toast.makeText(parentActivity, "Server response:\n" + response, Toast.LENGTH_LONG); + System.out.println("\n\n***\nSERVER RESPONSE: " + messageIn + "\n***\n\n"); + + } catch (UnknownHostException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + response = "UnknownHostException: " + e.toString(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + response = "IOException: " + e.toString(); + } finally { + if (socket != null) { + try { + socket.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + + return null; + } + + @Override + protected void onPostExecute(Void result) { + super.onPostExecute(result); + messageIn = response; + } + +} diff --git a/app/src/main/java/com/example/erickchandra/tubes1_android/ClientSync.java b/app/src/main/java/com/example/erickchandra/tubes1_android/ClientSync.java new file mode 100644 index 0000000..b7eb7b8 --- /dev/null +++ b/app/src/main/java/com/example/erickchandra/tubes1_android/ClientSync.java @@ -0,0 +1,94 @@ +package com.example.erickchandra.tubes1_android; + +import android.app.Activity; +import android.app.ProgressDialog; +import android.os.AsyncTask; +import android.util.Log; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.net.Socket; + +/** + * Created by erickchandra on 3/26/16. + */ + +public class ClientSync extends AsyncTask { + public AsyncResponse delegate = null; + + public static String hostname = "167.205.34.132"; + public static int hostport = 3111; + String msgSend, msgRecv; + Socket socket; + private ProgressDialog progressDialog; + + ClientSync(Activity parentActivity, String msgSend) { + progressDialog = new ProgressDialog(parentActivity); + this.msgSend = msgSend; + } + + public void SendAndThenRecvMessage() { + // Opening socket + try { + socket = new Socket(hostname, hostport); + // Sending Message + PrintWriter output = null; + try { + output = new PrintWriter(new OutputStreamWriter(socket.getOutputStream())); + } catch (IOException e) { + e.printStackTrace(); + } + output.print(msgSend + "\n"); + output.flush(); + System.out.println("Sent message: " + msgSend); + Log.d(this.getClass().toString(), "Send Message: " + msgSend); + + // Receiving Message + BufferedReader input = null; + try { + input = new BufferedReader(new InputStreamReader(socket.getInputStream())); + msgRecv = input.readLine(); + } catch (IOException e) { + e.printStackTrace(); + } + + System.out.println("Received message: " + msgRecv); + Log.d(this.getClass().toString(), "Received Message: " + msgRecv); + } catch (IOException e) { + e.printStackTrace(); + } + + + } + + String getRecvMsg() { + return msgRecv; + } + + @Override + protected void onPreExecute() { + this.progressDialog.setMessage("Connecting Server"); + this.progressDialog.show(); + Log.d(this.getClass().toString(), "Progress Dialog shown."); + } + + @Override + protected Void doInBackground(Void... params) { + SendAndThenRecvMessage(); + return null; + } + + @Override + protected void onPostExecute(Void result) { + super.onPostExecute(result); + if (this.progressDialog.isShowing()) { + this.progressDialog.dismiss(); + Log.d(this.getClass().toString(), "Progress Dialog dismissed."); + } + Log.d(this.getClass().toString(), "Received Message (ClientSync): " + msgRecv); + delegate.processFinish(msgRecv); + } +} diff --git a/app/src/main/java/com/example/erickchandra/tubes1_android/FinishFullscreenActivity.java b/app/src/main/java/com/example/erickchandra/tubes1_android/FinishFullscreenActivity.java new file mode 100644 index 0000000..f989ce2 --- /dev/null +++ b/app/src/main/java/com/example/erickchandra/tubes1_android/FinishFullscreenActivity.java @@ -0,0 +1,163 @@ +package com.example.erickchandra.tubes1_android; + +import android.annotation.SuppressLint; +import android.support.v7.app.ActionBar; +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; +import android.os.Handler; +import android.view.MotionEvent; +import android.view.View; + +/** + * An example full-screen activity that shows and hides the system UI (i.e. + * status bar and navigation/system bar) with user interaction. + */ +public class FinishFullscreenActivity extends AppCompatActivity { + /** + * Whether or not the system UI should be auto-hidden after + * {@link #AUTO_HIDE_DELAY_MILLIS} milliseconds. + */ + private static final boolean AUTO_HIDE = true; + + /** + * If {@link #AUTO_HIDE} is set, the number of milliseconds to wait after + * user interaction before hiding the system UI. + */ + private static final int AUTO_HIDE_DELAY_MILLIS = 3000; + + /** + * Some older devices needs a small delay between UI widget updates + * and a change of the status and navigation bar. + */ + private static final int UI_ANIMATION_DELAY = 300; + private final Handler mHideHandler = new Handler(); + private View mContentView; + private final Runnable mHidePart2Runnable = new Runnable() { + @SuppressLint("InlinedApi") + @Override + public void run() { + // Delayed removal of status and navigation bar + + // Note that some of these constants are new as of API 16 (Jelly Bean) + // and API 19 (KitKat). It is safe to use them, as they are inlined + // at compile-time and do nothing on earlier devices. + mContentView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE + | View.SYSTEM_UI_FLAG_FULLSCREEN + | View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION); + } + }; + private View mControlsView; + private final Runnable mShowPart2Runnable = new Runnable() { + @Override + public void run() { + // Delayed display of UI elements + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + actionBar.show(); + } + mControlsView.setVisibility(View.VISIBLE); + } + }; + private boolean mVisible; + private final Runnable mHideRunnable = new Runnable() { + @Override + public void run() { + hide(); + } + }; + /** + * Touch listener to use for in-layout UI controls to delay hiding the + * system UI. This is to prevent the jarring behavior of controls going away + * while interacting with activity UI. + */ + private final View.OnTouchListener mDelayHideTouchListener = new View.OnTouchListener() { + @Override + public boolean onTouch(View view, MotionEvent motionEvent) { + if (AUTO_HIDE) { + delayedHide(AUTO_HIDE_DELAY_MILLIS); + } + return false; + } + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.activity_finish_fullscreen); + + mVisible = true; + mControlsView = findViewById(R.id.fullscreen_content_controls); + mContentView = findViewById(R.id.fullscreen_content); + + + // Set up the user interaction to manually show or hide the system UI. + mContentView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + toggle(); + } + }); + + // Upon interacting with UI controls, delay any scheduled hide() + // operations to prevent the jarring behavior of controls going away + // while interacting with the UI. + findViewById(R.id.dummy_button).setOnTouchListener(mDelayHideTouchListener); + } + + @Override + protected void onPostCreate(Bundle savedInstanceState) { + super.onPostCreate(savedInstanceState); + + // Trigger the initial hide() shortly after the activity has been + // created, to briefly hint to the user that UI controls + // are available. + delayedHide(100); + } + + private void toggle() { + if (mVisible) { + hide(); + } else { + show(); + } + } + + private void hide() { + // Hide UI first + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + actionBar.hide(); + } + mControlsView.setVisibility(View.GONE); + mVisible = false; + + // Schedule a runnable to remove the status and navigation bar after a delay + mHideHandler.removeCallbacks(mShowPart2Runnable); + mHideHandler.postDelayed(mHidePart2Runnable, UI_ANIMATION_DELAY); + } + + @SuppressLint("InlinedApi") + private void show() { + // Show the system bar + mContentView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); + mVisible = true; + + // Schedule a runnable to display UI elements after a delay + mHideHandler.removeCallbacks(mHidePart2Runnable); + mHideHandler.postDelayed(mShowPart2Runnable, UI_ANIMATION_DELAY); + } + + /** + * Schedules a call to hide() in [delay] milliseconds, canceling any + * previously scheduled calls. + */ + private void delayedHide(int delayMillis) { + mHideHandler.removeCallbacks(mHideRunnable); + mHideHandler.postDelayed(mHideRunnable, delayMillis); + } +} diff --git a/app/src/main/java/com/example/erickchandra/tubes1_android/GifDecoder.java b/app/src/main/java/com/example/erickchandra/tubes1_android/GifDecoder.java new file mode 100644 index 0000000..f1e83c1 --- /dev/null +++ b/app/src/main/java/com/example/erickchandra/tubes1_android/GifDecoder.java @@ -0,0 +1,524 @@ +package com.example.erickchandra.tubes1_android; + +import android.graphics.Bitmap; + +import java.io.InputStream; +import java.util.Vector; + +/** + * Created by erickchandra on 3/25/16. + */ +public class GifDecoder { + + public static final int STATUS_OK = 0; + + public static final int STATUS_FORMAT_ERROR = 1; + + public static final int STATUS_OPEN_ERROR = 2; + + protected static final int MAX_STACK_SIZE = 4096; + protected InputStream in; + protected int status; + protected int width; // full image width + protected int height; // full image height + protected boolean gctFlag; // global color table used + protected int gctSize; // size of global color table + protected int loopCount = 1; // iterations; 0 = repeat forever + protected int[] gct; // global color table + protected int[] lct; // local color table + protected int[] act; // active color table + protected int bgIndex; // background color index + protected int bgColor; // background color + protected int lastBgColor; // previous bg color + protected int pixelAspect; // pixel aspect ratio + protected boolean lctFlag; // local color table flag + protected boolean interlace; // interlace flag + protected int lctSize; // local color table size + protected int ix, iy, iw, ih; // current image rectangle + protected int lrx, lry, lrw, lrh; + protected Bitmap image; // current frame + protected Bitmap lastBitmap; // previous frame + protected byte[] block = new byte[256]; // current data block + protected int blockSize = 0; // block size last graphic control extension info + protected int dispose = 0; // 0=no action; 1=leave in place; 2=restore to bg; 3=restore to prev + protected int lastDispose = 0; + protected boolean transparency = false; // use transparent color + protected int delay = 0; // delay in milliseconds + protected int transIndex; // transparent color index + // LZW decoder working arrays + protected short[] prefix; + protected byte[] suffix; + protected byte[] pixelStack; + protected byte[] pixels; + protected Vector frames; // frames read from current file + protected int frameCount; + + private static class GifFrame { + public GifFrame(Bitmap im, int del) { + image = im; + delay = del; + } + + public Bitmap image; + public int delay; + } + + + public int getDelay(int n) { + delay = -1; + if ((n >= 0) && (n < frameCount)) { + delay = frames.elementAt(n).delay; + } + return delay; + } + + + public int getFrameCount() { + return frameCount; + } + + + public Bitmap getBitmap() { + return getFrame(0); + } + + + public int getLoopCount() { + return loopCount; + } + protected void setPixels() { + + int[] dest = new int[width * height]; + + if (lastDispose > 0) { + if (lastDispose == 3) { + // use image before last + int n = frameCount - 2; + if (n > 0) { + lastBitmap = getFrame(n - 1); + } else { + lastBitmap = null; + } + } + if (lastBitmap != null) { + lastBitmap.getPixels(dest, 0, width, 0, 0, width, height); + if (lastDispose == 2) { + // fill last image rect area with background color + int c = 0; + if (!transparency) { + c = lastBgColor; + } + for (int i = 0; i < lrh; i++) { + int n1 = (lry + i) * width + lrx; + int n2 = n1 + lrw; + for (int k = n1; k < n2; k++) { + dest[k] = c; + } + } + } + } + } + int pass = 1; + int inc = 8; + int iline = 0; + for (int i = 0; i < ih; i++) { + int line = i; + if (interlace) { + if (iline >= ih) { + pass++; + switch (pass) { + case 2: + iline = 4; + break; + case 3: + iline = 2; + inc = 4; + break; + case 4: + iline = 1; + inc = 2; + break; + default: + break; + } + } + line = iline; + iline += inc; + } + line += iy; + if (line < height) { + int k = line * width; + int dx = k + ix; // start of line in dest + int dlim = dx + iw; // end of dest line + if ((k + width) < dlim) { + dlim = k + width; // past dest edge + } + int sx = i * iw; // start of line in source + while (dx < dlim) { + // map color and insert in destination + int index = ((int) pixels[sx++]) & 0xff; + int c = act[index]; + if (c != 0) { + dest[dx] = c; + } + dx++; + } + } + } + image = Bitmap.createBitmap(dest, width, height, Bitmap.Config.ARGB_4444); + } + public Bitmap getFrame(int n) { + if (frameCount <= 0) + return null; + n = n % frameCount; + return ((GifFrame) frames.elementAt(n)).image; + } + public int read(InputStream is) { + init(); + if (is != null) { + in = is; + readHeader(); + if (!err()) { + readContents(); + if (frameCount < 0) { + status = STATUS_FORMAT_ERROR; + } + } + } else { + status = STATUS_OPEN_ERROR; + } + try { + is.close(); + } catch (Exception e) { + } + return status; + } + protected void decodeBitmapData() { + int nullCode = -1; + int npix = iw * ih; + int available, clear, code_mask, code_size, end_of_information, in_code, old_code, bits, code, count, i, datum, data_size, first, top, bi, pi; + if ((pixels == null) || (pixels.length < npix)) { + pixels = new byte[npix]; // allocate new pixel array + } + if (prefix == null) { + prefix = new short[MAX_STACK_SIZE]; + } + if (suffix == null) { + suffix = new byte[MAX_STACK_SIZE]; + } + if (pixelStack == null) { + pixelStack = new byte[MAX_STACK_SIZE + 1]; + } + data_size = read(); + clear = 1 << data_size; + end_of_information = clear + 1; + available = clear + 2; + old_code = nullCode; + code_size = data_size + 1; + code_mask = (1 << code_size) - 1; + for (code = 0; code < clear; code++) { + prefix[code] = 0; // XXX ArrayIndexOutOfBoundsException + suffix[code] = (byte) code; + } + datum = bits = count = first = top = pi = bi = 0; + for (i = 0; i < npix;) { + if (top == 0) { + if (bits < code_size) { + // Load bytes until there are enough bits for a code. + if (count == 0) { + // Read a new data block. + count = readBlock(); + if (count <= 0) { + break; + } + bi = 0; + } + datum += (((int) block[bi]) & 0xff) << bits; + bits += 8; + bi++; + count--; + continue; + } + code = datum & code_mask; + datum >>= code_size; + bits -= code_size; + if ((code > available) || (code == end_of_information)) { + break; + } + if (code == clear) { + // Reset decoder. + code_size = data_size + 1; + code_mask = (1 << code_size) - 1; + available = clear + 2; + old_code = nullCode; + continue; + } + if (old_code == nullCode) { + pixelStack[top++] = suffix[code]; + old_code = code; + first = code; + continue; + } + in_code = code; + if (code == available) { + pixelStack[top++] = (byte) first; + code = old_code; + } + while (code > clear) { + pixelStack[top++] = suffix[code]; + code = prefix[code]; + } + first = ((int) suffix[code]) & 0xff; + if (available >= MAX_STACK_SIZE) { + break; + } + pixelStack[top++] = (byte) first; + prefix[available] = (short) old_code; + suffix[available] = (byte) first; + available++; + if (((available & code_mask) == 0) && (available < MAX_STACK_SIZE)) { + code_size++; + code_mask += available; + } + old_code = in_code; + } + // Pop a pixel off the pixel stack. + top--; + pixels[pi++] = pixelStack[top]; + i++; + } + for (i = pi; i < npix; i++) { + pixels[i] = 0; // clear missing pixels + } + } + protected boolean err() { + return status != STATUS_OK; + } + protected void init() { + status = STATUS_OK; + frameCount = 0; + frames = new Vector(); + gct = null; + lct = null; + } + protected int read() { + int curByte = 0; + try { + curByte = in.read(); + } catch (Exception e) { + status = STATUS_FORMAT_ERROR; + } + return curByte; + } + protected int readBlock() { + blockSize = read(); + int n = 0; + if (blockSize > 0) { + try { + int count = 0; + while (n < blockSize) { + count = in.read(block, n, blockSize - n); + if (count == -1) { + break; + } + n += count; + } + } catch (Exception e) { + e.printStackTrace(); + } + if (n < blockSize) { + status = STATUS_FORMAT_ERROR; + } + } + return n; + } + protected int[] readColorTable(int ncolors) { + int nbytes = 3 * ncolors; + int[] tab = null; + byte[] c = new byte[nbytes]; + int n = 0; + try { + n = in.read(c); + } catch (Exception e) { + e.printStackTrace(); + } + if (n < nbytes) { + status = STATUS_FORMAT_ERROR; + } else { + tab = new int[256]; // max size to avoid bounds checks + int i = 0; + int j = 0; + while (i < ncolors) { + int r = ((int) c[j++]) & 0xff; + int g = ((int) c[j++]) & 0xff; + int b = ((int) c[j++]) & 0xff; + tab[i++] = 0xff000000 | (r << 16) | (g << 8) | b; + } + } + return tab; + } + protected void readContents() { + // read GIF file content blocks + boolean done = false; + while (!(done || err())) { + int code = read(); + switch (code) { + case 0x2C: // image separator + readBitmap(); + break; + case 0x21: // extension + code = read(); + switch (code) { + case 0xf9: // graphics control extension + readGraphicControlExt(); + break; + case 0xff: // application extension + readBlock(); + String app = ""; + for (int i = 0; i < 11; i++) { + app += (char) block[i]; + } + if (app.equals("NETSCAPE2.0")) { + readNetscapeExt(); + } else { + skip(); // don't care + } + break; + case 0xfe:// comment extension + skip(); + break; + case 0x01:// plain text extension + skip(); + break; + default: // uninteresting extension + skip(); + } + break; + case 0x3b: // terminator + done = true; + break; + case 0x00: // bad byte, but keep going and see what happens break; + default: + status = STATUS_FORMAT_ERROR; + } + } + } + protected void readGraphicControlExt() { + read(); // block size + int packed = read(); // packed fields + dispose = (packed & 0x1c) >> 2; // disposal method + if (dispose == 0) { + dispose = 1; // elect to keep old image if discretionary + } + transparency = (packed & 1) != 0; + delay = readShort() * 10; // delay in milliseconds + transIndex = read(); // transparent color index + read(); // block terminator + } + protected void readHeader() { + String id = ""; + for (int i = 0; i < 6; i++) { + id += (char) read(); + } + if (!id.startsWith("GIF")) { + status = STATUS_FORMAT_ERROR; + return; + } + readLSD(); + if (gctFlag && !err()) { + gct = readColorTable(gctSize); + bgColor = gct[bgIndex]; + } + } + protected void readBitmap() { + ix = readShort(); // (sub)image position & size + iy = readShort(); + iw = readShort(); + ih = readShort(); + int packed = read(); + lctFlag = (packed & 0x80) != 0; // 1 - local color table flag interlace + lctSize = (int) Math.pow(2, (packed & 0x07) + 1); + interlace = (packed & 0x40) != 0; + if (lctFlag) { + lct = readColorTable(lctSize); // read table + act = lct; // make local table active + } else { + act = gct; // make global table active + if (bgIndex == transIndex) { + bgColor = 0; + } + } + int save = 0; + if (transparency) { + save = act[transIndex]; + act[transIndex] = 0; // set transparent color if specified + } + if (act == null) { + status = STATUS_FORMAT_ERROR; // no color table defined + } + if (err()) { + return; + } + decodeBitmapData(); // decode pixel data + skip(); + if (err()) { + return; + } + frameCount++; + // create new image to receive frame data + image = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_4444); + setPixels(); // transfer pixel data to image + frames.addElement(new GifFrame(image, delay)); // add image to frame + // list + if (transparency) { + act[transIndex] = save; + } + resetFrame(); + } + protected void readLSD() { + // logical screen size + width = readShort(); + height = readShort(); + // packed fields + int packed = read(); + gctFlag = (packed & 0x80) != 0; // 1 : global color table flag + // 2-4 : color resolution + // 5 : gct sort flag + gctSize = 2 << (packed & 7); // 6-8 : gct size + bgIndex = read(); // background color index + pixelAspect = read(); // pixel aspect ratio + } + protected void readNetscapeExt() { + do { + readBlock(); + if (block[0] == 1) { + // loop count sub-block + int b1 = ((int) block[1]) & 0xff; + int b2 = ((int) block[2]) & 0xff; + loopCount = (b2 << 8) | b1; + } + } while ((blockSize > 0) && !err()); + } + protected int readShort() { + // read 16-bit value, LSB first + return read() | (read() << 8); + } + protected void resetFrame() { + lastDispose = dispose; + lrx = ix; + lry = iy; + lrw = iw; + lrh = ih; + lastBitmap = image; + lastBgColor = bgColor; + dispose = 0; + transparency = false; + delay = 0; + lct = null; + } + protected void skip() { + do { + readBlock(); + } while ((blockSize > 0) && !err()); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/erickchandra/tubes1_android/GifDecoderView.java b/app/src/main/java/com/example/erickchandra/tubes1_android/GifDecoderView.java new file mode 100644 index 0000000..02746cd --- /dev/null +++ b/app/src/main/java/com/example/erickchandra/tubes1_android/GifDecoderView.java @@ -0,0 +1,69 @@ +package com.example.erickchandra.tubes1_android; + +import android.content.Context; +import android.graphics.Bitmap; +import android.os.Handler; +import android.widget.ImageView; + +import java.io.InputStream; + +/** + * Created by erickchandra on 3/25/16. + */ +public class GifDecoderView extends ImageView { + + private boolean mIsPlayingGif = false; + + private GifDecoder mGifDecoder; + + private Bitmap mTmpBitmap; + + final Handler mHandler = new Handler(); + + final Runnable mUpdateResults = new Runnable() { + public void run() { + if (mTmpBitmap != null && !mTmpBitmap.isRecycled()) { + GifDecoderView.this.setImageBitmap(mTmpBitmap); + } + } + }; + + public GifDecoderView(Context context, InputStream stream) { + super(context); + playGif(stream); + } + + private void playGif(InputStream stream) { + mGifDecoder = new GifDecoder(); + mGifDecoder.read(stream); + + mIsPlayingGif = true; + + new Thread(new Runnable() { + public void run() { + final int n = mGifDecoder.getFrameCount(); + final int ntimes = mGifDecoder.getLoopCount(); + int repetitionCounter = 0; + do { + for (int i = 0; i < n; i++) { + mTmpBitmap = mGifDecoder.getFrame(i); + int t = mGifDecoder.getDelay(i); + mHandler.post(mUpdateResults); + try { + Thread.sleep(t); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + if(ntimes != 0) { + repetitionCounter ++; + } + } while (mIsPlayingGif && (repetitionCounter <= ntimes)); + } + }).start(); + } + + public void stopRendering() { + mIsPlayingGif = true; + } +} diff --git a/app/src/main/java/com/example/erickchandra/tubes1_android/GifMovieView.java b/app/src/main/java/com/example/erickchandra/tubes1_android/GifMovieView.java new file mode 100644 index 0000000..b0d1c93 --- /dev/null +++ b/app/src/main/java/com/example/erickchandra/tubes1_android/GifMovieView.java @@ -0,0 +1,42 @@ +package com.example.erickchandra.tubes1_android; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Movie; +import android.os.SystemClock; +import android.view.View; + +import java.io.InputStream; + +/** + * Created by erickchandra on 3/25/16. + */ +public class GifMovieView extends View { + + private Movie mMovie; + + private long mMoviestart; + + public GifMovieView(Context context, InputStream stream) { + super(context); + + mMovie = Movie.decodeStream(stream); + } + + @Override + protected void onDraw(Canvas canvas) { + canvas.drawColor(Color.TRANSPARENT); + super.onDraw(canvas); + final long now = SystemClock.uptimeMillis(); + + if (mMoviestart == 0) { + mMoviestart = now; + } + + final int relTime = (int)((now - mMoviestart) % mMovie.duration()); + mMovie.setTime(relTime); + mMovie.draw(canvas, 10, 10); + this.invalidate(); + } +} diff --git a/app/src/main/java/com/example/erickchandra/tubes1_android/GifWebView.java b/app/src/main/java/com/example/erickchandra/tubes1_android/GifWebView.java new file mode 100644 index 0000000..21cd654 --- /dev/null +++ b/app/src/main/java/com/example/erickchandra/tubes1_android/GifWebView.java @@ -0,0 +1,16 @@ +package com.example.erickchandra.tubes1_android; + +import android.content.Context; +import android.webkit.WebView; + +/** + * Created by erickchandra on 3/25/16. + */ +public class GifWebView extends WebView { + + public GifWebView(Context context, String path) { + super(context); + + loadUrl(path); + } +} diff --git a/app/src/main/java/com/example/erickchandra/tubes1_android/HomeActivity.java b/app/src/main/java/com/example/erickchandra/tubes1_android/HomeActivity.java new file mode 100644 index 0000000..2c39fdc --- /dev/null +++ b/app/src/main/java/com/example/erickchandra/tubes1_android/HomeActivity.java @@ -0,0 +1,101 @@ +package com.example.erickchandra.tubes1_android; + +import android.app.ActionBar; +import android.content.Intent; +import android.graphics.Color; +import android.net.Uri; +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; +import android.widget.Toast; + +import java.io.IOException; +import java.io.InputStream; + +public class HomeActivity extends AppCompatActivity implements AsyncResponse { + ClientSync cs; + String msgRecv; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_home); + setTitle("Android Map ITB Project"); + getWindow().getDecorView().setBackgroundColor(Color.WHITE); + + InputStream stream = null; + try { + stream = getAssets().open("google_logo_animation_small.gif"); + } catch (IOException e) { + e.printStackTrace(); + } + + GifWebView gifWebView = new GifWebView(this, "file:///android_res/drawable/google_logo_animation_small.gif"); + gifWebView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 720)); + gifWebView.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY); + // setContentView(gifWebView); + + LinearLayout linearLayout_GifWebView = new LinearLayout(this); + linearLayout_GifWebView.addView(gifWebView); + linearLayout_GifWebView.setGravity(Gravity.BOTTOM); + LinearLayout.LayoutParams lp_linearLayout_GifWebView = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 720); + linearLayout_GifWebView.setLayoutParams(lp_linearLayout_GifWebView); +// setContentView(linearLayout_GifWebView); + + Button button_start = new Button(this); + button_start.setText("Start Seeking"); + LinearLayout.LayoutParams lp_button_start = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); + button_start.setLayoutParams(lp_button_start); + button_start.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + launchComm(); + } + }); + + LinearLayout linearLayout_button_start = new LinearLayout(this); + linearLayout_button_start.addView(button_start); + linearLayout_button_start.setGravity(Gravity.BOTTOM); + LinearLayout.LayoutParams lp_linearLayout_button_start = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); + linearLayout_button_start.setLayoutParams(lp_linearLayout_button_start); +// setContentView(linearLayout_button_start); + + LinearLayout linearLayout_whole = new LinearLayout(this); + linearLayout_whole.addView(linearLayout_GifWebView); + linearLayout_whole.addView(linearLayout_button_start); + linearLayout_whole.setOrientation(LinearLayout.VERTICAL); + linearLayout_whole.setGravity(Gravity.BOTTOM); + setContentView(linearLayout_whole); + } + + public void launchComm() { + MessageSendParser msp = new MessageSendParser(0, "13513021"); + cs = new ClientSync(this, msp.getJSONObjectStr()); +// cs.SendAndThenRecvMessage(); + cs.delegate = this; + cs.execute(); + } + + public void launchMap() { +// Client newComm = new Client("api.nitho.me", 8080, receivedMessage, "{\"com\":\"req_loc\",\"nim\":\"13513021\"}\n"); +// newComm.execute(); + + Intent mapIntent = new Intent(this, MapsActivity.class); + mapIntent.putExtra("Message", msgRecv); + startActivity(mapIntent); + } + + @Override + public void processFinish(String output) { + msgRecv = output; + launchMap(); + } +} diff --git a/app/src/main/java/com/example/erickchandra/tubes1_android/MainActivity.java b/app/src/main/java/com/example/erickchandra/tubes1_android/MainActivity.java new file mode 100644 index 0000000..15a120e --- /dev/null +++ b/app/src/main/java/com/example/erickchandra/tubes1_android/MainActivity.java @@ -0,0 +1,52 @@ +package com.example.erickchandra.tubes1_android; + +import android.os.Bundle; +import android.support.design.widget.FloatingActionButton; +import android.support.design.widget.Snackbar; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; + +public class MainActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + + FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); + fab.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG) + .setAction("Action", null).show(); + } + }); + } + + @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(); + + //noinspection SimplifiableIfStatement + if (id == R.id.action_settings) { + return true; + } + + return super.onOptionsItemSelected(item); + } +} diff --git a/app/src/main/java/com/example/erickchandra/tubes1_android/MapsActivity.java b/app/src/main/java/com/example/erickchandra/tubes1_android/MapsActivity.java new file mode 100644 index 0000000..f42a03a --- /dev/null +++ b/app/src/main/java/com/example/erickchandra/tubes1_android/MapsActivity.java @@ -0,0 +1,349 @@ +package com.example.erickchandra.tubes1_android; + +import android.Manifest; +import android.app.Activity; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.graphics.drawable.GradientDrawable; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.net.Uri; +import android.os.Environment; +import android.provider.MediaStore; +import android.support.v4.app.FragmentActivity; +import android.os.Bundle; +import android.support.v4.content.ContextCompat; +import android.support.v7.app.AppCompatActivity; +import android.util.Log; +import android.view.Surface; +import android.view.View; +import android.view.animation.Animation; +import android.view.animation.RotateAnimation; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.Toast; + +import com.google.android.gms.maps.CameraUpdate; +import com.google.android.gms.maps.CameraUpdateFactory; +import com.google.android.gms.maps.GoogleMap; +import com.google.android.gms.maps.OnMapReadyCallback; +import com.google.android.gms.maps.SupportMapFragment; +import com.google.android.gms.maps.model.LatLng; +import com.google.android.gms.maps.model.MarkerOptions; + +import java.io.File; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Date; + +public class MapsActivity extends AppCompatActivity implements OnMapReadyCallback, SensorEventListener { + + private GoogleMap mMap; + + // For Map LatLng + + // For Intent Information Passing + Intent myIntent; + String intentMsg; + String cStatus, cNIM, cLat, cLng, cToken; + double cLatDouble, cLngDouble; + MessageRecvParser cMRP; + LatLng cLatLng; + + // For Compass + private ImageView mPointer; + private SensorManager mSensorManager; + private Sensor mAccelerometer; + private Sensor mMagnetometer; + private float[] mLastAccelerometer = new float[3]; + private float[] mLastMagnetometer = new float[3]; + private boolean mLastAccelerometerSet = false; + private boolean mLastMagnetometerSet = false; + private float[] mR = new float[9]; + private float[] mOrientation = new float[3]; + private float mCurrentDegree = 0f; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_maps); + // Obtain the SupportMapFragment and get notified when the map is ready to be used. + SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager() + .findFragmentById(R.id.map); + mapFragment.getMapAsync(this); + setTitle("Map"); + + // For Camera Button + ImageButton cameraButton = (ImageButton) findViewById(R.id.button_camera); + cameraButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + dispatchTakePictureIntent(); + } + }); + + // For Message Submit Button + ImageButton msgSubmitButton = (ImageButton) findViewById(R.id.button_message); + msgSubmitButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + launchMsgSubmit(); + } + }); + + // For Compass + mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE); + mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); + mMagnetometer = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD); + mPointer = (ImageView) findViewById(R.id.pointer); + + // For Intent Information Passing + myIntent = getIntent(); + intentMsg = myIntent.getStringExtra("Message"); + Toast.makeText(getApplicationContext(), "Received Intent Message: " + intentMsg, Toast.LENGTH_SHORT).show(); + Log.d(this.getClass().toString(), "Received Intent Message: " + intentMsg); + cMRP = new MessageRecvParser(intentMsg); + } + + protected void onResume() { + super.onResume(); + mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME); + mSensorManager.registerListener(this, mMagnetometer, SensorManager.SENSOR_DELAY_GAME); + } + + protected void onPause() { + super.onPause(); + mSensorManager.unregisterListener(this, mAccelerometer); + mSensorManager.unregisterListener(this, mMagnetometer); + } + + // Sensor Implementation + @Override + public void onSensorChanged(SensorEvent event) { + if (event.sensor == mAccelerometer) { + System.arraycopy(event.values, 0, mLastAccelerometer, 0, event.values.length); + mLastAccelerometerSet = true; + } else if (event.sensor == mMagnetometer) { + System.arraycopy(event.values, 0, mLastMagnetometer, 0, event.values.length); + mLastMagnetometerSet = true; + } + if (mLastAccelerometerSet && mLastMagnetometerSet) { + SensorManager.getRotationMatrix(mR, null, mLastAccelerometer, mLastMagnetometer); + SensorManager.getOrientation(mR, mOrientation); + float azimuthInRadians = mOrientation[0]; + float azimuthInDegrees = (float)(Math.toDegrees(azimuthInRadians)+360)%360; + + RotateAnimation ra; +// System.out.println("ORIENTATION: " + getResources().getConfiguration().orientation + " " + ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + if (getResources().getConfiguration().orientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) { + if (this.getWindowManager().getDefaultDisplay().getRotation() == Surface.ROTATION_0) { + ra = new RotateAnimation( + mCurrentDegree, + -azimuthInDegrees, + Animation.RELATIVE_TO_SELF, 0.5f, + Animation.RELATIVE_TO_SELF, + 0.5f); + } + else { + ra = new RotateAnimation( + mCurrentDegree + 180, + -azimuthInDegrees, + Animation.RELATIVE_TO_SELF, 0.5f, + Animation.RELATIVE_TO_SELF, + 0.5f); + } + } + else { // SCREEN_ORIENTATION_LANDSCAPE + if (this.getWindowManager().getDefaultDisplay().getRotation() == Surface.ROTATION_90) { + ra = new RotateAnimation( + mCurrentDegree - 90, + -azimuthInDegrees, + Animation.RELATIVE_TO_SELF, 0.5f, + Animation.RELATIVE_TO_SELF, + 0.5f); + } + else { + ra = new RotateAnimation( + mCurrentDegree + 90, + -azimuthInDegrees, + Animation.RELATIVE_TO_SELF, 0.5f, + Animation.RELATIVE_TO_SELF, + 0.5f); + } + } + + ra.setDuration(250); + + ra.setFillAfter(true); + + mPointer.startAnimation(ra); + mCurrentDegree = -azimuthInDegrees; + } + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + // TODO Auto-generated method stub + + } + + + /** + * Manipulates the map once available. + * This callback is triggered when the map is ready to be used. + * This is where we can add markers or lines, add listeners or move the camera. In this case, + * we just add a marker near Sydney, Australia. + * If Google Play services is not installed on the device, the user will be prompted to install + * it inside the SupportMapFragment. This method will only be triggered once the user has + * installed Google Play services and returned to the app. + */ + @Override + public void onMapReady(GoogleMap googleMap) { + mMap = googleMap; + + // Add a marker in Sydney and move the camera +// LatLng sydney = new LatLng(-34, 151); +// mMap.addMarker(new MarkerOptions().position(sydney).title("Marker in Sydney")); +// mMap.moveCamera(CameraUpdateFactory.newLatLng(sydney)); + + // Passing Intent LatLng +// if (cMRP.getStatus() == "ok") { +// cLng = cMRP.getLat(); +// cLngDouble = Double.parseDouble(cLng); +// cLat = cMRP.getLng(); +// cLatDouble = Double.parseDouble(cLat); +// } + + // Set up Google Maps initial position +// LatLng default_itb = new LatLng(cLatDouble, cLngDouble); +// mMap.addMarker(new MarkerOptions().position(default_itb).title("Guess Place")); +// mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(default_itb, 16.0f)); + cMRP = new MessageRecvParser(intentMsg); + Toast.makeText(this.getApplicationContext(), "On Create, Intent Message", Toast.LENGTH_LONG).show(); + Log.d(this.getClass().toString(), "On Create, Intent Message"); + + // Passing Intent LatLng + cLng = cMRP.getLng(); //getLat(); // SWAP HERE!!! + cLngDouble = Double.parseDouble(cLng); + cLat = cMRP.getLat(); //getLng(); // SWAP HERE!!! + cLatDouble = Double.parseDouble(cLat); + + Toast.makeText(getApplicationContext(), "Current LatDouble: " + cLatDouble + "\nCurrent LngDouble: " + cLngDouble, Toast.LENGTH_SHORT).show(); + Log.d(this.getClass().toString(), "Current LatDouble: " + cLatDouble + "\nCurrent LngDouble" + cLngDouble); + + cLatLng = new LatLng(cLatDouble, cLngDouble); + mMap.addMarker(new MarkerOptions().position(cLatLng).title("Guess Place!")); + mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(cLatLng, 16.0f)); + + // For Map + if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) + == PackageManager.PERMISSION_GRANTED) { + mMap.setMyLocationEnabled(true); + } else { + // Show rationale and request permission. + } + } + + String mCurrentPhotoPath; + + private File createImageFile() throws IOException { + // Create an image file name + String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); + String imageFileName = "JPEG_" + timeStamp + "_"; + File storageDir = Environment.getExternalStoragePublicDirectory( + Environment.DIRECTORY_PICTURES); + File image = File.createTempFile( + imageFileName, /* prefix */ + ".jpg", /* suffix */ + storageDir /* directory */ + ); + + // Save a file: path for use with ACTION_VIEW intents + mCurrentPhotoPath = "file:" + image.getAbsolutePath(); + return image; + } + + static final int REQUEST_TAKE_PHOTO = 1; + + public void dispatchTakePictureIntent() { + Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); + // Ensure that there's a camera activity to handle the intent + if (takePictureIntent.resolveActivity(getPackageManager()) != null) { + // Create the File where the photo should go + File photoFile = null; + try { + photoFile = createImageFile(); + } catch (IOException ex) { + // Error occurred while creating the File + ex.printStackTrace(); + } + // Continue only if the File was successfully created + if (photoFile != null) { + takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, + Uri.fromFile(photoFile)); + startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO); + } + } + } + + public void launchMsgSubmit() { + Intent msgSubmitIntent = new Intent(this, SubmitActivity.class); + msgSubmitIntent.putExtra("Message", intentMsg); + startActivityForResult(msgSubmitIntent, 1); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == 1) { + if (resultCode == Activity.RESULT_OK && data != null && data.getStringExtra("SubmitReplyMessage") != null) { + cMRP = new MessageRecvParser(data.getStringExtra("SubmitReplyMessage")); + Toast.makeText(this.getApplicationContext(), data.getStringExtra("SubmitReplyMessage"), Toast.LENGTH_LONG).show(); + Log.d(this.getClass().toString(), "SUBMIT REPLY MESSAGE: " + data.getStringExtra("SubmitReplyMessage")); + + // Passing Intent LatLng + if (cMRP.getStatus().equals("ok")) { + cLng = cMRP.getLng(); //getLat(); // SWAP HERE!!! + cLngDouble = Double.parseDouble(cLng); + cLat = cMRP.getLat(); //getLng(); // SWAP HERE!!! + cLatDouble = Double.parseDouble(cLat); + + // Set up Google Maps initial position + cLatLng = new LatLng(cLngDouble, cLatDouble); + mMap.addMarker(new MarkerOptions().position(cLatLng).title("Guess Place!")); + mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(cLatLng, 16.0f)); + } + + Toast.makeText(getApplicationContext(), "Current LatDouble: " + cLatDouble + "\nCurrent LngDouble: " + cLngDouble, Toast.LENGTH_SHORT).show(); + Log.d(this.getClass().toString(), "Current LatDouble: " + cLatDouble + "\nCurrent LngDouble" + cLngDouble); + + + // Log for Status Check + Log.d(this.getClass().toString(), "STATUS: OK == " + cMRP.getStatus().equals("ok")); + Log.d(this.getClass().toString(), "STATUS: WRONG ANSWER == " + cMRP.getStatus().equals("wrong_answer")); + Log.d(this.getClass().toString(), "STATUS: FINISH == " + cMRP.getStatus().equals("finish")); + + // Check status + if (cMRP.getStatus().equals("ok")) { + Toast.makeText(getApplicationContext(), "You submitted correct answer.", Toast.LENGTH_SHORT).show(); + Log.d(this.getClass().toString(), "REPLY STATUS: CORRECT ANSWER."); + } + else if (cMRP.getStatus().equals("wrong_answer")) { + Toast.makeText(getApplicationContext(), "You submitted wrong answer.", Toast.LENGTH_SHORT).show(); + Toast.makeText(getApplicationContext(), "Please retry.", Toast.LENGTH_SHORT).show(); + Log.d(this.getClass().toString(), "REPLY STATUS: WRONG ANSWER."); + } + else if (cMRP.getStatus().equals("finish")) { + Toast.makeText(getApplicationContext(), "Congratulation! You have finished!", Toast.LENGTH_SHORT).show(); + finish(); + Log.d(this.getClass().toString(), "REPLY STATUS: FINISH."); + } + } + if (resultCode == Activity.RESULT_CANCELED) { + // If there is no result + } + } + } +} diff --git a/app/src/main/java/com/example/erickchandra/tubes1_android/MessageRecvParser.java b/app/src/main/java/com/example/erickchandra/tubes1_android/MessageRecvParser.java new file mode 100644 index 0000000..f42bb48 --- /dev/null +++ b/app/src/main/java/com/example/erickchandra/tubes1_android/MessageRecvParser.java @@ -0,0 +1,41 @@ +package com.example.erickchandra.tubes1_android; + +import org.json.JSONException; +import org.json.JSONObject; + +/** + * Created by erickchandra on 3/26/16. + */ +public class MessageRecvParser { + JSONObject jsonObject; + String recvMsg; + + MessageRecvParser(String recvMsg) { + this.recvMsg = recvMsg; + try { + jsonObject = new JSONObject(recvMsg); + } catch (JSONException e) { + e.printStackTrace(); + } + } + + public String getStatus() { + return jsonObject.optString("status"); + } + + public String getNIM() { + return jsonObject.optString("nim"); + } + + public String getLat() { + return jsonObject.optString("latitude"); + } + + public String getLng() { + return jsonObject.optString("longitude"); + } + + public String getToken() { + return jsonObject.optString("token"); + } +} diff --git a/app/src/main/java/com/example/erickchandra/tubes1_android/MessageSendParser.java b/app/src/main/java/com/example/erickchandra/tubes1_android/MessageSendParser.java new file mode 100644 index 0000000..86f4aa4 --- /dev/null +++ b/app/src/main/java/com/example/erickchandra/tubes1_android/MessageSendParser.java @@ -0,0 +1,39 @@ +package com.example.erickchandra.tubes1_android; + +import org.json.JSONException; +import org.json.JSONObject; + +/** + * Created by erickchandra on 3/26/16. + */ +public class MessageSendParser { + JSONObject jsonObject; + + MessageSendParser(int init, String _nim) { + jsonObject = new JSONObject(); + try { + jsonObject.put("com", "req_loc"); + jsonObject.put("nim", _nim); + } catch (JSONException e) { + e.printStackTrace(); + } + } + + MessageSendParser(String _com, String _nim, String _answer, String _lat, String _lng, String _token) { + jsonObject = new JSONObject(); + try { + jsonObject.put("com", _com); + jsonObject.put("nim", _nim); + jsonObject.put("answer", _answer); + jsonObject.put("longitude", _lng); + jsonObject.put("latitude", _lat); + jsonObject.put("token", _token); + } catch (JSONException e) { + e.printStackTrace(); + } + } + + public String getJSONObjectStr() { + return jsonObject.toString(); + } +} diff --git a/app/src/main/java/com/example/erickchandra/tubes1_android/SendMessageThread.java b/app/src/main/java/com/example/erickchandra/tubes1_android/SendMessageThread.java new file mode 100644 index 0000000..dc372ed --- /dev/null +++ b/app/src/main/java/com/example/erickchandra/tubes1_android/SendMessageThread.java @@ -0,0 +1,26 @@ +package com.example.erickchandra.tubes1_android; + +import java.io.IOException; +import java.net.Socket; + +/** + * Created by erickchandra on 3/25/16. + */ +public class SendMessageThread implements Runnable { + Socket sharedSocket; + String messageOut; + + SendMessageThread(Socket sharedSocket, String messageOut) { + this.sharedSocket = sharedSocket; + this.messageOut = messageOut; + } + + @Override + public void run() { + try { + sharedSocket.getOutputStream().write(messageOut.getBytes()); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/app/src/main/java/com/example/erickchandra/tubes1_android/SubmitActivity.java b/app/src/main/java/com/example/erickchandra/tubes1_android/SubmitActivity.java new file mode 100644 index 0000000..41b58b4 --- /dev/null +++ b/app/src/main/java/com/example/erickchandra/tubes1_android/SubmitActivity.java @@ -0,0 +1,111 @@ +package com.example.erickchandra.tubes1_android; + +import android.app.Activity; +import android.content.Intent; +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.Spinner; +import android.widget.Toast; + +import java.util.ArrayList; +import java.util.List; + +public class SubmitActivity extends AppCompatActivity implements AdapterView.OnItemSelectedListener, AsyncResponse { + public AsyncResponse delegate = null; + + String msgRecv; + + Button submitButton; + Spinner spinnerAnswer; + String selectedItemString; + int selectedItemPosition; + Intent myIntent; + String intentMsgStr; + MessageRecvParser intentMsgMSP; + MessageSendParser msp; + ClientSync cs; + String csRecvMsg; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_submit); + setTitle("Submit Answer"); + + spinnerAnswer = (Spinner) findViewById(R.id.spinner_choose); + // Create an ArrayAdapter using the string array and a default spinner layout + ArrayAdapter adapter = ArrayAdapter.createFromResource(this, R.array.location_array, android.R.layout.simple_spinner_item); + // Specify the layout to use when list of choices appears + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + // Apply the adapter to the spinner + spinnerAnswer.setAdapter(adapter); + spinnerAnswer.setOnItemSelectedListener(this); + + // For Submit Button + submitButton = (Button) findViewById(R.id.button_submit); + submitButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + submitAnswer(); + } + }); + + myIntent = getIntent(); + intentMsgStr = myIntent.getStringExtra("Message"); + intentMsgMSP = new MessageRecvParser(intentMsgStr); + } + + public void submitAnswer() { + msp = new MessageSendParser("answer", intentMsgMSP.getNIM(), getPlaceCodeStr(), intentMsgMSP.getLat(), intentMsgMSP.getLng(), intentMsgMSP.getToken()); +// cs.SendAndThenRecvMessage(); +// csRecvMsg = cs.getRecvMsg(); + cs = new ClientSync(this, msp.getJSONObjectStr()); + cs.delegate = this; + cs.execute(); + } + + public String getPlaceCodeStr() { + switch (selectedItemPosition) { + case 0: return "gku_barat"; + case 1: return "gku_timur"; + case 2: return "intel"; + case 3: return "cc_barat"; + case 4: return "cc_timur"; + case 5: return "dpr"; + case 6: return "oktagon"; // PERGANTIAN DARI SUNKEN DENGAN OKTAGON + case 7: return "perpustakaan"; + case 8: return "pau"; + case 9: return "kubus"; + default: return ""; + } + } + + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + // On selecting a spinner item + selectedItemString = parent.getItemAtPosition(position).toString(); + selectedItemPosition = parent.getSelectedItemPosition(); + + // Showing selected spinner item + Toast.makeText(parent.getContext(), "Selected: " + selectedItemString + "\nPosition: " + selectedItemPosition, Toast.LENGTH_LONG).show(); + } + public void onNothingSelected(AdapterView parent) { + // TODO Auto-generated method stub + } + + @Override + public void processFinish(String output) { + msgRecv = output; + + Intent returnIntent = new Intent(); + returnIntent.putExtra("SubmitReplyMessage", msgRecv); + Log.d(this.getClass().toString(), "SUBMIT ACTIVITY: msgRecv: " + msgRecv); + setResult(Activity.RESULT_OK, returnIntent); + finish(); + } +} diff --git a/app/src/main/res/drawable/android_robot.png b/app/src/main/res/drawable/android_robot.png new file mode 100644 index 0000000..2ff47a3 Binary files /dev/null and b/app/src/main/res/drawable/android_robot.png differ diff --git a/app/src/main/res/drawable/arrow_compass.png b/app/src/main/res/drawable/arrow_compass.png new file mode 100644 index 0000000..9ae6f47 Binary files /dev/null and b/app/src/main/res/drawable/arrow_compass.png differ diff --git a/app/src/main/res/drawable/flowdots.gif b/app/src/main/res/drawable/flowdots.gif new file mode 100644 index 0000000..f2eb912 Binary files /dev/null and b/app/src/main/res/drawable/flowdots.gif differ diff --git a/app/src/main/res/drawable/google_logo_animation_small.gif b/app/src/main/res/drawable/google_logo_animation_small.gif new file mode 100644 index 0000000..3d32d67 Binary files /dev/null and b/app/src/main/res/drawable/google_logo_animation_small.gif differ diff --git a/app/src/main/res/drawable/icon_camera.png b/app/src/main/res/drawable/icon_camera.png new file mode 100644 index 0000000..b9e5a5c Binary files /dev/null and b/app/src/main/res/drawable/icon_camera.png differ diff --git a/app/src/main/res/drawable/icon_message.png b/app/src/main/res/drawable/icon_message.png new file mode 100644 index 0000000..76c2a87 Binary files /dev/null and b/app/src/main/res/drawable/icon_message.png differ diff --git a/app/src/main/res/layout-land/activity_maps.xml b/app/src/main/res/layout-land/activity_maps.xml new file mode 100644 index 0000000..e5386c0 --- /dev/null +++ b/app/src/main/res/layout-land/activity_maps.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout-port/activity_maps.xml b/app/src/main/res/layout-port/activity_maps.xml new file mode 100644 index 0000000..f7e6108 --- /dev/null +++ b/app/src/main/res/layout-port/activity_maps.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_finish_fullscreen.xml b/app/src/main/res/layout/activity_finish_fullscreen.xml new file mode 100644 index 0000000..5588bc9 --- /dev/null +++ b/app/src/main/res/layout/activity_finish_fullscreen.xml @@ -0,0 +1,50 @@ + + + + + + + + + + +