usb-serial-for-android

https://github.com/mik3y/usb-serial-for-android

でのgradleの追加の仕方がよくわからなかった。

usb-serial-for-android-v010.jar

をlibsに追加してやろうとしたけどなんかUsbSerialPortが足りなさそうなので最新版にしようとした。

結論:

gradleに

allprojects {
    repositories {
        maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
        mavenCentral()
    }
}

dependency:

compile 'com.hoho.android:usb-serial-for-android:0.2.0-SNAPSHOT@aar'

でいけた。

DialogFragmentでViewPager使って回転時に落ちる問題

回転時にまつわる問題。

DialogFragmentでViewPager使ってるやつだと回転時に

java.lang.IllegalStateException: Activity has been destroyed

で落ちることがある。てかメモリが弱い機種だとすぐ落ちる。

もう

@Override
 public int show(FragmentTransaction transaction, String tag) {
    try {
        return super.show(transaction, tag);
    } catch (IllegalStateException e) {
        // ignore
    }
    return -1;
}

@Override
public void show(FragmentManager manager, String tag) {
    try {
        super.show(manager, tag);
    } catch (IllegalStateException e) {
        // ignore
    }
}

で対応しようとしたけどなんか無理っぽい。

わけわからんなーと思いつつどこで落ちるんだろうと思ってfragmentのサイクルをLOGしてみた

/***
 * Activityに関連付けされた際に一度だけ呼び出される
 */
static final String TAG = "FragmentTest";
@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    Log.d(TAG, "onAttach");
}

/***
 * Fragmentの初期化処理を行う
 */

/***
 * Activityの「onResume」に基づき開始される
 */
@Override
public void onResume() {
    super.onResume();
    Log.d(TAG, "onResume");
}

/***
 * Activityが「onPause」になった場合や、Fragmentが変更更新されて操作を受け付けなくなった場合に呼び出される
 */

/***
 * フォアグラウンドでなくなった場合に呼び出される
 */
@Override
public void onStop() {
    super.onStop();
    Log.d(TAG, "onStop");
}

/***
 * Fragmentの内部のViewリソースの整理を行う
 */
@Override
public void onDestroyView() {
    super.onDestroyView();
    Log.d(TAG, "onDestroyView");
}

/***
 * Fragmentが破棄される時、最後に呼び出される
 */
@Override
public void onDestroy() {
    super.onDestroy();
    Log.d(TAG, "onDestroy");
}

なぜか落ちなくなった。

↑の分を足すだけで解決した。

わけわからない・・・・

 

追記:ダメでした

Android Support Repositoryが見つけられない

Gradleのお話

なんか今までサポートライブラリーv4を使うときはEclipseからの継承でlibsにandroid-support-v4.jarを突っ込んで使ってた。

でもGradleはなんかすげースゴイらしくてなんか使おうと思ってたけど不思議なことに↓のようなエラーがずっとでてた。

Error:Could not find com.android.support:support-v4:22.2.0.
Searched in the following locations:
https://repo1.maven.org/maven2/com/android/support/support-v4/22.2.0/support-v4-22.2.0.pom
https://repo1.maven.org/maven2/com/android/support/support-v4/22.2.0/support-v4-22.2.0.jar
Required by:
:MedicalMonitor:unspecified

Please install the Android Support Repository from the Android SDK Manager.
<a href=”openAndroidSdkManager”>Open Android SDK Manager</a>

だが、ここにきてcompile ‘com.roomorama:caldroid:2.1.0’を使う必要があった。

どうもそれにはcom.android.support:support-v4:22.2.0が必要っぽい

さて困ったぞと

結局何が悪かったかというと

task copyLibs(type : Copy) {
    configurations.compile.each {
        from it.toString()
        into 'libs'
        exclude {
            new File('libs', it.name).exists()
        }
    }
}

というタスクがgradleで動いてたのが原因だった。

なんかよくわからないままコピペ放置されていたプログラムだ。

これを消すとすんなり動いた。

動いたのだから深いことは考えない。

しかしこれのせいで3日も無駄になってしまった。

頑張って早く作らないと

AsyncTaskLoaderの挙動

AsyncTaskLoaderについてはさんざん悩まされてきたけど、また再びバグって大変だった。

具体的にはActivityから離れて戻ってきた時にonStartLoading→onLoadFinishedが流れるんだけど、何度も何度もActivityに戻るときに同じ処理が流れてて本当に謎でバグの原因だった。

今使っている自作のAsyncLoaderはJSONを元に使っているのだけど、JSONに変換出来ない場合がある。その時はNULLを流すようにしてるんだけどそれがイケなかったようだ。

@Override
public void deliverResult(JSONObject data) {
    if (isReset()) {
        if (this.result != null) {
            //this.result = null;
        }
        return;
    }

    String strerror="{\"" + "this" + "\"" + ":" +"\""+"ReConnect" + "\""+"}";//{"this":"FailConnect"}
    JSONObject aaa=new JSONObject();
    try{aaa=new JSONObject(strerror);}catch (JSONException e2) {}//なんかめんどくさい


    this.result = aaa;

    super.deliverResult(data);
    if (isStarted()) {
    }
}

@Override
protected void onStartLoading() {
    if (this.result != null) {
        deliverResult(this.result);
    }
    if (takeContentChanged() || this.result == null) {
        forceLoad();
    }
}

取り敢えず荒療治でむりやりResultに値をセットして、復帰時の処理を判別できるようにした。

これなら取り敢えずは forceLoad()に行かずにそのままdeliverResultしてくれる。

ゼッタイ何か問題ある気がするけど取り敢えず様子見。

 

取り敢えず今のところの挙動として、

なんか違うURLかつLoaderIDが同じでgetSupportLoaderManager().restartLoaderをすると、最初したURLのLoaderでonLoadFinishedが流れる。

でもなんか同じURLで同じLoaderIDだったらonLoadFinishedは一度しか流れず正常に行く感じ

つまりひとつのURLに1つのLoaderIDを指定すれば大丈夫ってこと???

謎だー

スクリーンのON時にFragmentDialog作成

スクリーンをOnにした時のイベントを取得した時

private BroadcastReceiver screenStatusReceiver = new BroadcastReceiver() {//スクリーンのON/OFFイベントを検出する
    @Override
    public void onReceive(Context context, Intent intent) {
        // Receive screen off


        if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
            // OFF
            LoginTimeSave();


        }
        if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
            // ON fragmentだとIllegalStateExceptionでる
            LoginReqired();
        }
    }
};

でやるのだが、この時fragmentを生成しようとすると

java.lang.RuntimeException: Error receiving broadcast Intent { act=android.intent.action.SCREEN_ON flg=0x50000010 } in com.medicalmonitor.MainActivity$20@451d81d0

Caused by: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState

が出て強制終了してしまう。

onSaveInstanceState()はonPause()/onStop()の直後に発生するから、onPause()からonResume()の間に操作しなければ解決する。

結果的にcommitAllowingStateLossを使うことで解決できた。

でもこれActivityが再構築された時とかにおかしくなるからあんまり使わないほうがいいらしい。まぁとりあえずこれで様子見。

Fragment prev = getSupportFragmentManager().findFragmentByTag("DigLogin");
if (prev == null) {//これがないと何個もダイアログ作られる

    //Dialog_LoginReqired newFragment = Dialog_LoginReqired.newInstance(null);
    //newFragment.show(getSupportFragmentManager(), "DigLogin");//DigDrugはTAG findFragmentByTagでみつけれる

    Dialog_LoginReqired dialog = new Dialog_LoginReqired();
    //dialog.show(getSupportFragmentManager(), null);
    FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
    ft.add(dialog, "DigLogin");
    ft.commitAllowingStateLoss();

}

ListViewのクセ

なにかと使い勝手が悪いandroidのListviewだけど、globals関数を想定した使い方のメモ。

まず取り敢えず一番良さそうなのがMainActivityに

 ArrayList<CustomData> localobjects = new ArrayList<CustomData>();//ローカルオブジェクト
 globals.kanjaAdapter = new CustomItemView(this, 0,localobjects);

を定義してadapterを形成。

そしてfragmentに

ListView lv;
lv.setAdapter(globals.kanjaAdapter);

でListviewにアダプターを設定する。

なんかfragmentにadapterを形成してもいいけど、その場合fragmentから離れた場合にあたりまえだけど初期化されるから内容を保持したい場合はやっぱりMainActivityに書くのがよさそう

ちなみによくわからないけどglobals.kanjaobjectsとかobjectにはグローバルは使えなさそう。

なにかとilligalCatchClass的な例外が出る。

でもこれも違うクラスからglobals.kanjaAdapter.addしたりすると動いたり、よくわからない。

とりあえずlocalで定義するのが良さそうだ。

 

追記:globalsで定義して全然問題なかった。

他の処理でglobalsのobjectを操作してたからおかしくなってただけだった。

上の記事はあんまりどころか全く参考にならないかも

DialogでViewpager

DialogでViewpagerを使おうとした時のお話。

どうもAlertDialog.Builderを今まで使ってたのだが、とうとう不具合?が出てしまった。

今回DialogでViewpagerを使うために他のやつのように

// カスタムビューを設定
      LayoutInflater inflater = LayoutInflater.from(this);
      final View layout = inflater.inflate(R.layout.dialog_kusuri, (ViewGroup) findViewById(R.id.layout_root));

のDialogのlayoutからlayout.findViewById(R.id.pager)をViewPagerにしたのだが、何故かViewが見つからなくて落ちてしまう。(java.lang.IllegalStateException: Fragment does not have a view)

頑張ってpagerを見つけようとしたけどダメだった。

これは何でだろーってしらべた結果どうもAlertDialog.Builderを使ってるのが悪かったらしい

これからはDialogFragmentを使えとのこと。

ためしにDialogFragmentを使うとすんなり動いた。

PageDialog.java

package com.medicalmonitor;

import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v4.view.ViewPager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;

public class PageDialog extends DialogFragment {
    private final static String KEY_URL = "diaog_key_urls";

    public static PageDialog newInstance(String[] urls) {
        PageDialog fragment = new PageDialog();
        Bundle args = new Bundle();
        args.putStringArray(KEY_URL, urls);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        final View view = inflater.inflate(R.layout.dialog_kusuri, container, false);
        String[] urls = getArguments().getStringArray(KEY_URL);

        final ViewPager viewPager = (ViewPager) view.findViewById(R.id.pager2);
        final Dialog_Fragment_pager adapter = new Dialog_Fragment_pager(getChildFragmentManager());
        viewPager.setAdapter(adapter);

        getDialog().getWindow().requestFeature(Window.FEATURE_NO_TITLE);
        getDialog().setCanceledOnTouchOutside(true);

        return view;
    }
}

Dialog_Fragment_pager.java

package com.medicalmonitor;


import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.view.ViewGroup;

import java.util.HashMap;
import java.util.Map;


public class Dialog_Fragment_pager
  extends FragmentPagerAdapter {//FragmentStatePagerAdapterはメモリ破棄する、保存するならこっち
   private Map<Integer, String> mFragmentTags;
    private FragmentManager mFragmentManager;
  public Dialog_Fragment_pager(FragmentManager fm) {
    super(fm);
    mFragmentManager = fm;
    mFragmentTags = new HashMap<Integer, String>();
  }
  private static final String ARG_SECTION_NUMBER = "section_number";

  @Override
  public Fragment getItem(int position) {
     return frag_dialog_pager_main.newInstance(position);
  }

  @Override
  public int getCount() {
    return 3;
  }

  @Override
  public CharSequence getPageTitle(int position) {
     
    return "Page " + position;
  }
  @Override
  public Object instantiateItem(ViewGroup container, int position) {
      Object obj = super.instantiateItem(container, position);
      if (obj instanceof Fragment) {
          // record the fragment tag here.
          Fragment f = (Fragment) obj;
          String tag = f.getTag();
          mFragmentTags.put(position, tag);
      }
      return obj;
  }

  public Fragment getFragment(int position) {
      String tag = mFragmentTags.get(position);
      if (tag == null)
          return null;
      return mFragmentManager.findFragmentByTag(tag);
  }

}

frag_dialog_pager_main.java

package com.medicalmonitor;

import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.Loader;
import android.text.TextUtils;
import android.text.format.Time;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import org.json.JSONObject;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

public class frag_dialog_pager_main extends Fragment {
   private static final String KEY_URL_STR = "urlStr";
   private static final int KANJANUM = 0;
   private static final int UNFINISHEDNUM = 1;
   private static final int FINISHEDNUM = 2;
   float juuryou=0;
   int mNum;//画面の番号1はunfinished2はfinishedとか
    final List<CustomData> localobjects = new ArrayList<CustomData>();//ローカルオブジェクト
   static frag_dialog_pager_main newInstance(int num) {
      frag_dialog_pager_main f = new frag_dialog_pager_main();

        // Supply num input as an argument.
        Bundle args = new Bundle();
        args.putInt("num", num);
        f.setArguments(args);

        return f;
    }

    /**
     * When creating, retrieve this instance's number from its arguments.
     */
   @Override
   public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      mNum = getArguments() != null ? getArguments().getInt("num") : 1;
   }

   
    AlertDialog.Builder alertDlg2;
     private AlertDialog m_dlg;
      View rootView;
       ListView listView;
         //グローバル変数
          Globals globals;
         ArrayAdapter adapter;

            Fragment_pick pick = new Fragment_pick();
           // MainActivity activity = (MainActivity) getActivity();  
            MainActivity main = new MainActivity();
   @Override
   public View onCreateView(
      LayoutInflater inflater, 
      ViewGroup container, 
      Bundle savedInstanceState) {
      globals = (Globals) getActivity().getApplication();
      getActivity().getApplicationContext();

      return inflater.inflate(R.layout.frag_picking_finished, container, false);
   }
   @Override
    public void onStop() {
       //ImageCache.deleteAll(getCacheDir());
       super.onStop();
   
    }
   @Override
    public void onStart() {
       super.onStart();
       
   }
}

↑無駄な処理多いなー

ダイアログ使うときは

PageDialog newFragment = PageDialog.newInstance(null);
newFragment.show(getSupportFragmentManager(), TAG);

だけ!

まぁ動いてよかったー

Activityのローカル変数の保存について

自分のActivityをintentでほかのActivityに渡して再び戻った時にそのActivityが生きてる保証は全くない。

今開発のアプリではglobal関数に結構大事なもの(JSONとか)を保存さしてるのでActivityに戻った時に返り値がnullになってることがしばしばある(Acrobatとかメモリを食うものだとほぼ死ぬ)

なので戻った時にどうしても必要なのはonSaveInstanceStateで保存させるのだけどJSONはどうやって保存させようとなったらStackOverFlowにあったからメモ代わりにおいとく

//保存
if(globals.StockJSON!=null) outState.putString("StockJSON", globals.StockJSON.toString());
try {//JSON READ
    String jsonString = mSavedInstanceState.getString("StockJSON");
    globals.StockJSON = new JSONObject(jsonString);
} catch (JSONException e) {
    e.printStackTrace();
}catch (Exception e) {
    e.printStackTrace();
}

まぁいつもながら効率は考えてない。

しかしまぁAndroid Studioも慣れてきたといえまだまだ知らない事ばかりだ。

Gradleとか全然わからない。

fragmentの回転時の処理

fragmentで回転させると当然activityが初期化されるけど、fragmentでインスタンスを保存してonCreateで復元させるときなぜかnullpointさんがでた。

onCreateでgetActivityはできないようだ。

仕方ないのでonCreateでBundleに保存さしてonStartで復元させるというやり方にした。

たぶんスマートではないけど動いたのでいいとする。

Bundle mSavedInstanceState;
@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    Log.d("Lifecycle", "onSaveInstanceState()");

    //インスタンスの保存

    outState.putString("kusuriInfo", kusuriInfo.getText().toString());
}

@Override
 public void onStart() {
 super.onStart();
if(mSavedInstanceState!=null){//回転時の復元処理
    String value = mSavedInstanceState.getString("kusuriInfo");
    kusuriInfo.setText(value);

}
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    //ここだけだとnullpointなるからonstartで復元させる
    mSavedInstanceState=savedInstanceState;

}

GalaxyJのバグ

現在在庫管理システムを絶賛製作中だけど、機種によってUSBバーコードリーダの挙動がことなるから困ってしまう。GalaxyJのUsbManagerがnullを返す問題はSamsungにバグレポートを送るか悩み中。

バーコードリーダが接続されてるかのclassだけどこれでいいものだろうか

boolean USBexist=false;
boolean exist=false;
      boolean returnExist=false;
UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
   HashMap<String, UsbDevice> map=manager.getDeviceList();
   Iterator<UsbDevice> it = map.values().iterator();
   while(it.hasNext()) {
       UsbDevice device = it.next();
          USBexist=true;
          returnExist=true;
       Log.d("USB", "NAME =" + device.getDeviceName());
       // use device info
      //if(showMessage)Toast.makeText(getApplicationContext(), "USBが接続されました。", Toast.LENGTH_SHORT).show();
     }

      if(newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO) {
          exist=true;
          returnExist=true;
          //if(showMessage)Toast.makeText(getApplicationContext(), "バーコードスキャナーが接続されました。", Toast.LENGTH_SHORT).show();
          //((InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE)).showInputMethodPicker();
          //Toast.makeText(this, "Barcode Scanner detected. Please turn OFF Hardware/Physical keyboard to enable softkeyboard to function.", Toast.LENGTH_LONG).show();
      }