github.com/olivere/camlistore@v0.0.0-20140121221811-1b7ac2da0199/clients/android/src/org/camlistore/CamliActivity.java (about)

     1  /*
     2  Copyright 2011 Google Inc.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8       http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15   */
    16  
    17  package org.camlistore;
    18  
    19  import android.app.Activity;
    20  import android.app.AlertDialog;
    21  import android.content.ComponentName;
    22  import android.content.Context;
    23  import android.content.Intent;
    24  import android.content.ServiceConnection;
    25  import android.content.SharedPreferences;
    26  import android.os.Bundle;
    27  import android.os.Handler;
    28  import android.os.IBinder;
    29  import android.os.Looper;
    30  import android.os.MessageQueue;
    31  import android.os.RemoteException;
    32  import android.util.Log;
    33  import android.view.Menu;
    34  import android.view.MenuItem;
    35  import android.view.View;
    36  import android.view.View.OnClickListener;
    37  import android.widget.Button;
    38  import android.widget.ProgressBar;
    39  import android.widget.TextView;
    40  import android.widget.Toast;
    41  
    42  public class CamliActivity extends Activity {
    43      private static final String TAG = "CamliActivity";
    44      private static final int MENU_SETTINGS = 1;
    45      private static final int MENU_STOP = 2;
    46      private static final int MENU_STOP_DIE = 3;
    47      private static final int MENU_UPLOAD_ALL = 4;
    48      private static final int MENU_VERSION = 5;
    49  
    50      private IUploadService mServiceStub = null;
    51      private IStatusCallback mCallback = null;
    52  
    53      // Status text update state, since it updates too quickly to do it the naive way.
    54      private long mLastStatusUpdate = 0; // time in millis we lasted updated the screen
    55      private String mStatusTextCurrent = null; // what the screen says
    56      private String mStatusTextWant = null; // what the service wants it to say
    57  
    58      private final Handler mHandler = new Handler();
    59  
    60      private final MessageQueue.IdleHandler mIdleHandler = new MessageQueue.IdleHandler() {
    61          @Override
    62          public boolean queueIdle() {
    63              if (mStatusTextCurrent != mStatusTextWant) {
    64                  TextView textStats = (TextView) findViewById(R.id.textStats);
    65                  mLastStatusUpdate = System.currentTimeMillis();
    66                  mStatusTextCurrent = mStatusTextWant;
    67                  textStats.setText(mStatusTextWant);
    68              }
    69              return true;
    70          }
    71      };
    72  
    73      private final ServiceConnection mServiceConnection = new ServiceConnection() {
    74  
    75          @Override
    76          public void onServiceConnected(ComponentName name, IBinder service) {
    77              mServiceStub = IUploadService.Stub.asInterface(service);
    78              Log.d(TAG, "Service connected, registering callback " + mCallback);
    79  
    80              try {
    81                  mServiceStub.registerCallback(mCallback);
    82              } catch (RemoteException e) {
    83                  e.printStackTrace();
    84              }
    85          }
    86  
    87          @Override
    88          public void onServiceDisconnected(ComponentName name) {
    89              Log.d(TAG, "Service disconnected");
    90              mServiceStub = null;
    91          };
    92      };
    93  
    94      @Override
    95      public void onCreate(Bundle savedInstanceState) {
    96          super.onCreate(savedInstanceState);
    97          setContentView(R.layout.main);
    98  
    99          Looper.myQueue().addIdleHandler(mIdleHandler);
   100          final Button buttonToggle = (Button) findViewById(R.id.buttonToggle);
   101  
   102          final TextView textStatus = (TextView) findViewById(R.id.textStatus);
   103          final TextView textStats = (TextView) findViewById(R.id.textStats);
   104          final TextView textBlobsRemain = (TextView) findViewById(R.id.textBlobsRemain);
   105          final TextView textUploadStatus = (TextView) findViewById(R.id.textUploadStatus);
   106          final ProgressBar progressBytes = (ProgressBar) findViewById(R.id.progressByteStatus);
   107          final ProgressBar progressFile = (ProgressBar) findViewById(R.id.progressFileStatus);
   108  
   109          buttonToggle.setOnClickListener(new OnClickListener() {
   110              @Override
   111              public void onClick(View btn) {
   112                  Log.d(TAG, "button click!  text=" + buttonToggle.getText());
   113                  if (getString(R.string.pause).equals(buttonToggle.getText())) {
   114                      try {
   115                          Log.d(TAG, "Pausing..");
   116                          mServiceStub.pause();
   117                      } catch (RemoteException e) {
   118                      }
   119                  } else if (getString(R.string.resume).equals(buttonToggle.getText())) {
   120                      try {
   121                          Log.d(TAG, "Resuming..");
   122                          mServiceStub.resume();
   123                      } catch (RemoteException e) {
   124                      }
   125                  }
   126              }
   127          });
   128  
   129          mCallback = new IStatusCallback.Stub() {
   130              private volatile int mLastBlobsUploadRemain = 0;
   131              private volatile int mLastBlobsDigestRemain = 0;
   132  
   133              @Override
   134              public void logToClient(String stuff) throws RemoteException {
   135                  // TODO Auto-generated method stub
   136              }
   137  
   138              @Override
   139              public void setUploading(final boolean uploading) throws RemoteException {
   140                  mHandler.post(new Runnable() {
   141                      @Override
   142                      public void run() {
   143                          if (uploading) {
   144                              buttonToggle.setText(R.string.pause);
   145                              textStatus.setText(R.string.uploading);
   146                          } else if (mLastBlobsDigestRemain > 0) {
   147                              buttonToggle.setText(R.string.pause);
   148                              textStatus.setText(R.string.digesting);
   149                          } else {
   150                              buttonToggle.setText(R.string.resume);
   151                              int stepsRemain = mLastBlobsUploadRemain + mLastBlobsDigestRemain;
   152                              textStatus.setText(stepsRemain > 0 ? "Paused." : "Idle.");
   153                          }
   154                      }
   155                  });
   156              }
   157  
   158              @Override
   159              public void setFileStatus(final int done, final int inFlight, final int total) throws RemoteException {
   160                  mHandler.post(new Runnable() {
   161                      @Override
   162                      public void run() {
   163                          boolean finished = (done == total && mLastBlobsDigestRemain == 0);
   164                          buttonToggle.setEnabled(!finished);
   165                          progressFile.setMax(total);
   166                          progressFile.setProgress(done);
   167                          progressFile.setSecondaryProgress(done + inFlight);
   168                          if (finished) {
   169                              buttonToggle.setText(getString(R.string.pause_resume));
   170                          }
   171  
   172                          StringBuilder sb = new StringBuilder(40);
   173                          sb.append("Files to upload: ").append(total - done);
   174                          textBlobsRemain.setText(sb.toString());
   175                      }
   176                  });
   177              }
   178  
   179              @Override
   180              public void setByteStatus(final long done, final int inFlight, final long total) throws RemoteException {
   181                  mHandler.post(new Runnable() {
   182                      @Override
   183                      public void run() {
   184                          // setMax takes an (signed) int, but 2GB is a totally
   185                          // reasonable upload size, so use units of 1KB instead.
   186                          progressBytes.setMax((int) (total / 1024L));
   187                          progressBytes.setProgress((int) (done / 1024L));
   188                          progressBytes.setSecondaryProgress(progressBytes.getProgress() + inFlight / 1024);
   189                      }
   190                  });
   191              }
   192  
   193              @Override
   194              public void setUploadStatusText(final String text) throws RemoteException {
   195                  mHandler.post(new Runnable() {
   196                      @Override
   197                      public void run() {
   198                          textUploadStatus.setText(text);
   199                      }
   200                  });
   201              }
   202  
   203              @Override
   204              public void setUploadStatsText(final String text) throws RemoteException {
   205                  // We were getting these status updates so quickly that the calls to TextView.setText
   206                  // were consuming all CPU on the main thread and it was stalling the main thread
   207                  // for seconds. Ridiculous. So instead, only update this every 5 milliseconds,
   208                  // otherwise wait for the looper to be idle to update it.
   209                  mHandler.post(new Runnable() {
   210                      @Override
   211                      public void run() {
   212                          mStatusTextWant = text;
   213                          long now = System.currentTimeMillis();
   214                          if (mLastStatusUpdate < now - 5) {
   215                              mStatusTextCurrent = mStatusTextWant;
   216                              textStats.setText(mStatusTextWant);
   217                              mLastStatusUpdate = System.currentTimeMillis();
   218                          }
   219                      }
   220                  });
   221              }
   222          };
   223  
   224      }
   225  
   226      @Override
   227      protected void onActivityResult(int requestCode, int resultCode, Intent data) {
   228          super.onActivityResult(requestCode, resultCode, data);
   229  
   230          // TODO: picking files/photos to upload?
   231      }
   232  
   233      @Override
   234      protected void onDestroy() {
   235          // TODO Auto-generated method stub
   236          super.onDestroy();
   237      }
   238  
   239      @Override
   240      public boolean onCreateOptionsMenu(Menu menu) {
   241          super.onCreateOptionsMenu(menu);
   242  
   243          MenuItem uploadAll = menu.add(Menu.NONE, MENU_UPLOAD_ALL, 0, R.string.upload_all);
   244          uploadAll.setIcon(android.R.drawable.ic_menu_upload);
   245  
   246          MenuItem stop = menu.add(Menu.NONE, MENU_STOP, 0, R.string.stop);
   247          stop.setIcon(android.R.drawable.ic_menu_close_clear_cancel);
   248  
   249          MenuItem stopDie = menu.add(Menu.NONE, MENU_STOP_DIE, 0, R.string.stop_die);
   250          stopDie.setIcon(android.R.drawable.ic_menu_close_clear_cancel);
   251  
   252          MenuItem settings = menu.add(Menu.NONE, MENU_SETTINGS, 0, R.string.settings);
   253          settings.setIcon(android.R.drawable.ic_menu_preferences);
   254  
   255          menu.add(Menu.NONE, MENU_VERSION, 0, R.string.version);
   256          return true;
   257      }
   258  
   259      @Override
   260      public boolean onOptionsItemSelected(MenuItem item) {
   261          switch (item.getItemId()) {
   262          case MENU_STOP:
   263              try {
   264                  if (mServiceStub != null) {
   265                      mServiceStub.stopEverything();
   266                  }
   267              } catch (RemoteException e) {
   268                  // Ignore.
   269              }
   270              break;
   271          case MENU_STOP_DIE:
   272              System.exit(1);
   273          case MENU_SETTINGS:
   274              SettingsActivity.show(this);
   275              break;
   276          case MENU_VERSION:
   277              Toast.makeText(this, "camput version: " + ((UploadApplication) getApplication()).getCamputVersion(), Toast.LENGTH_LONG).show();
   278              break;
   279          case MENU_UPLOAD_ALL:
   280              Intent uploadAll = new Intent(UploadService.INTENT_UPLOAD_ALL);
   281              uploadAll.setClass(this, UploadService.class);
   282              Log.d(TAG, "Starting upload all...");
   283              startService(uploadAll);
   284              Log.d(TAG, "Back from upload all...");
   285              break;
   286          }
   287          return true;
   288      }
   289  
   290      @Override
   291      protected void onPause() {
   292          super.onPause();
   293          try {
   294              if (mServiceStub != null)
   295                  mServiceStub.unregisterCallback(mCallback);
   296          } catch (RemoteException e) {
   297              // Ignore.
   298          }
   299          if (mServiceConnection != null) {
   300              unbindService(mServiceConnection);
   301          }
   302      }
   303  
   304      @Override
   305      protected void onResume() {
   306          super.onResume();
   307  
   308          SharedPreferences sp = getSharedPreferences(Preferences.NAME, 0);
   309          try {
   310              HostPort hp = new HostPort(sp.getString(Preferences.HOST, ""));
   311              if (!hp.isValid()) {
   312                  // Crashes oddly in some Android Instrumentation thing if
   313                  // uncommented:
   314                  // SettingsActivity.show(this);
   315                  // return;
   316              }
   317          } catch (NumberFormatException enf) {
   318              AlertDialog.Builder builder = new AlertDialog.Builder(this);
   319              builder.setMessage("Server should be of form [https://]<host[:port]>")
   320                      .setTitle("Invalid Setting");
   321              AlertDialog alert = builder.create();
   322              alert.show();
   323          }
   324  
   325          bindService(new Intent(this, UploadService.class), mServiceConnection, Context.BIND_AUTO_CREATE);
   326  
   327          Intent intent = getIntent();
   328          String action = intent.getAction();
   329          Log.d(TAG, "onResume; action=" + action);
   330  
   331          if (Intent.ACTION_SEND.equals(action) || Intent.ACTION_SEND_MULTIPLE.equals(action)) {
   332              Intent serviceIntent = new Intent(intent);
   333              serviceIntent.setClass(this, UploadService.class);
   334              startService(serviceIntent);
   335              setIntent(new Intent(this, CamliActivity.class));
   336          } else {
   337              Log.d(TAG, "Normal CamliActivity viewing.");
   338          }
   339      }
   340  }